diff --git a/.all-contributorsrc b/.all-contributorsrc
new file mode 100644
index 0000000000..27f1209d73
--- /dev/null
+++ b/.all-contributorsrc
@@ -0,0 +1,3164 @@
+{
+ "projectName": "bids-specification",
+ "projectOwner": "bids-standard",
+ "repoType": "github",
+ "repoHost": "https://github.com",
+ "files": [
+ "README.md"
+ ],
+ "imageSize": 100,
+ "commit": true,
+ "commitConvention": "none",
+ "contributors": [
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "aaron_oliver-taylor",
+ "name": "Aaron Oliver-Taylor"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/3460267?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "adam2392",
+ "name": "Adam Li",
+ "profile": "https://adam2392.github.io/"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/7869017?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "agt24",
+ "name": "Adam Thomas",
+ "profile": "https://cmn.nimh.nih.gov/dsst"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "adeen_flinker",
+ "name": "Adeen Flinker",
+ "profile": "http://flinkerlab.org"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/29738718?v=4"
+ ],
+ "contributions": [
+ "design",
+ "code"
+ ],
+ "login": "adswa",
+ "name": "Adina S. Wagner",
+ "profile": "https://www.adina-wagner.com"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/9632322?v=4"
+ ],
+ "contributions": [
+ "question",
+ "doc",
+ "data",
+ "ideas",
+ "code"
+ ],
+ "login": "agahkarakuzu",
+ "name": "Agah Karakuzu",
+ "profile": "https://agahkarakuzu.github.io"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/4313908?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "AkiNikolaidis",
+ "name": "Aki Nikolaidis",
+ "profile": "https://github.com/AkiNikolaidis"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "alberto_lazari",
+ "name": "Alberto Lazari"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/2774448?v=4",
+ "contributions": [
+ "bug",
+ "code",
+ "test"
+ ],
+ "login": "adelavega",
+ "name": "Alejandro de la Vega",
+ "profile": "https://github.com/adelavega"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/44503635?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "alegiac95",
+ "name": "Alessio Giacomel",
+ "profile": "https://github.com/alegiac95"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/13473576?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "tool",
+ "code"
+ ],
+ "login": "alexrockhill",
+ "name": "Alex Rockhill",
+ "profile": "https://github.com/alexrockhill"
+ },
+ {
+ "contributions": [
+ "code",
+ "bug"
+ ],
+ "login": "alexander_jones",
+ "name": "Alexander Jones"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/8641279?v=4",
+ "contributions": [
+ "bug",
+ "code",
+ "doc",
+ "question"
+ ],
+ "login": "alexlicohen",
+ "name": "Alexander L. Cohen",
+ "profile": "https://bchcohenlab.com"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "alexander_von_lautz",
+ "name": "Alexander von Lautz"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/161052?v=4",
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "login": "agramfort",
+ "name": "Alexandre Gramfort",
+ "profile": "http://alexandre.gramfort.net"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "alexandre_hutton",
+ "name": "Alexandre Hutton"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/24808663?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "alexandreroutier",
+ "name": "Alexandre Routier",
+ "profile": "https://github.com/alexandreroutier"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/12564433?v=4",
+ "contributions": [
+ "doc",
+ "data"
+ ],
+ "login": "alexfoias",
+ "name": "Alexandru Foias",
+ "profile": "https://github.com/alexfoias"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "ali_khan",
+ "name": "Ali Khan"
+ },
+ {
+ "contributions": [
+ "userTesting"
+ ],
+ "login": "ana_fouto",
+ "name": "Ana Fouto"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/4303816?v=4",
+ "contributions": [
+ "doc",
+ "talk",
+ "code"
+ ],
+ "login": "wanderine",
+ "name": "Anders Eklund",
+ "profile": "https://github.com/wanderine"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "andrea_pigorini",
+ "name": "Andrea Pigorini"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "andrew_hoopes",
+ "name": "Andrew Hoopes"
+ },
+ {
+ "contributions": [
+ "userTesting"
+ ],
+ "login": "andrew_jahn",
+ "name": "Andrew Jahn"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/2618447?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "apjanke",
+ "name": "Andrew Janke",
+ "profile": "http://apjanke.net"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/562525?v=4"
+ ],
+ "contributions": [
+ "code",
+ "bug"
+ ],
+ "login": "anibalsolon",
+ "name": "Anibal Sólon",
+ "profile": "https://anibalsolon.com/"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/28850131?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "bendhouseart",
+ "name": "Anthony Galassi",
+ "profile": "https://github.com/bendhouseart"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/118582?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "arokem",
+ "name": "Ariel Rokem",
+ "profile": "https://arokem.org/"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "arjen_stolk",
+ "name": "Arjen Stolk"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1872705?v=4",
+ "contributions": [
+ "doc",
+ "example",
+ "ideas"
+ ],
+ "login": "arnodelorme",
+ "name": "Arnaud Delorme",
+ "profile": "https://www.arnauddelorme.com"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/49677712?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "arnaudmarcoux",
+ "name": "Arnaud Marcoux",
+ "profile": "https://github.com/arnaudmarcoux"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/10297203?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "infra",
+ "code"
+ ],
+ "login": "Arshitha",
+ "name": "Arshitha Basavaraj",
+ "profile": "https://github.com/Arshitha"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/816777?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "ashgillman",
+ "name": "Ashley G. Gillman",
+ "profile": "https://github.com/ashgillman"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/14014329?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "drmowinckels",
+ "name": "Athanasia Monika Mowinckel",
+ "profile": "https://github.com/drmowinckels"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "aysegul_gunduz",
+ "name": "Aysegul Gunduz"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "azeez_adebimpe",
+ "name": "Azeez Adebimpe"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "b_nolan_nichols",
+ "name": "B. Nolan Nichols"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "balint_kincses",
+ "name": "Balint Kincses"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/6898909?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "musicinmybrain",
+ "name": "Benjamin Beasley",
+ "profile": "https://github.com/musicinmybrain"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "benjamin_dichter",
+ "name": "Benjamin Dichter"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "benjamin_gagl",
+ "name": "Benjamin Gagl"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/234454?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "bthirion",
+ "name": "Bertrand Thirion",
+ "profile": "https://github.com/bthirion"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/58030?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "voytek",
+ "name": "Bradley Voytek",
+ "profile": "https://www.voyteklab.com/"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "brett_l_foster",
+ "name": "Brett L. Foster"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "brian_a_wandell",
+ "name": "Brian A. Wandell"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "brian_n_lundstrom",
+ "name": "Brian N. Lundstrom"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/5374264?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "cmaumet",
+ "name": "Camille Maumet",
+ "profile": "https://camillemaumet.com/"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "carlo_miniussi",
+ "name": "Carlo Miniussi"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "chloé_pasturel",
+ "name": "Chloé Pasturel"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "chris_benjamin",
+ "name": "Chris Benjamin"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "chris_gahnström",
+ "name": "Chris Gahnström"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/1839645?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "ideas",
+ "code"
+ ],
+ "login": "choldgraf",
+ "name": "Chris Holdgraf",
+ "profile": "http://chrisholdgraf.com"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/238759?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code",
+ "question",
+ "ideas",
+ "fundingFinding",
+ "talk",
+ "blog",
+ "example",
+ "plugin"
+ ],
+ "login": "chrisgorgo",
+ "name": "Chris J. Gorgolewski",
+ "profile": "http://chrisgorgolewski.org"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/8930807?v=4",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "neurolabusc",
+ "name": "Chris Rorden",
+ "profile": "https://www.mricro.com"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "christian_büchel",
+ "name": "Christian Büchel"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/950524?v=4"
+ ],
+ "contributions": [
+ "code",
+ "doc"
+ ],
+ "login": "TheChymera",
+ "name": "Christian Horea",
+ "profile": "http://www.chymera.eu"
+ },
+ {
+ "contributions": [
+ "data",
+ "doc",
+ "ideas",
+ "infra",
+ "tool"
+ ],
+ "login": "christinerogers",
+ "name": "Christine Rogers"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/2011934?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "ChristophePhillips",
+ "name": "Christophe Phillips",
+ "profile": "https://www.uliege.be/cms/c_9054334/en/directory/?uid=U016440"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "christopher_j_honey",
+ "name": "Christopher J. Honey",
+ "profile": "https://www.honeylab.org"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/83442?v=4"
+ ],
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "design",
+ "example",
+ "ideas",
+ "plugin",
+ "review",
+ "tool",
+ "talk",
+ "data",
+ "eventOrganizing",
+ "maintenance"
+ ],
+ "login": "effigies",
+ "name": "Christopher J. Markiewicz",
+ "profile": "https://github.com/effigies"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "christopher_lee-messer",
+ "name": "Christopher Lee-Messer"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "clara_moreau",
+ "name": "Clara Moreau"
+ },
+ {
+ "contributions": [
+ "doc",
+ "ideas"
+ ],
+ "login": "clint_hansen",
+ "name": "Clint Hansen"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/4772878?v=4",
+ "contributions": [
+ "question",
+ "blog",
+ "doc",
+ "design",
+ "example",
+ "eventOrganizing",
+ "ideas",
+ "talk"
+ ],
+ "login": "CPernet",
+ "name": "Cyril Pernet",
+ "profile": "https://cpernet.github.io/"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/25708582?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "DrCyPhi",
+ "name": "Cyrus Eierud",
+ "profile": "https://trendscenter.org/cyrus-eierud/"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/18649005?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "dasturge",
+ "name": "D. Sturgeon",
+ "profile": "https://dasturge.github.io"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/25011343?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "dlevitas",
+ "name": "Dan Levitas",
+ "profile": "https://github.com/dlevitas"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1873103?v=4",
+ "contributions": [
+ "ideas",
+ "doc",
+ "tool",
+ "plugin",
+ "code",
+ "question"
+ ],
+ "login": "danlurie",
+ "name": "Dan Lurie",
+ "profile": "https://github.com/danlurie"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "daniel_a_handwerker",
+ "name": "Daniel A. Handwerker"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "david_alsop",
+ "name": "David Alsop"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/30025716?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "dboas",
+ "name": "David Boas",
+ "profile": "http://bu.edu/neurophotonics"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/4405343?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "dmgroppe",
+ "name": "David Groppe",
+ "profile": "https://github.com/dmgroppe"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1322467?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "dbkeator",
+ "name": "David Keator",
+ "profile": "https://github.com/dbkeator"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "david_mcalpine",
+ "name": "David McAlpine"
+ },
+ {
+ "contributions": [
+ "doc",
+ "data"
+ ],
+ "login": "david_thomas",
+ "name": "David Thomas"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "dejan_draschkow",
+ "name": "Dejan Draschkow"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/965184?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "dkp",
+ "name": "Dianne Patterson",
+ "profile": "https://profiles.arizona.edu/person/dkp"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/3234522?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "example",
+ "ideas",
+ "question",
+ "code"
+ ],
+ "login": "DimitriPapadopoulos",
+ "name": "Dimitri Papadopoulos Orfanos",
+ "profile": "https://github.com/DimitriPapadopoulos"
+ },
+ {
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "dmitry_petrov",
+ "name": "Dmitry Petrov"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/4977351?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code",
+ "tutorial",
+ "fundingFinding",
+ "ideas"
+ ],
+ "login": "dorahermes",
+ "name": "Dora Hermes",
+ "profile": "https://github.com/dorahermes"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/58177697?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "DorienHuijser",
+ "name": "Dorien Huijser",
+ "profile": "https://github.com/DorienHuijser"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/26312022?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "dngreve",
+ "name": "Douglas N. Greve",
+ "profile": "https://github.com/dngreve"
+ },
+ {
+ "contributions": [
+ "doc",
+ "infra"
+ ],
+ "login": "duncan_macleod",
+ "name": "Duncan Macleod"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/10362238?v=4",
+ "contributions": [
+ "doc",
+ "code",
+ "tool",
+ "ideas"
+ ],
+ "login": "dungscout96",
+ "name": "Dung Truong",
+ "profile": "https://github.com/dungscout96"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/5289368?v=4",
+ "contributions": [
+ "doc",
+ "code",
+ "tool"
+ ],
+ "login": "Shotgunosine",
+ "name": "Dylan Nielson",
+ "profile": "https://github.com/Shotgunosine"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/8819465?v=4",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "eort",
+ "name": "Eduard Ort",
+ "profile": "https://github.com/eort"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "eleonora_marcantoni",
+ "name": "Eleonora Marcantoni"
+ },
+ {
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "login": "elizabeth_bock",
+ "name": "Elizabeth Bock"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/15017191?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "example",
+ "fundingFinding",
+ "ideas",
+ "question",
+ "code"
+ ],
+ "login": "emdupre",
+ "name": "Elizabeth DuPre",
+ "profile": "https://elizabeth-dupre.com"
+ },
+ {
+ "contributions": [
+ "data",
+ "doc"
+ ],
+ "login": "elke_warmerdam",
+ "name": "Elke Warmerdam"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/177225?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "erdalkaraca",
+ "name": "Erdal Karaca",
+ "profile": "https://github.com/erdalkaraca"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/5580023?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "question",
+ "bug",
+ "maintenance",
+ "tool",
+ "ideas",
+ "code"
+ ],
+ "login": "ericearl",
+ "name": "Eric A. Earl",
+ "profile": "https://ericearl.github.io/"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/22006949?v=4",
+ "contributions": [
+ "doc",
+ "data",
+ "userTesting"
+ ],
+ "login": "rikAchten",
+ "name": "Eric Achten",
+ "profile": "https://github.com/rikAchten"
+ },
+ {
+ "contributions": [
+ "doc",
+ "tool"
+ ],
+ "login": "eric_bridgeford",
+ "name": "Eric Bridgeford"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/13203958?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "ideas",
+ "review",
+ "talk",
+ "question",
+ "code"
+ ],
+ "login": "edickie",
+ "name": "Erin W. Dickie",
+ "profile": "http://imaging-genetics.camh.ca/"
+ },
+ {
+ "contributions": [
+ "review",
+ "doc"
+ ],
+ "login": "ethan_blackwood",
+ "name": "Ethan Blackwood"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/902484?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "eduff",
+ "name": "Eugene P. Duff",
+ "profile": "http://eduff.github.io"
+ },
+ {
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "ezequiel_mikulan",
+ "name": "Ezequiel Mikulan"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "felipe_orihuela-espina",
+ "name": "Felipe Orihuela-Espina"
+ },
+ {
+ "contributions": [
+ "question",
+ "doc",
+ "example",
+ "plugin"
+ ],
+ "login": "fidel_alfaro_almagro",
+ "name": "Fidel Alfaro Almagro"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "filip_szczepankiewicz",
+ "name": "Filip Szczepankiewicz"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/37574218?v=4",
+ "contributions": [
+ "doc",
+ "data"
+ ],
+ "login": "filippocastelli",
+ "name": "Filippo Maria Castelli",
+ "profile": "https://github.com/filippocastelli"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/2119795?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code",
+ "design",
+ "example",
+ "ideas",
+ "review",
+ "tool",
+ "eventOrganizing",
+ "fundingFinding",
+ "infra"
+ ],
+ "login": "francopestilli",
+ "name": "Franco Pestilli",
+ "profile": "https://liberalarts.utexas.edu/psychology/faculty/fp4834"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/35307458?v=4"
+ ],
+ "contributions": [
+ "eventOrganizing",
+ "blog",
+ "tutorial",
+ "question",
+ "ideas",
+ "design",
+ "talk",
+ "review",
+ "infra",
+ "content",
+ "projectManagement",
+ "code"
+ ],
+ "login": "franklin-feingold",
+ "name": "Franklin W. Feingold",
+ "profile": "https://github.com/franklin-feingold"
+ },
+ {
+ "contributions": [
+ "doc",
+ "plugin",
+ "example"
+ ],
+ "login": "françois_tadel",
+ "name": "François Tadel"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/16919574?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "gaiarizzo",
+ "name": "Gaia Rizzo",
+ "profile": "http://fair.dei.unipd.it/gaia-rizzo/"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/10375801?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "afni-gangc",
+ "name": "Gang Chen",
+ "profile": "https://github.com/afni-gangc"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/208217?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "GaelVaroquaux",
+ "name": "Gaël Varoquaux",
+ "profile": "https://github.com/GaelVaroquaux"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/1964655?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "ghisvail",
+ "name": "Ghislain Vaillant",
+ "profile": "https://github.com/ghisvail"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/39560621?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "GiacomoBert",
+ "name": "Giacomo Bertazzoli"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "giacomo_guidali",
+ "name": "Giacomo Guidali"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/18688794?v=4",
+ "contributions": [
+ "doc",
+ "data"
+ ],
+ "login": "gmazzamuto",
+ "name": "Giacomo Mazzamuto",
+ "profile": "https://github.com/gmazzamuto"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1112270?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "Gilles86",
+ "name": "Gilles de Hollander",
+ "profile": "https://github.com/Gilles86"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "gio_piantoni",
+ "name": "Gio Piantoni"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "gitte_m._knudsen",
+ "name": "Gitte M. Knudsen"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "giulio_castegnaro",
+ "name": "Giulio Castegnaro"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "giuseppe_gallitto",
+ "name": "Giuseppe Gallitto"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "graham_searle",
+ "name": "Graham Searle"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/7190884?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "mathesong",
+ "name": "Granville J. Matheson",
+ "profile": "http://www.granvillematheson.com"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/4883288?v=4",
+ "contributions": [
+ "doc",
+ "code",
+ "design",
+ "tool"
+ ],
+ "login": "gkiar",
+ "name": "Gregory Kiar",
+ "profile": "https://github.com/gkiar"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/3342083?v=4",
+ "contributions": [
+ "doc",
+ "code",
+ "test"
+ ],
+ "login": "thinknoack",
+ "name": "Gregory Noack",
+ "profile": "https://github.com/thinknoack"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/26314366?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "greydongilmore",
+ "name": "Greydon Gilmore",
+ "profile": "http://greydongilmore.com"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/5950855?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "gllmflndn",
+ "name": "Guillaume Flandin",
+ "profile": "https://www.fil.ion.ucl.ac.uk/team/spm-team/"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "gunnar_schaefer",
+ "name": "Gunnar Schaefer"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/5601007?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "GNilsonne",
+ "name": "Gustav Nilsonne",
+ "profile": "https://nilsonne.net/about/"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "hamish_innes-brown",
+ "name": "Hamish Innes-Brown"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "hanne_d._hansen",
+ "name": "Hanne D. Hansen"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "hanzhang_lu",
+ "name": "Hanzhang Lu"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/13743617?v=4",
+ "contributions": [
+ "doc",
+ "bug"
+ ],
+ "login": "htwangtw",
+ "name": "Hao-Ting Wang",
+ "profile": "https://github.com/htwangtw"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/54403982?v=4",
+ "contributions": [
+ "doc",
+ "ideas",
+ "question"
+ ],
+ "login": "helenacockx",
+ "name": "Helena Cockx",
+ "profile": "https://github.com/helenacockx"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/27774254?v=4",
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "example",
+ "eventOrganizing",
+ "ideas",
+ "projectManagement",
+ "talk",
+ "userTesting"
+ ],
+ "login": "HenkMutsaerts",
+ "name": "Henk Mutsaerts",
+ "profile": "http://www.ExploreASL.org"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "hernando_ombao",
+ "name": "Hernando Ombao"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/27290853?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "Hboni",
+ "name": "Hugo Boniface",
+ "profile": "https://github.com/Hboni"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/22478219?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "ilkayisik",
+ "name": "Ilkay Isik",
+ "profile": "https://github.com/ilkayisik"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/43202449?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "IlonaLipp",
+ "name": "Ilona Lipp",
+ "profile": "https://github.com/IlonaLipp"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/758175?v=4",
+ "contributions": [
+ "financial",
+ "eventOrganizing"
+ ],
+ "login": "INCF",
+ "name": "International Neuroinformatics Coordinating Facility",
+ "profile": "https://github.com/INCF"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/33101992?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "irisgroen",
+ "name": "Iris Groen",
+ "profile": "https://github.com/irisgroen"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "isla_staden",
+ "name": "Isla Staden"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "jaap_von_der_aar",
+ "name": "Jaap von der Aar"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/17690870?v=4",
+ "contributions": [
+ "doc",
+ "tool",
+ "infra"
+ ],
+ "login": "kaczmarj",
+ "name": "Jakub Kaczmarzyk",
+ "profile": "https://github.com/kaczmarj"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "james_gholam",
+ "name": "James Gholam"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/12564882?v=4",
+ "contributions": [
+ "question",
+ "code"
+ ],
+ "login": "jdkent",
+ "name": "James Kent",
+ "profile": "https://github.com/jdkent"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1517611?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "schoffelen",
+ "name": "Jan Mathijs Schoffelen",
+ "profile": "https://github.com/schoffelen"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/29886537?v=4",
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "data",
+ "example",
+ "eventOrganizing",
+ "ideas",
+ "projectManagement",
+ "test",
+ "talk"
+ ],
+ "login": "jan-petr",
+ "name": "Jan Petr",
+ "profile": "https://www.exploreasl.org"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "jan-mathijs_schoffelen",
+ "name": "Jan-Mathijs Schoffelen"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/275048?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "talk",
+ "ideas",
+ "design",
+ "code"
+ ],
+ "login": "jbpoline",
+ "name": "Jean-Baptiste Poline",
+ "profile": "https://github.com/jbpoline"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/2151032?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "jchoude",
+ "name": "Jean-Christophe Houde",
+ "profile": "http://scil.dinf.usherbrooke.ca/jchoude"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "jean-dominique_gallezot",
+ "name": "Jean-Dominique Gallezot"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "jean-philippe_lachaux",
+ "name": "Jean-Philippe Lachaux"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/8420875?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "jmumford",
+ "name": "Jeanette Mumford",
+ "profile": "https://jeanettemumford.org/"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "jeffrey_g_ojemann",
+ "name": "Jeffrey G. Ojemann"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/1634917?v=4"
+ ],
+ "contributions": [
+ "question",
+ "bug",
+ "tutorial",
+ "talk",
+ "code"
+ ],
+ "login": "jgrethe",
+ "name": "Jeffrey S. Grethe",
+ "profile": "http://profiles.ucsd.edu/jeffrey.grethe"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/45284001?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "JegouA",
+ "name": "JegouA",
+ "profile": "https://github.com/JegouA"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/32652989?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "jrdalenberg",
+ "name": "Jelle Dalenberg",
+ "profile": "https://github.com/jrdalenberg"
+ },
+ {
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "login": "jeremy_moreau",
+ "name": "Jeremy Moreau"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "jessica_a_turner",
+ "name": "Jessica A. Turner"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "jochem_rieger",
+ "name": "Jochem Rieger",
+ "profile": "https://uol.de/en/applied-neurocognitive-psychology"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "john_detre",
+ "name": "John Detre"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "john_pellman",
+ "name": "John Pellman"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/98207?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "jwodder",
+ "name": "John T. Wodder",
+ "profile": "https://github.com/jwodder"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/7630327?v=4",
+ "contributions": [
+ "doc",
+ "tool",
+ "code"
+ ],
+ "login": "jokedurnez",
+ "name": "Joke Durnez",
+ "profile": "https://github.com/jokedurnez"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/5576557?v=4"
+ ],
+ "contributions": [
+ "code",
+ "doc"
+ ],
+ "login": "jhlegarreta",
+ "name": "Jon Haitz Legarreta Gorroño",
+ "profile": "https://github.com/jhlegarreta"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "jonathan_c_lau",
+ "name": "Jonathan C. Lau"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/2119646?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "JWinawer",
+ "name": "Jonathan Winawer",
+ "profile": "https://github.com/JWinawer"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/2149732?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "wadqc",
+ "name": "Joost Kuijer",
+ "profile": "https://github.com/wadqc"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "jose_manuel_saborit",
+ "name": "Jose Manuel Saborit"
+ },
+ {
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "login": "joseph_wexler",
+ "name": "Joseph Wexler"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/62885897?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "JosephGWoods",
+ "name": "Joseph Woods",
+ "profile": "https://www.ndcn.ox.ac.uk/team/joseph-woods"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/4451818?v=4"
+ ],
+ "contributions": [
+ "ideas",
+ "design",
+ "fundingFinding",
+ "review",
+ "eventOrganizing",
+ "blog",
+ "tool",
+ "bug",
+ "code",
+ "data",
+ "tutorial",
+ "question",
+ "doc",
+ "example",
+ "talk"
+ ],
+ "login": "guiomar",
+ "name": "Julia Guiomar Niso Galán",
+ "profile": "http://guiomarniso.com"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/8088860?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "juliasprenger",
+ "name": "Julia Sprenger",
+ "profile": "https://github.com/juliasprenger"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/2482071?v=4",
+ "contributions": [
+ "doc",
+ "data",
+ "ideas"
+ ],
+ "login": "jcohenadad",
+ "name": "Julien Cohen-Adad",
+ "profile": "https://www.neuro.polymtl.ca"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/52565341?v=4",
+ "contributions": [
+ "doc",
+ "example",
+ "bug",
+ "code",
+ "data",
+ "ideas",
+ "question",
+ "userTesting"
+ ],
+ "login": "JuliusWelzel",
+ "name": "Julius Welzel",
+ "profile": "https://github.com/JuliusWelzel"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "kai_j_miller",
+ "name": "Kai J. Miller"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/13531096?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "Kangjoo",
+ "name": "Kangjoo Lee",
+ "profile": "https://github.com/Kangjoo"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/6297454?v=4",
+ "contributions": [
+ "tool"
+ ],
+ "login": "katjaq",
+ "name": "Katja Heuer",
+ "profile": "https://github.com/katjaq"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/1189050?v=4"
+ ],
+ "contributions": [
+ "code",
+ "doc",
+ "bug"
+ ],
+ "login": "VisLab",
+ "name": "Kay Robbins",
+ "profile": "http://www.cs.utsa.edu/~krobbins"
+ },
+ {
+ "contributions": [
+ "question"
+ ],
+ "login": "kevin_larcher",
+ "name": "Kevin Larcher"
+ },
+ {
+ "contributions": [
+ "doc",
+ "eventOrganizing",
+ "ideas",
+ "projectManagement"
+ ],
+ "login": "kimberlylray",
+ "name": "Kimberly Ray"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/3626306?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "example",
+ "fundingFinding",
+ "ideas",
+ "talk",
+ "question",
+ "code"
+ ],
+ "login": "KirstieJane",
+ "name": "Kirstie Whitaker",
+ "profile": "https://whitakerlab.github.io"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/49999446?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "greckla",
+ "name": "Klara Gregorova",
+ "profile": "https://github.com/greckla"
+ },
+ {
+ "contributions": [
+ "doc",
+ "ideas"
+ ],
+ "login": "klaus_gramann",
+ "name": "Klaus Gramann"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/6362141?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "KrisThielemans",
+ "name": "Kris Thielemans",
+ "profile": "https://iris.ucl.ac.uk/iris/browse/profile?upi=KTHIE60"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "kristofer_bouchard",
+ "name": "Kristofer Bouchard",
+ "profile": "https://bouchardlab.lbl.gov"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/8438868?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "schillkg",
+ "name": "Kurt Schilling",
+ "profile": "https://github.com/schillkg"
+ },
+ {
+ "contributions": [
+ "financial"
+ ],
+ "login": "laura_and_john_arnold_foundation",
+ "name": "Laura and John Arnold Foundation"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "leandro_beltrachini",
+ "name": "Leandro Beltrachini"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/660469?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "LeeKamentsky",
+ "name": "Lee Kamentsky",
+ "profile": "https://github.com/LeeKamentsky"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "lennart_walger",
+ "name": "Lennart Walger"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/42233065?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "lnnrtwttkhn",
+ "name": "Lennart Wittkuhn",
+ "profile": "https://github.com/lnnrtwttkhn"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/3268583?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "libertyh",
+ "name": "Liberty Hamilton",
+ "profile": "https://github.com/libertyh"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/17887222?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "lpollonini",
+ "name": "Luca Pollonini",
+ "profile": "https://polloninilab.com"
+ },
+ {
+ "contributions": [
+ "doc",
+ "userTesting"
+ ],
+ "login": "luis_hernandez-garcia",
+ "name": "Luis Hernandez-Garcia"
+ },
+ {
+ "contributions": [
+ "doc",
+ "question"
+ ],
+ "login": "lukeje",
+ "name": "Luke J. Edwards"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/6161552?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "lzehl",
+ "name": "Lyuba Zehl",
+ "profile": "https://github.com/lzehl"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/15852194?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "jasmainak",
+ "name": "Mainak Jas",
+ "profile": "http://jasmainak.github.io/"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "manjari_narayan",
+ "name": "Manjari Narayan"
+ },
+ {
+ "contributions": [
+ "doc",
+ "ideas"
+ ],
+ "login": "manuel_mercier",
+ "name": "Manuel Mercier"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "maqsood_yaqub",
+ "name": "Maqsood Yaqub"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/31040756?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "Moo-Marc",
+ "name": "Marc Lalancette",
+ "profile": "https://github.com/Moo-Marc"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/5088923?v=4",
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "example",
+ "test",
+ "talk",
+ "infra"
+ ],
+ "login": "marcocastellaro",
+ "name": "Marco Castellaro",
+ "profile": "https://github.com/marcocastellaro"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/3390888?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "maigva",
+ "name": "Maria de la Iglesia",
+ "profile": "https://bimcv.cipf.es"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/54086142?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "data",
+ "code",
+ "ideas"
+ ],
+ "login": "mariehbourget",
+ "name": "Marie-Hélène Bourget",
+ "profile": "https://github.com/mariehbourget"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/6051303?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "markmikkelsen",
+ "name": "Mark Mikkelsen",
+ "profile": "https://vivo.weill.cornell.edu/display/cwid-mam4041"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "markus_morawski",
+ "name": "Markus Morawski"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "marta_bortoletto",
+ "name": "Marta Bortoletto"
+ },
+ {
+ "contributions": [
+ "data"
+ ],
+ "login": "martin_craig",
+ "name": "Martin Craig"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/12412821?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "data",
+ "code",
+ "ideas",
+ "talk"
+ ],
+ "login": "mnoergaard",
+ "name": "Martin Noergaard",
+ "profile": "https://profiles.stanford.edu/martin-noergaard"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/22450126?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "mszinte",
+ "name": "Martin Szinte",
+ "profile": "http://www.martinszinte.net"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "martin_wilson",
+ "name": "Martin Wilson"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "martina_bulgari",
+ "name": "Martina Bulgari"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/9007100?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "bug",
+ "ideas",
+ "maintenance",
+ "review"
+ ],
+ "login": "mateuszpawlik",
+ "name": "Mateusz Pawlik",
+ "profile": "https://github.com/mateuszpawlik"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/14110164?v=4",
+ "contributions": [
+ "code",
+ "tool",
+ "talk"
+ ],
+ "login": "mgxd",
+ "name": "Mathias Goncalves",
+ "profile": "https://github.com/mgxd"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1421029?v=4",
+ "contributions": [
+ "question",
+ "ideas",
+ "talk"
+ ],
+ "login": "mathieuboudreau",
+ "name": "Mathieu Boudreau",
+ "profile": "https://github.com/mathieuboudreau"
+ },
+ {
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "matt_sanderson",
+ "name": "Matt Sanderson"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/9211296?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "matteotonietto",
+ "name": "Matteo Tonietto",
+ "profile": "https://github.com/matteotonietto"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "matthias_günther",
+ "name": "Matthias Günther"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "matthias_van_osch",
+ "name": "Matthias Van Osch"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "maureen_j_shader",
+ "name": "Maureen J Shader"
+ },
+ {
+ "contributions": [
+ "userTesting"
+ ],
+ "login": "maurice_pasternak",
+ "name": "Maurice Pasternak"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/43676624?v=4",
+ "contributions": [
+ "code",
+ "review",
+ "doc",
+ "bug"
+ ],
+ "login": "MaxvandenBoom",
+ "name": "Max A. van den Boom",
+ "profile": "https://github.com/MaxvandenBoom"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/25242978?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "data",
+ "code",
+ "ideas",
+ "projectManagement",
+ "fundingFinding",
+ "talk"
+ ],
+ "login": "melanieganz",
+ "name": "Melanie Ganz-Benjaminsen",
+ "profile": "https://sites.google.com/view/melanieganz/home"
+ },
+ {
+ "contributions": [
+ "doc",
+ "data",
+ "projectManagement"
+ ],
+ "login": "michael_chappell",
+ "name": "Michael Chappell"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/136479?v=4",
+ "contributions": [
+ "doc",
+ "ideas",
+ "tool",
+ "bug",
+ "talk"
+ ],
+ "login": "mih",
+ "name": "Michael Hanke",
+ "profile": "https://github.com/mih"
+ },
+ {
+ "contributions": [
+ "doc",
+ "test",
+ "tool"
+ ],
+ "login": "michael_p_harms",
+ "name": "Michael P. Harms"
+ },
+ {
+ "contributions": [
+ "example",
+ "fundingFinding"
+ ],
+ "login": "michael_p_milham",
+ "name": "Michael P. Milham"
+ },
+ {
+ "contributions": [
+ "question",
+ "blog",
+ "tutorial",
+ "talk",
+ "doc"
+ ],
+ "login": "michael_p_notter",
+ "name": "Michael P. Notter"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/29461056?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "MichaelSchirner",
+ "name": "Michael Schirner",
+ "profile": "https://www.brainsimulation.org"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1488318?v=4",
+ "contributions": [
+ "bug"
+ ],
+ "login": "naveau",
+ "name": "Mikaël Naveau",
+ "profile": "https://www.cyceron.fr"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "nader_pouratian",
+ "name": "Nader Pouratian"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "natalia_petridou",
+ "name": "Natalia Petridou"
+ },
+ {
+ "contributions": [
+ "financial"
+ ],
+ "login": "national_institute_of_mental_health",
+ "name": "National Institute of Mental Health"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/11369795?v=4"
+ ],
+ "contributions": [
+ "code",
+ "doc",
+ "ideas",
+ "infra",
+ "review",
+ "question"
+ ],
+ "login": "nellh",
+ "name": "Nell Hardcastle",
+ "profile": "https://github.com/nellh"
+ },
+ {
+ "contributions": [
+ "doc",
+ "tool",
+ "code"
+ ],
+ "login": "nicholas_traut",
+ "name": "Nicholas Traut"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "nick_f_ramsey",
+ "name": "Nick F. Ramsey"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "nicole_c_swann",
+ "name": "Nicole C. Swann"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "nima_bigdely_shamlo",
+ "name": "Nima Bigdely Shamlo"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "olivier_david",
+ "name": "Olivier David"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "orrin_devinsky",
+ "name": "Orrin Devinsky"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/598470?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "tool",
+ "ideas",
+ "question",
+ "code"
+ ],
+ "login": "oesteban",
+ "name": "Oscar Esteban",
+ "profile": "http://www.axonlab.org"
+ },
+ {
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "login": "pamela_lamontagne",
+ "name": "Pamela LaMontagne"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/11822050?v=4",
+ "contributions": [
+ "doc",
+ "tool",
+ "test",
+ "code"
+ ],
+ "login": "parulsethi",
+ "name": "Parul Sethi",
+ "profile": "https://github.com/parulsethi"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/41481345?v=4"
+ ],
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "data",
+ "example",
+ "eventOrganizing",
+ "ideas",
+ "projectManagement",
+ "test",
+ "talk"
+ ],
+ "login": "patsycle",
+ "name": "Patricia Clement",
+ "profile": "https://github.com/patsycle"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/12662110?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "example",
+ "question",
+ "code"
+ ],
+ "login": "Park-Patrick",
+ "name": "Patrick Park",
+ "profile": "https://github.com/Park-Patrick"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "paule-joanne_toussaint",
+ "name": "Paule-Joanne Toussaint"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/20129524?v=4",
+ "contributions": [
+ "question",
+ "doc",
+ "review",
+ "tool",
+ "tutorial",
+ "talk"
+ ],
+ "login": "peerherholz",
+ "name": "Peer Herholz",
+ "profile": "https://peerherholz.github.io/"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/8677353?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "BrainModes",
+ "name": "Petra Ritter",
+ "profile": "https://www.brainsimulation.org"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/777588?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "prioux",
+ "name": "Pierre Rioux",
+ "profile": "https://github.com/prioux"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/37624277?v=4",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "pvdemael",
+ "name": "Pieter Vandemaele",
+ "profile": "https://github.com/pvdemael"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/3196357?v=4",
+ "contributions": [
+ "code",
+ "tool"
+ ],
+ "login": "raamana",
+ "name": "Pradeep Reddy Raamana",
+ "profile": "https://github.com/raamana"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1725272?v=4",
+ "contributions": [
+ "doc",
+ "talk"
+ ],
+ "login": "ccraddock",
+ "name": "R. Cameron Craddock",
+ "profile": "https://github.com/ccraddock"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/6961185?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code",
+ "question",
+ "talk",
+ "bug",
+ "code",
+ "infra",
+ "review",
+ "tool",
+ "ideas"
+ ],
+ "login": "Remi-Gau",
+ "name": "Remi Gau",
+ "profile": "https://remi-gau.github.io/"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/2046265?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "hoechenberger",
+ "name": "Richard Höchenberger",
+ "profile": "https://hoechenberger.net/"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/2071058?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "RikHenson",
+ "name": "Richard N. Henson",
+ "profile": "http://www.mrc-cbu.cam.ac.uk/people/rik.henson/personal"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "robert_b._innis",
+ "name": "Robert B. Innis",
+ "profile": "https://www.nimh.nih.gov/research/research-conducted-at-nimh/research-areas/clinics-and-labs/mib/molecular-imaging-branch-mib"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/5637955?v=4"
+ ],
+ "contributions": [
+ "code",
+ "doc"
+ ],
+ "login": "Lestropie",
+ "name": "Robert E. Smith",
+ "profile": "http://www.mrtrix.org"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "robert_knight",
+ "name": "Robert Knight"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/748691?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "rob-luke",
+ "name": "Robert Luke",
+ "profile": "https://github.com/rob-luke"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/899043?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "tool",
+ "talk",
+ "example",
+ "tutorial",
+ "test",
+ "ideas",
+ "question",
+ "bug",
+ "blog",
+ "code",
+ "content",
+ "data",
+ "design",
+ "eventOrganizing",
+ "infra",
+ "review",
+ "userTesting",
+ "video"
+ ],
+ "login": "robertoostenveld",
+ "name": "Robert Oostenveld",
+ "profile": "https://robertoostenveld.nl"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/2310732?v=4",
+ "contributions": [
+ "tool"
+ ],
+ "login": "r03ert0",
+ "name": "Roberto Toro",
+ "profile": "https://github.com/r03ert0"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "rohan_goyal",
+ "name": "Rohan Goyal"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/14927911?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "rwblair",
+ "name": "Ross W. Blair",
+ "profile": "https://github.com/rwblair"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/871056?v=4",
+ "contributions": [
+ "doc",
+ "fundingFinding",
+ "talk"
+ ],
+ "login": "poldrack",
+ "name": "Russell A. Poldrack",
+ "profile": "http://www.poldracklab.org"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/2931080?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "remiadon",
+ "name": "Rémi Adon",
+ "profile": "https://github.com/remiadon"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/508512?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "samirdas",
+ "name": "Samir Das",
+ "profile": "https://github.com/samirdas"
+ },
+ {
+ "contributions": [
+ "ideas",
+ "review",
+ "doc"
+ ],
+ "login": "samuel_garcia",
+ "name": "Samuel Garcia"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/3496566?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "snastase",
+ "name": "Samuel Nastase",
+ "profile": "https://snastase.github.io/"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "sara_elgayar",
+ "name": "Sara Elgayar"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "sasha_d_ambrosio",
+ "name": "Sasha D'Ambrosio"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/184063?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "satra",
+ "name": "Satrajit S. Ghosh",
+ "profile": "https://satra.cogitatum.org"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/43755798?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "smakeig",
+ "name": "Scott Makeig",
+ "profile": "https://sccn.ucsd.edu/~scott"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/28531404?v=4",
+ "contributions": [
+ "doc",
+ "example",
+ "bug",
+ "code",
+ "data",
+ "ideas",
+ "question",
+ "tool",
+ "userTesting"
+ ],
+ "login": "sjeung",
+ "name": "Sein Jeung",
+ "profile": "https://github.com/sjeung"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "shashank_bansal",
+ "name": "Shashank Bansal"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/13783791?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "sjoerdvos",
+ "name": "Sjoerd B. Vos",
+ "profile": "https://research-repository.uwa.edu.au/en/persons/sjoerd-vos"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/923896?v=4",
+ "contributions": [
+ "doc",
+ "tool",
+ "bug"
+ ],
+ "login": "soichih",
+ "name": "Soichi Hayashi",
+ "profile": "https://github.com/soichih"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/9084751?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "question",
+ "ideas",
+ "bug",
+ "example",
+ "code",
+ "review",
+ "test",
+ "talk",
+ "tutorial",
+ "tool",
+ "plugin",
+ "blog",
+ "maintenance",
+ "data"
+ ],
+ "login": "sappelhoff",
+ "name": "Stefan Appelhoff",
+ "profile": "https://www.stefanappelhoff.com"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "stephan_bickel",
+ "name": "Stephan Bickel"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/11152799?v=4",
+ "contributions": [
+ "doc",
+ "example",
+ "test",
+ "tool",
+ "question"
+ ],
+ "login": "suyashdb",
+ "name": "Suyash Bhogawar",
+ "profile": "https://www.linkedin.com/in/suyashb/"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/18703677?v=4",
+ "contributions": [
+ "doc",
+ "fundingFinding"
+ ],
+ "login": "sbaillet",
+ "name": "Sylvain Baillet",
+ "profile": "https://github.com/sbaillet"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/7886280?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "SylvainTakerkart",
+ "name": "Sylvain Takerkart",
+ "profile": "https://github.com/SylvainTakerkart"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/22279770?v=4"
+ ],
+ "contributions": [
+ "ideas",
+ "review",
+ "talk",
+ "bug",
+ "code",
+ "doc"
+ ],
+ "login": "sebastientourbier",
+ "name": "Sébastien Tourbier",
+ "profile": "https://github.com/sebastientourbier"
+ },
+ {
+ "contributions": [
+ "doc",
+ "data",
+ "userTesting"
+ ],
+ "login": "sören_grothkopp",
+ "name": "Sören Grothkopp"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/24300712?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "tpatpa",
+ "name": "Tal Pal Attia",
+ "profile": "https://github.com/tpatpa"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/303932?v=4",
+ "contributions": [
+ "code",
+ "doc",
+ "ideas",
+ "fundingFinding",
+ "plugin",
+ "review",
+ "talk",
+ "bug",
+ "design"
+ ],
+ "login": "tyarkoni",
+ "name": "Tal Yarkoni",
+ "profile": "https://github.com/tyarkoni"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/21124251?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "spisakt",
+ "name": "Tamas Spisak",
+ "profile": "https://pni-lab.github.io/"
+ },
+ {
+ "contributions": [
+ "userTesting"
+ ],
+ "login": "tamás_józsa",
+ "name": "Tamás Józsa"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/8228902?v=4"
+ ],
+ "contributions": [
+ "question",
+ "doc",
+ "plugin",
+ "code"
+ ],
+ "login": "tsalo",
+ "name": "Taylor Salo",
+ "profile": "https://tsalo.github.io"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/1578674?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "code",
+ "test",
+ "question",
+ "review",
+ "ideas",
+ "tool",
+ "bug",
+ "talk"
+ ],
+ "login": "teonbrooks",
+ "name": "Teon L. Brooks",
+ "profile": "https://teonbrooks.com"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/5155907?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "talk",
+ "tool",
+ "review",
+ "maintenance",
+ "code"
+ ],
+ "login": "nicholst",
+ "name": "Thomas E. Nichols",
+ "profile": "http://nisox.org"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/6856252?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "tfunck",
+ "name": "Thomas Funck",
+ "profile": "https://github.com/tfunck"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "thomas_kirk",
+ "name": "Thomas Kirk"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "thomas_okell",
+ "name": "Thomas Okell"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/4816041?v=4",
+ "contributions": [
+ "question",
+ "doc",
+ "example",
+ "tool",
+ "talk",
+ "bug",
+ "ideas"
+ ],
+ "login": "tiborauer",
+ "name": "Tibor Auer",
+ "profile": "https://tiborauer.github.io"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1436846?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "dickscheid",
+ "name": "Timo Dickscheid",
+ "profile": "https://go.fzj.de/dickscheid"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/63448407?v=4",
+ "contributions": [
+ "doc",
+ "ideas",
+ "userTesting"
+ ],
+ "login": "timo-berg",
+ "name": "Timotheus Berg",
+ "profile": "https://github.com/timo-berg"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "tobey_betthauser",
+ "name": "Tobey Betthauser"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/202576?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "xi",
+ "name": "Tobias Bengfort",
+ "profile": "http://tobib.spline.de"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "tom_hampshire",
+ "name": "Tom Hampshire"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/6262700?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "torwager",
+ "name": "Tor Wager",
+ "profile": "https://github.com/torwager"
+ },
+ {
+ "contributions": [
+ "doc",
+ "tool",
+ "bug"
+ ],
+ "login": "travis_riddle",
+ "name": "Travis Riddle"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/5174953?v=4",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "glatard",
+ "name": "Tristan Glatard",
+ "profile": "https://github.com/glatard"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "ulrike_bingel",
+ "name": "Ulrike Bingel"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/814322?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "vsoch",
+ "name": "Vanessa Sochat",
+ "profile": "https://github.com/vsoch"
+ },
+ {
+ "contributions": [
+ "code",
+ "design",
+ "doc",
+ "tool"
+ ],
+ "login": "vasudev_raguram",
+ "name": "Vasudev Raguram"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/48063259?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "vdcalhoun",
+ "name": "Vince D. Calhoun",
+ "profile": "http://trendscenter.org"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/1639782?v=4",
+ "contributions": [
+ "doc"
+ ],
+ "login": "viacovella",
+ "name": "Vittorio Iacovella"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "vladimir_litvak",
+ "name": "Vladimir Litvak"
+ },
+ {
+ "contributions": [
+ "data",
+ "question"
+ ],
+ "login": "wietske_van_der_zwaag",
+ "name": "Wietske van der Zwaag"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "william_clarke",
+ "name": "William Clarke"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "william_triplett",
+ "name": "William Triplett"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/981436?v=4"
+ ],
+ "contributions": [
+ "code",
+ "doc"
+ ],
+ "login": "wouterpotters",
+ "name": "Wouter V. Potters",
+ "profile": "https://www.wouterpotters.nl"
+ },
+ {
+ "avatar_url": "https://avatars.githubusercontent.com/u/19894855?v=4",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "login": "xiangruili",
+ "name": "Xiangrui Li",
+ "profile": "https://github.com/xiangruili"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/39889?v=4"
+ ],
+ "contributions": [
+ "doc",
+ "talk",
+ "tool",
+ "question",
+ "bug",
+ "code"
+ ],
+ "login": "yarikoptic",
+ "name": "Yaroslav O. Halchenko",
+ "profile": "http://www.onerussian.com"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "yoni_ashar",
+ "name": "Yoni Ashar"
+ },
+ {
+ "contributions": [
+ "code"
+ ],
+ "login": "yuan_wang",
+ "name": "Yuan Wang"
+ },
+ {
+ "contributions": [
+ "doc"
+ ],
+ "login": "zachary_michael",
+ "name": "Zachary Michael"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/39155887?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "ezemikulan",
+ "name": "ezemikulan",
+ "profile": "https://github.com/ezemikulan"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/9729281?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "josator2",
+ "name": "josator2",
+ "profile": "https://github.com/josator2"
+ },
+ {
+ "avatar_url": [
+ "https://avatars.githubusercontent.com/u/8000597?v=4"
+ ],
+ "contributions": [
+ "code"
+ ],
+ "login": "monkeyman192",
+ "name": "monkeyman192",
+ "profile": "https://github.com/monkeyman192"
+ },
+ {
+ "contributions": [
+ "data",
+ "code"
+ ],
+ "login": "étienne_bergeron",
+ "name": "Étienne Bergeron"
+ }
+ ],
+ "contributorsPerLine": 7
+}
diff --git a/.circleci/config.yml b/.circleci/config.yml
index f884388e0a..9e6b41fa62 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -66,14 +66,16 @@ jobs:
build_docs_pdf:
docker:
- - image: danteev/texlive:latest
+ - image: texlive/texlive:latest
steps:
# checkout code to default ~/project
- checkout
- run:
name: install dependencies
command: |
- apt-get update && apt install -y python3-pip
+ apt-get update && apt install -y python3-pip python3-venv pandoc
+ python3 -m venv .venv
+ source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install -r ~/project/requirements.txt
python3 -m pip install ~/project/tools/schemacode/[render]
@@ -83,6 +85,7 @@ jobs:
- run:
name: generate pdf version docs
command: |
+ source .venv/bin/activate
cd ~/project/pdf_build_src
bash build_pdf.sh
mv ~/project/pdf_build_src/bids-spec.pdf ~/project/bids-spec.pdf
diff --git a/.codecov.yml b/.codecov.yml
index 302b31ddce..b46689d13f 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -1,3 +1,5 @@
ignore:
- - "*/*/tests/*"
- - "**/tests/*"
+ - "*/*/tests/*"
+ - "**/tests/*"
+codecov:
+ token: 7e84a7fb-8f7e-45f5-8dcc-9f5219fa3855
diff --git a/.codespell_dict b/.codespell_dict
index 81adf22162..e4e9c7cc90 100644
--- a/.codespell_dict
+++ b/.codespell_dict
@@ -1,6 +1,11 @@
sub-directory->subdirectory
+Sub-directory->Subdirectory
sub-directories->subdirectories
+Sub-directories->Subdirectories
file name->filename
+File name->Filename
file names->filenames
+File names->Filenames
folder->directory
-folders->directories
+Folder->Directory
+Folders->Directories
diff --git a/.codespellrc b/.codespellrc
index 28aeafee0c..4092a9a7bf 100644
--- a/.codespellrc
+++ b/.codespellrc
@@ -1,6 +1,8 @@
[codespell]
-skip = *.js,*.svg,*.eps,.git
-ignore-words-list = fo,te,als
-builtin = clear,rare
-dictionary = .codespell_dict
+skip = *.js,*.svg,*.eps,.git,node_modules,env,venv,.mypy_cache,package-lock.json,CITATION.cff,tools/new_contributors.tsv,./tools/schemacode/docs/build
+ignore-words-list = fo,te,als,Acknowledgements,acknowledgements,weill,bu,winn
+builtin = clear,rare,en-GB_to_en-US
+# this overloads default dictionaries and I have not yet figured out
+# how to have multiple https://github.com/codespell-project/codespell/issues/2727
+# dictionary = .codespell_dict
exclude-file = src/CHANGES.md
diff --git a/.github/labeler.yml b/.github/labeler.yml
new file mode 100644
index 0000000000..5c73894236
--- /dev/null
+++ b/.github/labeler.yml
@@ -0,0 +1,27 @@
+# labeler "full" schema
+# https://github.com/marketplace/actions/auto-labeler
+
+# enable labeler on issues, prs, or both.
+enable:
+ issues: false
+ prs: true
+# comments object allows you to specify a different message for issues and prs
+
+comments:
+ prs: |
+ I have applied any labels matching special text in your title and description.
+
+ Please review the labels and make any necessary changes.
+
+# Labels is an object where:
+# - keys are labels
+# - values are objects of { include: [ pattern ], exclude: [ pattern ] }
+# - pattern must be a valid regex, and is applied globally to
+# title + description of issues and/or prs (see enabled config above)
+# - 'include' patterns will associate a label if any of these patterns match
+# - 'exclude' patterns will ignore this label if any of these patterns match
+labels:
+ exclude-from-changelog:
+ include:
+ - 'pre-commit\.ci'
+ exclude: []
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
new file mode 100644
index 0000000000..355ce3d2de
--- /dev/null
+++ b/.github/workflows/labeler.yml
@@ -0,0 +1,21 @@
+name: "Label PRs"
+# See https://github.com/marketplace/actions/auto-labeler
+
+on:
+
+ pull_request_target:
+ types: [opened]
+
+jobs:
+
+ labeler:
+ runs-on: ubuntu-latest
+
+ if: github.repository_owner == 'bids-standard'
+
+ steps:
+ - name: Check Labels
+ id: labeler
+ uses: jimschubert/labeler-action@v2
+ with:
+ GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
diff --git a/.github/workflows/redirect_circleci_artifacts.yml b/.github/workflows/redirect_circleci_artifacts.yml
index 95810e311e..c40e051249 100644
--- a/.github/workflows/redirect_circleci_artifacts.yml
+++ b/.github/workflows/redirect_circleci_artifacts.yml
@@ -2,14 +2,17 @@ on: [status]
jobs:
circleci_artifacts_redirector_job:
- if: "${{ startsWith(github.event.context, 'ci/circleci: build_docs_pdf') }}"
runs-on: ubuntu-latest
+ if: "${{ startsWith(github.event.context, 'ci/circleci: build_docs_pdf') }}"
+ permissions:
+ statuses: write
name: Run CircleCI artifacts redirector
steps:
- name: GitHub Action step
uses: larsoner/circleci-artifacts-redirector-action@master
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
+ api-token: ${{ secrets.CIRCLECI_TOKEN }}
artifact-path: 0/bids-spec.pdf
circleci-jobs: build_docs_pdf
job-title: Check the rendered PDF version here!
diff --git a/.github/workflows/schema_docs.yaml b/.github/workflows/schema_docs.yaml
deleted file mode 100644
index 70b1ac4bc9..0000000000
--- a/.github/workflows/schema_docs.yaml
+++ /dev/null
@@ -1,69 +0,0 @@
-name: "schemacode_docs"
-
-on:
- push:
- branches:
- - "master"
- tags:
- - "schema-*"
- workflow_dispatch:
- inputs:
- debug_enabled:
- type: boolean
- description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)'
- required: false
- default: false
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-defaults:
- run:
- shell: bash
-
-jobs:
- push_schema_for_docs:
- runs-on: ${{ matrix.os }}
- name: Push Schema to Schema Docs
- strategy:
- matrix:
- os: ["ubuntu-latest"]
- steps:
- - uses: actions/checkout@v3
-
- - name: Debug
- uses: mxschmitt/action-tmate@v3
- if: ${{ inputs.debug_enabled }}
- timeout-minutes: 15
- with:
- limit-access-to-actor: true
-
- - name: Pull schema-docs and reqs from bids-specification
- run: |
-
- pushd $HOME
- git config --global user.name "${{ github.actor }}"
- git config --global user.email "${{ github.actor}}@noreply.com"
- git clone https://${{ github.repository_owner }}:${{ secrets.SCHEMA_DOCS_GITHUB_API_TOKEN }}@github.com/${{ github.repository_owner }}/schema-docs
- popd
- rsync --recursive --copy-links ${{ github.workspace }}/tools/schemacode/bidsschematools $HOME/schema-docs/
- rsync --recursive --copy-links ${{ github.workspace }}/tools/schemacode/docs $HOME/schema-docs/
- cat ${{ github.workspace }}/requirements.txt | grep -v tools/schemacode >> $HOME/schema-docs/requirements.txt
- sort $HOME/schema-docs/requirements.txt | uniq > $HOME/schema-docs/requirements.txt.tmp
- mv $HOME/schema-docs/requirements.txt.tmp $HOME/schema-docs/requirements.txt
-
- - name: Push Updates to schema-docs
- run: |
-
- pushd $HOME/schema-docs
-
- git diff --exit-code > $HOME/gitdiff.log
- if [[ $? == 0 ]]; then
- echo "No changes found schema code, this workflow should only run when changes are made to schema-*."
- echo "You must be up to no good."
- else
- git add --all
- git commit -m "Auto update from bids-specification $GITHUB_SHA"
- git push origin main
- fi
diff --git a/.github/workflows/schemacode_ci.yml b/.github/workflows/schemacode_ci.yml
index 9d237200a3..8188370cdd 100644
--- a/.github/workflows/schemacode_ci.yml
+++ b/.github/workflows/schemacode_ci.yml
@@ -24,7 +24,7 @@ jobs:
strategy:
matrix:
os: ["ubuntu-latest"]
- python-version: ["3.10"]
+ python-version: ["3.11"]
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
@@ -33,7 +33,7 @@ jobs:
- name: "Install build dependencies"
run: pip install --upgrade build twine
- name: "Install test dependencies on tag"
- run: pip install --upgrade pytest pyyaml pandas tabulate markdown-it-py
+ run: pip install --upgrade pytest pyyaml pandas tabulate markdown-it-py pyparsing
if: ${{ startsWith(github.ref, 'refs/tags/schema-') }}
- name: "Build archive on tag"
run: pytest tools/schemacode/bidsschematools -k make_archive
@@ -56,7 +56,7 @@ jobs:
fail-fast: false
matrix:
os: ["ubuntu-latest"]
- python-version: ["3.7", "3.8", "3.9", "3.10"]
+ python-version: ["3.8", "3.9", "3.10", "3.11"]
include:
- os: macos-latest
python-version: 3
@@ -104,7 +104,7 @@ jobs:
strategy:
matrix:
os: ["ubuntu-latest"]
- python-version: ["3.10"]
+ python-version: ["3.11"]
steps:
- name: "Fetch packages"
uses: actions/download-artifact@v3
diff --git a/.github/workflows/typescript_build.yml b/.github/workflows/typescript_build.yml
deleted file mode 100644
index ad18e40bda..0000000000
--- a/.github/workflows/typescript_build.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: "typescript_build"
-
-on:
- push:
- branches:
- - "master"
- paths:
- - "tools/typescript/**"
- - "src/schema/**"
- pull_request:
- branches:
- - "*"
- paths:
- - "tools/typescript/**"
- - "src/schema/**"
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- build_module:
- runs-on: ${{ matrix.os }}
- strategy:
- fail-fast: false
- matrix:
- os: ["ubuntu-latest"]
- deno-version: [1.21.3]
- name: ${{ matrix.os }} with Deno ${{ matrix.deno-version }}
- defaults:
- run:
- shell: bash
- steps:
- - uses: actions/checkout@v3
- - name: Use Deno Version ${{ matrix.deno-version }}
- uses: denolib/setup-deno@v2
- with:
- deno-version: ${{ matrix.deno-version }}
- - name: Run tests
- run: deno test --no-check
- - name: Build module
- run: deno run --allow-write --allow-read --no-check tools/typescript/build-schema-types.ts
- - name: Save module
- uses: actions/upload-artifact@v3
- with:
- name: typescript-module
- path: tools/typescript/output
diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml
index 91ba4d49e0..8e48d85bb6 100644
--- a/.github/workflows/validation.yml
+++ b/.github/workflows/validation.yml
@@ -81,3 +81,18 @@ jobs:
run: |
python no-bad-latin.py
working-directory: tools
+
+ # Validate CITATION.cff
+ validate_cff:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: actions/setup-python@v4
+ with:
+ python-version: 3
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip setuptools
+ pip3 install cffconvert
+ - name: Validate CITATION.cff
+ run: make validate_citation_cff
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 9cdcbf36dc..53baab42b6 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,7 +3,7 @@
exclude: 'tools/schemacode/bidsschematools/tests/data/broken_dataset_description.json'
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v4.3.0
+ rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
@@ -13,27 +13,53 @@ repos:
- id: check-added-large-files
- id: check-case-conflict
- repo: https://github.com/psf/black
- rev: 22.10.0
+ rev: 23.7.0
hooks:
- id: black
+ files: ^tools/(?!schemacode)
+ args: ["--verbose"]
+ - id: black
+ name: "black bidsschematools"
files: tools/schemacode
- args: [--verbose]
+ args: ["--config", "tools/schemacode/pyproject.toml", "--verbose"]
- repo: https://github.com/pyCQA/isort
- rev: 5.10.1
+ rev: 5.12.0
hooks:
- id: isort
- files: tools/
+ files: ^tools/(?!schemacode)
+ args: ["--profile", "black"]
+ - id: isort
+ name: "isort bidsschematools"
+ files: tools/schemacode
+ args: ["--settings-file", "tools/schemacode/pyproject.toml"]
- repo: https://github.com/pyCQA/flake8
- rev: 5.0.4
+ rev: 6.0.0
hooks:
- id: flake8
args: [--config=tools/schemacode/setup.cfg]
- repo: https://github.com/pre-commit/mirrors-prettier
- rev: v3.0.0-alpha.2
+ rev: v3.0.0
hooks:
- id: prettier
files: src/schema/.*/.*\.yaml
- repo: https://github.com/codespell-project/codespell
- rev: v2.2.2
+ rev: v2.2.5
hooks:
- id: codespell
+ args: ["--config=.codespellrc", "--dictionary=-", "--dictionary=.codespell_dict"]
+ - repo: https://github.com/pre-commit/mirrors-mypy
+ rev: v1.4.1
+ hooks:
+ - id: mypy
+ # Sync with project.optional-dependencies.typing
+ additional_dependencies:
+ - click
+ - markdown-it-py
+ - importlib_resources
+ - pandas-stubs
+ - pyparsing
+ - pytest
+ - types-PyYAML
+ - types-tabulate
+ args: ["tools/schemacode/bidsschematools"]
+ pass_filenames: false
diff --git a/.tributors b/.tributors
new file mode 100644
index 0000000000..b325271c22
--- /dev/null
+++ b/.tributors
@@ -0,0 +1,3064 @@
+{
+ "aaron_oliver-taylor": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Aaron Oliver-Taylor"
+ },
+ "adam2392": {
+ "bio": "I am a Postdoctoral Research Fellow at Columbia University in the Causal AI Lab. PhD in BME and MS in Applied Math&Stats from Johns Hopkins U.",
+ "blog": "https://adam2392.github.io/",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Adam Li"
+ },
+ "agt24": {
+ "affiliation": "NIMH, Intramural Research Program, Bethesda, MD, 20891, USA",
+ "blog": "https://cmn.nimh.nih.gov/dsst",
+ "contributions": [
+ "doc"
+ ],
+ "email": "adamt@nih.gov",
+ "name": "Adam Thomas",
+ "orcid": "0000-0002-2850-1419",
+ "publish_email": true
+ },
+ "adeen_flinker": {
+ "affiliation": "New York University, Department of Neurology, New York City, New York, 10016, USA",
+ "blog": "http://flinkerlab.org",
+ "contributions": [
+ "doc"
+ ],
+ "email": "adeen@nyu.edu",
+ "name": "Adeen Flinker",
+ "orcid": "0000-0003-1247-1283",
+ "publish_email": true
+ },
+ "adswa": {
+ "affiliation": "Institute of Neuroscience and Medicine (INM-7), Research Center Juelich, Juelich, 52428, Germany",
+ "bio": "Psychoinformagician in training",
+ "blog": "https://www.adina-wagner.com",
+ "contributions": [
+ "design",
+ "code"
+ ],
+ "email": "adina.wagner@t-online.de",
+ "name": "Adina S. Wagner",
+ "orcid": "0000-0003-2917-3450",
+ "publish_email": true
+ },
+ "agahkarakuzu": {
+ "blog": "https://agahkarakuzu.github.io",
+ "contributions": [
+ "question",
+ "doc",
+ "data",
+ "ideas",
+ "code"
+ ],
+ "email": "agahkarakuzu@gmail.com",
+ "name": "Agah Karakuzu",
+ "orcid": "0000-0001-7283-271X"
+ },
+ "AkiNikolaidis": {
+ "blog": "https://github.com/AkiNikolaidis",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Aki Nikolaidis",
+ "orcid": "0000-0001-8960-1934"
+ },
+ "alberto_lazari": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Alberto Lazari"
+ },
+ "adelavega": {
+ "blog": "https://github.com/adelavega",
+ "contributions": [
+ "bug",
+ "code",
+ "test"
+ ],
+ "name": "Alejandro de la Vega"
+ },
+ "alegiac95": {
+ "affiliation": "GlaxoSmithKline Research and Development",
+ "blog": "https://github.com/alegiac95",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Alessio Giacomel",
+ "orcid": "0000-0002-7784-2041"
+ },
+ "alexrockhill": {
+ "bio": "Graduate Student in the Swann Lab",
+ "blog": "https://github.com/alexrockhill",
+ "contributions": [
+ "doc",
+ "tool",
+ "code"
+ ],
+ "email": "aprockhill@mailbox.org",
+ "name": "Alex Rockhill",
+ "orcid": "0000-0003-3868-7453"
+ },
+ "alexander_jones": {
+ "contributions": [
+ "code",
+ "bug"
+ ],
+ "name": "Alexander Jones"
+ },
+ "alexlicohen": {
+ "affiliation": "Boston Children's Hospital, Department of Neurology, Boston, MA, 02115, USA",
+ "blog": "https://bchcohenlab.com",
+ "contributions": [
+ "bug",
+ "code",
+ "doc",
+ "question"
+ ],
+ "email": "alexander.cohen2@childrens.harvard.edu",
+ "name": "Alexander L. Cohen",
+ "orcid": "0000-0001-6557-5866",
+ "publish_email": true
+ },
+ "alexander_von_lautz": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Alexander von Lautz"
+ },
+ "agramfort": {
+ "affiliation": "Inria, Université Paris-Saclay",
+ "blog": "http://alexandre.gramfort.net",
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "email": "alexandre.gramfort@inria.fr",
+ "name": "Alexandre Gramfort",
+ "orcid": "0000-0001-9791-4404",
+ "publish_email": true
+ },
+ "alexandre_hutton": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Alexandre Hutton"
+ },
+ "alexandreroutier": {
+ "blog": "https://github.com/alexandreroutier",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Alexandre Routier",
+ "orcid": "0000-0003-1603-8049"
+ },
+ "alexfoias": {
+ "blog": "https://github.com/alexfoias",
+ "contributions": [
+ "doc",
+ "data"
+ ],
+ "name": "Alexandru Foias",
+ "orcid": "0000-0001-5160-1402"
+ },
+ "ali_khan": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Ali Khan"
+ },
+ "ana_fouto": {
+ "contributions": [
+ "userTesting"
+ ],
+ "name": "Ana Fouto"
+ },
+ "wanderine": {
+ "blog": "https://github.com/wanderine",
+ "contributions": [
+ "doc",
+ "talk",
+ "code"
+ ],
+ "name": "Anders Eklund"
+ },
+ "andrea_pigorini": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Andrea Pigorini"
+ },
+ "andrew_hoopes": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Andrew Hoopes"
+ },
+ "andrew_jahn": {
+ "contributions": [
+ "userTesting"
+ ],
+ "name": "Andrew Jahn"
+ },
+ "apjanke": {
+ "blog": "http://apjanke.net",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "email": "andrew@apjanke.net",
+ "name": "Andrew Janke"
+ },
+ "anibalsolon": {
+ "bio": "Everywhere: @anibalsolon",
+ "blog": "https://anibalsolon.com/",
+ "contributions": [
+ "code",
+ "bug"
+ ],
+ "email": "anibalsolon@gmail.com",
+ "name": "Anibal Sólon",
+ "orcid": "0000-0002-2050-0614"
+ },
+ "bendhouseart": {
+ "blog": "https://github.com/bendhouseart",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Anthony Galassi",
+ "orcid": "0000-0001-6550-4574"
+ },
+ "arokem": {
+ "affiliation": "University of Washington, Psychology, Seattle, WA, 98107, United States of America",
+ "blog": "https://arokem.org/",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "email": "arokem@uw.edu",
+ "name": "Ariel Rokem",
+ "orcid": "0000-0003-0679-1985",
+ "publish_email": true
+ },
+ "arjen_stolk": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Arjen Stolk"
+ },
+ "arnodelorme": {
+ "affiliation": "Swartz Center for Computational Neuroscience",
+ "blog": "https://www.arnauddelorme.com",
+ "contributions": [
+ "doc",
+ "example",
+ "ideas"
+ ],
+ "email": "adelorme@ucsd.edu",
+ "github": "arnodelorme",
+ "name": "Arnaud Delorme",
+ "orcid": "0000-0002-0799-3557",
+ "publish_email": true
+ },
+ "arnaudmarcoux": {
+ "blog": "https://github.com/arnaudmarcoux",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Arnaud Marcoux"
+ },
+ "Arshitha": {
+ "affiliation": "NIMH, Data Science and Sharing Team, Bethesda, MD, 20892, USA",
+ "bio": "Data Scientist",
+ "blog": "https://github.com/Arshitha",
+ "contributions": [
+ "doc",
+ "infra",
+ "code"
+ ],
+ "email": "arsh2794@gmail.com",
+ "name": "Arshitha Basavaraj",
+ "orcid": "0000-0002-6984-7969",
+ "publish_email": true
+ },
+ "ashgillman": {
+ "blog": "https://github.com/ashgillman",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Ashley G. Gillman"
+ },
+ "drmowinckels": {
+ "affiliation": "University of Oslo",
+ "blog": "https://github.com/drmowinckels",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Athanasia Monika Mowinckel",
+ "orcid": "0000-0002-5756-0223"
+ },
+ "aysegul_gunduz": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Aysegul Gunduz"
+ },
+ "azeez_adebimpe": {
+ "affiliation": "University of Pennsylvania",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Azeez Adebimpe",
+ "orcid": "0000-0001-9049-0135"
+ },
+ "b_nolan_nichols": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "B. Nolan Nichols"
+ },
+ "balint_kincses": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Balint Kincses"
+ },
+ "musicinmybrain": {
+ "blog": "https://github.com/musicinmybrain",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Benjamin Beasley",
+ "publish_email": true
+ },
+ "benjamin_dichter": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Benjamin Dichter"
+ },
+ "benjamin_gagl": {
+ "affiliation": "University of Vienna",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Benjamin Gagl",
+ "orcid": "0000-0002-2339-6293"
+ },
+ "bthirion": {
+ "blog": "https://github.com/bthirion",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Bertrand Thirion",
+ "orcid": "0000-0001-5018-7895"
+ },
+ "voytek": {
+ "affiliation": "UC San Diego, Cognitive Science and Data Science, La Jolla, CA, 92093, USA",
+ "blog": "https://www.voyteklab.com/",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Bradley Voytek",
+ "orcid": "0000-0003-1640-2525",
+ "publish_email": false
+ },
+ "brett_l_foster": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Brett L. Foster"
+ },
+ "brian_a_wandell": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Brian A. Wandell"
+ },
+ "brian_n_lundstrom": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Brian N. Lundstrom"
+ },
+ "cmaumet": {
+ "affiliation": "Inria, Univ Rennes, CNRS, Inserm, Rennes, 35042, France",
+ "blog": "https://camillemaumet.com/",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Camille Maumet",
+ "orcid": "0000-0002-6290-553X",
+ "publish_email": false
+ },
+ "carlo_miniussi": {
+ "affiliation": "University of Trento",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Carlo Miniussi",
+ "orcid": "0000-0002-5436-4745"
+ },
+ "chloé_pasturel": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Chloé Pasturel"
+ },
+ "chris_benjamin": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Chris Benjamin"
+ },
+ "chris_gahnström": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Chris Gahnström"
+ },
+ "choldgraf": {
+ "bio": "I work with Project Jupyter and other open communities to build tools and solve problems for scientific research and education.",
+ "blog": "http://chrisholdgraf.com",
+ "contributions": [
+ "doc",
+ "ideas",
+ "code"
+ ],
+ "name": "Chris Holdgraf",
+ "orcid": "0000-0002-9420-9301"
+ },
+ "chrisgorgo": {
+ "affiliation": "Google LLC",
+ "blog": "http://chrisgorgolewski.org",
+ "contributions": [
+ "doc",
+ "code",
+ "question",
+ "ideas",
+ "fundingFinding",
+ "talk",
+ "blog",
+ "example",
+ "plugin"
+ ],
+ "email": "krzysztof.gorgolewski@gmail.com",
+ "name": "Chris J. Gorgolewski",
+ "orcid": "0000-0003-3321-7583"
+ },
+ "neurolabusc": {
+ "affiliation": "University of South Carolina, Department of Psychology, Columbia, SC, 29016, USA",
+ "blog": "https://www.mricro.com",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "email": "crorden6@gmail.com",
+ "name": "Chris Rorden",
+ "orcid": "0000-0002-7554-6142",
+ "publish_email": true
+ },
+ "christian_büchel": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Christian Büchel"
+ },
+ "TheChymera": {
+ "affiliation": "Dartmouth College, PBS, Hanover NH/USA",
+ "bio": "I bring Reproducibility, Freedom, and Open Source to Neuroscience, Academic Publishing, Software, and Life.",
+ "blog": "http://www.chymera.eu",
+ "contributions": [
+ "code",
+ "doc"
+ ],
+ "email": "chr@chymera.eu",
+ "name": "Christian Horea",
+ "orcid": "0000-0001-7037-2449",
+ "publish_email": true
+ },
+ "christinerogers": {
+ "affiliation": "McGill Centre for Integrative Neuroscience, Montreal Neurological Institute",
+ "contributions": [
+ "data",
+ "doc",
+ "ideas",
+ "infra",
+ "tool"
+ ],
+ "email": "christine.rogers@mcgill.ca",
+ "github": "christinerogers",
+ "name": "Christine Rogers",
+ "orcid": "0000-0002-9893-8448"
+ },
+ "ChristophePhillips": {
+ "affiliation": "University of Liège, GIGA CRC in vivo imaging, Liège, 4000, Belgium",
+ "blog": "https://www.uliege.be/cms/c_9054334/en/directory/?uid=U016440",
+ "contributions": [
+ "doc"
+ ],
+ "email": "c.phillips@uliege.be",
+ "name": "Christophe Phillips",
+ "orcid": "0000-0002-4990-425X",
+ "publish_email": true
+ },
+ "christopher_j_honey": {
+ "affiliation": "Johns Hopkins University, Psychological and Brain Sciences, Baltimore, MD, 21218, United States",
+ "blog": "https://www.honeylab.org",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Christopher J. Honey",
+ "orcid": "0000-0002-0745-5089",
+ "publish_email": false
+ },
+ "effigies": {
+ "blog": "https://github.com/effigies",
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "design",
+ "example",
+ "ideas",
+ "plugin",
+ "review",
+ "tool",
+ "talk",
+ "data",
+ "eventOrganizing",
+ "maintenance"
+ ],
+ "email": "effigies@gmail.com",
+ "name": "Christopher J. Markiewicz"
+ },
+ "christopher_lee-messer": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Christopher Lee-Messer"
+ },
+ "clara_moreau": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Clara Moreau"
+ },
+ "clint_hansen": {
+ "affiliation": "Kiel University",
+ "contributions": [
+ "doc",
+ "ideas"
+ ],
+ "email": "c.hansen@neurologie.uni-kiel.de",
+ "name": "Clint Hansen"
+ },
+ "CPernet": {
+ "affiliation": "Neurobiology Research Unit, Copenhagen University Hospital Rigshospitalet, DK-2100 Copenhagen, Denmark",
+ "blog": "https://cpernet.github.io/",
+ "contributions": [
+ "question",
+ "blog",
+ "doc",
+ "design",
+ "example",
+ "eventOrganizing",
+ "ideas",
+ "talk"
+ ],
+ "email": "wamcyril@gmail.com",
+ "name": "Cyril Pernet",
+ "orcid": "0000-0003-4010-4632",
+ "publish_email": true
+ },
+ "DrCyPhi": {
+ "affiliation": "Georgia State University",
+ "blog": "https://trendscenter.org/cyrus-eierud/",
+ "contributions": [
+ "doc"
+ ],
+ "email": "ceierud@gsu.edu",
+ "name": "Cyrus Eierud",
+ "orcid": "0000-0002-9942-676X",
+ "publish_email": true
+ },
+ "dasturge": {
+ "bio": "Computer Vision, Dataflow Programming, and Deep Learning",
+ "blog": "https://dasturge.github.io",
+ "contributions": [
+ "code"
+ ],
+ "name": "D. Sturgeon"
+ },
+ "dlevitas": {
+ "affiliation": "Indiana University, Psychological & Brain Sciences, Bloomington, IN, 47405, USA",
+ "blog": "https://github.com/dlevitas",
+ "contributions": [
+ "doc"
+ ],
+ "email": "dlevitas@iu.edu",
+ "name": "Dan Levitas",
+ "orcid": "0000-0003-2279-7447",
+ "publish_email": true
+ },
+ "danlurie": {
+ "blog": "https://github.com/danlurie",
+ "contributions": [
+ "ideas",
+ "doc",
+ "tool",
+ "plugin",
+ "code",
+ "question"
+ ],
+ "name": "Dan Lurie"
+ },
+ "daniel_a_handwerker": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Daniel A. Handwerker"
+ },
+ "david_alsop": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "David Alsop"
+ },
+ "dboas": {
+ "affiliation": "Boston University, Biomedical Engineering, Boston, MA 02215 USA",
+ "blog": "http://bu.edu/neurophotonics",
+ "contributions": [
+ "doc"
+ ],
+ "email": "dboas@bu.edu",
+ "name": "David Boas",
+ "orcid": "0000-0002-6709-7711",
+ "publish_email": true
+ },
+ "dmgroppe": {
+ "affiliation": "Persyst Development Corporation, R&D, Toronto, ON M4K 1W8 Canada",
+ "blog": "https://github.com/dmgroppe",
+ "contributions": [
+ "doc"
+ ],
+ "email": "david.m.groppe@gmail.com",
+ "name": "David Groppe",
+ "orcid": "0000-0002-3282-2514",
+ "publish_email": true
+ },
+ "dbkeator": {
+ "affiliation": "Change Your Brain, Change Your Life Foundation",
+ "blog": "https://github.com/dbkeator",
+ "contributions": [
+ "doc"
+ ],
+ "name": "David Keator",
+ "orcid": "0000-0001-5281-5576"
+ },
+ "david_mcalpine": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "David McAlpine"
+ },
+ "david_thomas": {
+ "contributions": [
+ "doc",
+ "data"
+ ],
+ "name": "David Thomas"
+ },
+ "dejan_draschkow": {
+ "affiliation": "University of Oxford",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Dejan Draschkow",
+ "orcid": "0000-0003-1354-4835"
+ },
+ "dkp": {
+ "affiliation": "University of Arizona, RII, Tucson Arizona 85721 USA",
+ "blog": "https://profiles.arizona.edu/person/dkp",
+ "contributions": [
+ "doc"
+ ],
+ "email": "dkp@arizona.edu",
+ "name": "Dianne Patterson",
+ "orcid": "0000-0001-7518-3110",
+ "publish_email": true
+ },
+ "DimitriPapadopoulos": {
+ "blog": "https://github.com/DimitriPapadopoulos",
+ "contributions": [
+ "doc",
+ "example",
+ "ideas",
+ "question",
+ "code"
+ ],
+ "name": "Dimitri Papadopoulos Orfanos"
+ },
+ "dmitry_petrov": {
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Dmitry Petrov"
+ },
+ "dorahermes": {
+ "blog": "https://github.com/dorahermes",
+ "contributions": [
+ "doc",
+ "code",
+ "tutorial",
+ "fundingFinding",
+ "ideas"
+ ],
+ "name": "Dora Hermes"
+ },
+ "DorienHuijser": {
+ "affiliation": "Utrecht University",
+ "blog": "https://github.com/DorienHuijser",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Dorien Huijser",
+ "orcid": "0000-0003-3282-8083"
+ },
+ "dngreve": {
+ "blog": "https://github.com/dngreve",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Douglas N. Greve"
+ },
+ "duncan_macleod": {
+ "contributions": [
+ "doc",
+ "infra"
+ ],
+ "name": "Duncan Macleod"
+ },
+ "dungscout96": {
+ "affiliation": "Swartz Center for Computational Neuroscience",
+ "blog": "https://github.com/dungscout96",
+ "contributions": [
+ "doc",
+ "code",
+ "tool",
+ "ideas"
+ ],
+ "email": "dutruong@ucsd.edu",
+ "github": "dungscout96",
+ "name": "Dung Truong"
+ },
+ "Shotgunosine": {
+ "blog": "https://github.com/Shotgunosine",
+ "contributions": [
+ "doc",
+ "code",
+ "tool"
+ ],
+ "name": "Dylan Nielson",
+ "orcid": "0000-0003-4613-6643"
+ },
+ "eort": {
+ "affiliation": "Heinrich-Heine-University, Department of Experimental Psychology, Dusseldorf, 40224, Germany",
+ "blog": "https://github.com/eort",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Eduard Ort",
+ "orcid": "0000-0001-5546-3561",
+ "publish_email": false
+ },
+ "eleonora_marcantoni": {
+ "affiliation": "IRCCS Istituto Centro San Giovanni di Dio Fatebenefratelli di Brescia",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Eleonora Marcantoni",
+ "orcid": "0000-0003-1137-4983"
+ },
+ "elizabeth_bock": {
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "name": "Elizabeth Bock"
+ },
+ "emdupre": {
+ "affiliation": "Stanford University, Department of Psychology, Stanford, CA, 94063, USA",
+ "bio": "Neuroscience graduate student at McGill University",
+ "blog": "https://elizabeth-dupre.com",
+ "contributions": [
+ "doc",
+ "example",
+ "fundingFinding",
+ "ideas",
+ "question",
+ "code"
+ ],
+ "name": "Elizabeth DuPre",
+ "orcid": "0000-0003-1358-196X",
+ "publish_email": false
+ },
+ "elke_warmerdam": {
+ "affiliation": "Saarland University",
+ "contributions": [
+ "data",
+ "doc"
+ ],
+ "email": "elke.warmerdam@uni-saarland.de",
+ "name": "Elke Warmerdam"
+ },
+ "erdalkaraca": {
+ "blog": "https://github.com/erdalkaraca",
+ "contributions": [
+ "code"
+ ],
+ "name": "Erdal Karaca"
+ },
+ "ericearl": {
+ "affiliation": "NIMH Data Science & Sharing Team, Bethesda, MD, 20892, USA",
+ "bio": "Neuroinformaticist acting as a Data Scientist in the NIMH Data Science & Sharing Team",
+ "blog": "https://ericearl.github.io/",
+ "contributions": [
+ "doc",
+ "question",
+ "bug",
+ "maintenance",
+ "tool",
+ "ideas",
+ "code"
+ ],
+ "email": "eric.earl@nih.gov",
+ "name": "Eric A. Earl",
+ "orcid": "0000-0001-5512-0083",
+ "publish_email": true
+ },
+ "rikAchten": {
+ "blog": "https://github.com/rikAchten",
+ "contributions": [
+ "doc",
+ "data",
+ "userTesting"
+ ],
+ "name": "Eric Achten"
+ },
+ "eric_bridgeford": {
+ "contributions": [
+ "doc",
+ "tool"
+ ],
+ "name": "Eric Bridgeford"
+ },
+ "edickie": {
+ "blog": "http://imaging-genetics.camh.ca/",
+ "contributions": [
+ "doc",
+ "ideas",
+ "review",
+ "talk",
+ "question",
+ "code"
+ ],
+ "name": "Erin W. Dickie"
+ },
+ "ethan_blackwood": {
+ "affiliation": "University of Pennsylvania",
+ "contributions": [
+ "review",
+ "doc"
+ ],
+ "name": "Ethan Blackwood",
+ "orcid": "0000-0002-3049-0640"
+ },
+ "eduff": {
+ "affiliation": "UK Dementia Research Institute, Imperial College London, UK",
+ "blog": "http://eduff.github.io",
+ "contributions": [
+ "doc"
+ ],
+ "email": "eduff@imperial.ac.uk",
+ "name": "Eugene P. Duff",
+ "orcid": "0000-0001-8795-5472",
+ "publish_email": true
+ },
+ "ezequiel_mikulan": {
+ "affiliation": "University of Milan",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Ezequiel Mikulan",
+ "orcid": "0000-0001-7259-6120"
+ },
+ "felipe_orihuela-espina": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Felipe Orihuela-Espina"
+ },
+ "fidel_alfaro_almagro": {
+ "contributions": [
+ "question",
+ "doc",
+ "example",
+ "plugin"
+ ],
+ "name": "Fidel Alfaro Almagro"
+ },
+ "filip_szczepankiewicz": {
+ "affiliation": "Lund University",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Filip Szczepankiewicz",
+ "orcid": "0000-0002-5251-587X"
+ },
+ "filippocastelli": {
+ "affiliation": "University of Florence, European Laboratory for NonLinear Spectroscopy, Sesto Fiorentino, (FI), 50019, Italy",
+ "blog": "https://github.com/filippocastelli",
+ "contributions": [
+ "doc",
+ "data"
+ ],
+ "email": "castelli@lens.unifi.it",
+ "name": "Filippo Maria Castelli",
+ "orcid": "0000-0001-8170-8905",
+ "publish_email": true
+ },
+ "francopestilli": {
+ "bio": "{Cloud, Code, Science} Data {Human, \r\nBrain, Behavior}",
+ "blog": "https://liberalarts.utexas.edu/psychology/faculty/fp4834",
+ "contributions": [
+ "doc",
+ "code",
+ "design",
+ "example",
+ "ideas",
+ "review",
+ "tool",
+ "eventOrganizing",
+ "fundingFinding",
+ "infra"
+ ],
+ "name": "Franco Pestilli"
+ },
+ "franklin-feingold": {
+ "affiliation": "Stanford University",
+ "bio": "BIDS: https://github.com/bids-standard/bids-specification\r\n\r\nOpenNeuro: https://github.com/OpenNeuroOrg/openneuro",
+ "blog": "https://github.com/franklin-feingold",
+ "contributions": [
+ "eventOrganizing",
+ "blog",
+ "tutorial",
+ "question",
+ "ideas",
+ "design",
+ "talk",
+ "review",
+ "infra",
+ "content",
+ "projectManagement",
+ "code"
+ ],
+ "name": "Franklin W. Feingold",
+ "orcid": "0000-0002-6533-2909"
+ },
+ "françois_tadel": {
+ "contributions": [
+ "doc",
+ "plugin",
+ "example"
+ ],
+ "name": "François Tadel"
+ },
+ "gaiarizzo": {
+ "affiliation": "Invicro, Burlington Danes Building, Imperial College London, Hammersmith Hospital, Du Cane Road, London, W12 0NN, UK",
+ "blog": "http://fair.dei.unipd.it/gaia-rizzo/",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Gaia Rizzo",
+ "orcid": "0000-0001-7272-8576",
+ "publish_email": false
+ },
+ "afni-gangc": {
+ "affiliation": "National Institutes of Health, Scientific and Statistical Computing Core, NIMH, Bethesda, MD 20892, USA",
+ "blog": "https://github.com/afni-gangc",
+ "contributions": [
+ "doc"
+ ],
+ "email": "gangchen@mail.nih.gov",
+ "name": "Gang Chen",
+ "orcid": "0000-0002-2960-089X",
+ "publish_email": true
+ },
+ "GaelVaroquaux": {
+ "blog": "https://github.com/GaelVaroquaux",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Gaël Varoquaux"
+ },
+ "ghisvail": {
+ "bio": "Former research scientist turned software engineer, on a mission to improve tooling for medical research one project at a time.",
+ "blog": "https://github.com/ghisvail",
+ "contributions": [
+ "code"
+ ],
+ "name": "Ghislain Vaillant",
+ "orcid": "0000-0003-0267-3033"
+ },
+ "GiacomoBert": {
+ "affiliation": "Centro San Giovanni di Dio Fatebenefratelli",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Giacomo Bertazzoli",
+ "orcid": "0000-0003-1624-2576"
+ },
+ "giacomo_guidali": {
+ "affiliation": "IRCCS Centro San Giovanni di Dio Fatebenefratelli",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Giacomo Guidali",
+ "orcid": "0000-0002-3741-0404"
+ },
+ "gmazzamuto": {
+ "affiliation": "National Institute of Optics, National Research Council",
+ "blog": "https://github.com/gmazzamuto",
+ "contributions": [
+ "doc",
+ "data"
+ ],
+ "email": "mazzamuto@lens.unifi.it",
+ "name": "Giacomo Mazzamuto",
+ "orcid": "0000-0003-3077-3904",
+ "publish_email": true
+ },
+ "Gilles86": {
+ "blog": "https://github.com/Gilles86",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Gilles de Hollander"
+ },
+ "gio_piantoni": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Gio Piantoni"
+ },
+ "gitte_m._knudsen": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Gitte M. Knudsen"
+ },
+ "giulio_castegnaro": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Giulio Castegnaro"
+ },
+ "giuseppe_gallitto": {
+ "affiliation": "Essen University Hospital",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Giuseppe Gallitto",
+ "orcid": "0000-0001-5185-0206"
+ },
+ "graham_searle": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Graham Searle"
+ },
+ "mathesong": {
+ "blog": "http://www.granvillematheson.com",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Granville J. Matheson"
+ },
+ "gkiar": {
+ "affiliation": "Child Mind Institute",
+ "blog": "https://github.com/gkiar",
+ "contributions": [
+ "doc",
+ "code",
+ "design",
+ "tool"
+ ],
+ "name": "Gregory Kiar",
+ "orcid": "0000-0001-8915-496X"
+ },
+ "thinknoack": {
+ "blog": "https://github.com/thinknoack",
+ "contributions": [
+ "doc",
+ "code",
+ "test"
+ ],
+ "name": "Gregory Noack"
+ },
+ "greydongilmore": {
+ "bio": "Ph.D. Candidate in Biomedical Engineering",
+ "blog": "http://greydongilmore.com",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "email": "ggilmore@uwo.ca",
+ "name": "Greydon Gilmore",
+ "orcid": "0000-0001-7523-5734"
+ },
+ "gllmflndn": {
+ "affiliation": "University College London",
+ "blog": "https://www.fil.ion.ucl.ac.uk/team/spm-team/",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Guillaume Flandin",
+ "orcid": "0000-0003-0077-7859"
+ },
+ "gunnar_schaefer": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Gunnar Schaefer",
+ "orcid": "0000-0001-9661-2121"
+ },
+ "GNilsonne": {
+ "affiliation": "Karolinska Institutet, Department of Clinical Neuroscience, Stockholm, 17177, Sweden",
+ "blog": "https://nilsonne.net/about/",
+ "contributions": [
+ "doc"
+ ],
+ "email": "gustav.nilsonne@ki.se",
+ "name": "Gustav Nilsonne",
+ "orcid": "0000-0001-5273-0150",
+ "publish_email": true
+ },
+ "hamish_innes-brown": {
+ "affiliation": "Oticon A/S, Eriksholm Research Centre, Snekkersten, Denmark",
+ "contributions": [
+ "doc"
+ ],
+ "email": "hain@eriksholm.com",
+ "name": "Hamish Innes-Brown",
+ "orcid": "0000-0002-1512-2823",
+ "publish_email": true
+ },
+ "hanne_d._hansen": {
+ "affiliation": "Copenhagen University Hospital, Neurobiology Research Unit, Section 8057, Blegdamsvej 9, 2100 Copenhagen, Denmark",
+ "contributions": [
+ "doc"
+ ],
+ "email": "hanne.d.hansen@nru.dk",
+ "name": "Hanne D. Hansen",
+ "orcid": "0000-0001-5564-7627",
+ "publish_email": true
+ },
+ "hanzhang_lu": {
+ "affiliation": "Johns Hopkins Medicine",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Hanzhang Lu",
+ "orcid": "0000-0003-3871-1564"
+ },
+ "htwangtw": {
+ "affiliation": "CRIUGM, Montreal, Quebec, H3W 1W5, Canada",
+ "blog": "https://github.com/htwangtw",
+ "contributions": [
+ "doc",
+ "bug"
+ ],
+ "email": "htwangtw@gmail.com",
+ "name": "Hao-Ting Wang",
+ "orcid": "0000-0003-4078-2038",
+ "publish_email": true
+ },
+ "helenacockx": {
+ "affiliation": "Radboud University",
+ "blog": "https://github.com/helenacockx",
+ "contributions": [
+ "doc",
+ "ideas",
+ "question"
+ ],
+ "email": "helena.cockx@donders.ru.nl",
+ "github": "helenacockx",
+ "name": "Helena Cockx",
+ "orcid": "0000-0001-7782-7178"
+ },
+ "HenkMutsaerts": {
+ "affiliation": "Amsterdam University Medical Centers, Radiology and Nuclear Department, Amsterdam, 1013 EG, The Netherlands",
+ "blog": "http://www.ExploreASL.org",
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "example",
+ "eventOrganizing",
+ "ideas",
+ "projectManagement",
+ "talk",
+ "userTesting"
+ ],
+ "email": "h.j.mutsaerts@amsterdamumc.nl",
+ "name": "Henk Mutsaerts",
+ "orcid": "0000-0003-0894-0307",
+ "publish_email": true
+ },
+ "hernando_ombao": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Hernando Ombao"
+ },
+ "Hboni": {
+ "blog": "https://github.com/Hboni",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Hugo Boniface"
+ },
+ "ilkayisik": {
+ "affiliation": "Max Planck Institute for Empirical Aesthetics",
+ "blog": "https://github.com/ilkayisik",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Ilkay Isik",
+ "orcid": "0000-0002-1652-9297"
+ },
+ "IlonaLipp": {
+ "blog": "https://github.com/IlonaLipp",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Ilona Lipp"
+ },
+ "INCF": {
+ "blog": "https://github.com/INCF",
+ "contributions": [
+ "financial",
+ "eventOrganizing"
+ ],
+ "name": "International Neuroinformatics Coordinating Facility"
+ },
+ "irisgroen": {
+ "blog": "https://github.com/irisgroen",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Iris Groen"
+ },
+ "isla_staden": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Isla Staden",
+ "orcid": "0000-0002-0795-1154"
+ },
+ "jaap_von_der_aar": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Jaap von der Aar"
+ },
+ "kaczmarj": {
+ "blog": "https://github.com/kaczmarj",
+ "contributions": [
+ "doc",
+ "tool",
+ "infra"
+ ],
+ "name": "Jakub Kaczmarzyk",
+ "orcid": "0000-0002-5544-7577"
+ },
+ "james_gholam": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "James Gholam",
+ "orcid": "0000-0003-4513-2341"
+ },
+ "jdkent": {
+ "blog": "https://github.com/jdkent",
+ "contributions": [
+ "question",
+ "code"
+ ],
+ "name": "James Kent"
+ },
+ "schoffelen": {
+ "affiliation": "Radboud University Nijmegen, Donders Institute, Nijmegen, 6500 HB, The Netherlands",
+ "blog": "https://github.com/schoffelen",
+ "contributions": [
+ "doc"
+ ],
+ "email": "jm.schoffelen@gmail.com",
+ "name": "Jan Mathijs Schoffelen",
+ "orcid": "0000-0003-0923-6610",
+ "publish_email": true
+ },
+ "jan-petr": {
+ "affiliation": "Helmholtz-Zentrum Dresden-Rossendorf, Dresden, 01309, Germany",
+ "blog": "https://www.exploreasl.org",
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "data",
+ "example",
+ "eventOrganizing",
+ "ideas",
+ "projectManagement",
+ "test",
+ "talk"
+ ],
+ "email": "j.petr@hzdr.de",
+ "name": "Jan Petr",
+ "orcid": "0000-0002-3201-6002",
+ "publish_email": true
+ },
+ "jan-mathijs_schoffelen": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Jan-Mathijs Schoffelen"
+ },
+ "jbpoline": {
+ "blog": "https://github.com/jbpoline",
+ "contributions": [
+ "doc",
+ "talk",
+ "ideas",
+ "design",
+ "code"
+ ],
+ "name": "Jean-Baptiste Poline",
+ "orcid": "0000-0002-9794-749X"
+ },
+ "jchoude": {
+ "affiliation": "Université de Sherbrooke",
+ "blog": "http://scil.dinf.usherbrooke.ca/jchoude",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "email": "jean.christophe.houde@gmail.com",
+ "name": "Jean-Christophe Houde",
+ "orcid": "0000-0003-3026-021X"
+ },
+ "jean-dominique_gallezot": {
+ "affiliation": "Yale School of Medicine, Department of Radiology and Biomedical Imaging, New Haven, CT, 06511, USA",
+ "contributions": [
+ "doc"
+ ],
+ "email": "jean-dominique.gallezot@yale.edu",
+ "name": "Jean-Dominique Gallezot",
+ "orcid": "0000-0003-0399-8374",
+ "publish_email": true
+ },
+ "jean-philippe_lachaux": {
+ "affiliation": "INSERM",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Jean-Philippe Lachaux",
+ "orcid": "0000-0002-9459-0667"
+ },
+ "jmumford": {
+ "affiliation": "Stanford, Department of Psychology, Stanford, CA",
+ "blog": "https://jeanettemumford.org/",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Jeanette Mumford",
+ "publish_email": false
+ },
+ "jeffrey_g_ojemann": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Jeffrey G. Ojemann"
+ },
+ "jgrethe": {
+ "blog": "http://profiles.ucsd.edu/jeffrey.grethe",
+ "contributions": [
+ "question",
+ "bug",
+ "tutorial",
+ "talk",
+ "code"
+ ],
+ "name": "Jeffrey S. Grethe"
+ },
+ "JegouA": {
+ "blog": "https://github.com/JegouA",
+ "contributions": [
+ "code"
+ ],
+ "name": "JegouA"
+ },
+ "jrdalenberg": {
+ "affiliation": "University of Groningen, University Medical Center Groningen, Department of Neurology, Movement Disorders Groningen, Hanzeplein 1, 9713 GZ, Groningen",
+ "blog": "https://github.com/jrdalenberg",
+ "contributions": [
+ "doc"
+ ],
+ "email": "j.r.dalenberg@umcg.nl",
+ "name": "Jelle Dalenberg",
+ "orcid": "0000-0001-8580-5358",
+ "publish_email": true
+ },
+ "jeremy_moreau": {
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "name": "Jeremy Moreau"
+ },
+ "jessica_a_turner": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Jessica A. Turner"
+ },
+ "jochem_rieger": {
+ "affiliation": "Oldenburg University, Dept. of Psychology, 26129, Germany",
+ "blog": "https://uol.de/en/applied-neurocognitive-psychology",
+ "contributions": [
+ "doc"
+ ],
+ "email": "jochem.rieger@uni-oldenburg.de",
+ "name": "Jochem Rieger",
+ "orcid": "0000-0003-0955-2306",
+ "publish_email": true
+ },
+ "john_detre": {
+ "affiliation": "University of Pennsylvania",
+ "contributions": [
+ "doc"
+ ],
+ "name": "John Detre",
+ "orcid": "0000-0002-8115-6343"
+ },
+ "john_pellman": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "John Pellman",
+ "orcid": "0000-0001-6810-4461"
+ },
+ "jwodder": {
+ "blog": "https://github.com/jwodder",
+ "contributions": [
+ "code"
+ ],
+ "name": "John T. Wodder"
+ },
+ "jokedurnez": {
+ "affiliation": "Stanford University",
+ "blog": "https://github.com/jokedurnez",
+ "contributions": [
+ "doc",
+ "tool",
+ "code"
+ ],
+ "name": "Joke Durnez",
+ "orcid": "0000-0001-9030-2202"
+ },
+ "jhlegarreta": {
+ "affiliation": "Université de Sherbrooke, Department of Computer Science, Sherbrooke, (Québec), J1K 2R1, Canada",
+ "bio": "PhD student at the Université de Sherbrooke",
+ "blog": "https://github.com/jhlegarreta",
+ "contributions": [
+ "code",
+ "doc"
+ ],
+ "email": "jon.haitz.legarreta@gmail.com",
+ "name": "Jon Haitz Legarreta Gorroño",
+ "orcid": "0000-0002-9661-1396",
+ "publish_email": true
+ },
+ "jonathan_c_lau": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Jonathan C. Lau"
+ },
+ "JWinawer": {
+ "blog": "https://github.com/JWinawer",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Jonathan Winawer"
+ },
+ "wadqc": {
+ "affiliation": "Amsterdam University Medical Centers, Radiology and Nuclear Medicine, Amsterdam, 1081 HV, The Netherlands",
+ "blog": "https://github.com/wadqc",
+ "contributions": [
+ "doc"
+ ],
+ "email": "jpa.kuijer@amsterdamumc.nl",
+ "name": "Joost Kuijer",
+ "orcid": "0000-0002-4181-0427",
+ "publish_email": true
+ },
+ "jose_manuel_saborit": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Jose Manuel Saborit"
+ },
+ "joseph_wexler": {
+ "affiliation": "Loyola University Chicago",
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "name": "Joseph Wexler",
+ "orcid": "0000-0001-9086-8484"
+ },
+ "JosephGWoods": {
+ "affiliation": "University of Oxford, Nuffield Department of Clinical Neuroscience, Oxford, UK",
+ "blog": "https://www.ndcn.ox.ac.uk/team/joseph-woods",
+ "contributions": [
+ "doc"
+ ],
+ "email": "joseph.woods@ndcn.ox.ac.uk",
+ "name": "Joseph Woods",
+ "orcid": "0000-0002-0329-824X",
+ "publish_email": true
+ },
+ "guiomar": {
+ "affiliation": "Indiana University",
+ "bio": "Open neuroimaging: standards (BIDS), tools (Brainlife, Brainstorm, Hermes), data repositories (OMEGA).\r\nChair BIDS Steering Group",
+ "blog": "http://guiomarniso.com",
+ "contributions": [
+ "ideas",
+ "design",
+ "fundingFinding",
+ "review",
+ "eventOrganizing",
+ "blog",
+ "tool",
+ "bug",
+ "code",
+ "data",
+ "tutorial",
+ "question",
+ "doc",
+ "example",
+ "talk"
+ ],
+ "name": "Julia Guiomar Niso Galán",
+ "orcid": "0000-0001-5872-8924"
+ },
+ "juliasprenger": {
+ "affiliation": "Institute de Neuroscience de la Timone, CNRS, Marseille, 13005, France",
+ "blog": "https://github.com/juliasprenger",
+ "contributions": [
+ "doc"
+ ],
+ "email": "julia.sprenger@univ-amu.fr",
+ "name": "Julia Sprenger",
+ "orcid": "0000-0002-9986-7477",
+ "publish_email": true
+ },
+ "jcohenadad": {
+ "affiliation": "Polytechnique Montreal, Department of Electrical Engineering, Montreal, QC, Canada",
+ "blog": "https://www.neuro.polymtl.ca",
+ "contributions": [
+ "doc",
+ "data",
+ "ideas"
+ ],
+ "name": "Julien Cohen-Adad",
+ "orcid": "0000-0003-3662-9532",
+ "publish_email": false
+ },
+ "JuliusWelzel": {
+ "affiliation": "Kiel University",
+ "blog": "https://github.com/JuliusWelzel",
+ "contributions": [
+ "doc",
+ "example",
+ "bug",
+ "code",
+ "data",
+ "ideas",
+ "question",
+ "userTesting"
+ ],
+ "email": "julius.welzel@gmail.com",
+ "github": "JuliusWelzel",
+ "name": "Julius Welzel",
+ "orcid": "0000-0001-8958-0934",
+ "publish_email": true
+ },
+ "kai_j_miller": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Kai J. Miller"
+ },
+ "Kangjoo": {
+ "affiliation": "Yale School of Medicine",
+ "blog": "https://github.com/Kangjoo",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Kangjoo Lee",
+ "orcid": "0000-0002-7760-8079"
+ },
+ "katjaq": {
+ "affiliation": "Institut Pasteur",
+ "blog": "https://github.com/katjaq",
+ "contributions": [
+ "tool"
+ ],
+ "name": "Katja Heuer",
+ "orcid": "0000-0002-7237-0196"
+ },
+ "VisLab": {
+ "affiliation": "University of Texas at San Antonio Department of Computer Science San Antonio, TX 78249 USA",
+ "blog": "http://www.cs.utsa.edu/~krobbins",
+ "contributions": [
+ "code",
+ "doc",
+ "bug"
+ ],
+ "email": "kay.robbins@utsa.edu",
+ "name": "Kay Robbins",
+ "orcid": "0000-0002-7147-5797",
+ "publish_email": true
+ },
+ "kevin_larcher": {
+ "contributions": [
+ "question"
+ ],
+ "name": "Kevin Larcher"
+ },
+ "kimberlylray": {
+ "affiliation": "UT Austin",
+ "contributions": [
+ "doc",
+ "eventOrganizing",
+ "ideas",
+ "projectManagement"
+ ],
+ "email": "kimberlylray@gmail.com",
+ "github": "kimberlylray",
+ "name": "Kimberly Ray",
+ "orcid": "0000-0003-1302-2834"
+ },
+ "KirstieJane": {
+ "affiliation": "University of Cambridge",
+ "bio": "Programme Lead for Tools, Practices and Systems at the Alan Turing Institute. Passionate about diversity and reproducibility in all forms.",
+ "blog": "https://whitakerlab.github.io",
+ "contributions": [
+ "doc",
+ "example",
+ "fundingFinding",
+ "ideas",
+ "talk",
+ "question",
+ "code"
+ ],
+ "email": "kwhitaker@turing.ac.uk",
+ "name": "Kirstie Whitaker",
+ "orcid": "0000-0001-8498-4059"
+ },
+ "greckla": {
+ "blog": "https://github.com/greckla",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Klara Gregorova",
+ "orcid": "0000-0002-3828-9386"
+ },
+ "klaus_gramann": {
+ "affiliation": "Technical University Berlin",
+ "contributions": [
+ "doc",
+ "ideas"
+ ],
+ "email": "klaus.gramann@tu-berlin.de",
+ "name": "Klaus Gramann"
+ },
+ "KrisThielemans": {
+ "blog": "https://iris.ucl.ac.uk/iris/browse/profile?upi=KTHIE60",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Kris Thielemans"
+ },
+ "kristofer_bouchard": {
+ "affiliation": "LBNL, Scientific Data Division",
+ "blog": "https://bouchardlab.lbl.gov",
+ "contributions": [
+ "doc"
+ ],
+ "email": "kristofer.bouchard@gmail.com",
+ "name": "Kristofer Bouchard",
+ "orcid": "0000-0002-1974-4603",
+ "publish_email": true
+ },
+ "schillkg": {
+ "blog": "https://github.com/schillkg",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Kurt Schilling"
+ },
+ "laura_and_john_arnold_foundation": {
+ "contributions": [
+ "financial"
+ ],
+ "name": "Laura and John Arnold Foundation"
+ },
+ "leandro_beltrachini": {
+ "affiliation": "Cardiff University, Cardiff University Brain Research Imaging Centre (CUBRIC), UK",
+ "contributions": [
+ "doc"
+ ],
+ "email": "BeltrachiniL@cardiff.ac.uk",
+ "name": "Leandro Beltrachini",
+ "orcid": "0000-0003-4602-1416",
+ "publish_email": true
+ },
+ "LeeKamentsky": {
+ "affiliation": "MIT",
+ "blog": "https://github.com/LeeKamentsky",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Lee Kamentsky",
+ "orcid": "0000-0002-8161-3604"
+ },
+ "lennart_walger": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Lennart Walger",
+ "orcid": "0000-0002-3300-6877"
+ },
+ "lnnrtwttkhn": {
+ "affiliation": "Max Planck Institute for Human Development",
+ "blog": "https://github.com/lnnrtwttkhn",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Lennart Wittkuhn",
+ "orcid": "0000-0003-2966-6888"
+ },
+ "libertyh": {
+ "blog": "https://github.com/libertyh",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Liberty Hamilton"
+ },
+ "lpollonini": {
+ "affiliation": "University of Houston, Dept. of Engineering Technology, Houston TX 77204, USA",
+ "blog": "https://polloninilab.com",
+ "contributions": [
+ "doc"
+ ],
+ "email": "lpollonini@uh.edu",
+ "name": "Luca Pollonini",
+ "orcid": "0000-0003-2955-6355",
+ "publish_email": true
+ },
+ "luis_hernandez-garcia": {
+ "contributions": [
+ "doc",
+ "userTesting"
+ ],
+ "name": "Luis Hernandez-Garcia"
+ },
+ "lukeje": {
+ "affiliation": "Max Planck Institute for Human Cognitive and Brain Sciences",
+ "contributions": [
+ "doc",
+ "question"
+ ],
+ "github": "lukeje",
+ "name": "Luke J. Edwards",
+ "orcid": "0000-0002-8320-7298"
+ },
+ "lzehl": {
+ "affiliation": "Forschungszentrum Jülich",
+ "blog": "https://github.com/lzehl",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Lyuba Zehl",
+ "orcid": "0000-0002-5947-9939"
+ },
+ "jasmainak": {
+ "blog": "http://jasmainak.github.io/",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Mainak Jas",
+ "orcid": "0000-0002-3199-9027"
+ },
+ "manjari_narayan": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Manjari Narayan"
+ },
+ "manuel_mercier": {
+ "affiliation": "Dynamics of Cognitive Processes Group Institut de Neurosciences des Systèmes, INS - UMR 1106 Inserm Aix-Marseille Université, France",
+ "contributions": [
+ "doc",
+ "ideas"
+ ],
+ "name": "Manuel Mercier",
+ "orcid": "0000-0001-6358-4734",
+ "publish_email": false
+ },
+ "maqsood_yaqub": {
+ "affiliation": "Amsterdam UMC, locatie VUmc",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Maqsood Yaqub",
+ "orcid": "0000-0003-2122-740X"
+ },
+ "Moo-Marc": {
+ "affiliation": "Montreal Neurological Institute-Hospital, McConnell Brain Imaging Center, Montreal, Quebec, H2A",
+ "bio": "MEG lab manager, MSc Physics, UBC 2003",
+ "blog": "https://github.com/Moo-Marc",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Marc Lalancette",
+ "orcid": "0000-0003-1161-3972",
+ "publish_email": false
+ },
+ "marcocastellaro": {
+ "affiliation": "University of Padova, Department of Information Engineering, Padova, Italy",
+ "blog": "https://github.com/marcocastellaro",
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "example",
+ "test",
+ "talk",
+ "infra"
+ ],
+ "email": "marco.castellaro@gmail.com",
+ "name": "Marco Castellaro",
+ "orcid": "0000-0002-1203-2670",
+ "publish_email": true
+ },
+ "maigva": {
+ "affiliation": "FISABIO, Join Unit Biomedical Imagin, Valencia, Valencia, 46006, Spain",
+ "blog": "https://bimcv.cipf.es",
+ "contributions": [
+ "doc"
+ ],
+ "email": "delaiglesia_mar@gva.es",
+ "name": "Maria de la Iglesia",
+ "orcid": "0000-0003-4505-8399",
+ "publish_email": true
+ },
+ "mariehbourget": {
+ "blog": "https://github.com/mariehbourget",
+ "contributions": [
+ "doc",
+ "data",
+ "code",
+ "ideas"
+ ],
+ "name": "Marie-Hélène Bourget"
+ },
+ "markmikkelsen": {
+ "affiliation": "Weill Cornell Medicine, Department of Radiology, New York, NY, 10021, USA",
+ "blog": "https://vivo.weill.cornell.edu/display/cwid-mam4041",
+ "contributions": [
+ "doc"
+ ],
+ "email": "mam4041@med.cornell.edu",
+ "name": "Mark Mikkelsen",
+ "orcid": "0000-0002-0349-3782",
+ "publish_email": true
+ },
+ "markus_morawski": {
+ "affiliation": "Universität Leipzig",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Markus Morawski",
+ "orcid": "0000-0002-3817-5186"
+ },
+ "marta_bortoletto": {
+ "affiliation": "IRCCS Centro S Giovanni di Dio Fatebenefratelli",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Marta Bortoletto",
+ "orcid": "0000-0002-8489-8043"
+ },
+ "martin_craig": {
+ "contributions": [
+ "data"
+ ],
+ "name": "Martin Craig"
+ },
+ "mnoergaard": {
+ "affiliation": "Stanford University, Department of Psychology, CA, 94304, USA",
+ "bio": "Postdoctoral Researcher @ Stanford University, Center for Reproducible Neuroscience; interested in neuroimaging, data sharing, statistics and preprocessing.",
+ "blog": "https://profiles.stanford.edu/martin-noergaard",
+ "contributions": [
+ "doc",
+ "data",
+ "code",
+ "ideas",
+ "talk"
+ ],
+ "email": "noergard@stanford.edu",
+ "name": "Martin Noergaard",
+ "orcid": "0000-0003-2131-5688",
+ "publish_email": true
+ },
+ "mszinte": {
+ "affiliation": "CNRS, Institut des Neurosciences de la Timone, Marseille, 13008, France",
+ "blog": "http://www.martinszinte.net",
+ "contributions": [
+ "doc"
+ ],
+ "email": "mail@martinszinte.net",
+ "name": "Martin Szinte",
+ "orcid": "0000-0003-2040-4005",
+ "publish_email": true
+ },
+ "martin_wilson": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Martin Wilson"
+ },
+ "martina_bulgari": {
+ "affiliation": "IRCCS Centro San Giovanni di Dio Fatebenefratelli",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Martina Bulgari",
+ "orcid": "0000-0003-1594-9617"
+ },
+ "mateuszpawlik": {
+ "blog": "https://github.com/mateuszpawlik",
+ "contributions": [
+ "doc",
+ "bug",
+ "ideas",
+ "maintenance",
+ "review"
+ ],
+ "email": "mateusz.pawlik@plus.ac.at",
+ "name": "Mateusz Pawlik"
+ },
+ "mgxd": {
+ "affiliation": "Psychology Department, Stanford University, Palo Alto, CA, 94305, USA",
+ "blog": "https://github.com/mgxd",
+ "contributions": [
+ "code",
+ "tool",
+ "talk"
+ ],
+ "email": "mathiasg@stanford.edu",
+ "name": "Mathias Goncalves",
+ "orcid": "0000-0002-7252-7771",
+ "publish_email": true
+ },
+ "mathieuboudreau": {
+ "affiliation": "Polytechnique Montréal",
+ "blog": "https://github.com/mathieuboudreau",
+ "contributions": [
+ "question",
+ "ideas",
+ "talk"
+ ],
+ "name": "Mathieu Boudreau",
+ "orcid": "0000-0002-7726-4456"
+ },
+ "matt_sanderson": {
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Matt Sanderson"
+ },
+ "matteotonietto": {
+ "affiliation": "Hoffmann-La Roche Ltd, Research and Early Development, Basel, 4070, Switzerland",
+ "blog": "https://github.com/matteotonietto",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Matteo Tonietto",
+ "orcid": "0000-0001-9591-5710",
+ "publish_email": false
+ },
+ "matthias_günther": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Matthias Günther"
+ },
+ "matthias_van_osch": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Matthias Van Osch"
+ },
+ "maureen_j_shader": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Maureen J Shader"
+ },
+ "maurice_pasternak": {
+ "contributions": [
+ "userTesting"
+ ],
+ "name": "Maurice Pasternak"
+ },
+ "MaxvandenBoom": {
+ "affiliation": "Mayo Clinic, Department of neurosurgery, Rochester, MN, USA",
+ "blog": "https://github.com/MaxvandenBoom",
+ "contributions": [
+ "code",
+ "review",
+ "doc",
+ "bug"
+ ],
+ "email": "m.a.vandenboom84@gmail.com",
+ "name": "Max A. van den Boom",
+ "orcid": "0000-0001-5481-1659",
+ "publish_email": true
+ },
+ "melanieganz": {
+ "affiliation": "University of Copenhagen, Department of Computer Science, Copenhagen, 2100, Denmark",
+ "blog": "https://sites.google.com/view/melanieganz/home",
+ "contributions": [
+ "doc",
+ "data",
+ "code",
+ "ideas",
+ "projectManagement",
+ "fundingFinding",
+ "talk"
+ ],
+ "name": "Melanie Ganz-Benjaminsen",
+ "orcid": "0000-0002-9120-8098",
+ "publish_email": false
+ },
+ "michael_chappell": {
+ "contributions": [
+ "doc",
+ "data",
+ "projectManagement"
+ ],
+ "name": "Michael Chappell"
+ },
+ "mih": {
+ "blog": "https://github.com/mih",
+ "contributions": [
+ "doc",
+ "ideas",
+ "tool",
+ "bug",
+ "talk"
+ ],
+ "name": "Michael Hanke"
+ },
+ "michael_p_harms": {
+ "contributions": [
+ "doc",
+ "test",
+ "tool"
+ ],
+ "name": "Michael P. Harms"
+ },
+ "michael_p_milham": {
+ "contributions": [
+ "example",
+ "fundingFinding"
+ ],
+ "name": "Michael P. Milham"
+ },
+ "michael_p_notter": {
+ "contributions": [
+ "question",
+ "blog",
+ "tutorial",
+ "talk",
+ "doc"
+ ],
+ "name": "Michael P. Notter"
+ },
+ "MichaelSchirner": {
+ "affiliation": "Charité—Universitätsmedizin Berlin, Department of Neurology, Berlin, 10115, Germany",
+ "blog": "https://www.brainsimulation.org",
+ "contributions": [
+ "doc"
+ ],
+ "email": "michael.schirner@charite.de",
+ "name": "Michael Schirner",
+ "orcid": "0000-0001-8227-8476",
+ "publish_email": true
+ },
+ "naveau": {
+ "affiliation": "UAR3408-US50 CYCERON, Caen, 14000, France",
+ "blog": "https://www.cyceron.fr",
+ "contributions": [
+ "bug"
+ ],
+ "name": "Mikaël Naveau",
+ "orcid": "0000-0002-4685-0057",
+ "publish_email": false
+ },
+ "nader_pouratian": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Nader Pouratian",
+ "orcid": "0000-0002-0426-3241"
+ },
+ "natalia_petridou": {
+ "affiliation": "High Field Dpt, Center for Image Sciences, University Medical Center Utrecht, 3584 CX Utrecht, NL",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Natalia Petridou",
+ "orcid": "0000-0002-0783-0387",
+ "publish_email": false
+ },
+ "national_institute_of_mental_health": {
+ "contributions": [
+ "financial"
+ ],
+ "name": "National Institute of Mental Health"
+ },
+ "nellh": {
+ "blog": "https://github.com/nellh",
+ "contributions": [
+ "code",
+ "doc",
+ "ideas",
+ "infra",
+ "review",
+ "question"
+ ],
+ "name": "Nell Hardcastle",
+ "orcid": "0000-0002-3837-0707"
+ },
+ "nicholas_traut": {
+ "contributions": [
+ "doc",
+ "tool",
+ "code"
+ ],
+ "name": "Nicholas Traut"
+ },
+ "nick_f_ramsey": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Nick F. Ramsey"
+ },
+ "nicole_c_swann": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Nicole C. Swann"
+ },
+ "nima_bigdely_shamlo": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Nima Bigdely Shamlo",
+ "orcid": "0000-0001-6403-892X"
+ },
+ "olivier_david": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Olivier David"
+ },
+ "orrin_devinsky": {
+ "affiliation": "NYU School of Medicine",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Orrin Devinsky",
+ "orcid": "0000-0003-0044-4632"
+ },
+ "oesteban": {
+ "affiliation": "Department of Radiology, Lausanne University Hospital, Lausanne, CH-1011, Switzerland",
+ "blog": "http://www.axonlab.org",
+ "contributions": [
+ "doc",
+ "tool",
+ "ideas",
+ "question",
+ "code"
+ ],
+ "email": "phd@oscaresteban.es",
+ "name": "Oscar Esteban",
+ "orcid": "0000-0001-8435-6191",
+ "publish_email": true
+ },
+ "pamela_lamontagne": {
+ "contributions": [
+ "doc",
+ "example"
+ ],
+ "name": "Pamela LaMontagne",
+ "orcid": "0000-0002-6752-8518"
+ },
+ "parulsethi": {
+ "blog": "https://github.com/parulsethi",
+ "contributions": [
+ "doc",
+ "tool",
+ "test",
+ "code"
+ ],
+ "name": "Parul Sethi"
+ },
+ "patsycle": {
+ "affiliation": "Department of Medical Imaging / Department of Medical Sciences, Ghent University Hospital / Ghent University, Ghent, Belgium",
+ "blog": "https://github.com/patsycle",
+ "contributions": [
+ "question",
+ "bug",
+ "code",
+ "doc",
+ "data",
+ "example",
+ "eventOrganizing",
+ "ideas",
+ "projectManagement",
+ "test",
+ "talk"
+ ],
+ "email": "patricia.clement@ugent.be",
+ "name": "Patricia Clement",
+ "orcid": "0000-0001-8546-0134",
+ "publish_email": true
+ },
+ "Park-Patrick": {
+ "blog": "https://github.com/Park-Patrick",
+ "contributions": [
+ "doc",
+ "example",
+ "question",
+ "code"
+ ],
+ "name": "Patrick Park"
+ },
+ "paule-joanne_toussaint": {
+ "affiliation": "McGill University Faculty of Medicine and Health Sciences",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Paule-Joanne Toussaint",
+ "orcid": "0000-0002-7446-150X"
+ },
+ "peerherholz": {
+ "affiliation": "NeuroDataScience - ORIGAMI lab, McConnell Brain Imaging Centre, Montreal Neurological Institute and Hospital, McGill University, Montreal, Québec, Canada",
+ "blog": "https://peerherholz.github.io/",
+ "contributions": [
+ "question",
+ "doc",
+ "review",
+ "tool",
+ "tutorial",
+ "talk"
+ ],
+ "email": "herholz.peer@gmail.com",
+ "name": "Peer Herholz",
+ "orcid": "0000-0002-9840-6257",
+ "publish_email": true
+ },
+ "BrainModes": {
+ "affiliation": "Charité University Medicine Berlin, Berlin Institute of Health, Berlin, 10115, Germany",
+ "blog": "https://www.brainsimulation.org",
+ "contributions": [
+ "doc"
+ ],
+ "email": "petra.ritter@charite.de",
+ "name": "Petra Ritter",
+ "orcid": "0000-0002-4643-4782",
+ "publish_email": true
+ },
+ "prioux": {
+ "blog": "https://github.com/prioux",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Pierre Rioux"
+ },
+ "pvdemael": {
+ "affiliation": "Ghent University Hospital",
+ "blog": "https://github.com/pvdemael",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Pieter Vandemaele",
+ "orcid": "0000-0002-4523-2476"
+ },
+ "raamana": {
+ "blog": "https://github.com/raamana",
+ "contributions": [
+ "code",
+ "tool"
+ ],
+ "name": "Pradeep Reddy Raamana"
+ },
+ "ccraddock": {
+ "blog": "https://github.com/ccraddock",
+ "contributions": [
+ "doc",
+ "talk"
+ ],
+ "name": "R. Cameron Craddock"
+ },
+ "Remi-Gau": {
+ "affiliation": "Université catholique de Louvain",
+ "bio": "Neuroimaging (high-res & laminar fMRI) and multisensory.\r\n\r\nToo much matlab and presentation and not enough R and python",
+ "blog": "https://remi-gau.github.io/",
+ "contributions": [
+ "doc",
+ "code",
+ "question",
+ "talk",
+ "bug",
+ "code",
+ "infra",
+ "review",
+ "tool",
+ "ideas"
+ ],
+ "email": "remi.gau@tuebingen.mpg.de",
+ "name": "Remi Gau",
+ "orcid": "0000-0002-1535-9767"
+ },
+ "hoechenberger": {
+ "affiliation": "Inria Saclay - Île-de-France Research Centre",
+ "blog": "https://hoechenberger.net/",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "email": "richard.hoechenberger@gmail.com",
+ "name": "Richard Höchenberger",
+ "orcid": "0000-0002-0380-4798"
+ },
+ "RikHenson": {
+ "affiliation": "MRC CBU, University of Cambridge, UK",
+ "blog": "http://www.mrc-cbu.cam.ac.uk/people/rik.henson/personal",
+ "contributions": [
+ "doc"
+ ],
+ "email": "Rik.Henson@mrc-cbu.cam.ac.uk",
+ "name": "Richard N. Henson",
+ "orcid": "0000-0002-0712-2639",
+ "publish_email": true
+ },
+ "robert_b._innis": {
+ "affiliation": "NIMH Intramural Research Program, Bethesda, MD 20850 USA, MD 20892 USA",
+ "blog": "https://www.nimh.nih.gov/research/research-conducted-at-nimh/research-areas/clinics-and-labs/mib/molecular-imaging-branch-mib",
+ "contributions": [
+ "doc"
+ ],
+ "email": "robert.innis@nih.gov",
+ "name": "Robert B. Innis",
+ "orcid": "0000-0003-1238-7209",
+ "publish_email": true
+ },
+ "Lestropie": {
+ "affiliation": "The Florey Institute of Neuroscience and Mental Health, Epilepsy Neuroinformatics Laboratory, Heidelberg, Victoria, 3084, Australia",
+ "blog": "http://www.mrtrix.org",
+ "contributions": [
+ "code",
+ "doc"
+ ],
+ "email": "robert.smith@florey.edu.au",
+ "name": "Robert E. Smith",
+ "orcid": "0000-0003-3636-4642",
+ "publish_email": true
+ },
+ "robert_knight": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Robert Knight"
+ },
+ "rob-luke": {
+ "blog": "https://github.com/rob-luke",
+ "contributions": [
+ "code"
+ ],
+ "name": "Robert Luke"
+ },
+ "robertoostenveld": {
+ "affiliation": "Radboud University, Donders Institute, Nijmegen, 6525 EN, The Netherlands",
+ "blog": "https://robertoostenveld.nl",
+ "contributions": [
+ "doc",
+ "tool",
+ "talk",
+ "example",
+ "tutorial",
+ "test",
+ "ideas",
+ "question",
+ "bug",
+ "blog",
+ "code",
+ "content",
+ "data",
+ "design",
+ "eventOrganizing",
+ "infra",
+ "review",
+ "userTesting",
+ "video"
+ ],
+ "email": "r.oostenveld@donders.ru.nl",
+ "name": "Robert Oostenveld",
+ "orcid": "0000-0002-1974-1293",
+ "publish_email": true
+ },
+ "r03ert0": {
+ "blog": "https://github.com/r03ert0",
+ "contributions": [
+ "tool"
+ ],
+ "name": "Roberto Toro"
+ },
+ "rohan_goyal": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Rohan Goyal"
+ },
+ "rwblair": {
+ "blog": "https://github.com/rwblair",
+ "contributions": [
+ "code"
+ ],
+ "name": "Ross W. Blair"
+ },
+ "poldrack": {
+ "affiliation": "Stanford University, Stanford, CA 94305 USA",
+ "blog": "http://www.poldracklab.org",
+ "contributions": [
+ "doc",
+ "fundingFinding",
+ "talk"
+ ],
+ "email": "russpold@stanford.edu",
+ "name": "Russell A. Poldrack",
+ "orcid": "0000-0001-6755-0259",
+ "publish_email": true
+ },
+ "remiadon": {
+ "blog": "https://github.com/remiadon",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Rémi Adon"
+ },
+ "samirdas": {
+ "blog": "https://github.com/samirdas",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Samir Das"
+ },
+ "samuel_garcia": {
+ "contributions": [
+ "ideas",
+ "review",
+ "doc"
+ ],
+ "name": "Samuel Garcia"
+ },
+ "snastase": {
+ "affiliation": "Princeton University",
+ "blog": "https://snastase.github.io/",
+ "contributions": [
+ "code"
+ ],
+ "email": "sam.nastase@gmail.com",
+ "name": "Samuel Nastase",
+ "orcid": "0000-0001-7013-5275"
+ },
+ "sara_elgayar": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Sara Elgayar"
+ },
+ "sasha_d_ambrosio": {
+ "affiliation": "University of Milan, Department of Biomedical and Clinical Sciences, Milan, 20137, Italy",
+ "contributions": [
+ "doc"
+ ],
+ "email": "sasha.dambrosio@unimi.it",
+ "name": "Sasha D'Ambrosio",
+ "orcid": "0000-0002-6600-6419",
+ "publish_email": true
+ },
+ "satra": {
+ "affiliation": "MIT, McGovern Institute for Brain Research, Cambridge, MA 02139, USA",
+ "bio": "My research interests span computer science and neuroimaging, in the areas of applied machine learning, software engineering, and clinical applications.",
+ "blog": "https://satra.cogitatum.org",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "email": "satra@mit.edu",
+ "name": "Satrajit S. Ghosh",
+ "orcid": "0000-0002-5312-6729",
+ "publish_email": true
+ },
+ "smakeig": {
+ "affiliation": "Institute for Neural Computation, University of California San Diego, La Jolla, CA 92-93-0955, USA",
+ "blog": "https://sccn.ucsd.edu/~scott",
+ "contributions": [
+ "doc"
+ ],
+ "email": "smakeig@gmail.com",
+ "name": "Scott Makeig",
+ "orcid": "0000-0002-9048-8438",
+ "publish_email": true
+ },
+ "sjeung": {
+ "affiliation": "Technical University Berlin",
+ "blog": "https://github.com/sjeung",
+ "contributions": [
+ "doc",
+ "example",
+ "bug",
+ "code",
+ "data",
+ "ideas",
+ "question",
+ "tool",
+ "userTesting"
+ ],
+ "email": "seinjeung@gmail.com",
+ "github": "sjeung",
+ "name": "Sein Jeung",
+ "orcid": "0000-0002-0247-087X",
+ "publish_email": true
+ },
+ "shashank_bansal": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Shashank Bansal"
+ },
+ "sjoerdvos": {
+ "affiliation": "University of Western Australia;Centre for Microscopy, Characterisation, and Analysis;Perth;WA;6009;Australia",
+ "blog": "https://research-repository.uwa.edu.au/en/persons/sjoerd-vos",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Sjoerd B. Vos",
+ "orcid": "0000-0002-8502-4487",
+ "publish_email": false
+ },
+ "soichih": {
+ "affiliation": "Indiana University",
+ "blog": "https://github.com/soichih",
+ "contributions": [
+ "doc",
+ "tool",
+ "bug"
+ ],
+ "name": "Soichi Hayashi",
+ "orcid": "0000-0003-3641-3491"
+ },
+ "sappelhoff": {
+ "affiliation": "Max Planck Institute for Human Development",
+ "blog": "https://www.stefanappelhoff.com",
+ "contributions": [
+ "doc",
+ "question",
+ "ideas",
+ "bug",
+ "example",
+ "code",
+ "review",
+ "test",
+ "talk",
+ "tutorial",
+ "tool",
+ "plugin",
+ "blog",
+ "maintenance",
+ "data"
+ ],
+ "email": "stefan.appelhoff@mailbox.org",
+ "name": "Stefan Appelhoff",
+ "orcid": "0000-0001-8002-0877"
+ },
+ "stephan_bickel": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Stephan Bickel"
+ },
+ "suyashdb": {
+ "blog": "https://www.linkedin.com/in/suyashb/",
+ "contributions": [
+ "doc",
+ "example",
+ "test",
+ "tool",
+ "question"
+ ],
+ "name": "Suyash Bhogawar",
+ "publish_email": false
+ },
+ "sbaillet": {
+ "blog": "https://github.com/sbaillet",
+ "contributions": [
+ "doc",
+ "fundingFinding"
+ ],
+ "name": "Sylvain Baillet"
+ },
+ "SylvainTakerkart": {
+ "affiliation": "CNRS, Institut de Neurosciences de la Timone (INT), Marseille, France",
+ "blog": "https://github.com/SylvainTakerkart",
+ "contributions": [
+ "doc"
+ ],
+ "email": "sylvain.takerkart@univ-amu.fr",
+ "name": "Sylvain Takerkart",
+ "orcid": "0000-0001-8410-0962",
+ "publish_email": true
+ },
+ "sebastientourbier": {
+ "affiliation": "Lausanne University Hospital (CHUV), Department of Clinical Neurosciences (DNC), Lausanne, 1007, Switzerland",
+ "bio": "Scientist with special interests in computational image analysis and reproducible workflows with medical application",
+ "blog": "https://github.com/sebastientourbier",
+ "contributions": [
+ "ideas",
+ "review",
+ "talk",
+ "bug",
+ "code",
+ "doc"
+ ],
+ "email": "sebastien.tourbier1@gmail.com",
+ "name": "Sébastien Tourbier",
+ "orcid": "0000-0002-4441-899X",
+ "publish_email": false
+ },
+ "sören_grothkopp": {
+ "affiliation": "Technical University Berlin",
+ "contributions": [
+ "doc",
+ "data",
+ "userTesting"
+ ],
+ "email": "s.grothkopp@secure.mailbox.org",
+ "github": "sgrothk",
+ "name": "Sören Grothkopp"
+ },
+ "tpatpa": {
+ "blog": "https://github.com/tpatpa",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Tal Pal Attia"
+ },
+ "tyarkoni": {
+ "blog": "https://github.com/tyarkoni",
+ "contributions": [
+ "code",
+ "doc",
+ "ideas",
+ "fundingFinding",
+ "plugin",
+ "review",
+ "talk",
+ "bug",
+ "design"
+ ],
+ "name": "Tal Yarkoni",
+ "orcid": "0000-0002-6558-5113"
+ },
+ "spisakt": {
+ "affiliation": "University Hospital Essen, Center for Translational and Behavioral Neuroscience, Department of Diagnostic and Interventional Radiology and Neuroradiology",
+ "blog": "https://pni-lab.github.io/",
+ "contributions": [
+ "doc"
+ ],
+ "email": "tamas.spisak@uk-essen.de",
+ "name": "Tamas Spisak",
+ "orcid": "0000-0002-2942-0821",
+ "publish_email": true
+ },
+ "tamás_józsa": {
+ "contributions": [
+ "userTesting"
+ ],
+ "name": "Tamás Józsa"
+ },
+ "tsalo": {
+ "affiliation": "University of Pennsylvania, Department of Psychiatry, Philadelphia, Pennsylvania, 19104, United States",
+ "bio": "I am a Psychology Ph.D. student at FIU working with @angielaird in the @NBCLab. I am interested in performing reproducible, transparent research with fMRI.",
+ "blog": "https://tsalo.github.io",
+ "contributions": [
+ "question",
+ "doc",
+ "plugin",
+ "code"
+ ],
+ "email": "tsalo006@fiu.edu",
+ "name": "Taylor Salo",
+ "orcid": "0000-0001-9813-3167",
+ "publish_email": true
+ },
+ "teonbrooks": {
+ "blog": "https://teonbrooks.com",
+ "contributions": [
+ "doc",
+ "code",
+ "test",
+ "question",
+ "review",
+ "ideas",
+ "tool",
+ "bug",
+ "talk"
+ ],
+ "email": "teon.brooks@gmail.com",
+ "name": "Teon L. Brooks",
+ "orcid": "0000-0001-7344-3230"
+ },
+ "nicholst": {
+ "affiliation": "University of Oxford, Big Data Institute, Oxford, OX3 7LF, UK",
+ "blog": "http://nisox.org",
+ "contributions": [
+ "doc",
+ "talk",
+ "tool",
+ "review",
+ "maintenance",
+ "code"
+ ],
+ "email": "thomas.nichols@BDI.OX.AC.UK",
+ "name": "Thomas E. Nichols",
+ "orcid": "0000-0002-4516-5103",
+ "publish_email": true
+ },
+ "tfunck": {
+ "blog": "https://github.com/tfunck",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Thomas Funck"
+ },
+ "thomas_kirk": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Thomas Kirk"
+ },
+ "thomas_okell": {
+ "affiliation": "University of Oxford",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Thomas Okell",
+ "orcid": "0000-0001-8258-0659"
+ },
+ "tiborauer": {
+ "affiliation": "University of Surrey, School of Psychology, Guildford, GU2 7XH, United Kingdom",
+ "blog": "https://tiborauer.github.io",
+ "contributions": [
+ "question",
+ "doc",
+ "example",
+ "tool",
+ "talk",
+ "bug",
+ "ideas"
+ ],
+ "email": "tibor.auer@gmail.com",
+ "name": "Tibor Auer",
+ "orcid": "0000-0001-5153-1424",
+ "publish_email": true
+ },
+ "dickscheid": {
+ "affiliation": "Forschungszentrum Jülich, Institute of Neuroscience and Medicine (INM-1), Jülich, 52428, Germany",
+ "blog": "https://go.fzj.de/dickscheid",
+ "contributions": [
+ "doc"
+ ],
+ "email": "t.dickscheid@fz-juelich.de",
+ "name": "Timo Dickscheid",
+ "orcid": "0000-0002-9051-3701",
+ "publish_email": true
+ },
+ "timo-berg": {
+ "affiliation": "Technical University Berlin",
+ "blog": "https://github.com/timo-berg",
+ "contributions": [
+ "doc",
+ "ideas",
+ "userTesting"
+ ],
+ "email": "timo.berg@campus.tu-berlin.de",
+ "github": "timo-berg",
+ "name": "Timotheus Berg",
+ "orcid": "0000-0002-0746-3679",
+ "publish_email": true
+ },
+ "tobey_betthauser": {
+ "affiliation": "University of Wisconsin-Madison",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Tobey Betthauser",
+ "orcid": "0000-0001-8856-1352"
+ },
+ "xi": {
+ "blog": "http://tobib.spline.de",
+ "contributions": [
+ "code"
+ ],
+ "email": "tobias.bengfort@posteo.de",
+ "name": "Tobias Bengfort"
+ },
+ "tom_hampshire": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Tom Hampshire"
+ },
+ "torwager": {
+ "blog": "https://github.com/torwager",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Tor Wager"
+ },
+ "travis_riddle": {
+ "affiliation": "National Police Foundation",
+ "contributions": [
+ "doc",
+ "tool",
+ "bug"
+ ],
+ "name": "Travis Riddle",
+ "orcid": "0000-0001-8160-3986"
+ },
+ "glatard": {
+ "blog": "https://github.com/glatard",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "name": "Tristan Glatard",
+ "orcid": "0000-0003-2620-5883"
+ },
+ "ulrike_bingel": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Ulrike Bingel",
+ "orcid": "0000-0002-9528-3204"
+ },
+ "vsoch": {
+ "blog": "https://github.com/vsoch",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Vanessa Sochat",
+ "orcid": "0000-0002-4387-3819"
+ },
+ "vasudev_raguram": {
+ "contributions": [
+ "code",
+ "design",
+ "doc",
+ "tool"
+ ],
+ "name": "Vasudev Raguram"
+ },
+ "vdcalhoun": {
+ "affiliation": "Tri-institutional Center for Translational Research in Neuroimaging and Data Science (TReNDS), Georgia State, Georgia Tech, and Emory, Atlanta, GA 30030",
+ "blog": "http://trendscenter.org",
+ "contributions": [
+ "doc"
+ ],
+ "email": "vcalhoun@gsu.edu",
+ "name": "Vince D. Calhoun",
+ "publish_email": true
+ },
+ "viacovella": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Vittorio Iacovella",
+ "orcid": "0000-0002-0853-1573"
+ },
+ "vladimir_litvak": {
+ "affiliation": "UCL Queen Square Institute of Neurology",
+ "contributions": [
+ "doc"
+ ],
+ "name": "Vladimir Litvak",
+ "orcid": "0000-0001-8535-7452"
+ },
+ "wietske_van_der_zwaag": {
+ "contributions": [
+ "data",
+ "question"
+ ],
+ "name": "Wietske van der Zwaag"
+ },
+ "william_clarke": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "William Clarke"
+ },
+ "william_triplett": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "William Triplett"
+ },
+ "wouterpotters": {
+ "bio": "Technical Physician",
+ "blog": "https://www.wouterpotters.nl",
+ "contributions": [
+ "code",
+ "doc"
+ ],
+ "name": "Wouter V. Potters",
+ "orcid": "0000-0003-1249-1196"
+ },
+ "xiangruili": {
+ "affiliation": "The Ohio State University",
+ "blog": "https://github.com/xiangruili",
+ "contributions": [
+ "doc",
+ "code"
+ ],
+ "email": "xiangrui.li@gmail.com",
+ "name": "Xiangrui Li",
+ "publish_email": true
+ },
+ "yarikoptic": {
+ "affiliation": "Dartmouth College",
+ "bio": "Cheers!",
+ "blog": "http://www.onerussian.com",
+ "contributions": [
+ "doc",
+ "talk",
+ "tool",
+ "question",
+ "bug",
+ "code"
+ ],
+ "email": "debian@onerussian.com",
+ "name": "Yaroslav O. Halchenko",
+ "orcid": "0000-0003-3456-2493"
+ },
+ "yoni_ashar": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Yoni Ashar",
+ "orcid": "0000-0001-5602-5619"
+ },
+ "yuan_wang": {
+ "contributions": [
+ "code"
+ ],
+ "name": "Yuan Wang"
+ },
+ "zachary_michael": {
+ "contributions": [
+ "doc"
+ ],
+ "name": "Zachary Michael"
+ },
+ "ezemikulan": {
+ "blog": "https://github.com/ezemikulan",
+ "contributions": [
+ "code"
+ ],
+ "name": "ezemikulan"
+ },
+ "josator2": {
+ "blog": "https://github.com/josator2",
+ "contributions": [
+ "code"
+ ],
+ "name": "josator2"
+ },
+ "monkeyman192": {
+ "blog": "https://github.com/monkeyman192",
+ "contributions": [
+ "code"
+ ],
+ "name": "monkeyman192"
+ },
+ "étienne_bergeron": {
+ "affiliation": "Collège Sainte-Anne de Lachine",
+ "contributions": [
+ "data",
+ "code"
+ ],
+ "name": "Étienne Bergeron",
+ "orcid": "0000-0002-8329-7852"
+ }
+}
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 0000000000..ef9976621b
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,1399 @@
+# schema: https://github.com/citation-file-format/citation-file-format/blob/main/schema-guide.md
+
+cff-version: 1.2.0
+
+title: bids-specification
+
+abstract:
+ The Brain Imaging Data Structure (BIDS) is a simple and intuitive way to
+ organize and describe data.
+
+version: v1.8.1-dev
+
+license: CC-BY-4.0
+
+repository-code: https://github.com/bids-standard/bids-specification
+
+message: Please cite it as below.
+
+identifiers:
+ - description: bids-specification
+ type: doi
+ value: 10.5281/zenodo.3686061
+
+keywords:
+ - brain imaging data structure
+ - bids
+ - standards
+ - data-standards
+
+authors:
+ - given-names: Aaron
+ family-names: Oliver-Taylor
+ - given-names: Adam
+ family-names: Li
+ website: https://adam2392.github.io/
+ - given-names: Adam
+ family-names: Thomas
+ website: https://cmn.nimh.nih.gov/dsst
+ orcid: https://orcid.org/0000-0002-2850-1419
+ affiliation:
+ NIMH, Intramural Research Program, Bethesda, MD, 20891, United States of
+ America
+ email: adamt@nih.gov
+ - given-names: Adeen
+ family-names: Flinker
+ website: http://flinkerlab.org
+ orcid: https://orcid.org/0000-0003-1247-1283
+ affiliation:
+ New York University, Department of Neurology, New York City, New York,
+ 10016, United States of America
+ email: adeen@nyu.edu
+ - given-names: Adina S.
+ family-names: Wagner
+ website: https://www.adina-wagner.com
+ orcid: https://orcid.org/0000-0003-2917-3450
+ affiliation:
+ Institute of Neuroscience and Medicine (INM-7), Research Center Juelich,
+ Juelich, 52428, Germany
+ email: adina.wagner@t-online.de
+ - given-names: Agah
+ family-names: Karakuzu
+ website: https://agahkarakuzu.github.io
+ orcid: https://orcid.org/0000-0001-7283-271X
+ email: agahkarakuzu@gmail.com
+ - given-names: Aki
+ family-names: Nikolaidis
+ website: https://github.com/AkiNikolaidis
+ orcid: https://orcid.org/0000-0001-8960-1934
+ - given-names: Alberto
+ family-names: Lazari
+ - given-names: Alejandro
+ family-names: de la Vega
+ website: https://github.com/adelavega
+ - given-names: Alessio
+ family-names: Giacomel
+ website: https://github.com/alegiac95
+ orcid: https://orcid.org/0000-0002-7784-2041
+ affiliation: GlaxoSmithKline Research and Development
+ - given-names: Alex
+ family-names: Rockhill
+ website: https://github.com/alexrockhill
+ orcid: https://orcid.org/0000-0003-3868-7453
+ email: aprockhill@mailbox.org
+ - given-names: Alexander
+ family-names: Jones
+ - given-names: Alexander L.
+ family-names: Cohen
+ website: https://bchcohenlab.com
+ orcid: https://orcid.org/0000-0001-6557-5866
+ affiliation:
+ Boston Children's Hospital, Department of Neurology, Boston, 02115, United
+ States of America
+ email: alexander.cohen2@childrens.harvard.edu
+ - given-names: Alexander
+ family-names: von Lautz
+ - given-names: Alexandre
+ family-names: Gramfort
+ website: http://alexandre.gramfort.net
+ orcid: https://orcid.org/0000-0001-9791-4404
+ affiliation: Inria, Université Paris-Saclay
+ email: alexandre.gramfort@inria.fr
+ - given-names: Alexandre
+ family-names: Hutton
+ - given-names: Alexandre
+ family-names: Routier
+ website: https://github.com/alexandreroutier
+ orcid: https://orcid.org/0000-0003-1603-8049
+ - given-names: Alexandru
+ family-names: Foias
+ website: https://github.com/alexfoias
+ orcid: https://orcid.org/0000-0001-5160-1402
+ - given-names: Ali
+ family-names: Khan
+ - given-names: Ana
+ family-names: Fouto
+ - given-names: Anders
+ family-names: Eklund
+ website: https://github.com/wanderine
+ - given-names: Andrea
+ family-names: Pigorini
+ - given-names: Andrew
+ family-names: Hoopes
+ - given-names: Andrew
+ family-names: Jahn
+ - given-names: Andrew
+ family-names: Janke
+ website: http://apjanke.net
+ email: andrew@apjanke.net
+ - given-names: Anibal
+ family-names: Sólon
+ website: https://anibalsolon.com/
+ orcid: https://orcid.org/0000-0002-2050-0614
+ email: anibalsolon@gmail.com
+ - given-names: Anthony
+ family-names: Galassi
+ website: https://github.com/bendhouseart
+ orcid: https://orcid.org/0000-0001-6550-4574
+ - given-names: Ariel
+ family-names: Rokem
+ website: https://arokem.org/
+ orcid: https://orcid.org/0000-0003-0679-1985
+ affiliation:
+ University of Washington, Psychology, Seattle, WA, 98107, United States of
+ America
+ email: arokem@uw.edu
+ - given-names: Arjen
+ family-names: Stolk
+ - given-names: Arnaud
+ family-names: Delorme
+ website: https://www.arnauddelorme.com
+ orcid: https://orcid.org/0000-0002-0799-3557
+ affiliation: Swartz Center for Computational Neuroscience
+ email: adelorme@ucsd.edu
+ - given-names: Arnaud
+ family-names: Marcoux
+ website: https://github.com/arnaudmarcoux
+ - given-names: Arshitha
+ family-names: Basavaraj
+ website: https://github.com/Arshitha
+ orcid: https://orcid.org/0000-0002-6984-7969
+ affiliation:
+ NIMH, Data Science and Sharing Team, Bethesda, MD, 20892, United States of
+ America
+ email: arsh2794@gmail.com
+ - given-names: Ashley G.
+ family-names: Gillman
+ website: https://github.com/ashgillman
+ - given-names: Athanasia
+ family-names: Monika Mowinckel
+ website: https://github.com/drmowinckels
+ orcid: https://orcid.org/0000-0002-5756-0223
+ affiliation: University of Oslo
+ - given-names: Aysegul
+ family-names: Gunduz
+ - given-names: Azeez
+ family-names: Adebimpe
+ orcid: https://orcid.org/0000-0001-9049-0135
+ affiliation: University of Pennsylvania
+ - given-names: B.
+ family-names: Nolan Nichols
+ - given-names: Balint
+ family-names: Kincses
+ - given-names: Benjamin
+ family-names: Beasley
+ website: https://github.com/musicinmybrain
+ - given-names: Benjamin
+ family-names: Dichter
+ - given-names: Benjamin
+ family-names: Gagl
+ orcid: https://orcid.org/0000-0002-2339-6293
+ affiliation: University of Vienna
+ - given-names: Bertrand
+ family-names: Thirion
+ website: https://github.com/bthirion
+ orcid: https://orcid.org/0000-0001-5018-7895
+ - given-names: Bradley
+ family-names: Voytek
+ website: https://www.voyteklab.com/
+ orcid: https://orcid.org/0000-0003-1640-2525
+ affiliation:
+ UC San Diego, Cognitive Science and Data Science, La Jolla, CA, 92093,
+ United States of America
+ - given-names: Brett L.
+ family-names: Foster
+ - given-names: Brian A.
+ family-names: Wandell
+ - given-names: Brian N.
+ family-names: Lundstrom
+ - given-names: Camille
+ family-names: Maumet
+ website: https://camillemaumet.com/
+ orcid: https://orcid.org/0000-0002-6290-553X
+ affiliation: Inria, Univ Rennes, CNRS, Inserm, Rennes, 35042, France
+ - given-names: Carlo
+ family-names: Miniussi
+ orcid: https://orcid.org/0000-0002-5436-4745
+ affiliation: University of Trento
+ - given-names: Chloé
+ family-names: Pasturel
+ - given-names: Chris
+ family-names: Benjamin
+ - given-names: Chris
+ family-names: Gahnström
+ - given-names: Chris
+ family-names: Holdgraf
+ website: http://chrisholdgraf.com
+ orcid: https://orcid.org/0000-0002-9420-9301
+ - given-names: Chris J.
+ family-names: Gorgolewski
+ website: http://chrisgorgolewski.org
+ orcid: https://orcid.org/0000-0003-3321-7583
+ affiliation: Google LLC
+ email: krzysztof.gorgolewski@gmail.com
+ - given-names: Chris
+ family-names: Rorden
+ website: https://www.mricro.com
+ orcid: https://orcid.org/0000-0002-7554-6142
+ affiliation:
+ University of South Carolina, Department of Psychology, Columbia, SC,
+ 29016, United States of America
+ email: crorden6@gmail.com
+ - given-names: Christian
+ family-names: Büchel
+ - given-names: Christian
+ family-names: Horea
+ website: http://www.chymera.eu
+ orcid: https://orcid.org/0000-0001-7037-2449
+ affiliation: Dartmouth College, PBS, Hanover, NH, United States of America
+ email: chr@chymera.eu
+ - given-names: Christine
+ family-names: Rogers
+ orcid: https://orcid.org/0000-0002-9893-8448
+ affiliation:
+ McGill Centre for Integrative Neuroscience, Montreal Neurological
+ Institute, Montréal, Québec, Canada
+ email: christine.rogers@mcgill.ca
+ - given-names: Christophe
+ family-names: Phillips
+ website: https://www.uliege.be/cms/c_9054334/en/directory/?uid=U016440
+ orcid: https://orcid.org/0000-0002-4990-425X
+ affiliation:
+ University of Liège, GIGA CRC in vivo imaging, Liège, 4000, Belgium
+ email: c.phillips@uliege.be
+ - given-names: Christopher J.
+ family-names: Honey
+ website: https://www.honeylab.org
+ orcid: https://orcid.org/0000-0002-0745-5089
+ affiliation:
+ Johns Hopkins University, Psychological and Brain Sciences, Baltimore, MD,
+ 21218, United States of America
+ - given-names: Christopher J.
+ family-names: Markiewicz
+ website: https://github.com/effigies
+ email: effigies@gmail.com
+ - given-names: Christopher
+ family-names: Lee-Messer
+ - given-names: Clara
+ family-names: Moreau
+ - given-names: Clint
+ family-names: Hansen
+ affiliation: Kiel University
+ email: c.hansen@neurologie.uni-kiel.de
+ - given-names: Cyril
+ family-names: Pernet
+ website: https://cpernet.github.io/
+ orcid: https://orcid.org/0000-0003-4010-4632
+ affiliation:
+ Neurobiology Research Unit, Copenhagen University Hospital
+ Rigshospitalet, DK-2100 Copenhagen, Denmark
+ email: wamcyril@gmail.com
+ - given-names: Cyrus
+ family-names: Eierud
+ website: https://trendscenter.org/cyrus-eierud/
+ orcid: https://orcid.org/0000-0002-9942-676X
+ affiliation: Georgia State University
+ email: ceierud@gsu.edu
+ - given-names: D.
+ family-names: Sturgeon
+ website: https://dasturge.github.io
+ - given-names: Dan
+ family-names: Levitas
+ website: https://github.com/dlevitas
+ orcid: https://orcid.org/0000-0003-2279-7447
+ affiliation:
+ Indiana University, Psychological & Brain Sciences, Bloomington, IN,
+ 47405, United States of America
+ email: dlevitas@iu.edu
+ - given-names: Dan
+ family-names: Lurie
+ website: https://github.com/danlurie
+ - given-names: Daniel A.
+ family-names: Handwerker
+ - given-names: David
+ family-names: Alsop
+ - given-names: David
+ family-names: Boas
+ website: http://bu.edu/neurophotonics
+ orcid: https://orcid.org/0000-0002-6709-7711
+ affiliation:
+ Boston University, Biomedical Engineering, Boston, 02215, United States of
+ America
+ email: dboas@bu.edu
+ - given-names: David
+ family-names: Groppe
+ website: https://github.com/dmgroppe
+ orcid: https://orcid.org/0000-0002-3282-2514
+ affiliation:
+ Persyst Development Corporation, R&D, Toronto, ON M4K 1W8 Canada
+ email: david.m.groppe@gmail.com
+ - given-names: David
+ family-names: Keator
+ website: https://github.com/dbkeator
+ orcid: https://orcid.org/0000-0001-5281-5576
+ affiliation: Change Your Brain, Change Your Life Foundation
+ - given-names: David
+ family-names: McAlpine
+ - given-names: David
+ family-names: Thomas
+ - given-names: Dejan
+ family-names: Draschkow
+ orcid: https://orcid.org/0000-0003-1354-4835
+ affiliation: University of Oxford
+ - given-names: Dianne
+ family-names: Patterson
+ website: https://profiles.arizona.edu/person/dkp
+ orcid: https://orcid.org/0000-0001-7518-3110
+ affiliation:
+ University of Arizona, RII, Tucson Arizona 85721 United States of America
+ email: dkp@arizona.edu
+ - given-names: Dimitri
+ family-names: Papadopoulos Orfanos
+ website: https://github.com/DimitriPapadopoulos
+ - given-names: Dmitry
+ family-names: Petrov
+ - given-names: Dora
+ family-names: Hermes
+ website: https://github.com/dorahermes
+ - given-names: Dorien
+ family-names: Huijser
+ website: https://github.com/DorienHuijser
+ orcid: https://orcid.org/0000-0003-3282-8083
+ affiliation: Utrecht University
+ - given-names: Douglas N.
+ family-names: Greve
+ website: https://github.com/dngreve
+ - given-names: Duncan
+ family-names: Macleod
+ - given-names: Dung
+ family-names: Truong
+ website: https://github.com/dungscout96
+ affiliation: Swartz Center for Computational Neuroscience
+ email: dutruong@ucsd.edu
+ - given-names: Dylan
+ family-names: Nielson
+ website: https://github.com/Shotgunosine
+ orcid: https://orcid.org/0000-0003-4613-6643
+ - given-names: Eduard
+ family-names: Ort
+ website: https://github.com/eort
+ orcid: https://orcid.org/0000-0001-5546-3561
+ affiliation:
+ Heinrich-Heine-University, Department of Experimental Psychology,
+ Dusseldorf, 40224, Germany
+ - given-names: Eleonora
+ family-names: Marcantoni
+ orcid: https://orcid.org/0000-0003-1137-4983
+ affiliation:
+ IRCCS Istituto Centro San Giovanni di Dio Fatebenefratelli di Brescia
+ - given-names: Elizabeth
+ family-names: Bock
+ - given-names: Elizabeth
+ family-names: DuPre
+ website: https://elizabeth-dupre.com
+ orcid: https://orcid.org/0000-0003-1358-196X
+ affiliation:
+ Stanford University, Department of Psychology, Stanford, CA, 94063, United
+ States of America
+ - given-names: Elke
+ family-names: Warmerdam
+ affiliation: Saarland University
+ email: elke.warmerdam@uni-saarland.de
+ - given-names: Erdal
+ family-names: Karaca
+ website: https://github.com/erdalkaraca
+ - given-names: Eric A.
+ family-names: Earl
+ website: https://ericearl.github.io/
+ orcid: https://orcid.org/0000-0001-5512-0083
+ affiliation:
+ NIMH Data Science & Sharing Team, Bethesda, MD, 20892, United States of
+ America
+ email: eric.earl@nih.gov
+ - given-names: Eric
+ family-names: Achten
+ website: https://github.com/rikAchten
+ - given-names: Eric
+ family-names: Bridgeford
+ - given-names: Erin W.
+ family-names: Dickie
+ website: http://imaging-genetics.camh.ca/
+ - given-names: Ethan
+ family-names: Blackwood
+ orcid: https://orcid.org/0000-0002-3049-0640
+ affiliation: University of Pennsylvania
+ - given-names: Eugene P.
+ family-names: Duff
+ website: http://eduff.github.io
+ orcid: https://orcid.org/0000-0001-8795-5472
+ affiliation: UK Dementia Research Institute, Imperial College London, UK
+ email: eduff@imperial.ac.uk
+ - given-names: Ezequiel
+ family-names: Mikulan
+ orcid: https://orcid.org/0000-0001-7259-6120
+ affiliation: University of Milan
+ - given-names: Felipe
+ family-names: Orihuela-Espina
+ - given-names: Fidel
+ family-names: Alfaro Almagro
+ - given-names: Filip
+ family-names: Szczepankiewicz
+ orcid: https://orcid.org/0000-0002-5251-587X
+ affiliation: Lund University
+ - given-names: Filippo
+ family-names: Maria Castelli
+ website: https://github.com/filippocastelli
+ orcid: https://orcid.org/0000-0001-8170-8905
+ affiliation:
+ University of Florence, European Laboratory for NonLinear Spectroscopy,
+ Sesto Fiorentino, (FI), 50019, Italy
+ email: castelli@lens.unifi.it
+ - given-names: Franco
+ family-names: Pestilli
+ website: https://liberalarts.utexas.edu/psychology/faculty/fp4834
+ - given-names: Franklin W.
+ family-names: Feingold
+ website: https://github.com/franklin-feingold
+ orcid: https://orcid.org/0000-0002-6533-2909
+ affiliation: Stanford University
+ - given-names: François
+ family-names: Tadel
+ - given-names: Gaia
+ family-names: Rizzo
+ website: http://fair.dei.unipd.it/gaia-rizzo/
+ orcid: https://orcid.org/0000-0001-7272-8576
+ affiliation:
+ Invicro, Burlington Danes Building, Imperial College London, Hammersmith
+ Hospital, Du Cane Road, London, W12 0NN, UK
+ - given-names: Gang
+ family-names: Chen
+ website: https://github.com/afni-gangc
+ orcid: https://orcid.org/0000-0002-2960-089X
+ affiliation:
+ National Institutes of Health, Scientific and Statistical Computing Core,
+ NIMH, Bethesda, MD 20892, United States of America
+ email: gangchen@mail.nih.gov
+ - given-names: Gaël
+ family-names: Varoquaux
+ website: https://github.com/GaelVaroquaux
+ - given-names: Ghislain
+ family-names: Vaillant
+ website: https://github.com/ghisvail
+ orcid: https://orcid.org/0000-0003-0267-3033
+ - given-names: Giacomo
+ family-names: Bertazzoli
+ orcid: https://orcid.org/0000-0003-1624-2576
+ affiliation: Centro San Giovanni di Dio Fatebenefratelli
+ - given-names: Giacomo
+ family-names: Guidali
+ orcid: https://orcid.org/0000-0002-3741-0404
+ affiliation: IRCCS Centro San Giovanni di Dio Fatebenefratelli
+ - given-names: Giacomo
+ family-names: Mazzamuto
+ website: https://github.com/gmazzamuto
+ orcid: https://orcid.org/0000-0003-3077-3904
+ affiliation: National Institute of Optics, National Research Council
+ email: mazzamuto@lens.unifi.it
+ - given-names: Gilles
+ family-names: de Hollander
+ website: https://github.com/Gilles86
+ - given-names: Gio
+ family-names: Piantoni
+ - given-names: Gitte M.
+ family-names: Knudsen
+ - given-names: Giulio
+ family-names: Castegnaro
+ - given-names: Giuseppe
+ family-names: Gallitto
+ orcid: https://orcid.org/0000-0001-5185-0206
+ affiliation: Essen University Hospital
+ - given-names: Graham
+ family-names: Searle
+ - given-names: Granville J.
+ family-names: Matheson
+ website: http://www.granvillematheson.com
+ - given-names: Gregory
+ family-names: Kiar
+ website: https://github.com/gkiar
+ orcid: https://orcid.org/0000-0001-8915-496X
+ affiliation: Child Mind Institute
+ - given-names: Gregory
+ family-names: Noack
+ website: https://github.com/thinknoack
+ - given-names: Greydon
+ family-names: Gilmore
+ website: http://greydongilmore.com
+ orcid: https://orcid.org/0000-0001-7523-5734
+ email: ggilmore@uwo.ca
+ - given-names: Guillaume
+ family-names: Flandin
+ website: https://www.fil.ion.ucl.ac.uk/team/spm-team/
+ orcid: https://orcid.org/0000-0003-0077-7859
+ affiliation: University College London
+ - given-names: Gunnar
+ family-names: Schaefer
+ orcid: https://orcid.org/0000-0001-9661-2121
+ - given-names: Gustav
+ family-names: Nilsonne
+ website: https://nilsonne.net/about/
+ orcid: https://orcid.org/0000-0001-5273-0150
+ affiliation:
+ Karolinska Institutet, Department of Clinical Neuroscience, Stockholm,
+ 17177, Sweden
+ email: gustav.nilsonne@ki.se
+ - given-names: Hamish
+ family-names: Innes-Brown
+ orcid: https://orcid.org/0000-0002-1512-2823
+ affiliation: Oticon A/S, Eriksholm Research Centre, Snekkersten, Denmark
+ email: hain@eriksholm.com
+ - given-names: Hanne D.
+ family-names: Hansen
+ orcid: https://orcid.org/0000-0001-5564-7627
+ affiliation:
+ Copenhagen University Hospital, Neurobiology Research Unit, Section 8057,
+ Blegdamsvej 9, 2100 Copenhagen, Denmark
+ email: hanne.d.hansen@nru.dk
+ - given-names: Hanzhang
+ family-names: Lu
+ orcid: https://orcid.org/0000-0003-3871-1564
+ affiliation: Johns Hopkins Medicine
+ - given-names: Hao-Ting
+ family-names: Wang
+ website: https://github.com/htwangtw
+ orcid: https://orcid.org/0000-0003-4078-2038
+ affiliation: CRIUGM, Montreal, Quebec, H3W 1W5, Canada
+ email: htwangtw@gmail.com
+ - given-names: Helena
+ family-names: Cockx
+ website: https://github.com/helenacockx
+ orcid: https://orcid.org/0000-0001-7782-7178
+ affiliation: Radboud University
+ email: helena.cockx@donders.ru.nl
+ - given-names: Henk
+ family-names: Mutsaerts
+ website: http://www.ExploreASL.org
+ orcid: https://orcid.org/0000-0003-0894-0307
+ affiliation:
+ Amsterdam University Medical Centers, Radiology and Nuclear Department,
+ Amsterdam, 1013 EG, The Netherlands
+ email: h.j.mutsaerts@amsterdamumc.nl
+ - given-names: Hernando
+ family-names: Ombao
+ - given-names: Hugo
+ family-names: Boniface
+ website: https://github.com/Hboni
+ - given-names: Ilkay
+ family-names: Isik
+ website: https://github.com/ilkayisik
+ orcid: https://orcid.org/0000-0002-1652-9297
+ affiliation: Max Planck Institute for Empirical Aesthetics
+ - given-names: Ilona
+ family-names: Lipp
+ website: https://github.com/IlonaLipp
+ - given-names: International
+ family-names: Neuroinformatics Coordinating Facility
+ website: https://github.com/INCF
+ - given-names: Iris
+ family-names: Groen
+ website: https://github.com/irisgroen
+ - given-names: Isla
+ family-names: Staden
+ orcid: https://orcid.org/0000-0002-0795-1154
+ - given-names: Jaap
+ family-names: von der Aar
+ - given-names: Jakub
+ family-names: Kaczmarzyk
+ website: https://github.com/kaczmarj
+ orcid: https://orcid.org/0000-0002-5544-7577
+ - given-names: James
+ family-names: Gholam
+ orcid: https://orcid.org/0000-0003-4513-2341
+ - given-names: James
+ family-names: Kent
+ website: https://github.com/jdkent
+ - given-names: Jan
+ family-names: Mathijs Schoffelen
+ website: https://github.com/schoffelen
+ orcid: https://orcid.org/0000-0003-0923-6610
+ affiliation:
+ Radboud University Nijmegen, Donders Institute, Nijmegen, 6500 HB, The
+ Netherlands
+ email: jm.schoffelen@gmail.com
+ - given-names: Jan
+ family-names: Petr
+ website: https://www.exploreasl.org
+ orcid: https://orcid.org/0000-0002-3201-6002
+ affiliation: Helmholtz-Zentrum Dresden-Rossendorf, Dresden, 01309, Germany
+ email: j.petr@hzdr.de
+ - given-names: Jan-Mathijs
+ family-names: Schoffelen
+ - given-names: Jean-Baptiste
+ family-names: Poline
+ website: https://github.com/jbpoline
+ orcid: https://orcid.org/0000-0002-9794-749X
+ - given-names: Jean-Christophe
+ family-names: Houde
+ website: http://scil.dinf.usherbrooke.ca/jchoude
+ orcid: https://orcid.org/0000-0003-3026-021X
+ affiliation: Université de Sherbrooke
+ email: jean.christophe.houde@gmail.com
+ - given-names: Jean-Dominique
+ family-names: Gallezot
+ orcid: https://orcid.org/0000-0003-0399-8374
+ affiliation:
+ Yale School of Medicine, Department of Radiology and Biomedical Imaging,
+ New Haven, CT, 06511, United States of America
+ email: jean-dominique.gallezot@yale.edu
+ - given-names: Jean-Philippe
+ family-names: Lachaux
+ orcid: https://orcid.org/0000-0002-9459-0667
+ affiliation: INSERM
+ - given-names: Jeanette
+ family-names: Mumford
+ website: https://jeanettemumford.org/
+ affiliation: Stanford, Department of Psychology, Stanford, CA
+ - given-names: Jeffrey G.
+ family-names: Ojemann
+ - given-names: Jeffrey S.
+ family-names: Grethe
+ website: http://profiles.ucsd.edu/jeffrey.grethe
+ - given-names: JegouA
+ website: https://github.com/JegouA
+ - given-names: Jelle
+ family-names: Dalenberg
+ website: https://github.com/jrdalenberg
+ orcid: https://orcid.org/0000-0001-8580-5358
+ affiliation:
+ University of Groningen, University Medical Center Groningen, Department
+ of Neurology, Movement Disorders Groningen, Hanzeplein 1, 9713 GZ,
+ Groningen
+ email: j.r.dalenberg@umcg.nl
+ - given-names: Jeremy
+ family-names: Moreau
+ - given-names: Jessica A.
+ family-names: Turner
+ - given-names: Jochem
+ family-names: Rieger
+ website: https://uol.de/en/applied-neurocognitive-psychology
+ orcid: https://orcid.org/0000-0003-0955-2306
+ affiliation: Oldenburg University, Dept. of Psychology, 26129, Germany
+ email: jochem.rieger@uni-oldenburg.de
+ - given-names: John
+ family-names: Detre
+ orcid: https://orcid.org/0000-0002-8115-6343
+ affiliation: University of Pennsylvania
+ - given-names: John
+ family-names: Pellman
+ orcid: https://orcid.org/0000-0001-6810-4461
+ - given-names: John T.
+ family-names: Wodder
+ website: https://github.com/jwodder
+ - given-names: Joke
+ family-names: Durnez
+ website: https://github.com/jokedurnez
+ orcid: https://orcid.org/0000-0001-9030-2202
+ affiliation: Stanford University
+ - given-names: Jon
+ family-names: Haitz Legarreta Gorroño
+ website: https://github.com/jhlegarreta
+ orcid: https://orcid.org/0000-0002-9661-1396
+ affiliation:
+ Université de Sherbrooke, Department of Computer Science, Sherbrooke,
+ (Québec), J1K 2R1, Canada
+ email: jon.haitz.legarreta@gmail.com
+ - given-names: Jonathan C.
+ family-names: Lau
+ - given-names: Jonathan
+ family-names: Winawer
+ website: https://github.com/JWinawer
+ - given-names: Joost
+ family-names: Kuijer
+ website: https://github.com/wadqc
+ orcid: https://orcid.org/0000-0002-4181-0427
+ affiliation:
+ Amsterdam University Medical Centers, Radiology and Nuclear Medicine,
+ Amsterdam, 1081 HV, The Netherlands
+ email: jpa.kuijer@amsterdamumc.nl
+ - given-names: Jose
+ family-names: Manuel Saborit
+ - given-names: Joseph
+ family-names: Wexler
+ orcid: https://orcid.org/0000-0001-9086-8484
+ affiliation: Loyola University Chicago
+ - given-names: Joseph
+ family-names: Woods
+ website: https://www.ndcn.ox.ac.uk/team/joseph-woods
+ orcid: https://orcid.org/0000-0002-0329-824X
+ affiliation:
+ University of Oxford, Nuffield Department of Clinical Neuroscience,
+ Oxford, UK
+ email: joseph.woods@ndcn.ox.ac.uk
+ - given-names: Julia
+ family-names: Guiomar Niso Galán
+ website: http://guiomarniso.com
+ orcid: https://orcid.org/0000-0001-5872-8924
+ affiliation: Indiana University
+ - given-names: Julia
+ family-names: Sprenger
+ website: https://github.com/juliasprenger
+ orcid: https://orcid.org/0000-0002-9986-7477
+ affiliation:
+ Institute de Neuroscience de la Timone, CNRS, Marseille, 13005, France
+ email: julia.sprenger@univ-amu.fr
+ - given-names: Julien
+ family-names: Cohen-Adad
+ website: https://www.neuro.polymtl.ca
+ orcid: https://orcid.org/0000-0003-3662-9532
+ affiliation:
+ Polytechnique Montreal, Department of Electrical Engineering, Montreal,
+ QC, Canada
+ - given-names: Julius
+ family-names: Welzel
+ website: https://github.com/JuliusWelzel
+ orcid: https://orcid.org/0000-0001-8958-0934
+ affiliation: Kiel University
+ email: julius.welzel@gmail.com
+ - given-names: Kai J.
+ family-names: Miller
+ - given-names: Kangjoo
+ family-names: Lee
+ website: https://github.com/Kangjoo
+ orcid: https://orcid.org/0000-0002-7760-8079
+ affiliation: Yale School of Medicine
+ - given-names: Katja
+ family-names: Heuer
+ website: https://github.com/katjaq
+ orcid: https://orcid.org/0000-0002-7237-0196
+ affiliation: Institut Pasteur
+ - given-names: Kay
+ family-names: Robbins
+ website: http://www.cs.utsa.edu/~krobbins
+ orcid: https://orcid.org/0000-0002-7147-5797
+ affiliation:
+ University of Texas at San Antonio Department of Computer Science San
+ Antonio, TX 78249 United States of America
+ email: kay.robbins@utsa.edu
+ - given-names: Kevin
+ family-names: Larcher
+ - given-names: Kimberly
+ family-names: Ray
+ orcid: https://orcid.org/0000-0003-1302-2834
+ affiliation: UT Austin
+ email: kimberlylray@gmail.com
+ - given-names: Kirstie
+ family-names: Whitaker
+ website: https://whitakerlab.github.io
+ orcid: https://orcid.org/0000-0001-8498-4059
+ affiliation: University of Cambridge
+ email: kwhitaker@turing.ac.uk
+ - given-names: Klara
+ family-names: Gregorova
+ website: https://github.com/greckla
+ orcid: https://orcid.org/0000-0002-3828-9386
+ - given-names: Klaus
+ family-names: Gramann
+ affiliation: Technical University Berlin
+ email: klaus.gramann@tu-berlin.de
+ - given-names: Kris
+ family-names: Thielemans
+ website: https://iris.ucl.ac.uk/iris/browse/profile?upi=KTHIE60
+ - given-names: Kristofer
+ family-names: Bouchard
+ website: https://bouchardlab.lbl.gov
+ orcid: https://orcid.org/0000-0002-1974-4603
+ affiliation: LBNL, Scientific Data Division
+ email: kristofer.bouchard@gmail.com
+ - given-names: Kurt
+ family-names: Schilling
+ website: https://github.com/schillkg
+ - given-names: Laura
+ family-names: and John Arnold Foundation
+ - given-names: Leandro
+ family-names: Beltrachini
+ orcid: https://orcid.org/0000-0003-4602-1416
+ affiliation:
+ Cardiff University, Cardiff University Brain Research Imaging Centre
+ (CUBRIC), UK
+ email: BeltrachiniL@cardiff.ac.uk
+ - given-names: Lee
+ family-names: Kamentsky
+ website: https://github.com/LeeKamentsky
+ orcid: https://orcid.org/0000-0002-8161-3604
+ affiliation: MIT
+ - given-names: Lennart
+ family-names: Walger
+ orcid: https://orcid.org/0000-0002-3300-6877
+ - given-names: Lennart
+ family-names: Wittkuhn
+ website: https://github.com/lnnrtwttkhn
+ orcid: https://orcid.org/0000-0003-2966-6888
+ affiliation: Max Planck Institute for Human Development
+ - given-names: Liberty
+ family-names: Hamilton
+ website: https://github.com/libertyh
+ - given-names: Luca
+ family-names: Pollonini
+ website: https://polloninilab.com
+ orcid: https://orcid.org/0000-0003-2955-6355
+ affiliation:
+ University of Houston, Dept. of Engineering Technology, Houston TX 77204,
+ United States of America
+ email: lpollonini@uh.edu
+ - given-names: Luis
+ family-names: Hernandez-Garcia
+ - given-names: Luke J.
+ family-names: Edwards
+ orcid: https://orcid.org/0000-0002-8320-7298
+ affiliation: Max Planck Institute for Human Cognitive and Brain Sciences
+ - given-names: Lyuba
+ family-names: Zehl
+ website: https://github.com/lzehl
+ orcid: https://orcid.org/0000-0002-5947-9939
+ affiliation: Forschungszentrum Jülich, Jülich, 52428, Germany
+ - given-names: Mainak
+ family-names: Jas
+ website: http://jasmainak.github.io/
+ orcid: https://orcid.org/0000-0002-3199-9027
+ - given-names: Manjari
+ family-names: Narayan
+ - given-names: Manuel
+ family-names: Mercier
+ orcid: https://orcid.org/0000-0001-6358-4734
+ affiliation:
+ Dynamics of Cognitive Processes Group Institut de Neurosciences des
+ Systèmes, INS - UMR 1106 Inserm Aix-Marseille Université, France
+ - given-names: Maqsood
+ family-names: Yaqub
+ orcid: https://orcid.org/0000-0003-2122-740X
+ affiliation: Amsterdam UMC, locatie VUmc
+ - given-names: Marc
+ family-names: Lalancette
+ website: https://github.com/Moo-Marc
+ orcid: https://orcid.org/0000-0003-1161-3972
+ affiliation:
+ Montreal Neurological Institute-Hospital, McConnell Brain Imaging Center,
+ Montreal, Quebec, H2A
+ - given-names: Marco
+ family-names: Castellaro
+ website: https://github.com/marcocastellaro
+ orcid: https://orcid.org/0000-0002-1203-2670
+ affiliation:
+ University of Padova, Department of Information Engineering, Padova, Italy
+ email: marco.castellaro@gmail.com
+ - given-names: Maria
+ family-names: de la Iglesia
+ website: https://bimcv.cipf.es
+ orcid: https://orcid.org/0000-0003-4505-8399
+ affiliation:
+ FISABIO, Join Unit Biomedical Imagin, Valencia, Valencia, 46006, Spain
+ email: delaiglesia_mar@gva.es
+ - given-names: Marie-Hélène
+ family-names: Bourget
+ website: https://github.com/mariehbourget
+ - given-names: Mark
+ family-names: Mikkelsen
+ website: https://vivo.weill.cornell.edu/display/cwid-mam4041
+ orcid: https://orcid.org/0000-0002-0349-3782
+ affiliation:
+ Weill Cornell Medicine, Department of Radiology, New York, NY, 10021,
+ United States of America
+ email: mam4041@med.cornell.edu
+ - given-names: Markus
+ family-names: Morawski
+ orcid: https://orcid.org/0000-0002-3817-5186
+ affiliation: Universität Leipzig
+ - given-names: Marta
+ family-names: Bortoletto
+ orcid: https://orcid.org/0000-0002-8489-8043
+ affiliation: IRCCS Centro S Giovanni di Dio Fatebenefratelli
+ - given-names: Martin
+ family-names: Craig
+ - given-names: Martin
+ family-names: Noergaard
+ website: https://profiles.stanford.edu/martin-noergaard
+ orcid: https://orcid.org/0000-0003-2131-5688
+ affiliation:
+ Stanford University, Department of Psychology, CA, 94304, United States of
+ America
+ email: noergard@stanford.edu
+ - given-names: Martin
+ family-names: Szinte
+ website: http://www.martinszinte.net
+ orcid: https://orcid.org/0000-0003-2040-4005
+ affiliation:
+ CNRS, Institut des Neurosciences de la Timone, Marseille, 13008, France
+ email: mail@martinszinte.net
+ - given-names: Martin
+ family-names: Wilson
+ - given-names: Martina
+ family-names: Bulgari
+ orcid: https://orcid.org/0000-0003-1594-9617
+ affiliation: IRCCS Centro San Giovanni di Dio Fatebenefratelli
+ - given-names: Mateusz
+ family-names: Pawlik
+ website: https://github.com/mateuszpawlik
+ email: mateusz.pawlik@plus.ac.at
+ - given-names: Mathias
+ family-names: Goncalves
+ website: https://github.com/mgxd
+ orcid: https://orcid.org/0000-0002-7252-7771
+ affiliation:
+ Psychology Department, Stanford University, Palo Alto, CA, 94305, United
+ States of America
+ email: mathiasg@stanford.edu
+ - given-names: Mathieu
+ family-names: Boudreau
+ website: https://github.com/mathieuboudreau
+ orcid: https://orcid.org/0000-0002-7726-4456
+ affiliation: Polytechnique Montréal
+ - given-names: Matt
+ family-names: Sanderson
+ - given-names: Matteo
+ family-names: Tonietto
+ website: https://github.com/matteotonietto
+ orcid: https://orcid.org/0000-0001-9591-5710
+ affiliation:
+ Hoffmann-La Roche Ltd, Research and Early Development, Basel, 4070,
+ Switzerland
+ - given-names: Matthias
+ family-names: Günther
+ - given-names: Matthias
+ family-names: Van Osch
+ - given-names: Maureen
+ family-names: J Shader
+ - given-names: Maurice
+ family-names: Pasternak
+ - given-names: Max A.
+ family-names: van den Boom
+ website: https://github.com/MaxvandenBoom
+ orcid: https://orcid.org/0000-0001-5481-1659
+ affiliation:
+ Mayo Clinic, Department of neurosurgery, Rochester, MN, United States of
+ America
+ email: m.a.vandenboom84@gmail.com
+ - given-names: Melanie
+ family-names: Ganz-Benjaminsen
+ website: https://sites.google.com/view/melanieganz/home
+ orcid: https://orcid.org/0000-0002-9120-8098
+ affiliation:
+ University of Copenhagen, Department of Computer Science, Copenhagen,
+ 2100, Denmark
+ - given-names: Michael
+ family-names: Chappell
+ - given-names: Michael
+ family-names: Hanke
+ website: https://github.com/mih
+ - given-names: Michael P.
+ family-names: Harms
+ - given-names: Michael P.
+ family-names: Milham
+ - given-names: Michael P.
+ family-names: Notter
+ - given-names: Michael
+ family-names: Schirner
+ website: https://www.brainsimulation.org
+ orcid: https://orcid.org/0000-0001-8227-8476
+ affiliation:
+ Charité—Universitätsmedizin Berlin, Department of Neurology, Berlin,
+ 10115, Germany
+ email: michael.schirner@charite.de
+ - given-names: Mikaël
+ family-names: Naveau
+ website: https://www.cyceron.fr
+ orcid: https://orcid.org/0000-0002-4685-0057
+ affiliation: UAR3408-US50 CYCERON, Caen, 14000, France
+ - given-names: Nader
+ family-names: Pouratian
+ orcid: https://orcid.org/0000-0002-0426-3241
+ - given-names: Natalia
+ family-names: Petridou
+ orcid: https://orcid.org/0000-0002-0783-0387
+ affiliation:
+ High Field Dpt, Center for Image Sciences, University Medical Center
+ Utrecht, 3584 CX Utrecht, NL
+ - given-names: National
+ family-names: Institute of Mental Health
+ - given-names: Nell
+ family-names: Hardcastle
+ website: https://github.com/nellh
+ orcid: https://orcid.org/0000-0002-3837-0707
+ - given-names: Nicholas
+ family-names: Traut
+ - given-names: Nick F.
+ family-names: Ramsey
+ - given-names: Nicole C.
+ family-names: Swann
+ - given-names: Nima
+ family-names: Bigdely Shamlo
+ orcid: https://orcid.org/0000-0001-6403-892X
+ - given-names: Olivier
+ family-names: David
+ - given-names: Orrin
+ family-names: Devinsky
+ orcid: https://orcid.org/0000-0003-0044-4632
+ affiliation: NYU School of Medicine
+ - given-names: Oscar
+ family-names: Esteban
+ website: http://www.axonlab.org
+ orcid: https://orcid.org/0000-0001-8435-6191
+ affiliation:
+ Department of Radiology, Lausanne University Hospital, Lausanne, CH-1011,
+ Switzerland
+ email: phd@oscaresteban.es
+ - given-names: Pamela
+ family-names: LaMontagne
+ orcid: https://orcid.org/0000-0002-6752-8518
+ - given-names: Parul
+ family-names: Sethi
+ website: https://github.com/parulsethi
+ - given-names: Patricia
+ family-names: Clement
+ website: https://github.com/patsycle
+ orcid: https://orcid.org/0000-0001-8546-0134
+ affiliation:
+ Department of Medical Imaging / Department of Medical Sciences, Ghent
+ University Hospital / Ghent University, Ghent, Belgium
+ email: patricia.clement@ugent.be
+ - given-names: Patrick
+ family-names: Park
+ website: https://github.com/Park-Patrick
+ - given-names: Paule-Joanne
+ family-names: Toussaint
+ orcid: https://orcid.org/0000-0002-7446-150X
+ affiliation:
+ McGill University Faculty of Medicine and Health Sciences, Montréal,
+ Québec, Canada
+ - given-names: Peer
+ family-names: Herholz
+ website: https://peerherholz.github.io/
+ orcid: https://orcid.org/0000-0002-9840-6257
+ affiliation:
+ NeuroDataScience - ORIGAMI lab, McConnell Brain Imaging Centre, Montreal
+ Neurological Institute and Hospital, McGill University, Montreal, Québec,
+ Canada
+ email: herholz.peer@gmail.com
+ - given-names: Petra
+ family-names: Ritter
+ website: https://www.brainsimulation.org
+ orcid: https://orcid.org/0000-0002-4643-4782
+ affiliation:
+ Charité University Medicine Berlin, Berlin Institute of Health, Berlin,
+ 10115, Germany
+ email: petra.ritter@charite.de
+ - given-names: Pierre
+ family-names: Rioux
+ website: https://github.com/prioux
+ - given-names: Pieter
+ family-names: Vandemaele
+ website: https://github.com/pvdemael
+ orcid: https://orcid.org/0000-0002-4523-2476
+ affiliation: Ghent University Hospital
+ - given-names: Pradeep
+ family-names: Reddy Raamana
+ website: https://github.com/raamana
+ - given-names: R.
+ family-names: Cameron Craddock
+ website: https://github.com/ccraddock
+ - given-names: Remi
+ family-names: Gau
+ website: https://remi-gau.github.io/
+ orcid: https://orcid.org/0000-0002-1535-9767
+ affiliation: Université catholique de Louvain
+ email: remi.gau@tuebingen.mpg.de
+ - given-names: Richard
+ family-names: Höchenberger
+ website: https://hoechenberger.net/
+ orcid: https://orcid.org/0000-0002-0380-4798
+ affiliation: Inria Saclay - Île-de-France Research Centre
+ email: richard.hoechenberger@gmail.com
+ - given-names: Richard N.
+ family-names: Henson
+ website: http://www.mrc-cbu.cam.ac.uk/people/rik.henson/personal
+ orcid: https://orcid.org/0000-0002-0712-2639
+ affiliation: MRC CBU, University of Cambridge, UK
+ email: Rik.Henson@mrc-cbu.cam.ac.uk
+ - given-names: Robert B.
+ family-names: Innis
+ website: https://www.nimh.nih.gov/research/research-conducted-at-nimh/research-areas/clinics-and-labs/mib/molecular-imaging-branch-mib
+ orcid: https://orcid.org/0000-0003-1238-7209
+ affiliation:
+ NIMH Intramural Research Program, Bethesda, MD 20850 United States of
+ America, MD 20892 United States of America
+ email: robert.innis@nih.gov
+ - given-names: Robert E.
+ family-names: Smith
+ website: http://www.mrtrix.org
+ orcid: https://orcid.org/0000-0003-3636-4642
+ affiliation:
+ The Florey Institute of Neuroscience and Mental Health, Epilepsy
+ Neuroinformatics Laboratory, Heidelberg, Victoria, 3084, Australia
+ email: robert.smith@florey.edu.au
+ - given-names: Robert
+ family-names: Knight
+ - given-names: Robert
+ family-names: Luke
+ website: https://github.com/rob-luke
+ - given-names: Robert
+ family-names: Oostenveld
+ website: https://robertoostenveld.nl
+ orcid: https://orcid.org/0000-0002-1974-1293
+ affiliation:
+ Radboud University, Donders Institute, Nijmegen, 6525 EN, The Netherlands
+ email: r.oostenveld@donders.ru.nl
+ - given-names: Roberto
+ family-names: Toro
+ website: https://github.com/r03ert0
+ - given-names: Rohan
+ family-names: Goyal
+ - given-names: Ross W.
+ family-names: Blair
+ website: https://github.com/rwblair
+ - given-names: Russell A.
+ family-names: Poldrack
+ website: http://www.poldracklab.org
+ orcid: https://orcid.org/0000-0001-6755-0259
+ affiliation:
+ Stanford University, Stanford, CA 94305 United States of America
+ email: russpold@stanford.edu
+ - given-names: Rémi
+ family-names: Adon
+ website: https://github.com/remiadon
+ - given-names: Samir
+ family-names: Das
+ website: https://github.com/samirdas
+ - given-names: Samuel
+ family-names: Garcia
+ - given-names: Samuel
+ family-names: Nastase
+ website: https://snastase.github.io/
+ orcid: https://orcid.org/0000-0001-7013-5275
+ affiliation: Princeton University
+ email: sam.nastase@gmail.com
+ - given-names: Sara
+ family-names: Elgayar
+ - given-names: Sasha
+ family-names: D'Ambrosio
+ orcid: https://orcid.org/0000-0002-6600-6419
+ affiliation:
+ University of Milan, Department of Biomedical and Clinical Sciences,
+ Milan, 20137, Italy
+ email: sasha.dambrosio@unimi.it
+ - given-names: Satrajit S.
+ family-names: Ghosh
+ website: https://satra.cogitatum.org
+ orcid: https://orcid.org/0000-0002-5312-6729
+ affiliation:
+ MIT, McGovern Institute for Brain Research, Cambridge, 02139, United
+ States of America
+ email: satra@mit.edu
+ - given-names: Scott
+ family-names: Makeig
+ website: https://sccn.ucsd.edu/~scott
+ orcid: https://orcid.org/0000-0002-9048-8438
+ affiliation:
+ Institute for Neural Computation, University of California San Diego, La
+ Jolla, CA 92-93-0955, United States of America
+ email: smakeig@gmail.com
+ - given-names: Sein
+ family-names: Jeung
+ website: https://github.com/sjeung
+ orcid: https://orcid.org/0000-0002-0247-087X
+ affiliation: Technical University Berlin
+ email: seinjeung@gmail.com
+ - given-names: Shashank
+ family-names: Bansal
+ - given-names: Sjoerd B.
+ family-names: Vos
+ website: https://research-repository.uwa.edu.au/en/persons/sjoerd-vos
+ orcid: https://orcid.org/0000-0002-8502-4487
+ affiliation:
+ University of Western Australia;Centre for Microscopy, Characterisation,
+ and Analysis;Perth;WA;6009;Australia
+ - given-names: Soichi
+ family-names: Hayashi
+ website: https://github.com/soichih
+ orcid: https://orcid.org/0000-0003-3641-3491
+ affiliation: Indiana University
+ - given-names: Stefan
+ family-names: Appelhoff
+ website: https://www.stefanappelhoff.com
+ orcid: https://orcid.org/0000-0001-8002-0877
+ affiliation: Max Planck Institute for Human Development
+ email: stefan.appelhoff@mailbox.org
+ - given-names: Stephan
+ family-names: Bickel
+ - given-names: Suyash
+ family-names: Bhogawar
+ website: https://www.linkedin.com/in/suyashb/
+ - given-names: Sylvain
+ family-names: Baillet
+ website: https://github.com/sbaillet
+ - given-names: Sylvain
+ family-names: Takerkart
+ website: https://github.com/SylvainTakerkart
+ orcid: https://orcid.org/0000-0001-8410-0962
+ affiliation:
+ CNRS, Institut des Neurosciences de la Timone, Marseille, France
+ email: sylvain.takerkart@univ-amu.fr
+ - given-names: Sébastien
+ family-names: Tourbier
+ website: https://github.com/sebastientourbier
+ orcid: https://orcid.org/0000-0002-4441-899X
+ affiliation:
+ Lausanne University Hospital (CHUV), Department of Clinical Neurosciences
+ (DNC), Lausanne, 1007, Switzerland
+ email: sebastien.tourbier1@gmail.com
+ - given-names: Sören
+ family-names: Grothkopp
+ affiliation: Technical University Berlin
+ email: s.grothkopp@secure.mailbox.org
+ - given-names: Tal
+ family-names: Pal Attia
+ website: https://github.com/tpatpa
+ - given-names: Tal
+ family-names: Yarkoni
+ website: https://github.com/tyarkoni
+ orcid: https://orcid.org/0000-0002-6558-5113
+ - given-names: Tamas
+ family-names: Spisak
+ website: https://pni-lab.github.io/
+ orcid: https://orcid.org/0000-0002-2942-0821
+ affiliation:
+ University Hospital Essen, Center for Translational and Behavioral
+ Neuroscience, Department of Diagnostic and Interventional Radiology and
+ Neuroradiology
+ email: tamas.spisak@uk-essen.de
+ - given-names: Tamás
+ family-names: Józsa
+ - given-names: Taylor
+ family-names: Salo
+ website: https://tsalo.github.io
+ orcid: https://orcid.org/0000-0001-9813-3167
+ affiliation:
+ University of Pennsylvania, Department of Psychiatry, Philadelphia,
+ Pennsylvania, 19104, United States of America
+ email: tsalo006@fiu.edu
+ - given-names: Teon L.
+ family-names: Brooks
+ website: https://teonbrooks.com
+ orcid: https://orcid.org/0000-0001-7344-3230
+ email: teon.brooks@gmail.com
+ - given-names: Thomas E.
+ family-names: Nichols
+ website: http://nisox.org
+ orcid: https://orcid.org/0000-0002-4516-5103
+ affiliation: University of Oxford, Big Data Institute, Oxford, OX3 7LF, UK
+ email: thomas.nichols@BDI.OX.AC.UK
+ - given-names: Thomas
+ family-names: Funck
+ website: https://github.com/tfunck
+ - given-names: Thomas
+ family-names: Kirk
+ - given-names: Thomas
+ family-names: Okell
+ orcid: https://orcid.org/0000-0001-8258-0659
+ affiliation: University of Oxford
+ - given-names: Tibor
+ family-names: Auer
+ website: https://tiborauer.github.io
+ orcid: https://orcid.org/0000-0001-5153-1424
+ affiliation:
+ University of Surrey, School of Psychology, Guildford, GU2 7XH, UK
+ email: tibor.auer@gmail.com
+ - given-names: Timo
+ family-names: Dickscheid
+ website: https://go.fzj.de/dickscheid
+ orcid: https://orcid.org/0000-0002-9051-3701
+ affiliation:
+ Forschungszentrum Jülich, Institute of Neuroscience and Medicine, Jülich,
+ 52428, Germany
+ email: t.dickscheid@fz-juelich.de
+ - given-names: Timotheus
+ family-names: Berg
+ website: https://github.com/timo-berg
+ orcid: https://orcid.org/0000-0002-0746-3679
+ affiliation: Technical University Berlin
+ email: timo.berg@campus.tu-berlin.de
+ - given-names: Tobey
+ family-names: Betthauser
+ orcid: https://orcid.org/0000-0001-8856-1352
+ affiliation: University of Wisconsin-Madison
+ - given-names: Tobias
+ family-names: Bengfort
+ website: http://tobib.spline.de
+ email: tobias.bengfort@posteo.de
+ - given-names: Tom
+ family-names: Hampshire
+ - given-names: Tor
+ family-names: Wager
+ website: https://github.com/torwager
+ - given-names: Travis
+ family-names: Riddle
+ orcid: https://orcid.org/0000-0001-8160-3986
+ affiliation: National Police Foundation
+ - given-names: Tristan
+ family-names: Glatard
+ website: https://github.com/glatard
+ orcid: https://orcid.org/0000-0003-2620-5883
+ - given-names: Ulrike
+ family-names: Bingel
+ orcid: https://orcid.org/0000-0002-9528-3204
+ - given-names: Vanessa
+ family-names: Sochat
+ website: https://github.com/vsoch
+ orcid: https://orcid.org/0000-0002-4387-3819
+ - given-names: Vasudev
+ family-names: Raguram
+ - given-names: Vince D.
+ family-names: Calhoun
+ website: http://trendscenter.org
+ affiliation:
+ Tri-institutional Center for Translational Research in Neuroimaging and
+ Data Science (TReNDS), Georgia State, Georgia Tech, and Emory, Atlanta, GA
+ 30030
+ email: vcalhoun@gsu.edu
+ - given-names: Vittorio
+ family-names: Iacovella
+ orcid: https://orcid.org/0000-0002-0853-1573
+ - given-names: Vladimir
+ family-names: Litvak
+ orcid: https://orcid.org/0000-0001-8535-7452
+ affiliation: UCL Queen Square Institute of Neurology
+ - given-names: Wietske
+ family-names: van der Zwaag
+ - given-names: William
+ family-names: Clarke
+ - given-names: William
+ family-names: Triplett
+ - given-names: Wouter V.
+ family-names: Potters
+ website: https://www.wouterpotters.nl
+ orcid: https://orcid.org/0000-0003-1249-1196
+ - given-names: Xiangrui
+ family-names: Li
+ website: https://github.com/xiangruili
+ affiliation: The Ohio State University, United States of America
+ email: xiangrui.li@gmail.com
+ - given-names: Yaroslav O.
+ family-names: Halchenko
+ website: http://www.onerussian.com
+ orcid: https://orcid.org/0000-0003-3456-2493
+ affiliation: Dartmouth College, United States of America
+ email: debian@onerussian.com
+ - given-names: Yoni
+ family-names: Ashar
+ orcid: https://orcid.org/0000-0001-5602-5619
+ - given-names: Yuan
+ family-names: Wang
+ - given-names: Zachary
+ family-names: Michael
+ - given-names: ezemikulan
+ website: https://github.com/ezemikulan
+ - given-names: josator2
+ website: https://github.com/josator2
+ - given-names: monkeyman192
+ website: https://github.com/monkeyman192
+ - given-names: Étienne
+ family-names: Bergeron
+ orcid: https://orcid.org/0000-0002-8329-7852
+ affiliation: Collège Sainte-Anne de Lachine
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e74bbf98f4..40fae2e3ea 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -157,6 +157,8 @@ Some others need to fixed manually:
to some readers and try to replace them by common English equivalents such as
`"for example"`, `"that is"`, `"and so on"`.
+The BIDS specification is written in American English.
+
#### Soft rules
We follow certain "soft rules" in the way we format the specification in Markdown.
@@ -716,7 +718,7 @@ either make a change to the
part of a pull request or you can edit this
[page](https://github.com/bids-standard/bids-specification/wiki/Recent-Contributors)
of the specification WIKI.
-The WIKI is then synced with the specification with evert new release of the specifications.
+The WIKI is then synced with the specification with every new release of the specifications.
## Thank you!
diff --git a/DECISION-MAKING.md b/DECISION-MAKING.md
index eef8568a18..05e4c91887 100644
--- a/DECISION-MAKING.md
+++ b/DECISION-MAKING.md
@@ -14,20 +14,22 @@ BIDS governance.
### Steering Group
-Curent and past members of the steering group can be found
+Current and past members of the steering group can be found
[here](https://bids.neuroimaging.io/governance.html#bids-steering-group).
### Maintainers Group
-| Name | Time commitment | Scope |
-|--------------------------------------------------------------------------------|-----------------|----------------------------------|
-| Stefan Appelhoff ([@sappelhoff](https://github.com/sappelhoff)) | 5h/week | Lead Maintainer |
-| Chris Markiewicz ([@effigies](https://github.com/effigies)) | 5h/week | |
-| Taylor Salo ([@tsalo](https://github.com/tsalo)) | 3h/week | MRI |
-| Remi Gau ([@Remi-Gau](https://github.com/Remi-Gau)) | 3h/week | Community development, MRI |
-| Anthony Galassi ([@bendhouseart](https://github.com/bendhouseart)) | 3h/week | PET, Community development |
-| Eric Earl ([@ericearl](https://github.com/ericearl)) | 2h/week | |
-| Ross Blair ([@rwblair](https://github.com/rwblair)) | | Maintainer of the bids-validator |
+| Name | Time commitment | Scope | Joined |
+|---------------------------------------------------------------------------|-----------------|---------------------------------------|----------|
+| Stefan Appelhoff ([@sappelhoff](https://github.com/sappelhoff)) | 5h/week | Lead Maintainer | Mar 2020 |
+| Chris Markiewicz ([@effigies](https://github.com/effigies)) | 5h/week | | Mar 2020 |
+| Ross Blair ([@rwblair](https://github.com/rwblair)) | | Maintainer of the bids-validator | Mar 2020 |
+| Taylor Salo ([@tsalo](https://github.com/tsalo)) | 3h/week | MRI | Sep 2020 |
+| Remi Gau ([@Remi-Gau](https://github.com/Remi-Gau)) | 3h/week | Community development, MRI | Oct 2020 |
+| Anthony Galassi ([@bendhouseart](https://github.com/bendhouseart)) | 3h/week | PET, Community development | Sep 2021 |
+| Eric Earl ([@ericearl](https://github.com/ericearl)) | 2h/week | | Dec 2021 |
+| Christine Rogers ([@christinerogers](https://github.com/christinerogers)) | 2h/mo | Interoperability, EEG and multi-modal | Apr 2023 |
+| Nell Hardcastle ([@nellh](https://github.com/nellh)) | 2h/week | | Jul 2023 |
In addition to the [BIDS Governance](https://bids.neuroimaging.io/governance.html#bids-maintainers-group)
classification of a maintainer, maintainers may declare a limited scope of responsibility.
@@ -39,9 +41,9 @@ contributions elsewhere are welcome, as well.
#### Past maintainers group members
-| Name |
-|--------------------------------------------------------------------------------|
-| Franklin Feingold ([@franklin-feingold](https://github.com/franklin-feingold)) |
+| Name | Duration |
+|--------------------------------------------------------------------------------|---------------------|
+| Franklin Feingold ([@franklin-feingold](https://github.com/franklin-feingold)) | Mar 2020 - Jul 2022 |
### BEP Leads Group
diff --git a/Makefile b/Makefile
index 2662661f38..a56b672d82 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,12 @@
+.PHONY: tools/contributors.tsv
+
+validate_citation_cff: CITATION.cff
+ cffconvert --validate
+
+update_contributors:
+ python tools/add_contributors.py
+ python tools/print_contributors.py
+ yarn all-contributors generate
runprettier:
prettier --write "src/schema/**/*.yaml"
diff --git a/README.md b/README.md
index 796a057f65..54b7bd11d7 100644
--- a/README.md
+++ b/README.md
@@ -7,11 +7,12 @@
The [Brain Imaging Data Structure (BIDS)](https://bids.neuroimaging.io) is an emerging standard for the
-organisation of neuroimaging data.
+organization of neuroimaging data.
In this repository, we develop the
[BIDS specification](https://bids-specification.readthedocs.io/en/latest/).
+
# When to use BIDS
To organize your data in BIDS, all you need is neuro data, a computer, and the
@@ -28,6 +29,7 @@ BIDS currently supports the following data modalities with more to come in the f
- PET
- microscopy
- NIRS
+- motion
# Formatting your data with BIDS
@@ -59,3 +61,431 @@ We ask that all contributions to BIDS across all project-related spaces (includi
[GitHub](https://github.com/bids-standard),
the [Google group](https://groups.google.com/forum/#!forum/bids-discussion), and newsletter emails),
adhere to our [code of conduct](https://github.com/bids-standard/bids-specification/blob/master/CODE_OF_CONDUCT.md).
+
+## Contributors
+
+Thanks goes to these wonderful people.
+
+
+
+
+
+
+
+
+
+
diff --git a/Release_Protocol.md b/Release_Protocol.md
index cb1363f750..5773da78e3 100644
--- a/Release_Protocol.md
+++ b/Release_Protocol.md
@@ -42,6 +42,10 @@ git checkout -b rel/1.2.0 upstream/master
### 2. Update the version, contributors list, previous version URLs, and the Changelog
+#### 2.1 Update the version
+
+Update the version in CITATION.cff.
+
Change the "Unreleased" heading in
[src/CHANGES.md](https://github.com/bids-standard/bids-specification/blob/master/src/CHANGES.md)
to `v`, and link to the target ReadTheDocs URL.
@@ -64,17 +68,38 @@ In the figure below, we update `v1.2.0-dev` to `v1.2.0`.
Additionally, implement the same change in the version name perform above in the `src/schema/BIDS_VERSION` file.
-Note: this will make our continuous integration ([CircleCI](https://circleci.com/)) fail. This fails because the URL of the new ReadTheDocs rendering has not been generated at this time. It will be generated once the GitHub release has been completed.
+Note:
+this will make our continuous integration ([CircleCI](https://circleci.com/)) fail.
+This fails because the URL of the new ReadTheDocs rendering has not been generated at this time.
+It will be generated once the GitHub release has been completed.
+
+#### 2.2 Update the contributors list
Synchronize the [Contributors appendix](https://github.com/bids-standard/bids-specification/blob/master/src/appendices/contributors.md)
with the [Contributors wiki page](https://github.com/bids-standard/bids-specification/wiki/Contributors)
to ensure all contributors are duly credited.
Be sure not to remove credits if both have been edited.
+- add new contributors info to the `tools/new_contributors.tsv` file.
+- make sure that you have installed
+ - all the python packages listed in `tools/requirements.txt`
+ - the [allcontributors](https://allcontributors.org/docs/en/cli/installation) package
+- run:
+```bash
+make update_contributors
+```
+- you may need to fix some errors in the contributions names in case of crash
+- make sure to review the changes and not commit them blindly
+- commit the changes
+
+#### 2.3 Update the previous version URLs
+
Please change the previous version links from GitHub to ReadTheDocs.
In the figure below, we update v1.2.2.
![github-to-rtd](release_images/GitHub_to_RTD_spec_rendering.png "github-to-rtd")
+#### 2.4 Update the Changelog
+
Review `src/CHANGES.md` to ensure that the document produces a changelog that is useful to a
reader of the specification.
For example, several small PRs fixing typos might be merged into a single line-item, or less
@@ -214,6 +239,8 @@ on the expected next version.
Additionally, the same version name set above in `mkdocs.yaml` should be set in the `src/schema/BIDS_VERSION` schema version file.
+Similarly update the version in CITATION.cff with a `dev` suffix.
+
### 10. Uploading the stable PDF to Zenodo
1. Open a private browser window
diff --git a/macros_doc.md b/macros_doc.md
index bf8b10dae6..6ec9fcf5a2 100644
--- a/macros_doc.md
+++ b/macros_doc.md
@@ -92,6 +92,7 @@ All the macros we use are in listed in this
| make_suffix_table | Generate a markdown table of suffix information. | Yes | [link](https://github.com/bids-standard/bids-specification/blob/9201b203ffaa72d83b2fa30d1c61f46f089f77de/src/04-modality-specific-files/01-magnetic-resonance-imaging-data.md?plain=1#L199) |
| define_common_principles | List the common principles and definitions. | Yes | [link](https://github.com/bids-standard/bids-specification/blob/831ee55577b91aaa110153e9269e7829b095fb6f/src/02-common-principles.md?plain=1#L12) |
| define_allowed_top_directories | Create a list of allowed top-level directories with their descriptions. | Yes | [link](https://github.com/bids-standard/bids-specification/blob/2a701fd034d51c25e8fe18ba67bb7b76621ba477/src/02-common-principles.md?plain=1#L124) |
+| render_description | Renders the description of an object in the schema. | Yes | [link] (???) |
## When should I use a macro?
diff --git a/mkdocs.yml b/mkdocs.yml
index e633f544e2..1a24697360 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,4 +1,4 @@
-site_name: Brain Imaging Data Structure v1.8.1-dev
+site_name: Brain Imaging Data Structure v1.9.0-dev
site_url: https://bids-specification.readthedocs.io/en/stable/
nav:
- The BIDS Specification:
@@ -17,6 +17,7 @@ nav:
- Positron Emission Tomography: modality-specific-files/positron-emission-tomography.md
- Microscopy: modality-specific-files/microscopy.md
- Near-Infrared Spectroscopy: modality-specific-files/near-infrared-spectroscopy.md
+ - Motion: modality-specific-files/motion.md
- Derivatives:
- BIDS Derivatives: derivatives/introduction.md
- Common data types and metadata: derivatives/common-data-types.md
diff --git a/pdf_build_src/pandoc_script.py b/pdf_build_src/pandoc_script.py
index 54e6ec1ba5..b7b1842b96 100644
--- a/pdf_build_src/pandoc_script.py
+++ b/pdf_build_src/pandoc_script.py
@@ -2,7 +2,6 @@
This is done once the duplicate src directory is processed.
"""
-import os
import subprocess
import yaml
from pathlib import Path
diff --git a/readthedocs.yml b/readthedocs.yml
index b5e50426b3..cc3f461fc7 100644
--- a/readthedocs.yml
+++ b/readthedocs.yml
@@ -3,7 +3,7 @@ version: 2
build:
os: ubuntu-22.04
tools:
- python: "3.10"
+ python: "3.11"
jobs:
pre_build:
- bst -v export --output src/schema.json
diff --git a/src/CHANGES.md b/src/CHANGES.md
index 59849ec54c..261bb25a44 100644
--- a/src/CHANGES.md
+++ b/src/CHANGES.md
@@ -2,7 +2,38 @@
## [Unreleased](https://github.com/bids-standard/bids-specification/tree/HEAD)
+- \[FIX] Update links to BIDS examples [#1545](https://github.com/bids-standard/bids-specification/pull/1545) ([Remi-Gau](https://github.com/Remi-Gau))
+- \[FIX] match subject label in folder and filename in func example [#1536](https://github.com/bids-standard/bids-specification/pull/1536) ([Remi-Gau](https://github.com/Remi-Gau))
+- FIX: Trail MEG directory formats with `/` [#1534](https://github.com/bids-standard/bids-specification/pull/1534) ([effigies](https://github.com/effigies))
+- \[ENH] Permit CITATION.cff as structured alternative to some dataset_description fields [#1525](https://github.com/bids-standard/bids-specification/pull/1525) ([effigies](https://github.com/effigies))
+- \[FIX] Add missing column description in physio example [#1514](https://github.com/bids-standard/bids-specification/pull/1514) ([sappelhoff](https://github.com/sappelhoff))
+- \[ENH] Clarify that data files must be uniquely identified by entities/suffix [#1508](https://github.com/bids-standard/bids-specification/pull/1508) ([sappelhoff](https://github.com/sappelhoff))
+- DOC: Auto-generate full API docs [#1505](https://github.com/bids-standard/bids-specification/pull/1505) ([effigies](https://github.com/effigies))
+- \[INFRA] Publish schema tools API docs to additional ReadTheDocs site [#1491](https://github.com/bids-standard/bids-specification/pull/1491) ([bendhouseart](https://github.com/bendhouseart))
+- \[FIX] Remove references to unspecified aslcontext.json [#1485](https://github.com/bids-standard/bids-specification/pull/1485) ([Remi-Gau](https://github.com/Remi-Gau))
+- \[FIX] Revert index (entity format) definition to be a non-negative number, permitting zero [#1482](https://github.com/bids-standard/bids-specification/pull/1482) ([TheChymera](https://github.com/TheChymera))
+- ENH: Add version list to schema.meta and a warning on unknown BIDSVersion [#1472](https://github.com/bids-standard/bids-specification/pull/1472) ([effigies](https://github.com/effigies))
+- \[FIX] Clean up qMRI RB1COR notes [#1465](https://github.com/bids-standard/bids-specification/pull/1465) ([lukeje](https://github.com/lukeje))
+- \[ENH] Allow fractional seconds in StartTime [#1459](https://github.com/bids-standard/bids-specification/pull/1459) ([sappelhoff](https://github.com/sappelhoff))
+- \[MISC] consistently list filename templates; `ext` --> `extension`; `\_photo.jpg` --> `\_photo.\` [#1458](https://github.com/bids-standard/bids-specification/pull/1458) ([sappelhoff](https://github.com/sappelhoff))
+- \[FIX] Demote `sample` and `value` columns in `events.tsv` from OPTIONAL to arbitrary [#1457](https://github.com/bids-standard/bids-specification/pull/1457) ([sappelhoff](https://github.com/sappelhoff))
+- ENH: Clarify shape of PDT2 images and recommend acq entity for split PDw/T2w images [#1448](https://github.com/bids-standard/bids-specification/pull/1448) ([effigies](https://github.com/effigies))
+- \[ENH] clarify guiding principles for requirement levels [#1444](https://github.com/bids-standard/bids-specification/pull/1444) ([sappelhoff](https://github.com/sappelhoff))
+- \[FIX] Clarify that dataset_description.Genetics object is required for genetics data [#1442](https://github.com/bids-standard/bids-specification/pull/1442) ([sappelhoff](https://github.com/sappelhoff))
+- \[ENH] Clarify that BIDS specification is in American English [#1439](https://github.com/bids-standard/bids-specification/pull/1439) ([yarikoptic](https://github.com/yarikoptic))
+- \[MISC] Link to new BEP guidelines. [#1426](https://github.com/bids-standard/bids-specification/pull/1426) ([arokem](https://github.com/arokem))
+- \[FIX] reorganize anat filename templates [#1419](https://github.com/bids-standard/bids-specification/pull/1419) ([Remi-Gau](https://github.com/Remi-Gau))
+- \[FIX] Rename `channels.tsv` column: `orientation\_component` to `component` [#1417](https://github.com/bids-standard/bids-specification/pull/1417) ([sjeung](https://github.com/sjeung))
+- \[ENH] Some missing docstrings for bidsschematools [#1413](https://github.com/bids-standard/bids-specification/pull/1413) ([anibalsolon](https://github.com/anibalsolon))
+- \[FIX] fixing some minor issues introduced recently [#1411](https://github.com/bids-standard/bids-specification/pull/1411) ([yarikoptic](https://github.com/yarikoptic))
+- SCHEMA: Implement some fairly easy rules [#1410](https://github.com/bids-standard/bids-specification/pull/1410) ([effigies](https://github.com/effigies))
+- \[REF] refactor institution and task tables [#1397](https://github.com/bids-standard/bids-specification/pull/1397) ([Remi-Gau](https://github.com/Remi-Gau))
+- \[ENH] Add paragraph about richness versus distinctness in filenames [#1392](https://github.com/bids-standard/bids-specification/pull/1392) ([CPernet](https://github.com/CPernet))
+- \[FIX] Updated HED score library version [#1390](https://github.com/bids-standard/bids-specification/pull/1390) ([VisLab](https://github.com/VisLab))
+- \[FIX] Updated links and fixed typos in hed appendix [#1383](https://github.com/bids-standard/bids-specification/pull/1383) ([VisLab](https://github.com/VisLab))
+- \[ENH] add screen parameters metadata [#1369](https://github.com/bids-standard/bids-specification/pull/1369) ([Remi-Gau](https://github.com/Remi-Gau))
- RF: allow for any "recording" file to be listed in \_scans.tsv not just "neural recording" [#1368](https://github.com/bids-standard/bids-specification/pull/1368) ([yarikoptic](https://github.com/yarikoptic))
+- \[FIX] wrong usage of DOI in SourceDatasets example [#1361](https://github.com/bids-standard/bids-specification/pull/1361) ([sappelhoff](https://github.com/sappelhoff))
- \[FIX] make references to Neuromag/Elekta/MEGIN consistent [#1359](https://github.com/bids-standard/bids-specification/pull/1359) ([sappelhoff](https://github.com/sappelhoff))
- \[DOC] link to steering group section on bids website [#1358](https://github.com/bids-standard/bids-specification/pull/1358) ([Remi-Gau](https://github.com/Remi-Gau))
- \[ENH] Add reference for ASL BEP [#1357](https://github.com/bids-standard/bids-specification/pull/1357) ([Remi-Gau](https://github.com/Remi-Gau))
@@ -11,8 +42,12 @@
- \[ENH] Recommend gzip header fields be set to empty values [#1349](https://github.com/bids-standard/bids-specification/pull/1349) ([kousu](https://github.com/kousu))
- \[FIX] clarify TriggerChannelCount and TRIG type [#1342](https://github.com/bids-standard/bids-specification/pull/1342) ([sappelhoff](https://github.com/sappelhoff))
- \[ENH] Add qMRI fieldmap filename templates [#1336](https://github.com/bids-standard/bids-specification/pull/1336) ([Remi-Gau](https://github.com/Remi-Gau))
+- ENH: Introduce GIFTI formats in derivatives [#1333](https://github.com/bids-standard/bids-specification/pull/1333) ([effigies](https://github.com/effigies))
- \[ENH] Add ParallelReductionFactorOutOfPlane to MRI metadata [#1221](https://github.com/bids-standard/bids-specification/pull/1221) ([lukeje](https://github.com/lukeje))
- \[ENH]\[SCHEMA] Adding an OPTIONAL \_task-\ to structural MRI acquisitions [#1185](https://github.com/bids-standard/bids-specification/pull/1185) ([melanieganz](https://github.com/melanieganz))
+- \[INFRA] use tributors to list contributors and CITATION.cff for referencing [#1115](https://github.com/bids-standard/bids-specification/pull/1115) ([Remi-Gau](https://github.com/Remi-Gau))
+- \[ENH] Extend BIDS for Motion data (BEP029) [#981](https://github.com/bids-standard/bids-specification/pull/981) ([JuliusWelzel](https://github.com/JuliusWelzel))
+- \[SCHEMA] Add full object definitions for valid values in schema [#919](https://github.com/bids-standard/bids-specification/pull/919) ([tsalo](https://github.com/tsalo))
## [v1.8.0](https://github.com/bids-standard/bids-specification/tree/v1.8.0) (2022-10-29)
diff --git a/src/appendices/contributors.md b/src/appendices/contributors.md
index df5b102ab9..e2da78c45b 100644
--- a/src/appendices/contributors.md
+++ b/src/appendices/contributors.md
@@ -36,282 +36,326 @@ The following individuals have contributed to the Brain Imaging Data Structure
ecosystem (in alphabetical order).
If you contributed to the BIDS ecosystem and your name is not listed, please add it.
-- Eric Achten 📖🔣📓
-- Azeez Adebimpe 📖
-- Rémi Adon 📖
-- Fidel Alfaro Almagro 💬📖💡🔌
-- David Alsop 📖
-- Stefan Appelhoff 📖💬🤔🐛💡💻👀⚠️📢✅🔧🔌📝🚧🔣
-- Yoni Ashar 📖
-- Tal Pal Attia 📖
-- Tibor Auer 💬📖💡🔧📢🐛🤔
-- Sylvain Baillet 📖🔍
-- Shashank Bansal 📖
-- Arshitha Basavaraj 📖🚇💻
-- Ben Beasley 💻
-- Leandro Beltrachini 📖
-- Chris Benjamin 📖
-- Timo Berg 📖
-- Étienne Bergeron 🔣💻
-- Giacomo Bertazzoli 📖
-- Suyash Bhogawar 📖💡⚠️🔧💬
-- Stephan Bickel 📖
-- Ulrike Bingel 📖
-- Ethan Blackwood 👀📖
-- David Boas 📖
-- Elizabeth Bock 📖💡
-- Hugo Boniface 📖
-- Marta Bortoletto 📖
-- Kristofer Bouchard 📖
-- Mathieu Boudreau 💬🤔📢
-- Marie-Hélène Bourget 📖🔣💻🤔
-- Eric Bridgeford 📖🔧
-- Teon L. Brooks 📖💻⚠️💬👀🤔🔧🐛📢
-- Martina Bulgari 📖
-- Christian Büchel 📖
-- Vince D. Calhoun 📖
-- Giulio Castegnaro 📖
-- Marco Castellaro 💬🐛💻📖💡⚠️📢🚇
-- Filippo Maria Castelli 📖🔣
-- Michael Chappell 📖🔣📆
-- Gang Chen 📖
-- William Clarke 📖
-- Patricia Clement 💬🐛💻📖🔣💡📋🤔📆⚠️📢
-- Helena Cockx 📖
-- Alexander L. Cohen 🐛💻📖💬
-- Julien Cohen-Adad 📖🔣🤔
-- R. Cameron Craddock 📖📢
-- Martin Craig 🔣
-- Sasha D'Ambrosio 📖
-- Samir Das 📖
-- Olivier David 📖
-- Orrin Devinsky 📖
-- Gilles de Hollander 📖
-- Alejandro de la Vega 🐛💻⚠️
-- Arnaud Delorme 📖💡
-- John Detre 📖
-- Benjamin Dichter 📖
-- Erin W. Dickie 📖🤔👀📢💬
-- Timo Dickscheid 📖
-- Dejan Draschkow 📖
-- Eugene P. Duff 📖
-- Elizabeth DuPre 📖💡🔍🤔💬
-- Joke Durnez 📖🔧💻
-- Eric Earl 📖💬🐛🚧🔧🤔
-- Cyrus Eierud 💻
-- Anders Eklund 📖📢💻
-- Sara Elgayar 📖
-- Oscar Esteban 📖🔧🤔💬💻
-- Franklin W. Feingold 📋📝✅💬🤔🎨📢👀🚇🖋📆
-- Guillaume Flandin 📖💻
-- Adeen Flinker 📖
-- Alexandru Foias 📖🔣
-- Brett L. Foster 📖
-- Ana Fouto 📓
-- Benjamin Gagl 📖
-- Chris Gahnström 📖
-- Anthony Galassi 📖
-- Giuseppe Gallitto 📖
-- Melanie Ganz-Benjaminsen 📖🔣💻🤔📆🔍📢
-- Samuel Garcia 🤔👀📖
-- Remi Gau 📖💻💬📢🐛💻🚇👀🔧🤔
-- James Gholam 📖
-- Satrajit S. Ghosh 📖💻
-- Ashley G. Gillman 📖
-- Greydon Gilmore 📖
-- Tristan Glatard 📖💻
-- Mathias Goncalves 💻🔧📢
-- Krzysztof J. Gorgolewski 📖💻💬🤔🔍📢📝💡🔌
-- Rohan Goyal 💻
-- Alexandre Gramfort 📖💡
-- Klara Gregorova 📖
-- Jeffrey S. Grethe 💬🐛✅📢
-- Iris Groen 📖
-- David Groppe 📖
-- Sören Grothkopp 📖
-- Aysegul Gunduz 📖
-- Giacomo Guidali 📖
-- Matthias Günther 📖
-- Yaroslav O. Halchenko 📖📢🔧💬🐛
-- Liberty Hamilton 📖
-- Tom Hampshire 📖
-- Daniel A. Handwerker 📖
-- Michael Hanke 📖🤔🔧🐛📢
-- Nell Hardcastle 💻 🤔 🚇 💬 👀
-- Michael P. Harms 📖⚠️🔧
-- Soichi Hayashi 📖🔧🐛
-- Richard N. Henson 📖
-- Peer Herholz 💬📖👀🔧✅📢
-- Dora Hermes 📖💻✅🔍🤔
-- Luis Hernandez-Garcia 📖📓
-- Katja Heuer 🔧
-- Dorien Huijser 📖
-- Alexandre Hutton 📖
-- Richard Höchenberger 📖
-- Chris Holdgraf 📖🤔
-- Christopher J. Honey 📖
-- Andrew Hoopes 📖
-- Christian Horea 📖
-- Jean-Christophe Houde 📖
-- Vittorio Iacovella 📖
-- Maria de la Iglesia 📖
-- Ilkay Isik 📖
-- Hamish Innes-Brown 📖
-- International Neuroinformatics Coordinating Facility 💵📋
-- Andrew Jahn 📓
-- Andrew Janke 📖💻
-- Mainak Jas 📖💻
-- Sein Jeung 📖
-- Alexander Jones 💻🐛
-- Tamás Józsa 📓
-- Jakub Kaczmarzyk 📖🔧🚇
-- Lee Kamentsky 📖
-- Agah Karakuzu 💬📖🔣🤔
-- David Keator 📖
-- James Kent 💬💻
-- Ali Khan 📖
-- Gregory Kiar 📖💻🎨🔧
-- Balint Kincses 📖
-- Thomas Kirk 📖
-- Robert Knight 📖
-- Joost Kuijer 📖
-- Jean-Philippe Lachaux 📖
-- Marc Lalancette 📖
-- Pamela LaMontagne 📖💡
-- Kevin Larcher 💬
-- Jonathan C. Lau 📖
-- Laura and John Arnold Foundation 💵
-- Alexander von Lautz 📖
-- Alberto Lazari 📖
-- Kangjoo Lee 📖
-- Christopher Lee-Messer 📖
-- Jon Haitz Legarreta 💻📖
-- Dan Levitas 📖
-- Adam Li 📖💻
-- Xiangrui Li 📖💻
-- Ilona Lipp 📖
-- Vladimir Litvak 📖
-- Hanzhang Lu 📖
-- Robert Luke 📖
-- Brian N. Lundstrom 📖
-- Dan Lurie 🤔📖🔧🔌💻💬
-- Duncan Macleod 🚇
-- Eleonora Marcantoni 📖
-- Christopher J. Markiewicz 💬🐛💻📖🎨💡🤔🔌👀🔧📢🔣📋🚧
-- Camille Maumet 📖
-- Giacomo Mazzamuto 📖🔣
-- David McAlpine 📖
-- Manuel Mercier 📖🤔
-- Mark Mikkelsen 📖
-- Kai J. Miller 📖
-- Carlo Miniussi 📖
-- Markus Morawski 📖
-- Clara Moreau 📖
-- Jeremy Moreau 📖💡
-- Zachary Michael 📖
-- Ezequiel Mikulan 📖💻
-- Michael P. Milham 💡🔍
-- Jeanette Mumford 📖
-- Athanasia Monika Mowinckel 📖
-- Henk Mutsaerts 💬🐛💻📖💡📋🤔📆📢📓
-- Manjari Narayan 📖
-- National Institute of Mental Health 💵
-- Mikael Naveau 🐛
-- B. Nolan Nichols 📖
-- Thomas E. Nichols 📖📢🔧👀🚧
-- Dylan Nielson 📖💻🔧
-- Aki Nikolaidis 📖
-- Gustav Nilsonne 📖
-- Guiomar Niso 🤔🎨🔍👀📋📝🔧🐛💻🔣✅💬📖💡📢
-- Gregory Noack 💻 ⚠️
-- Martin Noergaard 📖🔣💻🤔📢
-- Michael P. Notter 💬📝✅📢📖
-- Jeffrey G. Ojemann 📖
-- Thomas Okell 📖
-- Aaron Oliver-Taylor 📖
-- Hernando Ombao 📖
-- Robert Oostenveld 📖🔧📢💡✅⚠️🤔💬🐛📝💻🖋🔣🎨📋🚇👀📓📹
-- Dimitri Papadopoulos Orfanos 📖💡🤔💬
-- Felipe Orihuela-Espina 📖
-- Eduard Ort 📖
-- Patrick Park 📖💡💬
-- Maurice Pasternak 📓
-- Chloé Pasturel 📖
-- Dianne Patterson 📖
-- Mateusz Pawlik 🤔 📖 🚧 👀 🐛
-- John Pellman 📖
-- Cyril Pernet 💬📝📖🎨💡📋🤔📢
-- Franco Pestilli 📖💻🎨💡🤔👀🔧📋🔍🚇
-- Jan Petr 💬🐛💻📖🔣💡📋🤔📆⚠️📢
-- Natalia Petridou 📖
-- Dmitry Petrov 📖💻
-- Christophe Phillips 📖
-- Gio Piantoni 📖
-- Andrea Pigorini 📖
-- Russell A. Poldrack 📖🔍📢
-- Jean-Baptiste Poline 📖📢🤔🎨
-- Luca Pollonini 📖
-- Wouter V. Potters 📖
-- Nader Pouratian 📖
-- Pradeep Reddy Raamana 💻🔧
-- Vasudev Raguram 💻🎨📖🔧
-- Nick F. Ramsey 📖
-- Travis Riddle 📖🔧🐛
-- Pierre Rioux 📖
-- Petra Ritter 📖
-- Kay Robbins 💻📖🐛
-- Alex Rockhill 📖🔧
-- Ariel Rokem 📖
-- Chris Rorden 📖💻
-- Jose Manuel Saborit 📖
-- Taylor Salo 💬📖🔌
-- Matt Sanderson 📖💻
-- Gunnar Schaefer 📖
-- Michael Schirner 📖
-- Jan-Mathijs Schoffelen 📖
-- Graham Searle 📖
-- Parul Sethi 📖🔧⚠️💻
-- Maureen J Shader 📖
-- Robert E. Smith 💻📖
-- Vanessa Sochat 📖
-- Anibal Sólon 🐛
-- Tamas Spisak 📖
-- Julia Sprenger 📖
-- Isla Staden 📖
-- Arjen Stolk 📖
-- Nicole C. Swann 📖
-- Filip Szczepankiewicz 📖
-- Martin Szinte 📖
-- François Tadel 📖🔌💡
-- Sylvain Takerkart 📖
-- Bertrand Thirion 📖
-- David Thomas 📖🔣
-- Roberto Toro 🔧
-- Sébastien Tourbier 🤔👀📢🐛💻📖
-- Paule-Joanne Toussaint 📖
-- Nicholas Traut 📖🔧💻
-- William Triplett 📖
-- Jessica A. Turner 📖
-- Pieter Vandemaele 📖💻
-- Max A. van den Boom 💻👀📖🐛
-- Wietske van der Zwaag 🔣💬
-- Matthias Van Osch 📖
-- Gaël Varoquaux 📖
-- Jaap von der Aar 📖
-- Sjoerd Vos 📖
-- Bradley Voytek 📖
-- Tor Wager 📖
-- Adina S. Wagner 🎨
-- Lennart Walger 📖
-- Brian A. Wandell 📖
-- Hao Ting Wang 📖🐛
-- Yuan Wang 💻
-- Julius Welzel 📖
-- Joseph Wexler 📖💡
-- Kirstie Whitaker 📖💡🔍🤔📢💬
-- Martin Wilson 📖
-- Jonathan Winawer 📖
-- Lennart Wittkuhn 📖
-- Joseph Woods 📖
-- Tal Yarkoni 💻📖🤔🔍🔌👀📢🐛🎨
-- Lyuba Zehl 📖
+
+
+| name | contributions |
+| ---------------------------------------------------- | -------------------------------------- |
+| Aaron Oliver-Taylor | 📖 |
+| Adam Li | 📖💻 |
+| Adam Thomas | 📖 |
+| Adeen Flinker | 📖 |
+| Adina S. Wagner | 🎨💻 |
+| Agah Karakuzu | 💬📖🔣🤔💻 |
+| Aki Nikolaidis | 📖 |
+| Alberto Lazari | 📖 |
+| Alejandro de la Vega | 🐛💻⚠️ |
+| Alessio Giacomel | 📖 |
+| Alex Rockhill | 📖🔧💻 |
+| Alexander Jones | 💻🐛 |
+| Alexander L. Cohen | 🐛💻📖💬 |
+| Alexander von Lautz | 📖 |
+| Alexandre Gramfort | 📖💡 |
+| Alexandre Hutton | 📖 |
+| Alexandre Routier | 📖 |
+| Alexandru Foias | 📖🔣 |
+| Ali Khan | 📖 |
+| Ana Fouto | 📓 |
+| Anders Eklund | 📖📢💻 |
+| Andrea Pigorini | 📖 |
+| Andrew Hoopes | 📖 |
+| Andrew Jahn | 📓 |
+| Andrew Janke | 📖💻 |
+| Anibal Sólon | 💻🐛 |
+| Anthony Galassi | 📖💻 |
+| Ariel Rokem | 📖💻 |
+| Arjen Stolk | 📖 |
+| Arnaud Delorme | 📖💡🤔 |
+| Arnaud Marcoux | 📖 |
+| Arshitha Basavaraj | 📖🚇💻 |
+| Ashley G. Gillman | 📖 |
+| Athanasia Monika Mowinckel | 📖 |
+| Aysegul Gunduz | 📖 |
+| Azeez Adebimpe | 📖 |
+| B. Nolan Nichols | 📖 |
+| Balint Kincses | 📖 |
+| Benjamin Beasley | 📖 |
+| Benjamin Dichter | 📖 |
+| Benjamin Gagl | 📖 |
+| Bertrand Thirion | 📖 |
+| Bradley Voytek | 📖 |
+| Brett L. Foster | 📖 |
+| Brian A. Wandell | 📖 |
+| Brian N. Lundstrom | 📖 |
+| Camille Maumet | 📖 |
+| Carlo Miniussi | 📖 |
+| Chloé Pasturel | 📖 |
+| Chris Benjamin | 📖 |
+| Chris Gahnström | 📖 |
+| Chris Holdgraf | 📖🤔💻 |
+| Chris J. Gorgolewski | 📖💻💬🤔🔍📢📝💡🔌 |
+| Chris Rorden | 📖💻 |
+| Christian Büchel | 📖 |
+| Christian Horea | 💻📖 |
+| Christine Rogers | 🔣📖🤔🚇🔧 |
+| Christophe Phillips | 📖 |
+| Christopher J. Honey | 📖 |
+| Christopher J. Markiewicz | 💬🐛💻📖🎨💡🤔🔌👀🔧📢🔣📋🚧 |
+| Christopher Lee-Messer | 📖 |
+| Clara Moreau | 📖 |
+| Clint Hansen | 📖🤔 |
+| Cyril Pernet | 💬📝📖🎨💡📋🤔📢 |
+| Cyrus Eierud | 📖 |
+| D. Sturgeon | 💻 |
+| Dan Levitas | 📖 |
+| Dan Lurie | 🤔📖🔧🔌💻💬 |
+| Daniel A. Handwerker | 📖 |
+| David Alsop | 📖 |
+| David Boas | 📖 |
+| David Groppe | 📖 |
+| David Keator | 📖 |
+| David McAlpine | 📖 |
+| David Thomas | 📖🔣 |
+| Dejan Draschkow | 📖 |
+| Dianne Patterson | 📖 |
+| Dimitri Papadopoulos Orfanos | 📖💡🤔💬💻 |
+| Dmitry Petrov | 📖💻 |
+| Dora Hermes | 📖💻✅🔍🤔 |
+| Dorien Huijser | 📖 |
+| Douglas N. Greve | 📖 |
+| Duncan Macleod | 📖🚇 |
+| Dung Truong | 📖💻🔧🤔 |
+| Dylan Nielson | 📖💻🔧 |
+| Eduard Ort | 📖💻 |
+| Eleonora Marcantoni | 📖 |
+| Elizabeth Bock | 📖💡 |
+| Elizabeth DuPre | 📖💡🔍🤔💬💻 |
+| Elke Warmerdam | 🔣📖 |
+| Erdal Karaca | 💻 |
+| Eric A. Earl | 📖💬🐛🚧🔧🤔💻 |
+| Eric Achten | 📖🔣📓 |
+| Eric Bridgeford | 📖🔧 |
+| Erin W. Dickie | 📖🤔👀📢💬💻 |
+| Ethan Blackwood | 👀📖 |
+| Eugene P. Duff | 📖 |
+| Ezequiel Mikulan | 📖💻 |
+| Felipe Orihuela-Espina | 📖 |
+| Fidel Alfaro Almagro | 💬📖💡🔌 |
+| Filip Szczepankiewicz | 📖 |
+| Filippo Maria Castelli | 📖🔣 |
+| Franco Pestilli | 📖💻🎨💡🤔👀🔧📋🔍🚇 |
+| Franklin W. Feingold | 📋📝✅💬🤔🎨📢👀🚇🖋️📆💻 |
+| François Tadel | 📖🔌💡 |
+| Gaia Rizzo | 📖 |
+| Gang Chen | 📖 |
+| Gaël Varoquaux | 📖 |
+| Ghislain Vaillant | 💻 |
+| Giacomo Bertazzoli | 📖 |
+| Giacomo Guidali | 📖 |
+| Giacomo Mazzamuto | 📖🔣 |
+| Gilles de Hollander | 📖 |
+| Gio Piantoni | 📖 |
+| Gitte M. Knudsen | 📖 |
+| Giulio Castegnaro | 📖 |
+| Giuseppe Gallitto | 📖 |
+| Graham Searle | 📖 |
+| Granville J. Matheson | 📖 |
+| Gregory Kiar | 📖💻🎨🔧 |
+| Gregory Noack | 📖💻⚠️ |
+| Greydon Gilmore | 📖💻 |
+| Guillaume Flandin | 📖💻 |
+| Gunnar Schaefer | 📖 |
+| Gustav Nilsonne | 📖 |
+| Hamish Innes-Brown | 📖 |
+| Hanne D. Hansen | 📖 |
+| Hanzhang Lu | 📖 |
+| Hao-Ting Wang | 📖🐛 |
+| Helena Cockx | 📖🤔💬 |
+| Henk Mutsaerts | 💬🐛💻📖💡📋🤔📆📢📓 |
+| Hernando Ombao | 📖 |
+| Hugo Boniface | 📖💻 |
+| Ilkay Isik | 📖 |
+| Ilona Lipp | 📖 |
+| International Neuroinformatics Coordinating Facility | 💵📋 |
+| Iris Groen | 📖 |
+| Isla Staden | 📖 |
+| Jaap von der Aar | 📖 |
+| Jakub Kaczmarzyk | 📖🔧🚇 |
+| James Gholam | 📖 |
+| James Kent | 💬💻 |
+| Jan Mathijs Schoffelen | 📖 |
+| Jan Petr | 💬🐛💻📖🔣💡📋🤔📆⚠️📢 |
+| Jan-Mathijs Schoffelen | 📖 |
+| Jean-Baptiste Poline | 📖📢🤔🎨💻 |
+| Jean-Christophe Houde | 📖💻 |
+| Jean-Dominique Gallezot | 📖 |
+| Jean-Philippe Lachaux | 📖 |
+| Jeanette Mumford | 📖 |
+| Jeffrey G. Ojemann | 📖 |
+| Jeffrey S. Grethe | 💬🐛✅📢💻 |
+| JegouA | 💻 |
+| Jelle Dalenberg | 📖 |
+| Jeremy Moreau | 📖💡 |
+| Jessica A. Turner | 📖 |
+| Jochem Rieger | 📖 |
+| John Detre | 📖 |
+| John Pellman | 📖 |
+| John T. Wodder | 💻 |
+| Joke Durnez | 📖🔧💻 |
+| Jon Haitz Legarreta Gorroño | 💻📖 |
+| Jonathan C. Lau | 📖 |
+| Jonathan Winawer | 📖 |
+| Joost Kuijer | 📖 |
+| Jose Manuel Saborit | 📖 |
+| Joseph Wexler | 📖💡 |
+| Joseph Woods | 📖 |
+| Julia Guiomar Niso Galán | 🤔🎨🔍👀📋📝🔧🐛💻🔣✅💬📖💡📢 |
+| Julia Sprenger | 📖 |
+| Julien Cohen-Adad | 📖🔣🤔 |
+| Julius Welzel | 📖💡🐛💻🔣🤔💬📓 |
+| Kai J. Miller | 📖 |
+| Kangjoo Lee | 📖 |
+| Katja Heuer | 🔧 |
+| Kay Robbins | 💻📖🐛 |
+| Kevin Larcher | 💬 |
+| Kimberly Ray | 📖📋🤔📆 |
+| Kirstie Whitaker | 📖💡🔍🤔📢💬💻 |
+| Klara Gregorova | 📖 |
+| Klaus Gramann | 📖🤔 |
+| Kris Thielemans | 📖 |
+| Kristofer Bouchard | 📖 |
+| Kurt Schilling | 📖 |
+| Laura and John Arnold Foundation | 💵 |
+| Leandro Beltrachini | 📖 |
+| Lee Kamentsky | 📖 |
+| Lennart Walger | 📖 |
+| Lennart Wittkuhn | 📖 |
+| Liberty Hamilton | 📖 |
+| Luca Pollonini | 📖 |
+| Luis Hernandez-Garcia | 📖📓 |
+| Luke J. Edwards | 📖💬 |
+| Lyuba Zehl | 📖 |
+| Mainak Jas | 📖💻 |
+| Manjari Narayan | 📖 |
+| Manuel Mercier | 📖🤔 |
+| Maqsood Yaqub | 📖 |
+| Marc Lalancette | 📖💻 |
+| Marco Castellaro | 💬🐛💻📖💡⚠️📢🚇 |
+| Maria de la Iglesia | 📖 |
+| Marie-Hélène Bourget | 📖🔣💻🤔 |
+| Mark Mikkelsen | 📖 |
+| Markus Morawski | 📖 |
+| Marta Bortoletto | 📖 |
+| Martin Craig | 🔣 |
+| Martin Noergaard | 📖🔣💻🤔📢 |
+| Martin Szinte | 📖 |
+| Martin Wilson | 📖 |
+| Martina Bulgari | 📖 |
+| Mateusz Pawlik | 📖🐛🤔🚧👀 |
+| Mathias Goncalves | 💻🔧📢 |
+| Mathieu Boudreau | 💬🤔📢 |
+| Matt Sanderson | 📖💻 |
+| Matteo Tonietto | 📖 |
+| Matthias Günther | 📖 |
+| Matthias Van Osch | 📖 |
+| Maureen J Shader | 📖 |
+| Maurice Pasternak | 📓 |
+| Max A. van den Boom | 💻👀📖🐛 |
+| Melanie Ganz-Benjaminsen | 📖🔣💻🤔📆🔍📢 |
+| Michael Chappell | 📖🔣📆 |
+| Michael Hanke | 📖🤔🔧🐛📢 |
+| Michael P. Harms | 📖⚠️🔧 |
+| Michael P. Milham | 💡🔍 |
+| Michael P. Notter | 💬📝✅📢📖 |
+| Michael Schirner | 📖 |
+| Mikaël Naveau | 🐛 |
+| Nader Pouratian | 📖 |
+| Natalia Petridou | 📖 |
+| National Institute of Mental Health | 💵 |
+| Nell Hardcastle | 💻📖🤔🚇👀💬 |
+| Nicholas Traut | 📖🔧💻 |
+| Nick F. Ramsey | 📖 |
+| Nicole C. Swann | 📖 |
+| Nima Bigdely Shamlo | 📖 |
+| Olivier David | 📖 |
+| Orrin Devinsky | 📖 |
+| Oscar Esteban | 📖🔧🤔💬💻 |
+| Pamela LaMontagne | 📖💡 |
+| Parul Sethi | 📖🔧⚠️💻 |
+| Patricia Clement | 💬🐛💻📖🔣💡📋🤔📆⚠️📢 |
+| Patrick Park | 📖💡💬💻 |
+| Paule-Joanne Toussaint | 📖 |
+| Peer Herholz | 💬📖👀🔧✅📢 |
+| Petra Ritter | 📖 |
+| Pierre Rioux | 📖 |
+| Pieter Vandemaele | 📖💻 |
+| Pradeep Reddy Raamana | 💻🔧 |
+| R. Cameron Craddock | 📖📢 |
+| Remi Gau | 📖💻💬📢🐛💻🚇👀🔧🤔 |
+| Richard Höchenberger | 📖💻 |
+| Richard N. Henson | 📖 |
+| Robert B. Innis | 📖 |
+| Robert E. Smith | 💻📖 |
+| Robert Knight | 📖 |
+| Robert Luke | 💻 |
+| Robert Oostenveld | 📖🔧📢💡✅⚠️🤔💬🐛📝💻🖋️🔣🎨📋🚇👀📓📹 |
+| Roberto Toro | 🔧 |
+| Rohan Goyal | 📖 |
+| Ross W. Blair | 💻 |
+| Russell A. Poldrack | 📖🔍📢 |
+| Rémi Adon | 📖 |
+| Samir Das | 📖 |
+| Samuel Garcia | 🤔👀📖 |
+| Samuel Nastase | 💻 |
+| Sara Elgayar | 📖 |
+| Sasha D'Ambrosio | 📖 |
+| Satrajit S. Ghosh | 📖💻 |
+| Scott Makeig | 📖 |
+| Sein Jeung | 📖💡🐛💻🔣🤔💬🔧📓 |
+| Shashank Bansal | 📖 |
+| Sjoerd B. Vos | 📖 |
+| Soichi Hayashi | 📖🔧🐛 |
+| Stefan Appelhoff | 📖💬🤔🐛💡💻👀⚠️📢✅🔧🔌📝🚧🔣 |
+| Stephan Bickel | 📖 |
+| Suyash Bhogawar | 📖💡⚠️🔧💬 |
+| Sylvain Baillet | 📖🔍 |
+| Sylvain Takerkart | 📖 |
+| Sébastien Tourbier | 🤔👀📢🐛💻📖 |
+| Sören Grothkopp | 📖🔣📓 |
+| Tal Pal Attia | 📖 |
+| Tal Yarkoni | 💻📖🤔🔍🔌👀📢🐛🎨 |
+| Tamas Spisak | 📖 |
+| Tamás Józsa | 📓 |
+| Taylor Salo | 💬📖🔌💻 |
+| Teon L. Brooks | 📖💻⚠️💬👀🤔🔧🐛📢 |
+| Thomas E. Nichols | 📖📢🔧👀🚧💻 |
+| Thomas Funck | 📖 |
+| Thomas Kirk | 📖 |
+| Thomas Okell | 📖 |
+| Tibor Auer | 💬📖💡🔧📢🐛🤔 |
+| Timo Dickscheid | 📖 |
+| Timotheus Berg | 📖🤔📓 |
+| Tobey Betthauser | 📖 |
+| Tobias Bengfort | 💻 |
+| Tom Hampshire | 📖 |
+| Tor Wager | 📖 |
+| Travis Riddle | 📖🔧🐛 |
+| Tristan Glatard | 📖💻 |
+| Ulrike Bingel | 📖 |
+| Vanessa Sochat | 📖 |
+| Vasudev Raguram | 💻🎨📖🔧 |
+| Vince D. Calhoun | 📖 |
+| Vittorio Iacovella | 📖 |
+| Vladimir Litvak | 📖 |
+| Wietske van der Zwaag | 🔣💬 |
+| William Clarke | 📖 |
+| William Triplett | 📖 |
+| Wouter V. Potters | 💻📖 |
+| Xiangrui Li | 📖💻 |
+| Yaroslav O. Halchenko | 📖📢🔧💬🐛💻 |
+| Yoni Ashar | 📖 |
+| Yuan Wang | 💻 |
+| Zachary Michael | 📖 |
+| ezemikulan | 💻 |
+| josator2 | 💻 |
+| monkeyman192 | 💻 |
+| Étienne Bergeron | 🔣💻 |
diff --git a/src/appendices/coordinate-systems.md b/src/appendices/coordinate-systems.md
index 16ab9e227f..157f327d79 100644
--- a/src/appendices/coordinate-systems.md
+++ b/src/appendices/coordinate-systems.md
@@ -205,7 +205,7 @@ Unless specified explicitly in the sidecar file in the
| fsLR | The `fsLR` is a **dual template** providing both volumetric and surface coordinates references. The volumetric template corresponds to `MNI152NLin6Asym`. Surface templates are given at several sampling densities: 164k (used by HCP pipelines for 3T and 7T anatomical analysis), 59k (used by HCP pipelines for 7T MRI bold and DWI analysis), 32k (used by HCP pipelines for 3T MRI bold and DWI analysis), or 4k (used by HCP pipelines for MEG analysis) fsaverage_LR surface reconstructed from the T1w image. | Freesurfer | |
| MNIColin27 | Average of 27 T1 scans of a single subject | SPM96 | [https://www.bic.mni.mcgill.ca/ServicesAtlases/Colin27Highres](https://www.bic.mni.mcgill.ca/ServicesAtlases/Colin27Highres) |
| MNI152Lin | Also known as ICBM (version with linear coregistration) | SPM99 to SPM8 | [https://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152Lin](https://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152Lin) |
-| MNI152NLin2009\[a-c\]\[Sym\|Asym\] | Also known as ICBM (non-linear coregistration with 40 iterations, released in 2009). It comes in either three different flavours each in symmetric or asymmetric version. | DARTEL toolbox in SPM12b | [https://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152NLin2009](https://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152NLin2009) |
+| MNI152NLin2009\[a-c\]\[Sym\|Asym\] | Also known as ICBM (non-linear coregistration with 40 iterations, released in 2009). It comes in either three different flavors each in symmetric or asymmetric version. | DARTEL toolbox in SPM12b | [https://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152NLin2009](https://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152NLin2009) |
| MNI152NLin6Sym | Also known as symmetric ICBM 6th generation (non-linear coregistration). | FSL | [https://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152NLin6](https://www.bic.mni.mcgill.ca/ServicesAtlases/ICBM152NLin6) |
| MNI152NLin6ASym | A variation of `MNI152NLin6Sym` built by A. Janke that is released as the _MNI template_ of FSL. Volumetric templates included with [HCP-Pipelines](https://github.com/Washington-University/HCPpipelines/tree/master/global/templates) correspond to this template too. | HCP-Pipelines | [doi:10.1016/j.neuroimage.2012.01.024](https://doi.org/10.1016/j.neuroimage.2012.01.024) |
| MNI305 | Also known as avg305. | | |
diff --git a/src/appendices/entities.md b/src/appendices/entities.md
index 3e9605a9d8..2cdd83855d 100644
--- a/src/appendices/entities.md
+++ b/src/appendices/entities.md
@@ -1,10 +1,10 @@
# Entities
-This section compiles the entities (key-value pairs within file names) described throughout this
-specification, and describes each.
+This section compiles the entities (key-value pairs within filenames)
+described throughout this specification, and describes each.
A general introduction to entities is given in the section on
-[filename structure](../common-principles.md#file-name-structure).
+[filename structure](../common-principles.md#filenames).
The ordering of entities, and whether each is OPTIONAL, REQUIRED, or MUST NOT
be specified for a given file type, is specified in the [Entity Table](entity-table.md).
diff --git a/src/appendices/entity-table.md b/src/appendices/entity-table.md
index 1a4ff0fffd..4dff2dcf93 100644
--- a/src/appendices/entity-table.md
+++ b/src/appendices/entity-table.md
@@ -1,7 +1,8 @@
# Entity table
-This section compiles the entities (key-value pairs within file names) described throughout this
-specification, and establishes a common order within a filename.
+This section compiles the entities (key-value pairs within filenames)
+described throughout this specification,
+and establishes a common order within a filename.
For example, if a file has an acquisition and reconstruction label, the
acquisition entity must precede the reconstruction entity.
REQUIRED and OPTIONAL entities for a given file type are denoted;
@@ -10,7 +11,7 @@ Entity formats indicate whether the value is alphanumeric
(``) or numeric (``).
A general introduction to entities is given in the section on
-[filename structure](../common-principles.md#file-name-structure),
+[filename structure](../common-principles.md#filenames),
while entity definitions are in the [Entities Appendix](entities.md).
+{{ MACROS___make_suffix_table(
+ [
+ "MESE",
+ "MEGRE",
+ "VFA",
+ "IRT1",
+ "MP2RAGE",
+ "MPM",
+ "MTS",
+ "MTR",
+ ]
+ )
+}}
+
+
+{{ MACROS___make_filename_template("raw", datatypes=["anat"], suffixes=[
+ "MESE",
+ "MEGRE",
+ "VFA",
+ "IRT1",
+ "MP2RAGE",
+ "MPM",
+ "MTS",
+ "MTR",
+ ])
+}}
+
Below is an example file collection for `MP2RAGE`:
+{{ MACROS___make_suffix_table(
+ [
+ "TB1DAM",
+ "TB1EPI",
+ "TB1AFI",
+ "TB1TFL",
+ "TB1RFM",
+ "RB1COR",
+ "TB1SRGE",
+ "TB1map",
+ "RB1map",
+ ]
+ )
+}}
+
[definitions]: ../common-principles.md#definitions
@@ -77,3 +109,7 @@ in [Derived dataset and pipeline description][derived-dataset-description].
[storage]: ../common-principles.md#storage-of-derived-datasets
[derived-dataset-description]: ../modality-agnostic-files.md#derived-dataset-and-pipeline-description
+
+[gifti]: https://www.nitrc.org/projects/gifti/
+
+[gifti-spec]: https://www.nitrc.org/frs/download.php/2871/GIFTI_Surface_Format.pdf
diff --git a/src/extensions.md b/src/extensions.md
index 92446727f4..8e91aa0ae3 100644
--- a/src/extensions.md
+++ b/src/extensions.md
@@ -3,7 +3,7 @@
The BIDS specification can be extended in a backwards compatible way and will
evolve over time.
This is accomplished with BIDS Extension Proposals (BEPs),
-which are community-driven processes (see [BEP guidelines Google Doc](https://docs.google.com/document/d/1pWmEEY-1-WuwBPNy5tDAxVJYQ9Een4hZJM06tQZg8X4/)).
+which are community-driven processes (see [BEP guidelines](https://bids-extensions.readthedocs.io/en/latest/)).
On the [BIDS homepage](https://bids.neuroimaging.io/) you can find a
[list of extension proposals](https://bids.neuroimaging.io/get_involved.html#extending-the-bids-specification)
diff --git a/src/introduction.md b/src/introduction.md
index 5e860d899b..4e54d79157 100644
--- a/src/introduction.md
+++ b/src/introduction.md
@@ -8,22 +8,22 @@ obtained in neuroimaging experiments. Even two researchers working in the same
lab can opt to arrange their data in a different way. Lack of consensus (or a
standard) leads to misunderstandings and time wasted on rearranging data or
rewriting scripts expecting certain structure. Here we describe a simple and
-easy-to-adopt way of organising neuroimaging and behavioral data. By using this
+easy-to-adopt way of organizing neuroimaging and behavioral data. By using this
standard you will benefit in the following ways:
- It will be easy for another researcher to work on your data. To understand
- the organisation of the files and their format you will only need to refer
+ the organization of the files and their format you will only need to refer
them to this document. This is especially important if you are running your
own lab and anticipate more than one person working on the same data over
time. By using BIDS you will save time trying to understand and reuse data
acquired by a graduate student or postdoc that has already left the lab.
- There are a growing number of data analysis software packages that can
- understand data organised according to BIDS (see the
+ understand data organized according to BIDS (see the
[up to date list](https://bids.neuroimaging.io/benefits.html)).
- Databases such as [OpenNeuro.org](https://openneuro.org/) accept datasets
- organised according to BIDS.
+ organized according to BIDS.
If you ever plan to share your data publicly (nowadays some journals require
this) you can minimize the additional time and energy spent on publication,
and speed up the curation process by using BIDS to structure and describe
@@ -168,6 +168,10 @@ For example:
- (publication forthcoming)
+#### Motion
+
+- (publication forthcoming)
+
### Research Resource Identifier (RRID)
BIDS has also a
diff --git a/src/longitudinal-and-multi-site-studies.md b/src/longitudinal-and-multi-site-studies.md
index 5f114b2534..ad831c516e 100644
--- a/src/longitudinal-and-multi-site-studies.md
+++ b/src/longitudinal-and-multi-site-studies.md
@@ -1,7 +1,7 @@
# Longitudinal and multi-site studies
Multiple sessions (visits) are encoded by adding an extra layer of directories
-and [filenames](common-principles.md#file-name-structure)
+and [filenames](common-principles.md#filenames)
in the form of a session (for example `ses-`) and
with a [`*_sessions.tsv` file](modality-agnostic-files.md#sessions-file).
diff --git a/src/modality-agnostic-files.md b/src/modality-agnostic-files.md
index 65a9bbf42c..fda6643276 100644
--- a/src/modality-agnostic-files.md
+++ b/src/modality-agnostic-files.md
@@ -11,7 +11,12 @@ Templates:
### `dataset_description.json`
-The file `dataset_description.json` is a JSON file describing the dataset.
+
+{{ MACROS___render_text("objects.files.dataset_description.description") }}
+
Every dataset MUST include this file with the following fields:
+{{ MACROS___render_text("objects.files.README.description") }}
+
+### `CITATION.cff`
+
+
+{{ MACROS___render_text("objects.files.CITATION.description") }}
+
+For most redundant fields between `CITATION.cff` and `dataset_description.json`,
+the `CITATION.cff` SHOULD take precedence.
+To avoid inconsistency, metadata present in `CITATION.cff` SHOULD NOT be
+be included in `dataset_description.json`, with the exception of `Name` and
+`DatasetDOI`, to ensure that `CITATION.cff`-unaware tools can generate
+references to the dataset.
+In particular, if `CITATION.cff` is present,
+the `"Authors"` field of `dataset_description.json` MUST be omitted,
+and the `"HowToAcknowledge"`, `"License"` and `"ReferencesAndLinks"` SHOULD be omitted
+in favor of the `CITATION.cff` fields `message`/`preferred-citation`, `license` and
+`references`.
### `CHANGES`
-Version history of the dataset (describing changes, updates and corrections) MAY
-be provided in the form of a `CHANGES` text file. This file MUST follow the
-[CPAN Changelog convention](https://metacpan.org/pod/release/HAARG/CPAN-Changes-0.400002/lib/CPAN/Changes/Spec.pod).
-The `CHANGES` file MUST be either in ASCII or UTF-8 encoding.
+
+{{ MACROS___render_text("objects.files.CHANGES.description") }}
Example:
@@ -190,10 +207,11 @@ Example:
### `LICENSE`
-A `LICENSE` file MAY be provided in addition to the short specification of the
-used license in the `dataset_description.json` `"License"` field.
-The `"License"` field and `LICENSE` file MUST correspond.
-The `LICENSE` file MUST be either in ASCII or UTF-8 encoding.
+
+{{ MACROS___render_text("objects.files.LICENSE.description") }}
## Participants file
@@ -204,23 +222,15 @@ participants.tsv
participants.json
```
-The purpose of this RECOMMENDED file is to describe properties of participants
-such as age, sex, handedness, species and strain.
-If this file exists, it MUST contain the column `participant_id`,
-which MUST consist of `sub-` values identifying one row for each participant,
-followed by a list of optional columns describing participants.
-Each participant MUST be described by one and only one row.
-
-The RECOMMENDED `species` column SHOULD be a binomial species name from the
-[NCBI Taxonomy](https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi)
-(for examples `homo sapiens`, `mus musculus`, `rattus norvegicus`).
-For backwards compatibility, if `species` is absent, the participant is assumed to be
-`homo sapiens`.
+
+{{ MACROS___render_text("objects.files.participants.description") }}
-Commonly used *optional* columns in `participants.tsv` files are `age`, `sex`,
-`handedness`, `strain`, and `strain_rrid`. We RECOMMEND to make use
-of these columns, and in case that you do use them, we RECOMMEND to use the
-following values for them:
+We RECOMMEND to make use of these columns, and
+in case that you do use them, we RECOMMEND to use the following values
+for them:
+{{ MACROS___render_text("objects.files.samples.description") }}
+
+{{ MACROS___make_sidecar_table("beh.BEHTaskInformation") }}
+
+### Institution information
-{{ MACROS___make_sidecar_table("beh.BEHTabularData") }}
+{{ MACROS___make_sidecar_table("beh.BEHInstitutionInformation") }}
-Example of the content of a `_beh.tsv` and its accompanying `_beh.json` sidecar file:
+## Example `_beh.tsv`
```Text
trial response response_time stim_file
diff --git a/src/modality-specific-files/electroencephalography.md b/src/modality-specific-files/electroencephalography.md
index f2ede709b3..781993e1bc 100644
--- a/src/modality-specific-files/electroencephalography.md
+++ b/src/modality-specific-files/electroencephalography.md
@@ -6,7 +6,7 @@ Please see [Citing BIDS](../introduction.md#citing-bids)
on how to appropriately credit this extension when referring to it in the
context of the academic literature.
-Several [example EEG datasets](https://github.com/bids-standard/bids-examples#eeg-datasets)
+Several [example EEG datasets](https://bids-standard.github.io/bids-examples/#eeg)
have been formatted using this specification
and can be used for practical guidance when curating a new dataset.
@@ -85,7 +85,11 @@ be specified.
### Sidecar JSON (`*_eeg.json`)
-Generic fields MUST be present:
+For consistency between studies and institutions,
+we encourage users to extract the values of these fields from the actual raw data.
+Whenever possible, please avoid using ad hoc wording.
+
+Those fields MUST be present:
-{{ MACROS___make_sidecar_table("eeg.EEGGeneric") }}
+{{ MACROS___make_sidecar_table("eeg.EEGRequired") }}
-SHOULD be present: For consistency between studies and institutions, we
-encourage users to extract the values of these fields from the actual raw data.
-Whenever possible, please avoid using ad hoc wording.
+Those fields SHOULD be present:
{{ MACROS___make_sidecar_table("eeg.EEGRecommended") }}
-Specific EEG fields MUST be present:
+#### Hardware information
-{{ MACROS___make_sidecar_table("eeg.EEGRequired") }}
+{{ MACROS___make_sidecar_table("eeg.EEGHardware") }}
+
+#### Task information
+
+
+{{ MACROS___make_sidecar_table("eeg.EEGTaskInformation") }}
-SHOULD be present:
+#### Institution information
-{{ MACROS___make_sidecar_table("eeg.EEGMoreRecommended") }}
+{{ MACROS___make_sidecar_table("eeg.EEGInstitutionInformation") }}
#### Example `*_eeg.json`
@@ -417,11 +431,11 @@ voxels (starting from `[0, 0, 0]`).
"EEGCoordinateSystem":"Other",
"EEGCoordinateUnits":"mm",
"EEGCoordinateSystemDescription":"RAS orientation: Origin halfway between LPA and RPA, positive x-axis towards RPA, positive y-axis orthogonal to x-axis through Nasion, z-axis orthogonal to xy-plane, pointing in superior direction.",
- "FiducialsDescription":"Electrodes and fiducials were digitized with Polhemus, fiducials were recorded as the centre of vitamin E capsules sticked on the left/right pre-auricular and on the nasion, these are also visible on the T1w MRI"
+ "FiducialsDescription":"Electrodes and fiducials were digitized with Polhemus, fiducials were recorded as the center of vitamin E capsules sticked on the left/right pre-auricular and on the nasion, these are also visible on the T1w MRI"
}
```
-## Landmark photos (`*_photo.jpg`)
+## Landmark photos (`*_photo.`)
Photos of the anatomical landmarks and/or fiducials.
@@ -444,7 +458,7 @@ indicate acquisition of different photos of
the same face (or other body part in different angles to show, for example, the
location of the nasion (NAS) as opposed to the right periauricular point (RPA).
-### Example `*_photo.jpg`
+### Example `*_photo.`
Picture of a NAS fiducial placed between the eyebrows, rather than at the
actual anatomical nasion: `sub-0001_ses-001_acq-NAS_photo.jpg`
diff --git a/src/modality-specific-files/genetic-descriptor.md b/src/modality-specific-files/genetic-descriptor.md
index a8438611c1..304359ed45 100644
--- a/src/modality-specific-files/genetic-descriptor.md
+++ b/src/modality-specific-files/genetic-descriptor.md
@@ -19,11 +19,12 @@ and can be used for practical guidance when curating a new dataset.
## Dataset Description
-Genetic descriptors are encoded as an additional, OPTIONAL entry in the
+If information on associated genetic data is supplied as part of a BIDS dataset,
+these "genetic descriptors" are encoded as an additional, REQUIRED entry in the
[`dataset_description.json`](../modality-agnostic-files.md#dataset_descriptionjson)
file.
-Datasets linked to a genetic database entry include the following REQUIRED or OPTIONAL
+Datasets linked to a genetic database entry include the following REQUIRED and OPTIONAL
keys in the `Genetics` sub-[object][] of `dataset_description.json`:
+{{ MACROS___render_text("objects.files.genetic_info.description") }}
-{{ MACROS___make_sidecar_table("ieeg.iEEGGeneric") }}
+{{ MACROS___make_sidecar_table("ieeg.iEEGRequired") }}
-Note that the `TaskName` field does not have to be a "behavioral task" that subjects perform, but can reflect some information about the conditions present when the data was acquired (for example, `"rest"`, `"sleep"`, or `"seizure"`).
-
-SHOULD be present: For consistency between studies and institutions, we
-encourage users to extract the values of these fields from the actual raw data.
-Whenever possible, please avoid using ad hoc wording.
+Those fields SHOULD be present:
{{ MACROS___make_sidecar_table("ieeg.iEEGRecommended") }}
-Specific iEEG fields MUST be present:
+These fields MAY be present:
-{{ MACROS___make_sidecar_table("ieeg.iEEGRequired") }}
+{{ MACROS___make_sidecar_table("ieeg.iEEGOptional") }}
-Specific iEEG fields SHOULD be present:
+Note that the date and time information SHOULD be stored in the study key file
+([`scans.tsv`](../modality-agnostic-files.md#scans-file)).
+Date time information MUST be expressed as indicated in [Units](../common-principles.md#units)
+
+#### Hardware information
-{{ MACROS___make_sidecar_table("ieeg.iEEGMoreRecommended") }}
+{{ MACROS___make_sidecar_table("ieeg.iEEGHardware") }}
-Specific iEEG fields MAY be present:
+#### Task information
-{{ MACROS___make_sidecar_table("ieeg.iEEGOptional") }}
+{{ MACROS___make_sidecar_table("ieeg.iEEGTaskInformation") }}
-Note that the date and time information SHOULD be stored in the study key file
-([`scans.tsv`](../modality-agnostic-files.md#scans-file)).
-Date time information MUST be expressed as indicated in [Units](../common-principles.md#units)
+Note that the `TaskName` field does not have to be a "behavioral task" that subjects perform, but can reflect some information about the conditions present when the data was acquired (for example, `"rest"`, `"sleep"`, or `"seizure"`).
+
+#### Institution information
+
+
+{{ MACROS___make_sidecar_table("ieeg.iEEGInstitutionInformation") }}
#### Example `*_ieeg.json`
@@ -430,7 +440,7 @@ data.
}
```
-## Photos of the electrode positions (`*_photo.jpg`)
+## Photos of the electrode positions (`*_photo.`)
-{{ MACROS___make_sidecar_table("mri.MRIScannerHardware") }}
+{{ MACROS___make_sidecar_table("mri.MRIHardware") }}
Example for `ReceiveCoilActiveElements`:
@@ -37,6 +40,18 @@ it is preferable for this field to be populated directly from the DICOM
for each individual scan, so that it can be used as a mechanism for checking
that a given scan was collected with the intended coil elements selected.
+### Institution information
+
+
+{{ MACROS___make_sidecar_table("mri.MRIInstitutionInformation") }}
+
### Sequence Specifics
{{ MACROS___make_sidecar_table("mri.MRIEchoPlanarImagingAndB0Mapping") }}
-### Institution information
+## Anatomy imaging data
-
-{{ MACROS___make_sidecar_table("mri.MRIInstitutionInformation") }}
+Anatomy MRI sequences measure static, structural features of the brain.
-When adding additional metadata please use the CamelCase version of
-[DICOM ontology terms](https://scicrunch.org/scicrunch/interlex/dashboard)
-whenever possible. See also
-[recommendations on JSON files](../common-principles.md#keyvalue-files-dictionaries).
+This datatype is divided into two groups:
+non-parametric and parametric.
-## Anatomy imaging data
+Non-parametric structural images have an arbitrary scale.
+For example, T1w data are T1-weighted,
+but the values do not correspond to actual T1 value estimates.
+
+Parametric structural imaging, on the other hand, use a non-arbitrary scale.
+For example, a T1map file contains T1 value estimates, in seconds.
+
+### Non-parametric structural MR images
-{{ MACROS___make_filename_template("raw", datatypes=["anat"]) }}
+{{ MACROS___make_filename_template("raw", datatypes=["anat"], suffixes=[
+ "T1w",
+ "T2w",
+ "PDw",
+ "T2starw",
+ "FLAIR",
+ "inplaneT1",
+ "inplaneT2",
+ "PDT2",
+ "UNIT1",
+ "angio",
+ ])
+}}
-Currently supported non-parametric structural MR images include:
+Currently supported non-parametric structural MR images include the following:
-{{ MACROS___make_sidecar_table("anat.TaskMetadata") }}
-
-Some meta information about the acquisition MAY be provided in an additional
-JSON file. See [Common metadata fields](#common-metadata-fields) for a
-list of terms and their definitions. There are also some OPTIONAL JSON
-fields specific to anatomical scans:
-
-
-{{ MACROS___make_sidecar_table("anat.MRIAnatomyCommonMetadataFields") }}
-
The [`part-`](../appendices/entities.md#part) entity is
used to indicate which component of the complex representation of the MRI
signal is represented in voxel data.
@@ -258,7 +248,8 @@ A guide for using macros can be found at
},
}
}
-) }}
+)
+}}
Phase images MAY be in radians or in arbitrary units.
The sidecar JSON file MUST include the units of the `phase` image.
@@ -274,12 +265,40 @@ For example, for `sub-01_part-phase_T1w.json`:
When there is only a magnitude image of a given type, the `part` entity MAY be omitted.
-Similarly, the OPTIONAL [`rec-`](../appendices/entities.md#rec)
-entity can be used to distinguish
-different reconstruction algorithms (for example ones using motion correction).
+### Parametric structural MR images
Structural MR images whose intensity is represented in a non-arbitrary scale
-constitute parametric maps. Currently supported parametric maps include:
+constitute parametric maps.
+
+
+{{ MACROS___make_filename_template("raw", datatypes=["anat"], suffixes=[
+ "T1map",
+ "R1map",
+ "T2map",
+ "R2map",
+ "T2starmap",
+ "R2starmap",
+ "PDmap",
+ "MTRmap",
+ "MTsat",
+ "T1rho",
+ "MWFmap",
+ "MTVmap",
+ "Chimap",
+ "TB1map",
+ "RB1map",
+ "S0map",
+ "M0map",
+ ])
+}}
+
+Currently supported parametric maps include:
+{{ MACROS___make_filename_template("raw", datatypes=["anat"], suffixes=[
+ "defacemask",
+ ])
+}}
+
+### Task metadata for anatomical scans
+
+The OPTIONAL [`task-`](../appendices/entities.md#task) entity can be used
+in order to allow tasks during structural MR acquisitions,
+for example pre-described motion paradigms such as nodding, to be described.
+
+
+{{ MACROS___make_sidecar_table("anat.TaskMetadata") }}
+
+Some meta information about the acquisition MAY be provided in an additional
+JSON file. See [Common metadata fields](#common-metadata-fields) for a
+list of terms and their definitions. There are also some OPTIONAL JSON
+fields specific to anatomical scans:
+
+
+{{ MACROS___make_sidecar_table("anat.MRIAnatomyCommonMetadataFields") }}
+
### Deprecated suffixes
Some suffixes that were available in versions of the specification prior to
@@ -525,7 +594,7 @@ A guide for using macros can be found at
{
"sub-01": {
"func": {
- "sub-control01_task-nback_bold.json": "",
+ "sub-01_task-nback_bold.json": "",
},
}
}
@@ -783,7 +852,7 @@ JSON example:
## Arterial Spin Labeling perfusion data
-Several [example ASL datasets](https://github.com/bids-standard/bids-examples#asl-datasets)
+Several [example ASL datasets](https://bids-standard.github.io/bids-examples/#asl)
have been formatted using this specification
and can be used for practical guidance when curating a new dataset.
@@ -825,20 +894,31 @@ If the `deltam` is not available,
When `cbf` is stored within the `*_asl.nii[.gz]`,
its units need to be specified in the `*_asl.json` as well.
Note that the raw images, including the `m0scan`, may also be used for quality control.
-See the [ASL Appendix](../appendices/arterial-spin-labeling.md#_aslcontexttsv-three-possible-cases) for examples of the three possible cases, in order of decreasing preference.
+See the [ASL Appendix](../appendices/arterial-spin-labeling.md#_aslcontexttsv-three-possible-cases)
+for examples of the three possible cases, in order of decreasing preference.
### Scaling
-The `*_asl.nii.gz` and `*_m0scan.nii.gz` should contain appropriately scaled data, and no additional scaling factors are allowed other than the scale slope in the respective
+The `*_asl.nii.gz` and `*_m0scan.nii.gz` should contain appropriately scaled data,
+and no additional scaling factors are allowed other than the scale slope in the respective
NIfTI headers.
+### `*_asllabeling.*`
+
+An anonymized screenshot of the planning of the labeling slab/plane
+with respect to the imaging slab or slices.
+This screenshot is based on DICOM macro C.8.13.5.14.
+
+See [`LabelingLocationDescription`](../glossary.md#labelinglocationdescription-metadata) for more details.
+
### M0
The `m0scan` can either be stored inside the 4D ASL time-series NIfTI file
or as a separate NIfTI file,
depending on whether it was acquired within the ASL time-series or as a separate scan.
These and other M0 options are specified in the REQUIRED `M0Type` field of the `*_asl.json` file.
-It can also be stored under `fmap/sub-[_ses-][_acq-][_ce-]_dir-[_run-]_m0scan.nii[.gz]`,
+It can also be stored under
+`fmap/sub-[_ses-][_acq-][_ce-]_dir-[_run-]_m0scan.nii[.gz]`,
when the [pepolar approach](#case-4-multiple-phase-encoded-directions-pepolar) is used.
### `*_asl.json` file
@@ -849,7 +929,8 @@ Additionally, some common metadata fields are REQUIRED for the `*_asl.json`:
`MagneticFieldStrength`, `MRAcquisitionType`, `EchoTime`,
`SliceTiming` in case `MRAcquisitionType` is defined as 2D,
`RepetitionTimePreparation`, and `FlipAngle` in case `LookLocker` is `true`.
-See the [ASL Appendix](../appendices/arterial-spin-labeling.md#summary-image-of-the-most-common-asl-sequences) for more information on the most common ASL sequences.
+See the [ASL Appendix](../appendices/arterial-spin-labeling.md#summary-image-of-the-most-common-asl-sequences)
+for more information on the most common ASL sequences.
#### Common metadata fields applicable to both (P)CASL and PASL
@@ -883,7 +964,9 @@ A guide for using macros can be found at
#### PASL-specific metadata fields
-These fields can only be used when `ArterialSpinLabelingType` is `PASL`. See the [ASL Appendix](../appendices/arterial-spin-labeling.md#pasl-sequence) for more information on the PASL sequence and the BolusCutOff fields.
+These fields can only be used when `ArterialSpinLabelingType` is `PASL`.
+See the [ASL Appendix](../appendices/arterial-spin-labeling.md#pasl-sequence)
+for more information on the PASL sequence and the BolusCutOff fields.
-{{ MACROS___make_sidecar_table("meg.MEGGeneric") }}
+{{ MACROS___make_sidecar_table("meg.MEGRequired") }}
-SHOULD be present: For consistency between studies and institutions, we
-encourage users to extract the values of these fields from the actual raw data.
-Whenever possible, please avoid using ad-hoc wording.
+Those fields SHOULD be present:
{{ MACROS___make_sidecar_table("meg.MEGRecommended") }}
-Specific MEG fields MUST be present:
+#### Hardware information
-{{ MACROS___make_sidecar_table("meg.MEGRequired") }}
+{{ MACROS___make_sidecar_table("meg.MEGHardware") }}
-SHOULD be present:
+#### Task information
+
+
+{{ MACROS___make_sidecar_table("meg.MEGTaskInformation") }}
+
+#### Institution information
-{{ MACROS___make_sidecar_table("meg.MEGMoreRecommended") }}
+{{ MACROS___make_sidecar_table("meg.MEGInstitutionInformation") }}
+
+#### Specific EEG fields
-Specific EEG fields
-(if recorded with MEG, see [Recording EEG simultaneously with MEG](#recording-eeg-simultaneously-with-meg)
+If recorded with MEG, see [Recording EEG simultaneously with MEG](#recording-eeg-simultaneously-with-meg)
SHOULD be present:
-{{ MACROS___make_sidecar_table("micr.MicroscopyDeviceHardware") }}
-
#### Image Acquisition
+{{ MACROS___make_sidecar_table("micr.MicroscopyHardware") }}
+
+#### Institution information
+
+
+
+{{ MACROS___make_sidecar_table("micr.MicroscopyInstitutionInformation") }}
+
#### Example of sidecar JSON file (`*_.json`)
```JSON
{
diff --git a/src/modality-specific-files/motion.md b/src/modality-specific-files/motion.md
new file mode 100644
index 0000000000..1e151baef9
--- /dev/null
+++ b/src/modality-specific-files/motion.md
@@ -0,0 +1,202 @@
+# Motion
+
+For information on how to cite this extension when referencing it in the context of the academic literature, please read [Citing BIDS](../introduction.md#citing-bids).
+
+Motion datasets formatted using this specification are available on the
+[BIDS examples repository](https://bids-standard.github.io/bids-examples/#motion)
+and can be used as helpful guidance when curating new datasets.
+
+## Motion recording data
+
+{{ MACROS___make_filename_template(
+"raw",
+datatypes=["motion"],
+suffixes=["motion", "events", "physio", "stim"])
+}}
+
+A wide variety of motion capture systems are used in human research, resulting in different proprietary data formats.
+
+This BIDS extension deals with common outputs from motion capture systems such as positions, orientations, or their time derivatives.
+
+The extension is not limited to motion data in physical space but also encompasses simulated movement in virtual space, as far as these are comparable to movements in physical space.
+Other dynamic objects than human body parts whose motion is tracked may as well be included as tracked objects.
+This specification does not include raw camera footages (from camera-based or optical motion capture recordings), but includes the positions or orientations computed using such data.
+
+In this specification, positions (and their time derivatives) are represented as Cartesian coordinates along up to three spatial axes,
+and orientations (and their time derivatives) are represented as Euler angles.
+However, to cover recordings from computer graphics applications (for example, virtual 3D motion or immersive virtual reality recording in physical space),
+orientations are also allowed to be represented as [quaternions](https://en.wikipedia.org/wiki/Quaternion).
+
+In this case, the quaternion channels can be distinguished from channels containing Euler angles based on the entries in columns `component` and `units` in the `*_channels.tsv` file.
+See subsection on `Channels description` for further details.
+
+Motion data from one tracking system MUST be stored in a single `*_motion.tsv` file.
+A tracking system is defined as a group of motion channels that share hardware properties (the recording device) and software properties (the recording duration and number of samples).
+For example, if the position time series of multiple optical markers is processed via one recording unit, this MAY be defined as a single tracking system.
+Note that it is not uncommon to have multiple tracking systems to record at the same time.
+
+Each tracking system MUST have its own `*_tracksys-_motion.tsv` file, where `` is a user-defined keyword to be used to identify each file belonging to a tracking system.
+This is especially helpful when more than one tracking system is used.
+Data from different tracking systems MUST be stored in different `*_tracksys-_motion.tsv` files,
+each of which is accompanied by `*_tracksys-_motion.json` and `*_tracksys-_channels.tsv` files.
+Between `tracksys-` entity and `*_motion.tsv`, `*_motion.json`, or `*_channels.tsv` suffixes, optional [`acq-`](../appendices/entities.md#acq) or [`run-`](../appendices/entities.md#run) entity MAY be inserted.
+
+One column in the `*_tracksys-_motion.tsv` file represents one data channel.
+The ordering of columns MUST match the order of rows in the `*_channels.tsv` file for unambiguous assignment.
+All relevant metadata about a tracking systems is stored in accompanying sidecar `*_tracksys-_motion.json` file.
+
+The source data from each tracking system in their original format, if different from `.tsv`,
+can be stored in the [`/sourcedata` directory](../common-principles.md#source-vs-raw-vs-derived-data).
+The original data format MAY hold more metadata than currently specified in the `*_motion.json` file.
+
+When multiple tracking systems are used to record motion or motion capture is used alongside the recording of other BIDS modalities and recordings should be interpreted together,
+it is advised to provide a possibility to synchronize recordings.
+The preferred way to do so is to use the acquisition time of the first data point of recordings and
+to store this information in the `acq_time` column of the [`*_scans.tsv`](../modality-agnostic-files.md#scans-file) file.
+The Note that the [BIDS date time format](../common-principles.md#units) allows optional fractional seconds, which SHOULD be used to maximize the precision of the synchronization.
+Only if the precision of the synchronization is not high enough, the `*_events.tsv` file SHOULD be used to synchronize recordings.
+In this file, the start- and stop time of the recording of a system are specified in relation to a system to synchronize with.
+If more than two systems are to be synchronized, it is up to the user to indntify the "main" system.
+
+In case a tracking system provides time information with every recorded sample,
+these time information MAY be stored in form of latencies to recording onset (first sample) in the `*_motion.tsv` file.
+If a system has uneven sampling rate behavior, the `LATENCY` channel can be used to share these information.
+
+To store events alongside motion data when there are multiple tracking systems simultaneously in use, it is RECOMMENDED to designate a tracking system to the events file.
+Such an events filename SHOULD include the `tracksys` key and looks like
+`sub-[_ses-]_task-[_tracksys-][_acq-][_run-]_events.tsv`.
+Event latencies can then be related to motion samples of multiple tracking systems also by using `acq_time` column entries in the `*_scans.tsv`.
+The same principle applies when the events file is saved alongside a simultaneously recorded non-motion data (for example EEG).
+
+### Sidecar JSON (`*_motion.json`)
+
+#### Task information
+
+{{ MACROS___make_sidecar_table("motion.motionTaskInformation") }}
+
+#### Hardware information
+
+{{ MACROS___make_sidecar_table("motion.motionHardware") }}
+
+#### Institution information
+
+{{ MACROS___make_sidecar_table("motion.motionInstitutionInformation") }}
+
+#### Motion specific fields
+
+Motion specific fields MUST be present:
+
+{{ MACROS___make_sidecar_table("motion.motionRequired") }}
+
+Motion specific fields SHOULD be present:
+
+{{ MACROS___make_sidecar_table("motion.motionRecommended") }}
+
+#### Example `*_tracksys-_motion.json`
+
+```JSON
+{
+ "SamplingFrequency": 60,
+ "SamplingFrequencyEffective": 60.00197437,
+ "TaskName": "BIDS Motion fictive example",
+ "TrackingSystemName": "IMU Right Hand",
+ "TaskDescription": "walking and talking",
+ "InstitutionAddress": "Fictive address",
+ "InstitutionName": "Fictive Institution",
+ "MotionChannelCount": 18,
+ "RecordingDuration": 4667.641106,
+ "RotationRule": "right-hand",
+ "RotationOrder": "ZXY",
+ "SpatialAxes": "FRU",
+ "SubjectArtefactDescription": "n/a",
+ "TrackedPointsCount" : 2,
+ "ACCELChannelCount": 6,
+ "GYROChannelCount": 6,
+ "MAGNChannelCount": 6,
+ "Manufacturer": "BWSensing",
+ "ManufacturersModelName": "BW-IMU600",
+}
+```
+
+In this example, the `*_motion.json` contains data from one tracking system consisting of two
+[inertial measurement units (imu)](https://en.wikipedia.org/wiki/Motion_capture#Inertial_systems).
+If there are additional tracking systems (for example [optical motion capture](https://en.wikipedia.org/wiki/Motion_capture#Optical_systems)),
+data from these MUST be stored as separate files like `*_tracksys-omcA_motion.tsv` and `*_tracksys-omcB_motion.tsv`.
+All specified tracking systems MAY share `tracked_point` defined in `*_channels.tsv`, when tracking devices are placed on the same object or body part.
+
+Note that the onsets of the recordings SHOULD be stored in the study key file [(`scans.tsv`)](../modality-agnostic-files.md#scans-file).
+Here, date-time information MUST be expressed as indicated in [Units](../common-principles.md#units).
+The [`scans.tsv`](../modality-agnostic-files.md#scans-file) file contains the filename and the acquisition time of a recording,
+which MAY be used to synchronize multiple recordings.
+
+## Channels description (`*_channels.tsv`)
+
+{{ MACROS___make_filename_template(
+"raw",
+datatypes=["motion"],
+suffixes=["channels"])
+}}
+
+This file is REQUIRED as it makes it easy to browse or query over larger collections of datasets.
+The REQUIRED columns are channel `name`, `component`, `type`, `tracked_point` and `units`.
+Any number of additional columns MAY be added to provide additional information about the channels.
+The `*_tracksys-_channels.tsv` file SHOULD give additional information about individual recorded channel,
+some of which my not be found summarized in `*_motion.json`.
+
+The columns of the channels description table stored in `*_channels.tsv` are:
+
+{{ MACROS___make_columns_table("motion.motionChannels") }}
+
+### Restricted keyword list for channel component
+
+Restricted keyword list for column `component`.
+When using quaternions to represent orientations,
+the axial components that corresponds to the three spatial axes MUST be specified as "quat_x", "quat_y", "quat_z", and the non-axial component as "quat_w".
+
+| **Keyword** | **Description** |
+| ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| x | Position along the X-axis, or rotation about the X-axis among the Euler angles that represent the orientation, or magnetic field strength along the X-axis. |
+| y | Position along the Y-axis or rotation about the Y-axis among the Euler angles that represent the orientation, or magnetic field strength along the Y-axis. |
+| z | Position along the Z-axis or rotation about the Z-axis among the Euler angles that represent the orientation, or magnetic field strength along the Z-axis. |
+| quat_x | Quaternion component associated with the X-axis. |
+| quat_y | Quaternion component associated with the Y-axis. |
+| quat_z | Quaternion component associated with the Z-axis. |
+| quat_w | Non-axial quaternion component. |
+| n/a | Channels that have no corresponding spatial axis. |
+
+### Restricted keyword list for channel type
+
+Restricted keyword list for column `type` in alphabetic order.
+Note that upper-case is REQUIRED:
+
+| **Keyword** | **Description** |
+| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| ACCEL | Accelerometer channel, one channel for each spatial axis. Column component for the axis MUST be added to the *_channels.tsv file (x, y, or z). |
+| ANGACCEL | Angular acceleration channel, one channel for each spatial axis. Column component for the axis MUST be added to the *_channels.tsv file (x, y, or z). |
+| GYRO | Gyrometer channel, one channel for each spatial axis. Column component for the axis MUST be added to the *_channels.tsv file (x, y, or z). |
+| JNTANG | Joint angle channel between two fixed axis belonging to two bodyparts. Angle SHOULD be defined between proximal and distal bodypart in deg. |
+| LATENCY | Latency of samples in seconds from recording onset (see `acq_time` column of the respective `*_scans.tsv` file). MUST be in form of `s[.000000]`, where `s` reflects whole seconds, and `.000000` reflects OPTIONAL fractional seconds. |
+| MAGN | Magnetic field strength, one channel for each spatial axis. Column component for the axis MUST be added to the *_channels.tsv file (x, y or z). |
+| MISC | Miscellaneous channels. |
+| ORNT | Orientation channel, one channel for each spatial axis or quaternion component. Column component for the axis or quaternion label MUST be added to the *_channels.tsv file (x, y, z, quat_x, quat_y, quat_z, or quat_w). |
+| POS | Position in space, one channel for each spatial axis. Column component for the axis MUST be added to the *_channels.tsv file (x, y or z). |
+| VEL | Velocity, one channel for each spatial axis. Column component for the axis MUST be added to the *_channels.tsv file (x, y or z). |
+
+### Example `*_channels.tsv`
+
+```Text
+name component type tracked_point units
+t1_acc_x x ACCEL LeftFoot m/s^2
+t1_acc_y y ACCEL LeftFoot m/s^2
+t1_acc_z z ACCEL LeftFoot m/s^2
+t1_gyro_x x GYRO LeftFoot rad/s
+t1_gyro_y y GYRO LeftFoot rad/s
+t1_gyro_z z GYRO LeftFoot rad/s
+…
+t2_acc_x x ACCEL RightWrist m/s^2
+t2_acc_y y ACCEL RightWrist m/s^2
+t2_acc_z z ACCEL RightWrist m/s^2
+t2_gyro_x x GYRO RightWrist rad/s
+t2_gyro_y y GYRO RightWrist rad/s
+t2_gyro_z z GYRO RightWrist rad/s
+```
diff --git a/src/modality-specific-files/near-infrared-spectroscopy.md b/src/modality-specific-files/near-infrared-spectroscopy.md
index 8262110afa..1947836c17 100644
--- a/src/modality-specific-files/near-infrared-spectroscopy.md
+++ b/src/modality-specific-files/near-infrared-spectroscopy.md
@@ -6,15 +6,23 @@ Please see [Citing BIDS](../introduction.md#citing-bids)
on how to appropriately credit this extension when referring to it in the
context of the academic literature.
-Several [example NIRS datasets](https://github.com/bids-standard/bids-examples#nirs-datasets)
+Several [example NIRS datasets](https://bids-standard.github.io/bids-examples/#nirs)
have been formatted using this specification and can be used for practical guidance when curating a new dataset.
## NIRS recording data
+
+
{{ MACROS___make_filename_template(
"raw",
datatypes=["nirs"],
- suffixes=["nirs", "events", "channels", "optodes", "coordsystem", "physio", "stim"])
+ suffixes=["nirs", "events", "physio", "stim"])
}}
Only the *Shared Near Infrared Spectroscopy Format* ([SNIRF](https://github.com/fNIRS/snirf))
@@ -109,7 +117,7 @@ was used:
Closely spaced or short-separation source-detector pairs are often included in NIRS measurements to
obtain a measure of systemic, rather than neural, activity. These source-detector
pairs are referred to as *short channels*. There is variation in how manufacturers
-implement these short channels, some use specialised sources or detectors,
+implement these short channels, some use specialized sources or detectors,
and the placement mechanisms vary.
It is beyond the scope of the BIDS specification to define what constitutes a short channel,
and detailed characteristics of channels may be stored within the SNIRF file
@@ -120,20 +128,83 @@ is stored in the field `ShortChannelCount`.
If the field `ShortChannelCount` is populated, then the optional column `short_channel`
may be used in `*_channels.tsv` to describe which channels were specified as short.
-Generic fields: For consistency between studies and institutions,
-we encourage users to extract the values of these fields from the actual raw data.
-Whenever possible, please avoid using ad hoc wording.
+For consistency between studies and institutions, we encourage users to extract
+the values of these fields from the actual raw data. Whenever possible, please
+avoid using ad hoc wording.
-{{ MACROS___make_sidecar_table("nirs.NirsBase") }}
-
-Specific NIRS fields that are REQUIRED or may be REQUIRED depending on other metadata values:
+Specific NIRS fields that are REQUIRED or may be REQUIRED depending on other
+metadata values:
+
{{ MACROS___make_sidecar_table("nirs.NirsRequired") }}
Specific NIRS fields that SHOULD be present:
+
{{ MACROS___make_sidecar_table("nirs.NirsRecommend") }}
+#### Generic information
+
+
+{{ MACROS___make_sidecar_table("nirs.NirsBase") }}
+
+#### Hardware information
+
+
+{{ MACROS___make_sidecar_table("nirs.NirsHardware") }}
+
+#### Institution information
+
+
+{{ MACROS___make_sidecar_table("nirs.NirsInstitutionInformation") }}
+
+#### Task information
+
+
+{{ MACROS___make_sidecar_table("nirs.NirsTaskInformation") }}
+
#### Example `*_nirs.json`
```JSON
@@ -161,6 +232,14 @@ Specific NIRS fields that SHOULD be present:
## Channels description (`*_channels.tsv`)
+
+
{{ MACROS___make_filename_template(
"raw",
datatypes=["nirs"],
@@ -197,27 +276,27 @@ and a guide for using macros can be found at
All NIRS channels types MUST correspond to a [valid SNIRF data type](https://github.com/fNIRS/snirf/blob/master/snirf_specification.md#appendix).
Additional channels that are recorded simultaneously with the NIRS
device and stored in the same data file SHOULD be included as well.
-However, additional channels that are simultaneously recorded with a different device
+However, additional channels that are simultaneously recorded with a different device
SHOULD be stored according to their appropriate modality specification.
For example, motion data that was simultaneously recorded with a different device should be specified
-according to BEP029 and not according to the NIRS data type.
+according to the [Motion](../modality-specific-files/motion.md) and not according to the NIRS data type.
Whereas, if the motion data was acquired in with the NIRS device itself, it should be included here with the NIRS data.
Any of the channel types defined in other BIDS specification MAY be used here as well such as `ACCEL` or `MAGN`.
As several of these data types are commonly acquired using NIRS devices they are included as an example at the base of the table.
Note that upper-case is REQUIRED.
-| **Keyword** | **Description** |
-| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| NIRSCWAMPLITUDE | Continuous wave amplitude measurements. Equivalent to dataType 001 in SNIRF. |
-| NIRSCWFLUORESCENSEAMPLITUDE | Continuous wave fluorescence amplitude measurements. Equivalent to dataType 051 in SNIRF. |
-| NIRSCWOPTICALDENSITY | Continuous wave change in optical density measurements. Equivalent to dataTypeLabel dOD in SNIRF. |
-| NIRSCWHBO | Continuous wave oxygenated hemoglobin (oxyhemoglobin) concentration measurements. Equivalent to dataTypeLabel HbO in SNIRF. |
-| NIRSCWHBR | Continuous wave deoxygenated hemoglobin (deoxyhemoglobin) concentration measurements. Equivalent to dataTypeLabel HbR in SNIRF. |
-| NIRSCWMUA | Continuous wave optical absorption measurements. Equivalent to dataTypeLabel mua in SNIRF. |
-| ACCEL | Accelerometer channel, one channel for each orientation. An extra column `component` for the axis of the orientation MUST be added to the `*_channels.tsv` file (x, y or z). |
-| GYRO | Gyrometer channel, one channel for each orientation. An extra column `component` for the axis of the orientation MUST be added to the `*_channels.tsv` file (x, y or z). |
-| MAGN | Magnetomenter channel, one channel for each orientation. An extra column `component` for the axis of the orientation MUST be added to the `*_channels.tsv` file (x, y or z). |
-| MISC | Miscellaneous |
+| **Keyword** | **Description** |
+| --------------------------- | -----------------------------------------------------------------------------------------------------------------------------------------------------------|
+| NIRSCWAMPLITUDE | Continuous wave amplitude measurements. Equivalent to dataType 001 in SNIRF. |
+| NIRSCWFLUORESCENSEAMPLITUDE | Continuous wave fluorescence amplitude measurements. Equivalent to dataType 051 in SNIRF. |
+| NIRSCWOPTICALDENSITY | Continuous wave change in optical density measurements. Equivalent to dataTypeLabel dOD in SNIRF. |
+| NIRSCWHBO | Continuous wave oxygenated hemoglobin (oxyhemoglobin) concentration measurements. Equivalent to dataTypeLabel HbO in SNIRF. |
+| NIRSCWHBR | Continuous wave deoxygenated hemoglobin (deoxyhemoglobin) concentration measurements. Equivalent to dataTypeLabel HbR in SNIRF. |
+| NIRSCWMUA | Continuous wave optical absorption measurements. Equivalent to dataTypeLabel mua in SNIRF. |
+| ACCEL | Accelerometer channel, one channel for each spatial axis. An extra column `component` for the axis MUST be added to the `*_channels.tsv` file (x, y or z). |
+| GYRO | Gyrometer channel, one channel for each spatial axis. An extra column `component` for the axis MUST be added to the `*_channels.tsv` file (x, y or z). |
+| MAGN | Magnetomenter channel, one channel for each spatial axis. An extra column `component` for the axis MUST be added to the `*_channels.tsv` file (x, y or z). |
+| MISC | Miscellaneous |
### Example `*_channels.tsv`
@@ -232,6 +311,14 @@ S3-D4 NIRSCWAMPLITUDE VisS2 VisD4 760
## Optode description (`*_optodes.tsv`)
+
+
{{ MACROS___make_filename_template(
"raw",
datatypes=["nirs"],
@@ -269,6 +356,14 @@ VisD4 detector 0.0322 0.2214 0.2299 0.02 0.22
## Coordinate System JSON (`*_coordsystem.json`)
+
+
{{ MACROS___make_filename_template(
"raw",
datatypes=["nirs"],
@@ -289,18 +384,54 @@ In this case, only x and y are specified and z is `"n/a"`.
General fields:
+
+
{{ MACROS___make_sidecar_table("nirs.CoordsystemGeneral") }}
Fields relating to the NIRS optode positions:
+
+
{{ MACROS___make_sidecar_table(["nirs.CoordinateSystem", "nirs.CoordinateSystemDescriptionRec"]) }}
Fields relating to the position of fiducials measured during an NIRS session/run:
+
+
{{ MACROS___make_sidecar_table(["nirs.Fiducials", "nirs.FiducialsCoordinateSystemDescriptionRec"]) }}
Fields relating to the position of anatomical landmarks measured during an NIRS session/run:
+
+
{{ MACROS___make_sidecar_table(["nirs.AnatomicalLandmark", "nirs.AnatomicalLandmarkCoordinateSystemDescriptionRec"]) }}
### Example `*_coordsystem.json`
@@ -310,6 +441,6 @@ Fields relating to the position of anatomical landmarks measured during an NIRS
"NIRSCoordinateSystem": "Other",
"NIRSCoordinateUnits": "mm",
"NIRSCoordinateSystemDescription": "RAS orientation: Origin halfway between LPA and RPA, positive x-axis towards RPA, positive y-axis orthogonal to x-axis through Nasion, z-axis orthogonal to xy-plane, pointing in superior direction.",
- "FiducialsDescription": "Optodes and fiducials were digitized with Polhemus, fiducials were recorded as the centre of vitamin E capsules sticked on the left/right pre-auricular and on the nasion, these are also visible on the T1w MRI"
+ "FiducialsDescription": "Optodes and fiducials were digitized with Polhemus, fiducials were recorded as the center of vitamin E capsules sticked on the left/right pre-auricular and on the nasion, these are also visible on the T1w MRI"
}
```
diff --git a/src/modality-specific-files/physiological-and-other-continuous-recordings.md b/src/modality-specific-files/physiological-and-other-continuous-recordings.md
index 72ee2faefa..72df5d4814 100644
--- a/src/modality-specific-files/physiological-and-other-continuous-recordings.md
+++ b/src/modality-specific-files/physiological-and-other-continuous-recordings.md
@@ -9,7 +9,7 @@ specified using two files:
1. a JSON file for storing metadata fields (see below)
-[Example datasets](https://github.com/bids-standard/bids-examples)
+[Example datasets](https://bids-standard.github.io/bids-examples/#dataset-index)
with physiological data have been formatted using this specification
and can be used for practical guidance when curating a new dataset:
@@ -118,7 +118,7 @@ A guide for using macros can be found at
{
"SamplingFrequency": 100.0,
"StartTime": -22.345,
- "Columns": ["cardiac", "respiratory"],
+ "Columns": ["cardiac", "respiratory", "trigger"],
"Manufacturer": "Brain Research Equipment ltd.",
"cardiac": {
"Description": "continuous pulse measurement",
@@ -127,6 +127,9 @@ A guide for using macros can be found at
"respiratory": {
"Description": "continuous measurements by respiration belt",
"Units": "mV"
+ },
+ "trigger": {
+ "Description": "continuous measurement of the scanner trigger signal"
}
}
```
diff --git a/src/modality-specific-files/positron-emission-tomography.md b/src/modality-specific-files/positron-emission-tomography.md
index d26ba71df6..c16d144b4f 100644
--- a/src/modality-specific-files/positron-emission-tomography.md
+++ b/src/modality-specific-files/positron-emission-tomography.md
@@ -6,7 +6,7 @@ Please see [Citing BIDS](../introduction.md#citing-bids)
on how to appropriately credit this extension when referring to it in the
context of the academic literature.
-Several [example PET datasets](https://github.com/bids-standard/bids-examples#pet-datasets)
+Several [example PET datasets](https://bids-standard.github.io/bids-examples/#pet)
have been formatted using this specification
and can be used for practical guidance when curating a new dataset.
@@ -139,18 +139,6 @@ These fields are derived from the recommendations in
Knudsen et al. 2020, [doi:10.1177/0271678X20905433](https://doi.org/10.1177/0271678X20905433),
which we divide into several categories:
-#### Scanner Hardware
-
-
-{{ MACROS___make_sidecar_table("pet.PETScannerHardware") }}
-
#### Radiochemistry
+{{ MACROS___make_sidecar_table("pet.PETHardware") }}
+
+#### Institution information
+
+
+
+{{ MACROS___make_sidecar_table("pet.PETInstitutionInformation") }}
+
#### Task
If the OPTIONAL [`task-`](../appendices/entities.md#task) is used,
@@ -214,15 +227,7 @@ The definitions of these fields can be found in
and a guide for using macros can be found at
https://github.com/bids-standard/bids-specification/blob/master/macros_doc.md
-->
-{{ MACROS___make_metadata_table(
- {
- "CogPOID": "RECOMMENDED",
- "CogAtlasID": "RECOMMENDED",
- "TaskDescription": "RECOMMENDED",
- "Instructions": ("RECOMMENDED", "This is especially important in context of resting state recordings and distinguishing between eyes open and eyes closed paradigms."),
- "TaskName": ("RECOMMENDED", "If used to denote resting scans, a RECOMMENDED convention is to use labels beginning with `rest`."),
- }
-) }}
+{{ MACROS___make_sidecar_table("pet.PETTask") }}
#### Example (`*_pet.json`)
diff --git a/src/modality-specific-files/task-events.md b/src/modality-specific-files/task-events.md
index 86429f6724..aba8a3ec18 100644
--- a/src/modality-specific-files/task-events.md
+++ b/src/modality-specific-files/task-events.md
@@ -57,7 +57,7 @@ For example in an EEG experiment with devices operating at 1000 Hz sampling freq
dataset curators SHOULD specify **at least** 3 figures after the decimal point.
An arbitrary number of additional columns can be added. Those allow describing
-other properties of events that could be later referenced in modelling and
+other properties of events that could be later referenced in modeling and
hypothesis extensions of BIDS.
Note that the `trial_type` and any additional columns in a TSV file
SHOULD be documented in an accompanying JSON sidecar file.
@@ -153,7 +153,7 @@ but they should be stored in the `/stimuli` directory.
References to existing databases can also be encoded using additional columns.
The following example includes references to the
-[Karolinska Directed Emotional Faces (KDEF) database](https://www.emotionlab.se/resources/kdef).
+[Karolinska Directed Emotional Faces (KDEF) database](https://www.kdef.se/).
Example:
diff --git a/src/schema/BIDS_VERSION b/src/schema/BIDS_VERSION
index 00f91b858b..b57588e592 100644
--- a/src/schema/BIDS_VERSION
+++ b/src/schema/BIDS_VERSION
@@ -1 +1 @@
-1.8.1-dev
+1.9.0-dev
diff --git a/src/schema/README.md b/src/schema/README.md
index 335c56522f..b61740bbcc 100644
--- a/src/schema/README.md
+++ b/src/schema/README.md
@@ -1,10 +1,14 @@
-# BIDS-schema
+# BIDS schema description
Portions of the BIDS specification are defined using YAML files in order to
make the specification machine-readable.
-Currently the portions of the specification that rely on this schema are
-the entity tables, entity definitions, filename templates, and metadata tables.
+Currently the portions of the specification that rely on this schema are:
+- the entity tables,
+- entity definitions,
+- filename templates,
+- metadata tables.
+
Any changes to the specification should be mirrored in the schema.
## Organization and syntax
@@ -12,21 +16,39 @@ Any changes to the specification should be mirrored in the schema.
At the time of this writing, the schema has the following file layout:
```plain
-src/schema
-├── BIDS_VERSION
├── meta
-│ └── context.yaml
+│ ├── ...
+│ └── versions.yaml
├── objects
-│ ├── columns.yaml
│ ├── ...
│ └── suffixes.yaml
├── rules
│ ├── checks
-│ │ ├── asl.yaml
│ │ ├── ...
-│ │ └── mri.yaml
+│ │ └── references.yaml
+│ ├── files
+│ │ ├── common
+│ │ │ ├── core.yaml
+│ │ │ └── tables.yaml
+│ │ ├── deriv
+│ │ │ ├── imaging.yaml
+│ │ │ └── preprocessed_data.yaml
+│ │ └── raw
+│ │ ├── ...
+│ │ └── task.yaml
+│ ├── sidecars
+│ │ ├── derivatives
+│ │ │ └── common_derivatives.yaml
+│ │ ├── ...
+│ │ └── pet.yaml
+│ ├── tabular_data
+│ │ ├── derivatives
+│ │ │ └── common_derivatives.yaml
+│ │ ├── ...
+│ │ └── task.yaml
│ ├── ...
-│ └── suffixes.yaml
+│ └── modalities.yaml
+├── BIDS_VERSION
└── SCHEMA_VERSION
```
@@ -39,7 +61,6 @@ Each file is made up of YAML data, most often an *object*.
For example, the file `rules/checks/mri.yaml` contains the contents:
```YAML
----
PhasePartUnits:
issue:
code: PHASE_UNITS
@@ -76,13 +97,15 @@ These qualified names may be used in this README, as well as in *references* and
### Description formatting
-Many objects throughout the schema have a `description` field, which will typically be
-rendered somewhere in the specification. Because the specification is written in
-[Markdown](https://en.wikipedia.org/wiki/Markdown), these `description` fields may also
-contain Markdown, including links to other locations in the specification.
+Many objects throughout the schema have a `description` field,
+which will typically be rendered somewhere in the specification.
+Because the specification is written in [Markdown](https://en.wikipedia.org/wiki/Markdown),
+these `description` fields may also contain Markdown,
+including links to other locations in the specification.
-Because the same description may be used in multiple locations, a mechanism is needed
-to ensure that the correct path is discovered to render the description in each location.
+Because the same description may be used in multiple locations,
+a mechanism is needed to ensure that the correct path is discovered
+to render the description in each location.
To do this, the path should follow the form `SPEC_ROOT/path/within/source.md#anchor`.
For example, to link to the
[Definitions](https://bids-specification.readthedocs.io/en/stable/common-principles.html#definitions)
@@ -108,8 +131,8 @@ ObjectName:
$ref: objects.metadata.OtherObjectName
```
-This object may be *dereferenced* by replacing the `$ref` entry with the object being
-referenced.
+This object may be *dereferenced* by replacing the `$ref` entry
+with the object being referenced.
The following two prototypical examples are presented to clarify the semantics of
references (the cases in which they are used will be presented later):
@@ -152,10 +175,11 @@ references (the cases in which they are used will be presented later):
description: optional
```
Here, the derivative datatype rule starts by copying the raw datatype rule
- `rules.datatypes.anat.nonparametric`. It then *overrides* the `entities` portion
- of that rule with a new object. To *extend* the original `entities`, it again
- begins by referencing `rules.datatypes.anat.nonparametric.entities`, and adding
- the new entities `space` and `description`.
+ `rules.datatypes.anat.nonparametric`.
+ It then *overrides* the `entities` portion of that rule with a new object.
+ To *extend* the original `entities`, it again begins
+ by referencing `rules.datatypes.anat.nonparametric.entities`,
+ and adding the new entities `space` and `description`.
### Expressions
@@ -166,7 +190,6 @@ or `checks`, determining whether a rule is satisfied.
Re-examining `rules.checks.mri.PhasePartUnits` from above:
```YAML
----
PhasePartUnits:
issue:
code: PHASE_UNITS
@@ -209,35 +232,41 @@ which (currently) contains at the top level:
- `nifti_header`: selected contents of the current NIfTI file's header
Some of these are strings, while others are nested objects.
-These are to be populated by an *interpreter* of the schema, and provide the
-*namespace* in which expressions are evaluated.
+These are to be populated by an *interpreter* of the schema,
+and provide the *namespace* in which expressions are evaluated.
The following operators should be defined by an interpreter:
-| Operator | Definition | Example |
-| --------- | ------------------------------------------------------------- | --------------------------------------------- |
-| `==` | equality | `suffix == "T1w"` |
-| `!=` | inequality | `entities.task != "rest"` |
-| `<`/`>` | less-than / greater-than | `sidecar.EchoTime < 0.5` |
-| `<=`/`>=` | less-than-or-equal / greater-than-or-equal | `0 <= 4` |
-| `in` | object lookup, true if RHS is a subfield of LHS | `"Units" in sidecar` |
-| `!` | negation, true if the following value is false, or vice versa | `!true == false` |
-| `&&` | conjunction, true if both RHS and LHS are true | `"Units" in sidecar && sidecar.Units == "mm"` |
-| `\|\|` | disjunction, true if either RHS or LHS is true | `a < mn \|\| a > mx` |
-| `.` | object query, returns value of subfield | `sidecar.Units` |
-| `[]` | array index, returns value of Nth element (0-indexed) of list | `columns.participant_label[0]` |
+| Operator | Definition | Example |
+| ----------- | ------------------------------------------------------------- | --------------------------------------------- |
+| `==` | equality | `suffix == "T1w"` |
+| `!=` | inequality | `entities.task != "rest"` |
+| `<`/`>` | less-than / greater-than | `sidecar.EchoTime < 0.5` |
+| `<=`/`>=` | less-than-or-equal / greater-than-or-equal | `0 <= 4` |
+| `in` | object lookup, true if RHS is a subfield of LHS | `"Units" in sidecar` |
+| `!` | negation, true if the following value is false, or vice versa | `!true == false` |
+| `&&` | conjunction, true if both RHS and LHS are true | `"Units" in sidecar && sidecar.Units == "mm"` |
+| `\|\|` | disjunction, true if either RHS or LHS is true | `a < mn \|\| a > mx` |
+| `.` | object query, returns value of subfield | `sidecar.Units` |
+| `[]` | array/string index, returns value of Nth element (0-indexed) | `columns.participant_label[0]` |
+| `+` | numeric addition / string concatenation | `x + 1`, `stem + "suffix"` |
+| `-`/`*`/`/` | numeric operators (division coerces to float) | `length(array) - 2`, `x * 2`, `1 / 2 == 0.5` |
The following functions should be defined by an interpreter:
-| Function | Definition | Example | Note |
-| ---------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------ |
-| `match(arg: str, pattern: str) -> bool` | `true` if `arg` matches the regular expression `pattern` (anywhere in string) | `match(extension, ".gz$")` | True if the file extension ends with `.gz` |
-| `type(arg: Any) -> str` | The name of the type, including `"array"`, `"object"`, `"null"` | `type(datatypes)` | Returns `"array"` |
-| `intersects(a: array, b: array) -> bool` | `true` if arguments contain any shared elements | `intersects(dataset.modalities, ["pet", "mri"])` | True if either PET or MRI data is found in dataset |
-| `length(arg: array) -> int` | Number of elements in an array | `length(columns.onset) > 0` | True if there is at least one value in the onset column |
-| `count(arg: array, val: any)` | Number of elements in an array equal to `val` | `count(columns.type, "EEG")` | The number of times "EEG" appears in the column "type" of the current TSV file |
-| `min(arg: array)` | The smallest non-`n/a` value in an array | `min(sidecar.SliceTiming) == 0` | A check that the onset of the first slice is 0s |
-| `max(arg: array)` | The largest non-`n/a` value in an array | `max(columns.onset)` | The time of the last onset in an events.tsv file |
+| Function | Definition | Example | Note |
+| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------ |
+| `count(arg: array, val: any)` | Number of elements in an array equal to `val` | `count(columns.type, "EEG")` | The number of times "EEG" appears in the column "type" of the current TSV file |
+| `exists(arg: str \| array, rule: str) -> int` | Count of files in an array that exist in the dataset. String is array with length 1. Rules include `"bids-uri"`, `"dataset"`, `"subject"` and `"stimuli"`. | `exists(sidecar.IntendedFor, "subject")` | True if all files in `IntendedFor` exist, relative to the subject directory. |
+| `index(arg: array, val: any)` | Index of first element in an array equal to `val`, `null` if not found | `index(["i", "j", "k"], axis)` | The number, from 0-2 corresponding to the string `axis` |
+| `intersects(a: array, b: array) -> bool` | `true` if arguments contain any shared elements | `intersects(dataset.modalities, ["pet", "mri"])` | True if either PET or MRI data is found in dataset |
+| `length(arg: array) -> int` | Number of elements in an array | `length(columns.onset) > 0` | True if there is at least one value in the onset column |
+| `match(arg: str, pattern: str) -> bool` | `true` if `arg` matches the regular expression `pattern` (anywhere in string) | `match(extension, ".gz$")` | True if the file extension ends with `.gz` |
+| `max(arg: array) -> number` | The largest non-`n/a` value in an array | `max(columns.onset)` | The time of the last onset in an events.tsv file |
+| `min(arg: array) -> number` | The smallest non-`n/a` value in an array | `min(sidecar.SliceTiming) == 0` | A check that the onset of the first slice is 0s |
+| `sorted(arg: array) -> array` | The sorted values of the input array | `sorted(sidecar.VolumeTiming) == sidecar.VolumeTiming` | True if `sidecar.VolumeTiming` is sorted |
+| `substr(arg: str, start: int, end: int) -> str` | The portion of the input string spanning from start position to end position | `substr(path, 0, length(path) - 3)` | `path` with the last three characters dropped |
+| `type(arg: Any) -> str` | The name of the type, including `"array"`, `"object"`, `"null"` | `type(datatypes)` | Returns `"array"` |
#### The special value `null`
@@ -246,21 +275,33 @@ This value propagates through all of the above operations in a fully-defined,
hopefully intuitive way.
Most operations involving `null` simply resolve to `null`:
-| Operation | Result |
-| ------------------------ | ------ |
-| `sidecar.MissingValue` | `null` |
-| `null.anything` | `null` |
-| `null[0]` | `null` |
-| `null && true` | `null` |
-| `null \|\| true` | `null` |
-| `!null` | `null` |
-| `match(null, pattern)` | `null` |
-| `intersects(list, null)` | `null` |
-| `length(null)` | `null` |
-| `count(null, val)` | `null` |
-| `count(list, null)` | `null` |
-| `min(null)` | `null` |
-| `max(null)` | `null` |
+| Operation | Result |
+| -------------------------- | ------ |
+| `sidecar.MissingValue` | `null` |
+| `null.anything` | `null` |
+| `null[0]` | `null` |
+| `null && true` | `null` |
+| `null \|\| true` | `null` |
+| `!null` | `null` |
+| `null + 1` | `null` |
+| `null - 1` | `null` |
+| `null * 1` | `null` |
+| `null / 1` | `null` |
+| `match(null, pattern)` | `null` |
+| `intersects(list, null)` | `null` |
+| `substr(null, 0, 1)` | `null` |
+| `substr(str, null, 1)` | `null` |
+| `substr(str, 0, null)` | `null` |
+| `length(null)` | `null` |
+| `count(null, val)` | `null` |
+| `count(list, null)` | `null` |
+| `index(null, val)` | `null` |
+| `index([0], null)` | `null` |
+| `index([], val)` | `null` |
+| `min(null)` | `null` |
+| `max(null)` | `null` |
+| `exists(null, "bids-uri")` | `null` |
+| `exists("/path", null)` | `null` |
The following operators have boolean results:
@@ -290,19 +331,20 @@ check will fail.
Object files define "objects" or "terms", which are semantic descriptions of
concepts used in BIDS. These reside under the `object.*` namespace in the schema.
These files **do not** describe how objects of different types
-(for example file suffixes and file entities) interact with one another, or
-whether objects are required in a given dataset or file.
+(for example file suffixes and file entities) interact with one another,
+or whether objects are required in a given dataset or file.
### Overview
-There are currently 11 sub-namespaces, which fall into five rough categories.
+There are currently 12 sub-namespaces, which fall into five rough categories.
+
The namespaces are:
| Namespace | Description | Group |
| --------------------------- | ----------------------------------------------------------------------------------- | ---------------- |
| `objects.common_principles` | Terms that are used throughout BIDS | General terms |
| `objects.modalities` | Broad categories of data represented in BIDS, roughly matching recording instrument | General terms |
-| `objects.entities` | Name-value pairs appearing in file names | Name/value terms |
+| `objects.entities` | Name-value pairs appearing in filenames | Name/value terms |
| `objects.metadata` | Name-value pairs appearing in JSON files | Name/value terms |
| `objects.columns` | Column headings and values appearing in TSV files | Name/value terms |
| `objects.datatypes` | Subdirectories that organize files by type (such as `anat`, `eeg`) | Value terms |
@@ -310,18 +352,21 @@ The namespaces are:
| `objects.extensions` | Filename component that describe the format of the file | Value terms |
| `objects.formats` | Terms that define the forms values (for example, in metadata) might take | Formats |
| `objects.files` | Files and directories that may appear at the root of a dataset | Files |
+| `objects.enums` | Full descriptions of enumerated values used in other sub-namespaces | Value terms |
Because these objects vary, the contents of each namespace can vary.
+
Common fields to all objects:
| Field | Description |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
-| `display_name` | A human-friendly name, for tools to display; may include spaces |
| `description` | A description of the term that can be understood that should not depend on particular surrounding text; may contain markdown for rendering |
+| `display_name` | A human-friendly name, for tools to display; may include spaces |
The name/value terms groups (`entities`, `metadata` and `columns`) define terms where
-a name, when present, has a given meaning, and its value may be restricted. These objects
-additionally have the field:
+a name, when present, has a given meaning, and its value may be restricted.
+
+These objects additionally have the field:
| Field | Description |
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -330,8 +375,10 @@ additionally have the field:
| `format` | The format of the term (defined in `objects.formats`) |
Value terms groups (`datatypes`, `suffixes`, `extensions`) define terms where a field
-can take on multiple values. For example, a file has one datatype, as compared to a
-collection of entities. These objects may have the fields:
+can take on multiple values.
+For example, a file has one datatype, as compared to a collection of entities.
+
+These objects may have the fields:
| Field | Description |
| ------- | ---------------------------------------------------------------------------------------------------------------- |
@@ -373,6 +420,7 @@ In a few cases, two objects with the same name appear multiple times in the spec
When this happens, it is preferred to find a common definition, and clarify it in the rules (see below).
However, in some cases, the object description and permissible values differ, and it needs to be defined
as two separate objects.
+
Consider the following examples:
```yaml
@@ -507,6 +555,13 @@ The convention can be summed up in the following rules:
| `description` | Term definition |
| `file_type` | Indicator that the file is a regular file (`"regular"`) or directory (`"directory"`) |
+- `objects.enums`
+ | Field | Description |
+ | -------------- | ---------------------- |
+ | `display_name` | Human-friendly name |
+ | `description` | Term definition |
+ | `value` | String value of `enum` |
+
## Rule files
The `rules.*` namespace contains most of the validatable content of the schema,
@@ -522,17 +577,22 @@ Core concepts are [expressions](#expressions) (defined above), requirement level
#### Requirement levels and severity
BIDS follows RFC 2119 and has three requirement levels: OPTIONAL, RECOMMENDED and REQUIRED.
-In the schema, we use `optional`, `recommended` and `required`. A rule interpreter (validator)
-is expected to treat missing REQUIRED data/metadata as an error, missing RECOMMENDED
-data/metadata as a warning, and silently pass over missing OPTIONAL data.
+In the schema, we use `optional`, `recommended` and `required`.
+
+A rule interpreter (validator) is expected to treat:
+- missing REQUIRED data/metadata as an error,
+- missing RECOMMENDED data/metadata as a warning,
+- and silently pass over missing OPTIONAL data.
-BIDS also defines a level `DEPRECATED`, rendered in the schema as `deprecated`, and corresponding
-to a warning if the data/metadata is present.
+BIDS also defines a level `DEPRECATED`, rendered in the schema as `deprecated`,
+and corresponding to a warning if the data/metadata is present.
#### Issues
Issues are messages intended to be communicated to a dataset curator to indicate an issue
-with their dataset. They have a code and severity as well:
+with their dataset.
+
+They have a code and severity as well:
| Field | Description |
| --------- | ---------------------------------------------- |
@@ -540,17 +600,19 @@ with their dataset. They have a code and severity as well:
| `level` | Issue severity (`warning` or `error`) |
| `message` | Message for display to a user |
-A level of `warning` corresponds to a rule in the specification that is RECOMMENDED, while a
-level of `error` corresponds to a rule that is REQUIRED.
+A level of `warning` corresponds to a rule in the specification that is RECOMMENDED,
+while a level of `error` corresponds to a rule that is REQUIRED.
-In some cases, an issue is contained next to a `level: required` or `level: recommended` as part
-of a larger rule. In these cases, the `level` field should be omitted from the issue to avoid
-duplication or conflict.
+In some cases, an issue is contained next to a `level: required` or `level: recommended`
+as part of a larger rule.
+In these cases, the `level` field should be omitted from the issue
+to avoid duplication or conflict.
### Filename construction rules
-A significant portion of BIDS is devoted to the naming of files, and almost all file names consist
-of entities, a suffix, an extension, and a data type. Exceptions will be noted below.
+A significant portion of BIDS is devoted to the naming of files,
+and almost all filenames consist of entities, a suffix, an extension, and a data type.
+Exceptions will be noted below.
`rules.files` contains the following subdivisions.
@@ -597,6 +659,7 @@ Here, `README` and `README.md` are both valid, while only `dataset_description.j
including `participants.tsv`, `samples.tsv`, `*_sessions.tsv` and `*_scans.tsv`.
The first two use the `stem` field, while the latter two specify the entities used
to construct the filename.
+
The valid fields are:
| Field | Description |
@@ -631,6 +694,7 @@ Note that these files do not have a `datatype`, but otherwise follow the same ru
`rules.files.raw` and `rules.files.deriv` contain series of related rules.
These are largely grouped by datatype, but file types that appear in multiple locations may be grouped together.
+
The files described take the form:
```plain
@@ -696,8 +760,11 @@ while files in the second group may not.
Also, when files in the second group have the `acq` entity, the associated value MUST be `crosstalk`.
A common derivatives type is preprocessed data, where the type of the generated data is the same
-as the input data. BIDS Derivatives specifies that these files may be distinguished from raw data
-with the new entities `space-` or `desc-`. This rule is encoded:
+as the input data.
+BIDS Derivatives specifies that these files may be distinguished from raw data
+with the new entities `space-` or `desc-`.
+
+This rule is encoded:
```yaml
meg_meg_common:
@@ -775,8 +842,8 @@ RuleNameReq:
Here we show an example of two fields, one that is RECOMMENDED in most cases
but REQUIRED in another, the other of which is OPTIONAL.
-`selectors` indicate whether the current rule applies to a given file. This
-is not rendered in the text, but may be used by a validator.
+`selectors` indicate whether the current rule applies to a given file.
+This is not rendered in the text, but may be used by a validator.
`fields` is an object with keys that appear in `objects.metadata`/`objects.columns`.
If the value is a string, then it is a requirement level.
If it is an object, then the it has the following fields
@@ -786,7 +853,7 @@ If it is an object, then the it has the following fields
| `level` | REQUIRED | Requirement level of field, one of (`optional`, `recommended`, `required`, `deprecated`) |
| `level_addendum` | OPTIONAL | Additional text to describe cases where requirement level changes |
| `description_addendum` | OPTIONAL | Additional text to follow the `objects.metadata..description` |
-| `issues` | OPTIONAL | [issue object](#issues), if additional communication is warranted |
+| `issue` | OPTIONAL | [issue object](#issues), if additional communication is warranted |
The second table implements the change in the first table's `level_addendum`.
The `expression3` selector indicates the additional case where the more stringent
@@ -795,12 +862,14 @@ rule is applied.
#### Valid fields for definitions
1. `rules.sidecars.*`
+
| Field | Description |
| ----------- | -------------------------------------------------------------------------------------------------------- |
| `selectors` | List of expressions; any evaluating false indicate rule does not apply |
| `fields` | Object with keys that may be found in `objects.metadata`, values either a requirement level or an object |
1. `rules.tabular_data.*`
+
| Field | Description |
| -------------------- | -------------------------------------------------------------------------------------------------------------- |
| `selectors` | List of expressions; any evaluating false indicate rule does not apply |
@@ -809,8 +878,8 @@ rule is applied.
| `index_columns` | An optional list of columns that uniquely identify a row. |
| `additional_columns` | Indicates whether additional columns may be defined. One of `allowed`, `allowed_if_defined` and `not_allowed`. |
-The following tables demonstrate how mutual exclusive, required fields, may be
-set in `rules.sidecars.*`:
+The following tables demonstrate how mutual exclusive, required fields,
+may be set in `rules.sidecars.*`:
```YAML
MRIFuncRepetitionTime:
@@ -836,8 +905,8 @@ MRIFuncVolumeTiming:
level_addendum: mutually exclusive with `RepetitionTime`
```
-An additional check will be required to assert that both are not present, but
-these tables may be combined for rendering purposes.
+An additional check will be required to assert that both are not present,
+but these tables may be combined for rendering purposes.
Here we present an example rule in `rules.tabular_data.eeg`:
@@ -849,11 +918,11 @@ EEGChannels:
- extension == ".tsv"
initial_columns:
- name__channels
- - type__eeg_channels
+ - type__channels
- units
columns:
name__channels: required
- type__eeg_channels: required
+ type__channels: required
units: required
description: optional
sampling_frequency: optional
@@ -869,7 +938,8 @@ EEGChannels:
### Checks
`rules.checks` can contain more complex rules. Structurally, these are similar to sidecar rules,
-in that they have selectors. They additionally have a `checks` list, and an explicit issue.
+in that they have selectors.
+They additionally have a `checks` list, and an explicit issue.
| Field | Description |
| ----------- | ---------------------------------------------------------------------------------------------- |
@@ -933,3 +1003,18 @@ we expect that the `MAJOR` component
will be incremented whenever schema organization introduces "breaking changes",
`MINOR` - when adding new components to the schema,
and `PATCH` - when fixing errors in existing components.
+
+## Schema publication
+
+The BIDS Schema is compiled into a single, dereferenced object during
+the ReadTheDocs build of the specification.
+This object is published as a JSON document that can be found at `/schema.json`
+at the root of the specification.
+For example, the schema used to construct the 1.8.0 release of BIDS can be found at
+,
+and the latest version that includes unreleased changes to BIDS and the schema may
+be found at .
+
+The JSON version of the schema contains `schema_version` and `bids_version` keys
+that identify the state of both the schema and the specification at the time it was
+compiled.
diff --git a/src/schema/SCHEMA_VERSION b/src/schema/SCHEMA_VERSION
index e1bde8025c..c7d2522718 100644
--- a/src/schema/SCHEMA_VERSION
+++ b/src/schema/SCHEMA_VERSION
@@ -1 +1 @@
-0.7.0-dev
+0.7.2-dev
diff --git a/src/schema/meta/associations.yaml b/src/schema/meta/associations.yaml
new file mode 100644
index 0000000000..9658b25fa4
--- /dev/null
+++ b/src/schema/meta/associations.yaml
@@ -0,0 +1,92 @@
+# These rules indicate whether an association (defined in meta.context.context.associations)
+# applies to a given file. These are hints to allow implementations to avoid unnecessary
+# directory and file reads.
+#
+# Structure:
+#
+# - "selectors" is a sequence of expressions that apply to a file that may have an association.
+# If matched, a tool MUST attempt to find the associated file.
+# - "target" contains a set of path components that may be used to search for the associated file.
+# These override the path components of the original file and MUST match for the associated file.
+# A list of values, such as extensions, indicates multiple possible matches.
+# - "inherit" is a boolean indicating whether the associated file may be found at a shallower level
+# of the hierarchy.
+---
+events:
+ selectors:
+ - 'task in entities'
+ - extension != '.json'
+ target:
+ suffix: events
+ extension: .tsv
+ inherit: true
+
+aslcontext:
+ selectors:
+ - suffix == 'asl'
+ - matches(extension, '\.nii(\.gz)?$')
+ target:
+ suffix: aslcontext
+ extension: .tsv
+ inherit: true
+
+m0scan:
+ selectors:
+ - suffix == 'asl'
+ - matches(extension, '\.nii(\.gz)?$')
+ target:
+ suffix: m0scan
+ extension: [.nii, .nii.gz]
+ inherit: false
+
+magnitude:
+ selectors:
+ - suffix == 'fieldmap'
+ - matches(extension, '\.nii(\.gz)?$')
+ target:
+ suffix: magnitude
+ extension: [.nii, .nii.gz]
+ inherit: false
+
+magnitude1:
+ selectors:
+ - matches(suffix, 'phase(diff|1)$')
+ - matches(extension, '\.nii(\.gz)?$')
+ target:
+ suffix: magnitude1
+ extension: [.nii, .nii.gz]
+ inherit: false
+
+bval:
+ selectors:
+ - suffix == 'dwi'
+ - matches(extension, '\.nii(\.gz)?$')
+ target:
+ extension: .bval
+ inherit: true
+
+bvec:
+ selectors:
+ - suffix == 'dwi'
+ - matches(extension, '\.nii(\.gz)?$')
+ target:
+ extension: .bvec
+ inherit: true
+
+channels:
+ selectors:
+ - intersects([suffix], ['eeg', 'ieeg', 'meg', 'nirs', 'motion', 'optodes'])
+ - extension != '.json'
+ target:
+ suffix: channels
+ extension: .tsv
+ inherit: true
+
+coordsystem:
+ selectors:
+ - intersects([suffix], ['eeg', 'ieeg', 'meg', 'nirs', 'motion', 'electrodes', 'optodes'])
+ - extension != '.json'
+ target:
+ suffix: coordsystem
+ extension: .json
+ inherit: true
diff --git a/src/schema/meta/context.yaml b/src/schema/meta/context.yaml
index d88ba7fd5e..5f10be59d6 100644
--- a/src/schema/meta/context.yaml
+++ b/src/schema/meta/context.yaml
@@ -92,6 +92,9 @@ context:
path:
description: 'Path of the current file'
type: string
+ size:
+ description: 'Length of the current file in bytes'
+ type: int
entities:
description: 'Entities parsed from the current filename'
type: object
@@ -223,7 +226,7 @@ context:
description: 'Modification time, unix timestamp'
type: number
filename:
- description: 'File name'
+ description: 'Filename'
type: string
comment:
description: 'Comment'
@@ -266,6 +269,22 @@ context:
maxItems: 8
items:
type: number
+ shape:
+ name: 'Data shape'
+ description: 'Data array shape, equal to dim[1:dim[0] + 1]'
+ type: array
+ minItems: 0
+ maxItems: 7
+ items:
+ type: integer
+ voxel_sizes:
+ name: 'Voxel sizes'
+ description: 'Voxel sizes, equal to pixdim[1:dim[0] + 1]'
+ type: array
+ minItems: 0
+ maxItems: 7
+ items:
+ type: number
xyzt_units:
name: 'XYZT Units'
description: 'Units of pixdim[1..4]'
@@ -297,3 +316,41 @@ context:
name: 'sform code'
description: 'Use of the affine fields.'
type: integer
+ ome:
+ name: 'Open Microscopy Environment fields'
+ description: 'Parsed contents of OME-XML header, which may be found in OME-TIFF or OME-ZARR files'
+ type: object
+ properties:
+ PhysicalSizeX:
+ name: 'PhysicalSizeX'
+ description: 'Pixels / @PhysicalSizeX'
+ type: float
+ PhysicalSizeY:
+ name: 'PhysicalSizeY'
+ description: 'Pixels / @PhysicalSizeY'
+ type: float
+ PhysicalSizeZ:
+ name: 'PhysicalSizeZ'
+ description: 'Pixels / @PhysicalSizeZ'
+ type: float
+ PhysicalSizeXUnit:
+ name: 'PhysicalSizeXUnit'
+ description: 'Pixels / @PhysicalSizeXUnit'
+ type: string
+ PhysicalSizeYUnit:
+ name: 'PhysicalSizeYUnit'
+ description: 'Pixels / @PhysicalSizeYUnit'
+ type: string
+ PhysicalSizeZUnit:
+ name: 'PhysicalSizeZUnit'
+ description: 'Pixels / @PhysicalSizeZUnit'
+ type: string
+ tiff:
+ name: 'TIFF'
+ description: 'TIFF file format metadata'
+ type: object
+ properties:
+ version:
+ name: 'Version'
+ description: 'TIFF file format version (the second 2-byte block)'
+ type: int
diff --git a/src/schema/meta/expression_tests.yaml b/src/schema/meta/expression_tests.yaml
index 0f15d03f76..4d8c63f008 100644
--- a/src/schema/meta/expression_tests.yaml
+++ b/src/schema/meta/expression_tests.yaml
@@ -1,4 +1,5 @@
---
+# null fall-through logic
- expression: sidecar.MissingValue
result: null
- expression: null.anything
@@ -11,8 +12,36 @@
result: null
- expression: null || true
result: null
+- expression: true && null
+ result: null
+- expression: false && null
+ result: false
+- expression: true || null
+ result: true
+- expression: false || null
+ result: null
- expression: '!null'
result: null
+- expression: null + 1
+ result: null
+- expression: null - 1
+ result: null
+- expression: null * 1
+ result: null
+- expression: null / 1
+ result: null
+- expression: 1 + null
+ result: null
+- expression: 1 - null
+ result: null
+- expression: 1 * null
+ result: null
+- expression: 1 / null
+ result: null
+- expression: "'str1' + null"
+ result: null
+- expression: "null + 'str1'"
+ result: null
- expression: intersects([], null)
result: null
- expression: intersects(null, [])
@@ -21,6 +50,12 @@
result: null
- expression: match('string', null)
result: null
+- expression: substr(null, 1, 4)
+ result: null
+- expression: substr('string', null, 4)
+ result: null
+- expression: substr('string', 1, null)
+ result: null
- expression: min(null)
result: null
- expression: max(null)
@@ -45,9 +80,65 @@
result: false
- expression: '"VolumeTiming" in null'
result: false
+- expression: exists(null, "bids-uri")
+ result: false
+- expression: exists([], null)
+ result: false
+
+# Truth/falsity of final expressions
- expression: evaluate(true)
result: true
- expression: evaluate(false)
result: false
- expression: evaluate(null)
result: false
+
+# General expressions
+- expression: 1 + 2
+ result: 3
+- expression: '"cat" + "dog"'
+ result: 'catdog'
+- expression: '1 + "cat"'
+ result: null
+- expression: match('string', '.*')
+ result: true
+- expression: match('', '.')
+ result: false
+- expression: substr('string', 1, 4)
+ result: 'tri'
+- expression: substr('string', 0, 20)
+ result: 'string'
+- expression: type(1)
+ result: 'number'
+- expression: type([])
+ result: 'array'
+- expression: type({})
+ result: 'object'
+- expression: type(true)
+ result: 'boolean'
+- expression: intersects([1], [1, 2])
+ result: true
+- expression: intersects([1], [])
+ result: false
+- expression: length([1, 2, 3])
+ result: 3
+- expression: length([])
+ result: 0
+- expression: count([1, 2, 3], 1)
+ result: 1
+- expression: index(["i", "j", "k"], "i")
+ result: 0
+- expression: index(["i", "j", "k"], "j")
+ result: 1
+- expression: index(["i", "j", "k"], "x")
+ result: null
+- expression: sorted([3, 2, 1])
+ result: [1, 2, 3]
+- expression: min([-1, "n/a", 1])
+ result: -1
+- expression: max([-1, "n/a", 1])
+ result: 1
+- expression: '[3, 2, 1][0]'
+ result: 3
+- expression: '"string"[0]'
+ result: 's'
diff --git a/src/schema/meta/versions.yaml b/src/schema/meta/versions.yaml
new file mode 100644
index 0000000000..06285c7e30
--- /dev/null
+++ b/src/schema/meta/versions.yaml
@@ -0,0 +1,19 @@
+---
+# A list of released BIDS versions as of the next schema release
+- '1.9.0'
+- '1.8.0'
+- '1.7.0'
+- '1.6.0'
+- '1.5.0'
+- '1.4.1'
+- '1.4.0'
+- '1.3.0'
+- '1.2.2'
+- '1.2.1'
+- '1.2.0'
+- '1.1.2'
+- '1.1.1'
+- '1.1.0'
+- '1.0.2'
+- '1.0.1'
+- '1.0.0'
diff --git a/src/schema/objects/columns.yaml b/src/schema/objects/columns.yaml
index 381ebd667a..bd1ab1478e 100644
--- a/src/schema/objects/columns.yaml
+++ b/src/schema/objects/columns.yaml
@@ -52,6 +52,23 @@ color:
Hexadecimal. Label color for visualization.
type: string
unit: hexadecimal
+component:
+ name: component
+ display_name: Component
+ description: |
+ Description of the spatial axis or label of quaternion component associated with the channel.
+ For example, `x`,`y`,`z` for position channels,
+ or `quat_x`, `quat_y`, `quat_z`, `quat_w` for quaternion orientation channels.
+ type: string
+ enum:
+ - x
+ - y
+ - z
+ - quat_x
+ - quat_y
+ - quat_z
+ - quat_w
+ - n/a
detector__channels:
name: detector
display_name: Detector Name
@@ -166,8 +183,8 @@ hemisphere:
The hemisphere in which the electrode is placed.
type: string
enum:
- - L
- - R
+ - $ref: objects.enums.left_hemisphere.value
+ - $ref: objects.enums.right_hemisphere.value
high_cutoff:
name: high_cutoff
display_name: High cutoff
@@ -308,16 +325,6 @@ onset:
acquired data point.
type: number
unit: s
-orientation_component:
- name: orientation_component
- display_name: Orientation Component
- description: |
- Description of the orientation of the channel.
- type: string
- enum:
- - x
- - y
- - z
pathology:
name: pathology
display_name: Pathology
@@ -335,6 +342,14 @@ participant_id:
matching a participant entity found in the dataset.
type: string
pattern: ^sub-[0-9a-zA-Z]+$
+placement__motion:
+ name: placement
+ display_name: Placement
+ description: |
+ Placement of the tracked point on the body (for example,
+ participant, avatar centroid, torso, left arm).
+ It can refer to an external vocabulary for describing body parts.
+ type: string
plasma_radioactivity:
name: plasma_radioactivity
display_name: Plasma radioactivity
@@ -381,16 +396,6 @@ response_time:
- type: string
enum:
- n/a
-sample:
- name: sample
- display_name: Sample index
- description: |
- Onset of the event according to the sampling scheme of the recorded modality
- (that is, referring to the raw data file that the `events.tsv` file accompanies).
- When there are several sampling schemes present in the raw data file (as can be
- the case for example for `.edf` files), this column is ambiguous and
- SHOULD NOT be used.
- type: integer
sample_id:
name: sample_id
display_name: Sample ID
@@ -574,6 +579,13 @@ time:
For example, 5.
type: number
unit: s
+tracked_point__channels:
+ name: tracked_point
+ display_name: Tracked point channel
+ description: |
+ Label of the point that is being tracked, for example, label of a tracker
+ or a marker (for example,`"LeftFoot"`, `"RightWrist"`).
+ type: string
trial_type:
name: trial_type
display_name: Trial type
@@ -591,32 +603,7 @@ trigger:
continuous measurement of the scanner trigger signal
type: number
# type column in channels.tsv files
-type__eeg_channels:
- name: type
- display_name: Channel type
- description: |
- Type of channel; MUST use the channel types listed below.
- Note that the type MUST be in upper-case.
- type: string
- enum:
- - AUDIO
- - EEG
- - EOG
- - ECG
- - EMG
- - EYEGAZE
- - GSR
- - HEOG
- - MISC
- - PPG
- - PUPIL
- - REF
- - RESP
- - SYSCLOCK
- - TEMP
- - TRIG
- - VEOG
-type__meg_channels:
+type__channels:
name: type
display_name: Channel type
description: |
@@ -624,108 +611,54 @@ type__meg_channels:
Note that the type MUST be in upper-case.
type: string
enum:
- - MEGMAG
- - MEGGRADAXIAL
- - MEGGRADPLANAR
- - MEGREFMAG
- - MEGREFGRADAXIAL
- - MEGREFGRADPLANAR
- - MEGOTHER
- - EEG
- - ECOG
- - SEEG
- - DBS
- - VEOG
- - HEOG
- - EOG
- - ECG
- - EMG
- - TRIG
- - AUDIO
- - PD
- - EYEGAZE
- - PUPIL
- - MISC
- - SYSCLOCK
- - ADC
- - DAC
- - HLU
- - FITERR
- - OTHER
-type__ieeg_channels:
- name: type
- display_name: Channel type
- description: |
- Type of channel; MUST use the channel types listed below.
- Note that the type MUST be in upper-case.
- type: string
- enum:
- - EEG
- - ECOG
- - SEEG
- - DBS
- - VEOG
- - HEOG
- - EOG
- - ECG
- - EMG
- - TRIG
- - AUDIO
- - PD
- - EYEGAZE
- - PUPIL
- - MISC
- - SYSCLOCK
- - ADC
- - DAC
- - REF
- - OTHER
-type__nirs_channels:
- name: type
- display_name: Channel type
- description: |
- Type of channel; MUST use the channel types listed below.
- Note that the type MUST be in upper-case.
- type: string
- enum:
- - NIRSCWAMPLITUDE
- - NIRSCWFLUORESCENSEAMPLITUDE
- - NIRSCWOPTICALDENSITY
- - NIRSCWHBO
- - NIRSCWHBR
- - NIRSCWMUA
- - MEGMAG
- - MEGGRADAXIAL
- - MEGGRADPLANAR
- - MEGREFMAG
- - MEGREFGRADAXIAL
- - MEGREFGRADPLANAR
- - MEGOTHER
- - EEG
- - ECOG
- - SEEG
- - DBS
- - VEOG
- - HEOG
- - EOG
- - ECG
- - EMG
- - TRIG
- - AUDIO
- - PD
- - EYEGAZE
- - PUPIL
- - MISC
- - SYSCLOCK
- - ADC
- - DAC
- - HLU
- - FITERR
- - ACCEL
- - GYRO
- - MAGN
- - MISC
- - OTHER
+ - $ref: objects.enums.ACCEL.value
+ - $ref: objects.enums.ADC.value
+ - $ref: objects.enums.ANGACCEL.value
+ - $ref: objects.enums.AUDIO.value
+ - $ref: objects.enums.DAC.value
+ - $ref: objects.enums.DBS.value
+ - $ref: objects.enums.ECG.value
+ - $ref: objects.enums.ECOG.value
+ - $ref: objects.enums.EEG.value
+ - $ref: objects.enums.EMG.value
+ - $ref: objects.enums.EOG.value
+ - $ref: objects.enums.EYEGAZE.value
+ - $ref: objects.enums.FITERR.value
+ - $ref: objects.enums.GSR.value
+ - $ref: objects.enums.GYRO.value
+ - $ref: objects.enums.HEOG.value
+ - $ref: objects.enums.HLU.value
+ - $ref: objects.enums.JNTANG.value
+ - $ref: objects.enums.LATENCY.value
+ - $ref: objects.enums.MAGN.value
+ - $ref: objects.enums.MEGGRADAXIAL.value
+ - $ref: objects.enums.MEGGRADPLANAR.value
+ - $ref: objects.enums.MEGMAG.value
+ - $ref: objects.enums.MEGOTHER.value
+ - $ref: objects.enums.MEGREFGRADAXIAL.value
+ - $ref: objects.enums.MEGREFGRADPLANAR.value
+ - $ref: objects.enums.MEGREFMAG.value
+ - $ref: objects.enums.MISC.value
+ - $ref: objects.enums.NIRSCWAMPLITUDE.value
+ - $ref: objects.enums.NIRSCWFLUORESCENSEAMPLITUDE.value
+ - $ref: objects.enums.NIRSCWHBO.value
+ - $ref: objects.enums.NIRSCWHBR.value
+ - $ref: objects.enums.NIRSCWMUA.value
+ - $ref: objects.enums.NIRSCWOPTICALDENSITY.value
+ - $ref: objects.enums.ORNT.value
+ - $ref: objects.enums.OTHER.value
+ - $ref: objects.enums.PD.value
+ - $ref: objects.enums.POS.value
+ - $ref: objects.enums.PPG.value
+ - $ref: objects.enums.PUPIL.value
+ - $ref: objects.enums.REF.value
+ - $ref: objects.enums.RESP.value
+ - $ref: objects.enums.SEEG.value
+ - $ref: objects.enums.SYSCLOCK.value
+ - $ref: objects.enums.TEMP.value
+ - $ref: objects.enums.TRIG.value
+ - $ref: objects.enums.VEL.value
+ - $ref: objects.enums.VEOG.value
# type column for electrodes.tsv files
type__electrodes:
name: type
@@ -763,15 +696,20 @@ units__nirs:
and [Common Principles](SPEC_ROOT/common-principles.md#units) pages.
type: string
format: unit
-value:
- name: value
- display_name: Marker value
+units__motion:
+ name: units
+ display_name: Units
description: |
- Marker value associated with the event (for example, the value of a TTL
- trigger that was recorded at the onset of the event).
- anyOf:
- - type: number
- - type: string
+ Physical or virtual unit of the value represented in this channel,
+ for example, '"rad"' or '"deg"' for angular quantities or '"m"' for position data.
+ If motion data is recorded in a virtual space and deviate from standard SI units,
+ the unit used MUST be specified in the sidecar `*_motion.json`
+ file (for example `"vm"` for virtual meters). `"rad"` is used for Euler angles
+ and `"n/a"` for quaternions.
+ For guidelines about units see the [Appendix](SPEC_ROOT/appendices/units.md)
+ and [Common Principles](SPEC_ROOT/common-principles.md#units) pages.
+ type: string
+ format: unit
volume_type:
name: volume_type
display_name: ASL volume type
diff --git a/src/schema/objects/common_principles.yaml b/src/schema/objects/common_principles.yaml
index 87c0d16729..7e669efb52 100644
--- a/src/schema/objects/common_principles.yaml
+++ b/src/schema/objects/common_principles.yaml
@@ -4,13 +4,11 @@
# The order in which these terms are presented in the specification is defined in `rules/common_principles.yaml`,
# rather than this file (`objects/common_principles.yaml`).
data_acquisition:
- name: Data acquisition
display_name: Data acquisition
description: |
A continuous uninterrupted block of time during which a brain scanning instrument was acquiring data according to
particular scanning sequence/protocol.
data_type:
- name: Data type
display_name: Data type
description: |
A functional group of different types of data.
@@ -41,14 +39,14 @@ data_type:
11. `micr` (microscopy)
12. `nirs` (near infrared spectroscopy)
+
+ 13. `motion` (motion)
dataset:
- name: Dataset
display_name: Dataset
description: |
A set of neuroimaging and behavioral data acquired for a purpose of a particular study.
A dataset consists of data acquired from one or more subjects, possibly from multiple sessions.
deprecated:
- name: DEPRECATED
display_name: DEPRECATED
description: |
A "deprecated" entity or metadata field SHOULD NOT be used in the generation of new datasets.
@@ -56,7 +54,6 @@ deprecated:
Validating software SHOULD warn when deprecated practices are detected
and provide a suggestion for updating the dataset to preserve the curator's intent.
event:
- name: Event
display_name: Event
description: |
Something that happens or may be perceived by a test subject as happening
@@ -72,21 +69,18 @@ event:
In BIDS, each event has an onset time and duration.
Note that not all tasks will have recorded events (for example, "resting state").
extension:
- name: File extension
display_name: File extension
description: |
- A portion of the file name after the left-most period (`.`) preceded by any other alphanumeric.
+ A portion of the filename after the left-most period (`.`) preceded by any other alphanumeric.
For example, `.gitignore` does not have a file extension,
but the file extension of `test.nii.gz` is `.nii.gz`.
Note that the left-most period is included in the file extension.
index:
- name: index
display_name: index
description: |
A nonnegative integer, possibly prefixed with arbitrary number of 0s for consistent indentation,
for example, it is `01` in `run-01` following `run-` specification.
label:
- name: label
display_name: label
description: |
An alphanumeric value, possibly prefixed with arbitrary number of 0s for consistent indentation,
@@ -94,7 +88,6 @@ label:
Note that labels MUST not collide when casing is ignored
(see [Case collision intolerance](SPEC_ROOT/common-principles.md#case-collision-intolerance)).
modality:
- name: Modality
display_name: Modality
description: |
The category of brain data recorded by a file.
@@ -105,7 +98,6 @@ modality:
When applicable, the modality is indicated in the **suffix**.
The modality may overlap with, but should not be confused with the **data type**.
run:
- name: Run
display_name: Run
description: |
An uninterrupted repetition of data acquisition that has the same acquisition parameters and task
@@ -119,14 +111,12 @@ run:
For some types of [PET](SPEC_ROOT/modality-specific-files/positron-emission-tomography.md) acquisitions,
a subject may leave and re-enter the scanner without interrupting the scan.
sample:
- name: Sample
display_name: Sample
description: |
A sample pertaining to a subject such as tissue, primary cell or cell-free sample.
Sample labels MUST be unique within a subject and it is RECOMMENDED
that they be unique throughout the dataset.
session:
- name: Session
display_name: Session
description: |
A logical grouping of neuroimaging and behavioral data consistent across subjects.
@@ -144,7 +134,6 @@ session:
In the [PET](SPEC_ROOT/modality-specific-files/positron-emission-tomography.md) context,
a session may also indicate a group of related scans, taken in one or more visits.
suffix:
- name: suffix
display_name: suffix
description: |
An alphanumeric string that forms part of a filename, located after all
@@ -152,13 +141,11 @@ suffix:
following a final `_`, right before the **file extension**;
for example, it is `eeg` in `sub-05_task-matchingpennies_eeg.vhdr`.
subject:
- name: Subject
display_name: Subject
description: |
A person or animal participating in the study.
Used interchangeably with term **Participant**.
task:
- name: Task
display_name: Task
description: |
A set of structured activities performed by the participant.
diff --git a/src/schema/objects/datatypes.yaml b/src/schema/objects/datatypes.yaml
index bbb1197a7b..c51fa41382 100644
--- a/src/schema/objects/datatypes.yaml
+++ b/src/schema/objects/datatypes.yaml
@@ -43,6 +43,10 @@ micr:
value: micr
display_name: Microscopy
description: Microscopy
+motion:
+ value: motion
+ display_name: Motion
+ description: Motion data from a tracking system
perf:
value: perf
display_name: Perfusion imaging
diff --git a/src/schema/objects/entities.yaml b/src/schema/objects/entities.yaml
index e26b793c44..3260b4a3d4 100644
--- a/src/schema/objects/entities.yaml
+++ b/src/schema/objects/entities.yaml
@@ -132,8 +132,8 @@ hemisphere:
type: string
format: label
enum:
- - 'L'
- - 'R'
+ - $ref: objects.enums.left_hemisphere.value
+ - $ref: objects.enums.right_hemisphere.value
inversion:
name: inv
display_name: Inversion Time
@@ -184,8 +184,8 @@ mtransfer:
type: string
format: label
enum:
- - 'on'
- - 'off'
+ - $ref: objects.enums.on__mtransfer.value
+ - $ref: objects.enums.off__mtransfer.value
part:
name: part
display_name: Part
@@ -207,10 +207,10 @@ part:
type: string
format: label
enum:
- - mag
- - phase
- - real
- - imag
+ - $ref: objects.enums.magnitude.value
+ - $ref: objects.enums.phase.value
+ - $ref: objects.enums.real.value
+ - $ref: objects.enums.imaginary.value
processing:
name: proc
display_name: Processed (on device)
@@ -334,7 +334,7 @@ split:
Instead of a simple renaming, files should be read in and saved under their
new names with dedicated tools like [MNE-Python](https://mne.tools/),
- which will ensure that not only the file names, but also the internal file pointers, will be updated.
+ which will ensure that not only the filenames, but also the internal file pointers, will be updated.
It is RECOMMENDED that `.fif` files with multiple parts use the `split-` entity to indicate each part.
If there are multiple parts of a recording and the optional `scans.tsv` is provided,
@@ -405,3 +405,17 @@ tracer:
Please note that the `` does not need to match the actual value of the field.
type: string
format: label
+tracksys:
+ name: tracksys
+ display_name: Tracking system
+ description: |
+ The `tracksys-` entity can be used as a key-value pair
+ to label *_motion.tsv and *_motion.json files.
+ It can also be used to label *_channel.tsv or *_events.tsv files
+ when they belong to a specific tracking system.
+
+ This entity corresponds to the `"TrackingSystemName"` metadata field in a *_motion.json file.
+ `tracksys-` entity is a concise string whereas `"TrackingSystemName"`
+ may be longer and more human readable.
+ type: string
+ format: label
diff --git a/src/schema/objects/enums.yaml b/src/schema/objects/enums.yaml
new file mode 100644
index 0000000000..16b2e6c618
--- /dev/null
+++ b/src/schema/objects/enums.yaml
@@ -0,0 +1,1089 @@
+---
+# This file defines valid values in BIDS key-value pairs.
+_EEGCoordSys:
+ type: string
+ enum:
+ - $ref: objects.enums.CapTrak.value
+ - $ref: objects.enums.EEGLAB.value
+ - $ref: objects.enums.EEGLAB-HJ.value
+ - $ref: objects.enums.Other.value
+_GeneticLevelEnum:
+ type: string
+ enum:
+ - $ref: objects.enums.Genetic.value
+ - $ref: objects.enums.Genomic.value
+ - $ref: objects.enums.Epigenomic.value
+ - $ref: objects.enums.Transcriptomic.value
+ - $ref: objects.enums.Metabolomic.value
+ - $ref: objects.enums.Proteomic.value
+_MEGCoordSys:
+ type: string
+ enum:
+ - $ref: objects.enums.CTF.value
+ - $ref: objects.enums.ElektaNeuromag.value
+ - $ref: objects.enums.4DBti.value
+ - $ref: objects.enums.KitYokogawa.value
+ - $ref: objects.enums.ChietiItab.value
+ - $ref: objects.enums.Other.value
+_StandardTemplateCoordSys:
+ type: string
+ enum:
+ - $ref: objects.enums.ICBM452AirSpace.value
+ - $ref: objects.enums.ICBM452Warp5Space.value
+ - $ref: objects.enums.IXI549Space.value
+ - $ref: objects.enums.fsaverage.value
+ - $ref: objects.enums.fsaverageSym.value
+ - $ref: objects.enums.fsLR.value
+ - $ref: objects.enums.MNIColin27.value
+ - $ref: objects.enums.MNI152Lin.value
+ - $ref: objects.enums.MNI152NLin2009aSym.value
+ - $ref: objects.enums.MNI152NLin2009bSym.value
+ - $ref: objects.enums.MNI152NLin2009cSym.value
+ - $ref: objects.enums.MNI152NLin2009aAsym.value
+ - $ref: objects.enums.MNI152NLin2009bAsym.value
+ - $ref: objects.enums.MNI152NLin2009cAsym.value
+ - $ref: objects.enums.MNI152NLin6Sym.value
+ - $ref: objects.enums.MNI152NLin6ASym.value
+ - $ref: objects.enums.MNI305.value
+ - $ref: objects.enums.NIHPD.value
+ - $ref: objects.enums.OASIS30AntsOASISAnts.value
+ - $ref: objects.enums.OASIS30Atropos.value
+ - $ref: objects.enums.Talairach.value
+ - $ref: objects.enums.UNCInfant.value
+_StandardTemplateDeprecatedCoordSys:
+ type: string
+ enum:
+ - $ref: objects.enums.fsaverage3.value
+ - $ref: objects.enums.fsaverage4.value
+ - $ref: objects.enums.fsaverage5.value
+ - $ref: objects.enums.fsaverage6.value
+ - $ref: objects.enums.fsaveragesym.value
+ - $ref: objects.enums.UNCInfant0V21.value
+ - $ref: objects.enums.UNCInfant1V21.value
+ - $ref: objects.enums.UNCInfant2V21.value
+ - $ref: objects.enums.UNCInfant0V22.value
+ - $ref: objects.enums.UNCInfant1V22.value
+ - $ref: objects.enums.UNCInfant2V22.value
+ - $ref: objects.enums.UNCInfant0V23.value
+ - $ref: objects.enums.UNCInfant1V23.value
+ - $ref: objects.enums.UNCInfant2V23.value
+_iEEGCoordSys:
+ type: string
+ enum:
+ - $ref: objects.enums.Pixels.value
+ - $ref: objects.enums.ACPC.value
+ - $ref: objects.enums.ScanRAS.value
+ - $ref: objects.enums.Other.value
+left_hemisphere:
+ value: 'L'
+ display_name: Left Hemisphere
+ description: |
+ A left hemibrain image.
+right_hemisphere:
+ value: 'R'
+ display_name: Right Hemisphere
+ description: |
+ A right hemibrain image.
+CASL:
+ value: CASL
+ display_name: Continuous arterial spin labeling
+ description: |
+ Continuous arterial spin labeling was employed.
+PCASL:
+ value: PCASL
+ display_name: Pseudo-continuous arterial spin labeling
+ description: |
+ Pseudo-continuous arterial spin labeling was employed.
+PASL:
+ value: PASL
+ display_name: Pulsed arterial spin labeling
+ description: |
+ Pulsed arterial spin labeling was employed.
+Separate:
+ value: Separate
+ display_name: Separate
+ description: |
+ A separate `m0scan` file is present.
+Included:
+ value: Included
+ display_name: Included
+ description: |
+ An m0scan volume is contained within the associated `asl` file.
+Estimate:
+ value: Estimate
+ display_name: Estimate
+ description: |
+ A single whole-brain M0 value is provided in the metadata.
+Absent:
+ value: Absent
+ display_name: Absent
+ description: |
+ No specific M0 information is present.
+TwoD:
+ value: 2D
+ display_name: Two-dimensional
+ description: |
+ Two-dimensional MR acquisition.
+ThreeD:
+ value: 3D
+ display_name: Three-dimensional
+ description: |
+ Three-dimensional MR acquisition.
+HARD:
+ value: HARD
+ display_name: Hard pulse
+ description: |
+ A very brief, strong, rectangular pulse.
+GAUSSIAN:
+ value: GAUSSIAN
+ display_name: Gaussian pulse
+ description: |
+ A Gaussian pulse.
+GAUSSHANN:
+ value: GAUSSHANN
+ display_name: Gaussian-Hanning pulse.
+ description: |
+ A Gaussian pulse with a Hanning window.
+SINC:
+ value: SINC
+ display_name: Sinc pulse
+ description: |
+ A sinc-shaped pulse.
+SINCHANN:
+ value: SINCHANN
+ display_name: Sinc-Hanning pulse
+ description: |
+ A sinc-shaped pulse with a Hanning window.
+SINCGAUSS:
+ value: SINCGAUSS
+ display_name: Sinc-Gauss pulse
+ description: |
+ A sinc-shaped pulse with a Gaussian window.
+FERMI:
+ value: FERMI
+ display_name: Fermi pulse
+ description: |
+ A Fermi-shaped pulse.
+i:
+ value: i
+ display_name: i
+ description: |
+ The encoding direction is along the first axis of the data in the NIFTI file,
+ and the encoding value increases from the zero index to the maximum index.
+j:
+ value: j
+ display_name: j
+ description: |
+ The encoding direction is along the second axis of the data in the NIFTI file,
+ and the encoding value increases from the zero index to the maximum index.
+k:
+ value: k
+ display_name: k
+ description: |
+ The encoding direction is along the third axis of the data in the NIFTI file,
+ and the encoding value increases from the zero index to the maximum index.
+iMinus:
+ value: i-
+ display_name: i-
+ description: |
+ The encoding direction is along the first axis of the data in the NIFTI file,
+ and the encoding value decreases from the zero index to the maximum index.
+jMinus:
+ value: j-
+ display_name: j-
+ description: |
+ The encoding direction is along the second axis of the data in the NIFTI file,
+ and the encoding value decreases from the zero index to the maximum index.
+kMinus:
+ value: k-
+ display_name: k-
+ description: |
+ The encoding direction is along the third axis of the data in the NIFTI file,
+ and the encoding value decreases from the zero index to the maximum index.
+continuous:
+ value: continuous
+ display_name: Continuous recording
+ description: |
+ Continuous recording.
+epoched:
+ value: epoched
+ display_name: Epoched recording
+ description: |
+ Recording is limited to time windows around events of interest
+ (for example, stimulus presentations or subject responses).
+discontinuous:
+ value: discontinuous
+ display_name: Discontinuous recording
+ description: |
+ Discontinuous recording.
+orig:
+ value: orig
+ display_name: orig
+ description: |
+ A (potentially unique) per-image space.
+ Useful for describing the source of transforms from an input image to a target space.
+Brain:
+ value: Brain
+ display_name: Brain mask
+ description: |
+ A brain mask.
+Lesion:
+ value: Lesion
+ display_name: Lesion mask
+ description: |
+ A lesion mask.
+Face:
+ value: Face
+ display_name: Face mask
+ description: |
+ A face mask.
+ROI:
+ value: ROI
+ display_name: ROI mask
+ description: |
+ A region of interest mask.
+CapTrak:
+ value: CapTrak
+ display_name: CapTrak
+ description: |
+ RAS orientation and the origin approximately between LPA and RPA
+EEGLAB:
+ value: EEGLAB
+ display_name: EEGLAB
+ description: |
+ ALS orientation and the origin exactly between LPA and RPA.
+ For more information, see the
+ [EEGLAB wiki page](https://eeglab.org/tutorials/ConceptsGuide/coordinateSystem.html#eeglab-coordinate-system).
+EEGLAB-HJ:
+ value: EEGLAB-HJ
+ display_name: EEGLAB-HJ
+ description: |
+ ALS orientation and the origin exactly between LHJ and RHJ.
+ For more information, see the
+ [EEGLAB wiki page](https://eeglab.org/tutorials/ConceptsGuide/coordinateSystem.html#\
+ eeglab-hj-coordinate-system).
+Other:
+ value: Other
+ display_name: Other
+ description: |
+ Other coordinate system.
+Genetic:
+ value: Genetic
+ display_name: Genetic
+ description: |
+ Data report on a single genetic location (typically directly in the `participants.tsv` file).
+Genomic:
+ value: Genomic
+ display_name: Genomic
+ description: |
+ Data link to participants' genome (multiple genetic locations).
+Epigenomic:
+ value: Epigenomic
+ display_name: Epigenomic
+ description: |
+ Data link to participants' characterization of reversible modifications of DNA.
+Transcriptomic:
+ value: Transcriptomic
+ display_name: Transcriptomic
+ description: |
+ Data link to participants RNA levels.
+Metabolomic:
+ value: Metabolomic
+ display_name: Metabolomic
+ description: |
+ Data link to participants' products of cellular metabolic functions.
+Proteomic:
+ value: Proteomic
+ display_name: Proteomic
+ description: |
+ Data link to participants peptides and proteins quantification.
+CTF:
+ value: CTF
+ display_name: CTF
+ description: |
+ ALS orientation and the origin between the ears.
+ElektaNeuromag:
+ value: ElektaNeuromag
+ display_name: Elekta Neuromag
+ description: |
+ RAS orientation and the origin between the ears.
+4DBti:
+ value: 4DBti
+ display_name: 4D BTI
+ description: |
+ ALS orientation and the origin between the ears.
+KitYokogawa:
+ value: KitYokogawa
+ display_name: KIT/Yokogawa
+ description: |
+ ALS orientation and the origin between the ears.
+ChietiItab:
+ value: ChietiItab
+ display_name: Chieti ITAB
+ description: |
+ RAS orientation and the origin between the ears.
+individual:
+ value: individual
+ display_name: individual
+ description: |
+ Participant specific anatomical space (for example derived from T1w and/or T2w images).
+ This coordinate system requires specifying an additional, participant-specific file to be fully defined.
+ In context of surfaces this space has been referred to as `fsnative`.
+
+ In order for this space to be interpretable, `SpatialReference` metadata MUST be provided.
+study:
+ value: study
+ display_name: study
+ description: |
+ Custom space defined using a group/study-specific template.
+ This coordinate system requires specifying an additional file to be fully defined.
+
+ In order for this space to be interpretable, `SpatialReference` metadata MUST be provided.
+scanner:
+ value: scanner
+ display_name: scanner
+ description: |
+ The intrinsic coordinate system of the original image (the first entry of `RawSources`)
+ after reconstruction and conversion to NIfTI or equivalent for the case of surfaces and dual volume/surface
+ files.
+
+ The `scanner` coordinate system is implicit and assumed by default if the derivative filename does not
+ define **any** `space-`.
+ Please note that `space-scanner` SHOULD NOT be used,
+ it is mentioned in this specification to make its existence explicit.
+ICBM452AirSpace:
+ value: ICBM452AirSpace
+ display_name: ICBM452AirSpace
+ description: |
+ Reference space defined by the "average of 452 T1-weighted MRIs of normal young adult brains"
+ with "linear transforms of the subjects into the atlas space using a 12-parameter affine
+ transformation".
+ICBM452Warp5Space:
+ value: ICBM452Warp5Space
+ display_name: ICBM452Warp5Space
+ description: |
+ Reference space defined by the "average of 452 T1-weighted MRIs of normal young adult brains"
+ "based on a 5th order polynomial transformation into the atlas space".
+IXI549Space:
+ value: IXI549Space
+ display_name: IXI549Space
+ description: |
+ Reference space defined by the average of the "549 (...) subjects from the IXI dataset"
+ linearly transformed to ICBM MNI 452.
+
+ Used by SPM12.
+fsaverage:
+ value: fsaverage
+ display_name: fsaverage
+ description: |
+ The `fsaverage` is a **dual template** providing both volumetric and surface coordinates references.
+ The volumetric template corresponds to a FreeSurfer variant of `MNI305` space.
+ The `fsaverage` atlas also defines a surface reference system (formerly described as fsaverage[3|4|5|6|sym]).
+
+ Used by Freesurfer.
+fsaverageSym:
+ value: fsaverageSym
+ display_name: fsaverageSym
+ description: |
+ The `fsaverage` is a **dual template** providing both volumetric and surface coordinates references.
+ The volumetric template corresponds to a FreeSurfer variant of `MNI305` space.
+ The `fsaverageSym` atlas also defines a symmetric surface reference system
+ (formerly described as `fsaveragesym`).
+
+ Used by Freesurfer.
+fsLR:
+ value: fsLR
+ display_name: fsLR
+ description: |
+ The `fsLR` is a **dual template** providing both volumetric and surface coordinates references.
+ The volumetric template corresponds to `MNI152NLin6Asym`.
+ Surface templates are given at several sampling densities:
+ 164k (used by HCP pipelines for 3T and 7T anatomical analysis),
+ 59k (used by HCP pipelines for 7T MRI bold and DWI analysis),
+ 32k (used by HCP pipelines for 3T MRI bold and DWI analysis), or
+ 4k (used by HCP pipelines for MEG analysis) fsaverage_LR surface reconstructed from the T1w image.
+
+ Used by Freesurfer.
+MNIColin27:
+ value: MNIColin27
+ display_name: MNIColin27
+ description: |
+ Average of 27 T1 scans of a single subject.
+
+ Used by SPM96.
+MNI152Lin:
+ value: MNI152Lin
+ display_name: MNI152Lin
+ description: |
+ Also known as ICBM (version with linear coregistration).
+
+ Used by SPM99 to SPM8.
+MNI152NLin2009aSym:
+ value: MNI152NLin2009aSym
+ display_name: MNI152NLin2009aSym
+ description: |
+ Also known as ICBM (non-linear coregistration with 40 iterations, released in 2009).
+ This is the first symmetric version.
+
+ Used by the DARTEL toolbox in SPM12b.
+MNI152NLin2009bSym:
+ value: MNI152NLin2009bSym
+ display_name: MNI152NLin2009bSym
+ description: |
+ Also known as ICBM (non-linear coregistration with 40 iterations, released in 2009).
+ This is the second symmetric version.
+
+ Used by the DARTEL toolbox in SPM12b.
+MNI152NLin2009cSym:
+ value: MNI152NLin2009cSym
+ display_name: MNI152NLin2009cSym
+ description: |
+ Also known as ICBM (non-linear coregistration with 40 iterations, released in 2009).
+ This is the third symmetric version.
+
+ Used by the DARTEL toolbox in SPM12b.
+MNI152NLin2009aAsym:
+ value: MNI152NLin2009aAsym
+ display_name: MNI152NLin2009aAsym
+ description: |
+ Also known as ICBM (non-linear coregistration with 40 iterations, released in 2009).
+ This is the first asymmetric version.
+
+ Used by the DARTEL toolbox in SPM12b.
+MNI152NLin2009bAsym:
+ value: MNI152NLin2009bAsym
+ display_name: MNI152NLin2009bAsym
+ description: |
+ Also known as ICBM (non-linear coregistration with 40 iterations, released in 2009).
+ This is the second asymmetric version.
+
+ Used by the DARTEL toolbox in SPM12b.
+MNI152NLin2009cAsym:
+ value: MNI152NLin2009cAsym
+ display_name: MNI152NLin2009cAsym
+ description: |
+ Also known as ICBM (non-linear coregistration with 40 iterations, released in 2009).
+ This is the third asymmetric version.
+
+ Used by the DARTEL toolbox in SPM12b.
+MNI152NLin6Sym:
+ value: MNI152NLin6Sym
+ display_name: MNI152NLin6Sym
+ description: |
+ Also known as symmetric ICBM 6th generation (non-linear coregistration).
+
+ Used by FSL.
+MNI152NLin6ASym:
+ value: MNI152NLin6ASym
+ display_name: MNI152NLin6ASym
+ description: |
+ A variation of `MNI152NLin6Sym` built by A. Janke that is released as the _MNI template_ of FSL.
+ Volumetric templates included with
+ [HCP-Pipelines](https://github.com/Washington-University/HCPpipelines/tree/master/global/templates)
+ correspond to this template too.
+
+ Used by FSL and HPC-Pipelines.
+MNI305:
+ value: MNI305
+ display_name: MNI205
+ description: |
+ Also known as avg305.
+NIHPD:
+ value: NIHPD
+ display_name: NIHPD
+ description: |
+ Pediatric templates generated from the NIHPD sample.
+ Available for different age groups
+ (4.5-18.5 y.o., 4.5-8.5 y.o., 7-11 y.o., 7.5-13.5 y.o., 10-14 y.o., 13-18.5 y.o.).
+ This template also comes in either -symmetric or -asymmetric flavor.
+OASIS30AntsOASISAnts:
+ value: OASIS30AntsOASISAnts
+ display_name: OASIS30AntsOASISAnts
+ description: |
+ OASIS30AntsOASISAnts
+OASIS30Atropos:
+ value: OASIS30Atropos
+ display_name: OASIS30Atropos
+ description: |
+ OASIS30Atropos
+Talairach:
+ value: Talairach
+ display_name: Talairach
+ description: |
+ Piecewise linear scaling of the brain is implemented as described in TT88.
+UNCInfant:
+ value: UNCInfant
+ display_name: UNCInfant
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds.
+fsaverage3:
+ value: fsaverage3
+ display_name: fsaverage3
+ description: |
+ Images were sampled to the FreeSurfer surface reconstructed from the subject's T1w image,
+ and registered to an fsaverage template.
+ For new datasets, the recommended alternative is fsaverage.
+fsaverage4:
+ value: fsaverage4
+ display_name: fsaverage4
+ description: |
+ Images were sampled to the FreeSurfer surface reconstructed from the subject's T1w image,
+ and registered to an fsaverage template.
+ For new datasets, the recommended alternative is fsaverage.
+fsaverage5:
+ value: fsaverage5
+ display_name: fsaverage5
+ description: |
+ Images were sampled to the FreeSurfer surface reconstructed from the subject's T1w image,
+ and registered to an fsaverage template.
+ For new datasets, the recommended alternative is fsaverage.
+fsaverage6:
+ value: fsaverage6
+ display_name: fsaverage6
+ description: |
+ Images were sampled to the FreeSurfer surface reconstructed from the subject's T1w image,
+ and registered to an fsaverage template.
+ For new datasets, the recommended alternative is fsaverage.
+fsaveragesym:
+ value: fsaveragesym
+ display_name: fsaveragesym
+ description: |
+ Images were sampled to the FreeSurfer surface reconstructed from the subject's T1w image,
+ and registered to an fsaverage template.
+ For new datasets, the recommended alternative is fsaverageSym.
+UNCInfant0V21:
+ value: UNCInfant0V21
+ display_name: UNCInfant0V21
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds. See https://www.nitrc.org/projects/pediatricatlas.
+ For new datasets, the recommended alternative is UNCInfant.
+UNCInfant1V21:
+ value: UNCInfant1V21
+ display_name: UNCInfant1V21
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds. See https://www.nitrc.org/projects/pediatricatlas.
+ For new datasets, the recommended alternative is UNCInfant.
+UNCInfant2V21:
+ value: UNCInfant2V21
+ display_name: UNCInfant2V21
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds. See https://www.nitrc.org/projects/pediatricatlas.
+ For new datasets, the recommended alternative is UNCInfant.
+UNCInfant0V22:
+ value: UNCInfant0V22
+ display_name: UNCInfant0V22
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds. See https://www.nitrc.org/projects/pediatricatlas.
+ For new datasets, the recommended alternative is UNCInfant.
+UNCInfant1V22:
+ value: UNCInfant1V22
+ display_name: UNCInfant1V22
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds. See https://www.nitrc.org/projects/pediatricatlas.
+ For new datasets, the recommended alternative is UNCInfant.
+UNCInfant2V22:
+ value: UNCInfant2V22
+ display_name: UNCInfant2V22
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds. See https://www.nitrc.org/projects/pediatricatlas.
+ For new datasets, the recommended alternative is UNCInfant.
+UNCInfant0V23:
+ value: UNCInfant0V23
+ display_name: UNCInfant0V23
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds. See https://www.nitrc.org/projects/pediatricatlas.
+ For new datasets, the recommended alternative is UNCInfant.
+UNCInfant1V23:
+ value: UNCInfant1V23
+ display_name: UNCInfant1V23
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds. See https://www.nitrc.org/projects/pediatricatlas.
+ For new datasets, the recommended alternative is UNCInfant.
+UNCInfant2V23:
+ value: UNCInfant2V23
+ display_name: UNCInfant2V23
+ description: |
+ Infant Brain Atlases from Neonates to 1- and 2-year-olds. See https://www.nitrc.org/projects/pediatricatlas.
+ For new datasets, the recommended alternative is UNCInfant.
+Pixels:
+ value: Pixels
+ display_name: Pixels
+ description: |
+ If electrodes are localized in 2D space (only x and y are
+ specified and z is n/a), then the positions in this file must correspond to
+ the locations expressed in pixels on the photo/drawing/rendering of the
+ electrodes on the brain. In this case, coordinates must be (row,column)
+ pairs, with (0,0) corresponding to the upper left pixel and (N,0)
+ corresponding to the lower left pixel.
+ACPC:
+ value: ACPC
+ display_name: ACPC
+ description: |
+ The origin of the coordinate system is at the Anterior Commissure
+ and the negative y-axis is passing through the Posterior Commissure. The
+ positive z-axis is passing through a mid-hemispheric point in the superior
+ direction. The anatomical landmarks are determined in the individual's
+ anatomical scan and no scaling or deformations have been applied to the
+ individual's anatomical scan. For more information, see the
+ [ACPC site](https://www.fieldtriptoolbox.org/faq/acpc/) on the FieldTrip
+ toolbox wiki.
+ScanRAS:
+ value: ScanRAS
+ display_name: ScanRAS
+ description: |
+ The origin of the coordinate system is the center of the
+ gradient coil for the corresponding T1w image of the subject, and the x-axis
+ increases left to right, the y-axis increases posterior to anterior and
+ the z-axis increases inferior to superior. For more information see the
+ [Nipy Documentation](https://nipy.org/nibabel/coordinate_systems.html). It is
+ strongly encouraged to align the subject's T1w to ACPC so that the `ACPC`
+ coordinate system can be used. If the subject's T1w in the BIDS dataset
+ is not aligned to ACPC, `ScanRAS` should be used.
+on__mtransfer:
+ value: 'on'
+ display_name: 'On'
+ description: |
+ The image acquired in the presence of the magnetization transfer pulse,
+ also known as the off-resonance pulse.
+off__mtransfer:
+ value: 'off'
+ display_name: 'Off'
+ description: |
+ The image acquired in the absence of the magnetization transfer pulse.
+magnitude:
+ value: mag
+ display_name: Magnitude
+ description: |
+ A magnitude image, typically paired with an associated "phase" image.
+phase:
+ value: phase
+ display_name: Phase
+ description: |
+ A phase image, typically paird with an associated "magnitude" (part-mag) image.
+ Images with this key/value pair MAY be in radians or in arbitrary units.
+ The sidecar JSON file MUST include the units of the `phase` image.
+ The possible options are `rad` or `arbitrary`.
+real:
+ value: real
+ display_name: Real
+ description: |
+ A real-valued image, typically paired with an associated "imaginary" (part-imag) image.
+imaginary:
+ value: imag
+ display_name: Imaginary
+ description: |
+ An imaginary-valued image, typically paird with an associated "real" image.
+ACCEL:
+ value: ACCEL
+ display_name: ACCEL
+ tags:
+ - fnirs
+ - motion
+ description: |
+ Accelerometer channel, one channel for each spatial axis.
+ Column `component` for the axis MUST be added to the `*_channels.tsv` file (x, y, or z).
+ANGACCEL:
+ value: ANGACCEL
+ display_name: ANGACCEL
+ tags:
+ - motion
+ description: |
+ Angular acceleration channel, one channel for each spatial axis.
+ Column `component` for the axis MUST be added to the `*_channels.tsv` file (x, y, or z).
+GYRO:
+ value: GYRO
+ display_name: GYRO
+ tags:
+ - fnirs
+ - motion
+ description: |
+ Gyrometer channel, one channel for each spatial axis.
+ Column `component` for the axis MUST be added to the `*_channels.tsv` file (x, y, or z).
+JNTANG:
+ value: JNTANG
+ display_name: JNTANG
+ tags:
+ - motion
+ description: |
+ Joint angle channel between two fixed axis belonging to two bodyparts.
+ Angle SHOULD be defined between proximal and distal bodypart in `deg`.
+LATENCY:
+ value: LATENCY
+ display_name: LATENCY
+ tags:
+ - motion
+ description: |
+ Latency of samples in seconds from recording onset.
+ MUST be in form of `ss[.000000]`,
+ where `[.000000]` is an optional subsecond resolution between 1 and 6 decimal points.
+MAGN:
+ value: MAGN
+ display_name: MAGN
+ tags:
+ - fnirs
+ - motion
+ description: |
+ Magnetic field strength, one channel for each spatial axis.
+ Column `component` for the axis MUST be added to the `*_channels.tsv` file (x, y or z).
+MISC:
+ value: MISC
+ display_name: MISC
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ - motion
+ description: |
+ Miscellaneous channels.
+ORNT:
+ value: ORNT
+ display_name: ORNT
+ tags:
+ - fnirs
+ - motion
+ description: |
+ Orientation channel, one channel for each spatial axis or quaternion component.
+ Column `component` for the axis or quaternion label MUST be added to the `*_channels.tsv` file
+ (x, y, z, quat_x, quat_y, quat_z, or quat_w).
+POS:
+ value: POS
+ display_name: POS
+ tags:
+ - motion
+ description: |
+ Position in space, one channel for each spatial axis.
+ Column `component` for the axis MUST be added to the `*_channels.tsv` file (x, y or z).
+VEL:
+ value: VEL
+ display_name: VEL
+ tags:
+ - motion
+ description: |
+ Velocity, one channel for each spatial axis.
+ Column `component` for the axis MUST be added to the `*_channels.tsv` file (x, y or z).
+AUDIO:
+ value: AUDIO
+ display_name: AUDIO
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Audio signal.
+EEG:
+ value: EEG
+ display_name: EEG
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Electroencephalogram channel.
+EOG:
+ value: EOG
+ display_name: EOG
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Generic electrooculogram (eye), different from HEOG and VEOG.
+ECG:
+ value: ECG
+ display_name: ECG
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Electrocardiogram (heart).
+EMG:
+ value: EMG
+ display_name: EMG
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Electromyogram (muscle).
+EYEGAZE:
+ value: EYEGAZE
+ display_name: EYEGAZE
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Eye tracker gaze.
+GSR:
+ value: GSR
+ display_name: GSR
+ tags:
+ - eeg
+ description: |
+ Galvanic skin response.
+HEOG:
+ value: HEOG
+ display_name: HEOG
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Horizontal EOG (eye).
+PPG:
+ value: PPG
+ display_name: PPG
+ tags:
+ - eeg
+ description: |
+ Photoplethysmography.
+PUPIL:
+ value: PUPIL
+ display_name: PUPIL
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Eye tracker pupil diameter.
+REF:
+ value: REF
+ display_name: REF
+ tags:
+ - eeg
+ - ieeg
+ description: |
+ Reference channel.
+RESP:
+ value: RESP
+ display_name: RESP
+ tags:
+ - eeg
+ description: |
+ Respiration.
+SYSCLOCK:
+ value: SYSCLOCK
+ display_name: SYSCLOCK
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ System time showing elapsed time since trial started.
+TEMP:
+ value: TEMP
+ display_name: TEMP
+ tags:
+ - eeg
+ description: |
+ Temperature.
+TRIG:
+ value: TRIG
+ display_name: TRIG
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Analog (TTL in Volt) or digital (binary TTL) trigger channel.
+VEOG:
+ value: VEOG
+ display_name: VEOG
+ tags:
+ - eeg
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Vertical EOG (eye).
+MEGMAG:
+ value: MEGMAG
+ display_name: MEGMAG
+ tags:
+ - meg
+ - fnirs
+ description: |
+ MEG magnetometer.
+MEGGRADAXIAL:
+ value: MEGGRADAXIAL
+ display_name: MEGGRADAXIAL
+ tags:
+ - meg
+ - fnirs
+ description: |
+ MEG axial gradiometer.
+MEGGRADPLANAR:
+ value: MEGGRADPLANAR
+ display_name: MEGGRADPLANAR
+ tags:
+ - meg
+ - fnirs
+ description: |
+ MEG planargradiometer.
+MEGREFMAG:
+ value: MEGREFMAG
+ display_name: MEGREFMAG
+ tags:
+ - meg
+ - fnirs
+ description: |
+ MEG reference magnetometer.
+MEGREFGRADAXIAL:
+ value: MEGREFGRADAXIAL
+ display_name: MEGREFGRADAXIAL
+ tags:
+ - meg
+ - fnirs
+ description: |
+ MEG reference axial gradiometer.
+MEGREFGRADPLANAR:
+ value: MEGREFGRADPLANAR
+ display_name: MEGREFGRADPLANAR
+ tags:
+ - meg
+ - fnirs
+ description: |
+ MEG reference planar gradiometer.
+MEGOTHER:
+ value: MEGOTHER
+ display_name: MEGOTHER
+ tags:
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Any other type of MEG sensor.
+ECOG:
+ value: ECOG
+ display_name: ECOG
+ tags:
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Electrode channel.
+SEEG:
+ value: SEEG
+ display_name: SEEG
+ tags:
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Electrode channel.
+DBS:
+ value: DBS
+ display_name: DBS
+ tags:
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Electrode channel.
+PD:
+ value: PD
+ display_name: PD
+ tags:
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Photodiode.
+ADC:
+ value: ADC
+ display_name: ADC
+ tags:
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Analog to Digital input.
+DAC:
+ value: DAC
+ display_name: DAC
+ tags:
+ - meg
+ - ieeg
+ - fnirs
+ description: |
+ Digital to Analog output.
+HLU:
+ value: HLU
+ display_name: HLU
+ tags:
+ - meg
+ - fnirs
+ description: |
+ Measured position of head and head coils.
+FITERR:
+ value: FITERR
+ display_name: FITERR
+ tags:
+ - meg
+ - fnirs
+ description: |
+ Fit error signal from each head localization coil.
+OTHER:
+ value: OTHER
+ display_name: OTHER
+ tags:
+ - meg
+ - fnirs
+ description: |
+ Any other type of channel.
+NIRSCWAMPLITUDE:
+ value: NIRSCWAMPLITUDE
+ display_name: NIRSCWAMPLITUDE
+ tags:
+ - fnirs
+ description: |
+ Continuous wave amplitude measurements. Equivalent to dataType 001 in SNIRF.
+NIRSCWFLUORESCENSEAMPLITUDE:
+ value: NIRSCWFLUORESCENSEAMPLITUDE
+ display_name: NIRSCWFLUORESCENSEAMPLITUDE
+ tags:
+ - fnirs
+ description: |
+ Continuous wave fluorescence amplitude measurements. Equivalent to dataType 051 in SNIRF.
+NIRSCWOPTICALDENSITY:
+ value: NIRSCWOPTICALDENSITY
+ display_name: NIRSCWOPTICALDENSITY
+ tags:
+ - fnirs
+ description: |
+ Continuous wave change in optical density measurements. Equivalent to dataTypeLabel dOD in SNIRF.
+NIRSCWHBO:
+ value: NIRSCWHBO
+ display_name: NIRSCWHBO
+ tags:
+ - fnirs
+ description: |
+ Continuous wave oxygenated hemoglobin (oxyhemoglobin) concentration measurements.
+ Equivalent to dataTypeLabel HbO in SNIRF.
+NIRSCWHBR:
+ value: NIRSCWHBR
+ display_name: NIRSCWHBR
+ tags:
+ - fnirs
+ description: |
+ Continuous wave deoxygenated hemoglobin (deoxyhemoglobin) concentration measurements.
+ Equivalent to dataTypeLabel HbR in SNIRF.
+NIRSCWMUA:
+ value: NIRSCWMUA
+ display_name: NIRSCWMUA
+ tags:
+ - fnirs
+ description: |
+ Continuous wave optical absorption measurements. Equivalent to dataTypeLabel mua in SNIRF.
diff --git a/src/schema/objects/files.yaml b/src/schema/objects/files.yaml
index 1180509921..8adb534619 100644
--- a/src/schema/objects/files.yaml
+++ b/src/schema/objects/files.yaml
@@ -12,6 +12,14 @@ CHANGES:
[CPAN Changelog convention](https://metacpan.org/pod/release/HAARG/CPAN-Changes-0.400002/lib/\
CPAN/Changes/Spec.pod).
The `CHANGES` file MUST be either in ASCII or UTF-8 encoding.
+CITATION:
+ display_name: CITATION.cff
+ file_type: regular
+ description: |
+ A description of the citation information for the dataset, following the
+ [Citation File Format](https://citation-file-format.github.io/) specification.
+ This file permits more detailed and structured descriptions than
+ [dataset_description.json](SPEC_ROOT/glossary.md#dataset_description-files).
LICENSE:
display_name: License
file_type: regular
@@ -37,7 +45,7 @@ README:
The `README` file SHOULD be structured such that its contents can be easily understood
even if the used format is not rendered.
A guideline for creating a good `README` file can be found in the
- [bids-starter-kit](https://github.com/bids-standard/bids-starter-kit/blob/master/templates/README).
+ [bids-starter-kit](https://github.com/bids-standard/bids-starter-kit/tree/main/templates/).
dataset_description:
display_name: Dataset Description
file_type: regular
@@ -50,6 +58,7 @@ genetic_info:
The `genetic_info.json` file describes the genetic information available in the
`participants.tsv` file and/or the genetic database described in
`dataset_description.json`.
+
Datasets containing the `Genetics` field in `dataset_description.json` or the
`genetic_id` column in `participants.tsv` MUST include this file.
participants:
@@ -57,75 +66,27 @@ participants:
file_type: regular
description: |
The purpose of this RECOMMENDED file is to describe properties of participants
- such as age, sex, handedness.
+ such as age, sex, handedness, species and strain.
If this file exists, it MUST contain the column `participant_id`,
which MUST consist of `sub-` values identifying one row for each participant,
followed by a list of optional columns describing participants.
Each participant MUST be described by one and only one row.
- Commonly used *optional* columns in `participant.tsv` files are `age`, `sex`,
- and `handedness`. We RECOMMEND to make use of these columns, and
- in case that you do use them, we RECOMMEND to use the following values
- for them:
-
- - `age`: numeric value in years (float or integer value)
-
- - `sex`: string value indicating phenotypical sex, one of "male", "female",
- "other"
-
- - for "male", use one of these values: `male`, `m`, `M`, `MALE`, `Male`
-
- - for "female", use one of these values: `female`, `f`, `F`, `FEMALE`,
- `Female`
-
- - for "other", use one of these values: `other`, `o`, `O`, `OTHER`,
- `Other`
-
- - `handedness`: string value indicating one of "left", "right",
- "ambidextrous"
-
- - for "left", use one of these values: `left`, `l`, `L`, `LEFT`, `Left`
-
- - for "right", use one of these values: `right`, `r`, `R`, `RIGHT`,
- `Right`
-
- - for "ambidextrous", use one of these values: `ambidextrous`, `a`, `A`,
- `AMBIDEXTROUS`, `Ambidextrous`
-
- Throughout BIDS you can indicate missing values with `n/a` (for "not
- available").
+ Commonly used *optional* columns in `participants.tsv` files are `age`, `sex`,
+ `handedness`, `strain`, and `strain_rrid`.
+ The RECOMMENDED `species` column SHOULD be a binomial species name from the
+ [NCBI Taxonomy](https://www.ncbi.nlm.nih.gov/Taxonomy/Browser/wwwtax.cgi)
+ (for examples `homo sapiens`, `mus musculus`, `rattus norvegicus`).
+ For backwards compatibility, if `species` is absent, the participant is assumed to be
+ `homo sapiens`.
samples:
display_name: Sample Information
file_type: regular
description: |
The purpose of this file is to describe properties of samples, indicated by the `sample` entity.
- This file is REQUIRED if `sample-` is present in any file name within the dataset.
- If this file exists, it MUST contain the three following columns:
-
- - `sample_id`: MUST consist of `sample-` values identifying one row
- for each sample
-
- - `participant_id`: MUST consist of `sub-`
-
- - `sample_type`: MUST consist of sample type values, either `cell line`, `in vitro differentiated cells`,
- `primary cell`, `cell-free sample`, `cloning host`, `tissue`, `whole organisms`, `organoid` or
- `technical sample` from [ENCODE Biosample Type](https://www.encodeproject.org/profiles/biosample_type)
-
- Other optional columns MAY be used to describe the samples.
+ This file is REQUIRED if `sample-` is present in any filename within the dataset.
Each sample MUST be described by one and only one row.
-
- Commonly used *optional* columns in `samples.tsv` files are `pathology` and
- `derived_from`. We RECOMMEND to make use of these columns, and in case that
- you do use them, we RECOMMEND to use the following values for them:
-
- - `pathology`: string value describing the pathology of the sample or type of control.
- When different from `healthy`, pathology SHOULD be specified in `samples.tsv`.
- The pathology MAY instead be specified in
- [Sessions files](SPEC_ROOT/modality-agnostic-files.md#sessions-file) in case it changes over time.
-
- - `derived_from`: `sample-` key/value pair from which a sample is derived from,
- for example a slice of tissue (`sample-02`) derived from a block of tissue (`sample-01`)
code:
display_name: Code
file_type: directory
diff --git a/src/schema/objects/formats.yaml b/src/schema/objects/formats.yaml
index 88d1bafbe5..e0704bee31 100644
--- a/src/schema/objects/formats.yaml
+++ b/src/schema/objects/formats.yaml
@@ -4,9 +4,8 @@
index:
display_name: Index
description: |
- Non-negative, non-zero integers, optionally prefixed with leading zeros for sortability.
- An index may not be all zeros.
- pattern: '[0-9]*[1-9]+[0-9]*'
+ Non-negative, optionally prefixed with leading zeros for better visual homogeneity and sorting.
+ pattern: '[0-9]+'
label:
display_name: Label
description: |
diff --git a/src/schema/objects/metadata.yaml b/src/schema/objects/metadata.yaml
index 1d0697c46a..ea6e2fb5eb 100644
--- a/src/schema/objects/metadata.yaml
+++ b/src/schema/objects/metadata.yaml
@@ -4,9 +4,9 @@
# This file **does not** define how and when metadata fields can be used with a given file.
ACCELChannelCount:
name: ACCELChannelCount
- display_name: Accelerometer channel count
+ display_name: Acceleration channel count
description: |
- Number of accelerometer channels.
+ Number of acceleration channels.
type: integer
minimum: 0
Acknowledgements:
@@ -59,7 +59,7 @@ AnalyticalApproach:
name: AnalyticalApproach
display_name: Analytical Approach
description: |
- Methodology or methodologies used to analyse the `"GeneticLevel"`.
+ Methodology or methodologies used to analyze the `"GeneticLevel"`.
Values MUST be taken from the
[database of Genotypes and Phenotypes
(dbGaP)](https://www.ncbi.nlm.nih.gov/gap/advanced)
@@ -80,10 +80,10 @@ AnatomicalLandmarkCoordinateSystem:
If `"Other"`, provide definition of the coordinate system in
`"AnatomicalLandmarkCoordinateSystemDescription"`.
anyOf:
- - $ref: objects.metadata._MEGCoordSys
- - $ref: objects.metadata._EEGCoordSys
- - $ref: objects.metadata._StandardTemplateCoordSys
- - $ref: objects.metadata._StandardTemplateDeprecatedCoordSys
+ - $ref: objects.enums._MEGCoordSys
+ - $ref: objects.enums._EEGCoordSys
+ - $ref: objects.enums._StandardTemplateCoordSys
+ - $ref: objects.enums._StandardTemplateDeprecatedCoordSys
AnatomicalLandmarkCoordinateSystemDescription:
name: AnatomicalLandmarkCoordinateSystemDescription
display_name: Anatomical Landmark Coordinate System Description
@@ -140,6 +140,13 @@ AnatomicalLandmarkCoordinates__mri:
type: number
minItems: 3
maxItems: 3
+ANGACCELChannelCount:
+ name: ANGACCELChannelCount
+ display_name: Angular acceleration channel count
+ description: |
+ Number of angular acceleration channels.
+ type: integer
+ minimum: 0
ArterialSpinLabelingType:
name: ArterialSpinLabelingType
display_name: Arterial Spin Labeling Type
@@ -147,9 +154,9 @@ ArterialSpinLabelingType:
The arterial spin labeling type.
type: string
enum:
- - CASL
- - PCASL
- - PASL
+ - $ref: objects.enums.CASL.value
+ - $ref: objects.enums.PCASL.value
+ - $ref: objects.enums.PASL.value
AssociatedEmptyRoom:
name: AssociatedEmptyRoom
display_name: Associated Empty Room
@@ -652,10 +659,10 @@ DigitizedHeadPointsCoordinateSystem:
If `"Other"`, provide definition of the coordinate system in
`"DigitizedHeadPointsCoordinateSystemDescription"`.
anyOf:
- - $ref: objects.metadata._MEGCoordSys
- - $ref: objects.metadata._EEGCoordSys
- - $ref: objects.metadata._StandardTemplateCoordSys
- - $ref: objects.metadata._StandardTemplateDeprecatedCoordSys
+ - $ref: objects.enums._MEGCoordSys
+ - $ref: objects.enums._EEGCoordSys
+ - $ref: objects.enums._StandardTemplateCoordSys
+ - $ref: objects.enums._StandardTemplateDeprecatedCoordSys
DigitizedHeadPointsCoordinateSystemDescription:
name: DigitizedHeadPointsCoordinateSystemDescription
display_name: Digitized Head Points Coordinate System Description
@@ -753,10 +760,10 @@ EEGCoordinateSystem:
If `"Other"`, provide definition of the coordinate system in
`EEGCoordinateSystemDescription`.
anyOf:
- - $ref: objects.metadata._MEGCoordSys
- - $ref: objects.metadata._EEGCoordSys
- - $ref: objects.metadata._StandardTemplateCoordSys
- - $ref: objects.metadata._StandardTemplateDeprecatedCoordSys
+ - $ref: objects.enums._MEGCoordSys
+ - $ref: objects.enums._EEGCoordSys
+ - $ref: objects.enums._StandardTemplateCoordSys
+ - $ref: objects.enums._StandardTemplateDeprecatedCoordSys
EEGCoordinateSystemDescription:
name: EEGCoordinateSystemDescription
display_name: EEG Coordinate System Description
@@ -960,10 +967,10 @@ FiducialsCoordinateSystem:
If `"Other"`, provide definition of the coordinate system in
`"FiducialsCoordinateSystemDescription"`.
anyOf:
- - $ref: objects.metadata._MEGCoordSys
- - $ref: objects.metadata._EEGCoordSys
- - $ref: objects.metadata._StandardTemplateCoordSys
- - $ref: objects.metadata._StandardTemplateDeprecatedCoordSys
+ - $ref: objects.enums._MEGCoordSys
+ - $ref: objects.enums._EEGCoordSys
+ - $ref: objects.enums._StandardTemplateCoordSys
+ - $ref: objects.enums._StandardTemplateDeprecatedCoordSys
FiducialsCoordinateSystemDescription:
name: FiducialsCoordinateSystemDescription
display_name: Fiducials Coordinate System Description
@@ -1126,30 +1133,35 @@ GeneticLevel:
Describes the level of analysis.
Values MUST be one of `"Genetic"`, `"Genomic"`, `"Epigenomic"`,
`"Transcriptomic"`, `"Metabolomic"`, or `"Proteomic"`.
+
+ For more information on these levels, see
+ [Multi-omics approaches to disease](https://genomebiology.biomedcentral.com/articles/10.1186/s13059-017-1215-1)
+ by Hasin et al. 2017.
anyOf:
- - $ref: objects.metadata._GeneticLevelEnum
+ - $ref: objects.enums._GeneticLevelEnum
- type: array
items:
- $ref: objects.metadata._GeneticLevelEnum
+ $ref: objects.enums._GeneticLevelEnum
Genetics:
name: Genetics
display_name: Genetics
description: |
An object containing information about the genetics descriptor.
type: object
+ required_fields: [Dataset]
properties:
- Database:
- name: Database
+ Dataset:
+ name: Dataset
description: |
[URI](SPEC_ROOT/common-principles.md#uniform-resource-indicator)
- of database where the dataset is hosted.
+ where data can be retrieved.
type: string
format: uri
- Dataset:
- name: Dataset
+ Database:
+ name: Database
description: |
[URI](SPEC_ROOT/common-principles.md#uniform-resource-indicator)
- where data can be retrieved.
+ of database where the dataset is hosted.
type: string
format: uri
Descriptors:
@@ -1255,10 +1267,10 @@ HeadCoilCoordinateSystem:
If `"Other"`, provide definition of the coordinate system in
`HeadCoilCoordinateSystemDescription`.
anyOf:
- - $ref: objects.metadata._MEGCoordSys
- - $ref: objects.metadata._EEGCoordSys
- - $ref: objects.metadata._StandardTemplateCoordSys
- - $ref: objects.metadata._StandardTemplateDeprecatedCoordSys
+ - $ref: objects.enums._MEGCoordSys
+ - $ref: objects.enums._EEGCoordSys
+ - $ref: objects.enums._StandardTemplateCoordSys
+ - $ref: objects.enums._StandardTemplateDeprecatedCoordSys
HeadCoilCoordinateSystemDescription:
name: HeadCoilCoordinateSystemDescription
display_name: Head Coil Coordinate System Description
@@ -1544,6 +1556,13 @@ InversionTime:
type: number
unit: s
exclusiveMinimum: 0
+JNTANGChannelCount:
+ name: JNTANGChannelCount
+ display_name: Joint angle channel count
+ description: |
+ Number of joint angle channels.
+ type: integer
+ minimum: 0
LabelingDistance:
name: LabelingDistance
display_name: Labeling Distance
@@ -1598,7 +1617,7 @@ LabelingLocationDescription:
`LabelingOrientation` or `LabelingDistance`.
May include a link to an anonymized screenshot of the planning of the
labeling slab/plane with respect to the imaging slab or slices
- `*_asllabeling.jpg`.
+ `*_asllabeling.*`.
Based on DICOM macro C.8.13.5.14.
type: string
LabelingOrientation:
@@ -1674,6 +1693,13 @@ LabelingSlabThickness:
type: number
exclusiveMinimum: 0
unit: mm
+LATENCYChannelCount:
+ name: LATENCYChannelCount
+ display_name: Latency channel count
+ description: |
+ Number of Latency channels.
+ type: integer
+ minimum: 0
Levels:
name: Levels
display_name: Levels
@@ -1726,10 +1752,10 @@ M0Type:
`"Absent"` means that no specific M0 information is present.
type: string
enum:
- - Separate
- - Included
- - Estimate
- - Absent
+ - $ref: objects.enums.Separate.value
+ - $ref: objects.enums.Included.value
+ - $ref: objects.enums.Estimate.value
+ - $ref: objects.enums.Absent.value
MAGNChannelCount:
name: MAGNChannelCount
display_name: Magnetometer Channel Count
@@ -1755,10 +1781,10 @@ MEGCoordinateSystem:
If `"Other"`, provide definition of the coordinate system in
`"MEGCoordinateSystemDescription"`.
anyOf:
- - $ref: objects.metadata._MEGCoordSys
- - $ref: objects.metadata._EEGCoordSys
- - $ref: objects.metadata._StandardTemplateCoordSys
- - $ref: objects.metadata._StandardTemplateDeprecatedCoordSys
+ - $ref: objects.enums._MEGCoordSys
+ - $ref: objects.enums._EEGCoordSys
+ - $ref: objects.enums._StandardTemplateCoordSys
+ - $ref: objects.enums._StandardTemplateDeprecatedCoordSys
MEGCoordinateSystemDescription:
name: MEGCoordinateSystemDescription
display_name: MEG Coordinate System Description
@@ -1787,6 +1813,20 @@ MEGREFChannelCount:
`MEGREFChannelCount` should be set to `0`.
type: integer
minimum: 0
+MISCChannelCount:
+ name: MISCChannelCount
+ display_name: Miscellaneous channel count
+ description: |
+ Number of miscellaneous channels not covered otherwise.
+ type: integer
+ minimum: 0
+MotionChannelCount:
+ name: MotionChannelCount
+ display_name: Motion Channel Count
+ description: |
+ Number of motion channels (for example, `275`).
+ type: integer
+ minimum: 0
MRAcquisitionType:
name: MRAcquisitionType
display_name: MR Acquisition Type
@@ -1795,8 +1835,8 @@ MRAcquisitionType:
Corresponds to DICOM Tag 0018, 0023 `MR Acquisition Type`.
type: string
enum:
- - 2D
- - 3D
+ - $ref: objects.enums.TwoD.value
+ - $ref: objects.enums.ThreeD.value
MRTransmitCoilSequence:
name: MRTransmitCoilSequence
display_name: MR Transmit Coil Sequence
@@ -1842,13 +1882,13 @@ MTPulseShape:
The value `"SINCGAUSS"` refers to a sinc pulse with a Gaussian window.
type: string
enum:
- - HARD
- - GAUSSIAN
- - GAUSSHANN
- - SINC
- - SINCHANN
- - SINCGAUSS
- - FERMI
+ - $ref: objects.enums.HARD.value
+ - $ref: objects.enums.GAUSSIAN.value
+ - $ref: objects.enums.GAUSSHANN.value
+ - $ref: objects.enums.SINC.value
+ - $ref: objects.enums.SINCHANN.value
+ - $ref: objects.enums.SINCGAUSS.value
+ - $ref: objects.enums.FERMI.value
MTState:
name: MTState
display_name: MT State
@@ -1953,6 +1993,13 @@ MiscChannelCount:
Number of miscellaneous analog channels for auxiliary signals.
type: integer
minimum: 0
+MissingValues:
+ name: MissingValues
+ display_name: MissingValues
+ description: |
+ Describes how missing values are represented in the given recording system
+ (for example a tracking system in motion), can take values such as, "NaN", "0".
+ type: string
MixingTime:
name: MixingTime
display_name: Mixing Time
@@ -2074,10 +2121,10 @@ NIRSCoordinateSystem:
If `"Other"`, a definition of the coordinate system MUST be
provided in `NIRSCoordinateSystemDescription`.
anyOf:
- - $ref: objects.metadata._MEGCoordSys
- - $ref: objects.metadata._EEGCoordSys
- - $ref: objects.metadata._StandardTemplateCoordSys
- - $ref: objects.metadata._StandardTemplateDeprecatedCoordSys
+ - $ref: objects.enums._MEGCoordSys
+ - $ref: objects.enums._EEGCoordSys
+ - $ref: objects.enums._StandardTemplateCoordSys
+ - $ref: objects.enums._StandardTemplateDeprecatedCoordSys
NIRSCoordinateSystemDescription:
name: NIRSCoordinateSystemDescription
display_name: NIRS Coordinate System Description
@@ -2174,6 +2221,13 @@ OperatingSystem:
Operating system used to run the stimuli presentation software
(for formatting recommendations, see examples below this table).
type: string
+ORNTChannelCount:
+ name: ORNTChannelCount
+ display_name: Orientation Channel Count
+ description: |
+ Number of orientation channels.
+ type: integer
+ minimum: 0
OtherAcquisitionParameters:
name: OtherAcquisitionParameters
display_name: Other Acquisition Parameters
@@ -2193,6 +2247,7 @@ PCASLType:
description: |
The type of gradient pulses used in the `control` condition.
type: string
+ # TODO: Add definitions for these values.
enum:
- balanced
- unbalanced
@@ -2303,12 +2358,12 @@ PhaseEncodingDirection:
`InPlanePhaseEncodingDirection` which can have `ROW` or `COL` values.
type: string
enum:
- - i
- - j
- - k
- - i-
- - j-
- - k-
+ - $ref: objects.enums.i.value
+ - $ref: objects.enums.iMinus.value
+ - $ref: objects.enums.j.value
+ - $ref: objects.enums.jMinus.value
+ - $ref: objects.enums.k.value
+ - $ref: objects.enums.kMinus.value
PhotoDescription:
name: PhotoDescription
display_name: Photo Description
@@ -2365,6 +2420,13 @@ PlasmaFreeFractionMethod:
description: |
Method used to estimate free fraction.
type: string
+POSChannelCount:
+ name: POSChannelCount
+ display_name: Position Channel Count
+ description: |
+ Number of position channels.
+ type: integer
+ minimum: 0
PostLabelingDelay:
name: PostLabelingDelay
display_name: Post Labeling Delay
@@ -2551,9 +2613,9 @@ RecordingType:
interest (for example, stimulus presentations or subject responses).
type: string
enum:
- - continuous
- - epoched
- - discontinuous
+ - $ref: objects.enums.continuous.value
+ - $ref: objects.enums.epoched.value
+ - $ref: objects.enums.discontinuous.value
ReferencesAndLinks:
name: ReferencesAndLinks
display_name: References And Links
@@ -2642,6 +2704,32 @@ Resolution:
- type: object
additionalProperties:
type: string
+RotationOrder:
+ name: RotationOrder
+ display_name: RotationOrder
+ description: |
+ Specify the sequence in which the elemental 3D extrinsic rotations are applied around the three distinct axes.
+ type: string
+ enum:
+ - XYZ
+ - XZY
+ - YXZ
+ - YZX
+ - ZXY
+ - ZYX
+ - n/a
+RotationRule:
+ name: RotationRule
+ display_name: Rotation Rule
+ description: |
+ In case orientation channels are present, indicate whether rotations are applied
+ clockwise around an axis when seen from the positive direction (left-hand rule) or
+ counter-clockwise (right-hand rule). Must be one of: "left-hand", "right-hand".
+ type: string
+ enum:
+ - left-hand
+ - right-hand
+ - n/a
SEEGChannelCount:
name: SEEGChannelCount
display_name: SEEG Channel Count
@@ -2694,6 +2782,7 @@ SampleOrigin:
description: |
Describes from which tissue the genetic information was extracted.
type: string
+ # TODO: Add definitions for these values.
enum:
- blood
- saliva
@@ -2708,7 +2797,7 @@ SamplePrimaryAntibody:
display_name: Sample Primary Antibody
description: |
Description(s) of the primary antibody used for immunostaining.
- Either an [RRID](https://scicrunch.org/resources) or the name, supplier and catalogue
+ Either an [RRID](https://scicrunch.org/resources) or the name, supplier and catalog
number of a commercial antibody.
For non-commercial antibodies either an [RRID](https://scicrunch.org/resources) or the
host-animal and immunogen used (for examples: `"RRID:AB_2122563"` or
@@ -2724,7 +2813,7 @@ SampleSecondaryAntibody:
display_name: Sample Secondary Antibody
description: |
Description(s) of the secondary antibody used for immunostaining.
- Either an [RRID](https://scicrunch.org/resources) or the name, supplier and catalogue
+ Either an [RRID](https://scicrunch.org/resources) or the name, supplier and catalog
number of a commercial antibody.
For non-commercial antibodies either an [RRID](https://scicrunch.org/resources) or the
host-animal and immunogen used (for examples: `"RRID:AB_228322"` or
@@ -2755,6 +2844,15 @@ SamplingFrequency:
regardless of their type (for example, `2400`).
type: number
unit: Hz
+SamplingFrequencyEffective:
+ name: SamplingFrequencyEffective
+ display_name: Effective Sampling Frequency
+ description: |
+ Effective sampling frequency (in Hz) of all the data in the recording,
+ regardless of their type (for example, `2400`) which can be determined if timestamps
+ per sample are provided.
+ type: number
+ unit: Hz
SamplingFrequency__nirs:
name: SamplingFrequency
display_name: Sampling Frequency
@@ -2932,12 +3030,12 @@ SliceEncodingDirection:
slice index as defined by the NIfTI header.
type: string
enum:
- - i
- - j
- - k
- - i-
- - j-
- - k-
+ - $ref: objects.enums.i.value
+ - $ref: objects.enums.iMinus.value
+ - $ref: objects.enums.j.value
+ - $ref: objects.enums.jMinus.value
+ - $ref: objects.enums.k.value
+ - $ref: objects.enums.kMinus.value
SliceThickness:
name: SliceThickness
display_name: Slice Thickness
@@ -3066,6 +3164,18 @@ SourceType:
then the field here should be specified as "mixed"
and this column should be included in optodes.tsv.
type: string
+SpatialAxes:
+ name: SpatialAxes
+ display_name: Spatial axes
+ description: |
+ Refers to the coordinate system in which the motion data are to be interpreted,
+ if the recorded data can be mapped to a fixed reference frame. A sequence of
+ characters F/B (forward-backward), L/R (left-right), and U/D (up-down). The
+ position of a character in the sequence determines which of the X,Y,Z axes it
+ maps to. For example, "FRD" for X-forward, Y-right, Z-down. For 1D or 2D cases,
+ only specify the used axes and use the character "_" for unused axes
+ ("F_R" when the Y axis is not used, for instance).
+ type: string
SpatialReference:
name: SpatialReference
display_name: Spatial Reference
@@ -3076,7 +3186,7 @@ SpatialReference:
anyOf:
- type: string
enum:
- - orig
+ - $ref: objects.enums.orig.value
- type: string
format: uri
- type: string
@@ -3086,7 +3196,7 @@ SpatialReference:
anyOf:
- type: string
enum:
- - orig
+ - $ref: objects.enums.orig.value
- type: string
format: uri
- type: string
@@ -3163,6 +3273,7 @@ SpoilingType:
description: |
Specifies which spoiling method(s) are used by a spoiled sequence.
type: string
+ # TODO: Add definitions for these values.
enum:
- RF
- GRADIENT
@@ -3172,7 +3283,9 @@ StartTime:
display_name: Start Time
description: |
Start time in seconds in relation to the start of acquisition of the first
- data sample in the corresponding neural dataset (negative values are allowed).
+ data sample in the corresponding (neural) dataset (negative values are allowed).
+ This data MAY be specified with sub-second precision using the syntax `s[.000000]`,
+ where `s` reflects whole seconds, and `.000000` reflects OPTIONAL fractional seconds.
type: number
unit: s
StationName:
@@ -3219,9 +3332,9 @@ StimulusPresentation:
$ref: objects.metadata.Code
SubjectArtefactDescription:
name: SubjectArtefactDescription
- display_name: Subject Artefact Description
+ display_name: Subject Artifact Description
description: |
- Freeform description of the observed subject artefact and its possible cause
+ Freeform description of the observed subject artifact and its possible cause
(for example, `"Vagus Nerve Stimulator"`, `"non-removable implant"`).
If this field is set to `"n/a"`, it will be interpreted as absence of major
source of artifacts except cardiac and blinks.
@@ -3238,9 +3351,9 @@ TaskName:
description: |
Name of the task.
No two tasks should have the same name.
- The task label included in the file name is derived from this `"TaskName"` field
+ The task label included in the filename is derived from this `"TaskName"` field
by removing all non-alphanumeric characters (that is, all except those matching `[0-9a-zA-Z]`).
- For example `"TaskName"` `"faces n-back"` or `"head nodding" will correspond to task labels
+ For example `"TaskName"` `"faces n-back"` or `"head nodding"` will correspond to task labels
`facesnback` and `headnodding`, respectively.
type: string
TermURL:
@@ -3274,6 +3387,7 @@ TissueOrigin:
description: |
Describes the type of tissue analyzed for `"SampleOrigin"` `brain`.
type: string
+ # TODO: Add definitions for these values.
enum:
- gray matter
- white matter
@@ -3330,7 +3444,7 @@ TracerRadionuclide:
name: TracerRadionuclide
display_name: Tracer Radionuclide
description: |
- Radioisotope labelling tracer (for example, `"C11"`).
+ Radioisotope labeling tracer (for example, `"C11"`).
type: string
TracerSNOMED:
name: TracerSNOMED
@@ -3339,6 +3453,13 @@ TracerSNOMED:
ID of the tracer compound from the SNOMED Ontology
(subclass of Radioactive isotope).
type: string
+TubingLength:
+ name: TubingLength
+ display_name: Tubing Length
+ description: |
+ The length of the blood tubing, from the subject to the detector in meters.
+ type: number
+ unit: m
TriggerChannelCount:
name: TriggerChannelCount
display_name: Trigger Channel Count
@@ -3347,13 +3468,20 @@ TriggerChannelCount:
Corresponds to the `TRIG` channel type.
type: integer
minimum: 0
-TubingLength:
- name: TubingLength
- display_name: Tubing Length
+TrackedPointsCount:
+ name: TrackedPointsCount
+ display_name: Tracked Points Count
description: |
- The length of the blood tubing, from the subject to the detector in meters.
+ Number of different tracked points tracked in a motion tracking system.
type: number
unit: m
+TrackingSystemName:
+ name: TrackingSystemName
+ display_name: Tracking System Name
+ description: |
+ A human-readable name of the tracking system to complement `"tracksys"` label
+ of the corresponding *_motion.tsv filename.
+ type: string
TubingType:
name: TubingType
display_name: Tubing Type
@@ -3372,10 +3500,10 @@ Type:
The value `"ROI"` refers to a region of interest mask.
type: string
enum:
- - Brain
- - Lesion
- - Face
- - ROI
+ - $ref: objects.enums.Brain.value
+ - $ref: objects.enums.Lesion.value
+ - $ref: objects.enums.Face.value
+ - $ref: objects.enums.ROI.value
Units:
name: Units
display_name: Units
@@ -3408,6 +3536,13 @@ VascularCrushingVENC:
items:
type: number
unit: cm/s
+VELChannelCount:
+ name: VELChannelCount
+ display_name: Velocity Channel Count
+ description: |
+ Number of linear velocity channels.
+ type: integer
+ minimum: 0
VolumeTiming:
name: VolumeTiming
display_name: Volume Timing
@@ -3441,94 +3576,6 @@ WithdrawalRate:
The unit of the specified withdrawal rate should be in `"mL/s"`.
type: number
unit: mL/s
-_CoordUnits:
- type: string
- enum:
- - m
- - mm
- - cm
- - n/a
-_EEGCoordSys:
- type: string
- enum:
- - CapTrak
- - EEGLAB
- - EEGLAB-HJ
- - Other
-_GeneticLevelEnum:
- type: string
- enum:
- - Genetic
- - Genomic
- - Epigenomic
- - Transcriptomic
- - Metabolomic
- - Proteomic
-_LandmarkCoordinates:
- type: object
- additionalProperties:
- type: array
- items:
- type: number
- minItems: 3
- maxItems: 3
-_MEGCoordSys:
- type: string
- enum:
- - CTF
- - ElektaNeuromag
- - 4DBti
- - KitYokogawa
- - ChietiItab
- - Other
-_StandardTemplateCoordSys:
- type: string
- enum:
- - ICBM452AirSpace
- - ICBM452Warp5Space
- - IXI549Space
- - fsaverage
- - fsaverageSym
- - fsLR
- - MNIColin27
- - MNI152Lin
- - MNI152NLin2009aSym
- - MNI152NLin2009bSym
- - MNI152NLin2009cSym
- - MNI152NLin2009aAsym
- - MNI152NLin2009bAsym
- - MNI152NLin2009cAsym
- - MNI152NLin6Sym
- - MNI152NLin6ASym
- - MNI305
- - NIHPD
- - OASIS30AntsOASISAnts
- - OASIS30Atropos
- - Talairach
- - UNCInfant
-_StandardTemplateDeprecatedCoordSys:
- type: string
- enum:
- - fsaverage3
- - fsaverage4
- - fsaverage5
- - fsaverage6
- - fsaveragesym
- - UNCInfant0V21
- - UNCInfant1V21
- - UNCInfant2V21
- - UNCInfant0V22
- - UNCInfant1V22
- - UNCInfant2V22
- - UNCInfant0V23
- - UNCInfant1V23
- - UNCInfant2V23
-_iEEGCoordSys:
- type: string
- enum:
- - Pixels
- - ACPC
- - Other
iEEGCoordinateProcessingDescription:
name: iEEGCoordinateProcessingDescription
display_name: iEEG Coordinate Processing Description
@@ -3560,9 +3607,9 @@ iEEGCoordinateSystem:
[2D coordinate systems](SPEC_ROOT/modality-specific-files/intracranial\
-electroencephalography.md#allowed-2d-coordinate-systems).
anyOf:
- - $ref: objects.metadata._iEEGCoordSys
- - $ref: objects.metadata._StandardTemplateCoordSys
- - $ref: objects.metadata._StandardTemplateDeprecatedCoordSys
+ - $ref: objects.enums._iEEGCoordSys
+ - $ref: objects.enums._StandardTemplateCoordSys
+ - $ref: objects.enums._StandardTemplateDeprecatedCoordSys
iEEGCoordinateSystemDescription:
name: iEEGCoordinateSystemDescription
display_name: iEEG Coordinate System Description
diff --git a/src/schema/objects/modalities.yaml b/src/schema/objects/modalities.yaml
index 16c7d5474d..4809e380df 100644
--- a/src/schema/objects/modalities.yaml
+++ b/src/schema/objects/modalities.yaml
@@ -28,6 +28,10 @@ micr:
display_name: Microscopy
description: |
Data acquired with a microscope.
+motion:
+ display_name: Motion
+ description: |
+ Data acquired with Motion-Capture systems.
nirs:
display_name: Near-Infrared Spectroscopy
description: Data acquired with NIRS.
diff --git a/src/schema/objects/suffixes.yaml b/src/schema/objects/suffixes.yaml
index fa40af5778..caa943ee7c 100644
--- a/src/schema/objects/suffixes.yaml
+++ b/src/schema/objects/suffixes.yaml
@@ -207,17 +207,11 @@ PDT2:
display_name: PD and T2 weighted image
description: |
In arbitrary units (arbitrary).
- PDw and T2w images acquired using a dual echo FSE sequence through view
- sharing process
- ([Johnson et al. 1994](https://pubmed.ncbi.nlm.nih.gov/8010268/)).
- unit: arbitrary
-PDT2map:
- value: PDT2map
- display_name: Combined PD/T2 image
- description: |
- In arbitrary units (arbitrary).
- Combined PD/T2 maps are REQUIRED to use this suffix regardless of the method
- used to generate them.
+ A two-volume 4D image, where the volumes are, respectively, PDw and T2w
+ images acquired simultaneously.
+ If separated into 3D volumes, the `PDw` and `T2w` suffixes SHOULD be used instead,
+ and an acquisition entity MAY be used to distinguish the images from others with
+ the same suffix, for example, `acq-PDT2_PDw.nii` and `acq-PDT2_T2w.nii`.
unit: arbitrary
PDmap:
value: PDmap
@@ -513,9 +507,9 @@ asllabeling:
value: asllabeling
display_name: ASL Labeling Screenshot
description: |
- An anonymized screenshot of the planning of the labeling slab/plane with
- respect to the imaging slab or slices `*_asllabeling.jpg`.
- Based on DICOM macro C.8.13.5.14.
+ An anonymized screenshot of the planning of the labeling slab/plane
+ with respect to the imaging slab or slices.
+ This screenshot is based on DICOM macro C.8.13.5.14.
beh:
value: beh
display_name: Behavioral recording
@@ -669,6 +663,11 @@ meg:
description: |
Unprocessed MEG data stored in the native file format of the MEG instrument
with which the data was collected.
+motion:
+ value: motion
+ display_name: Motion
+ description: |
+ Data recorded from a tracking system store.
nirs:
value: nirs
display_name: Near Infrared Spectroscopy
diff --git a/src/schema/rules/checks/anat.yaml b/src/schema/rules/checks/anat.yaml
new file mode 100644
index 0000000000..cb8a709261
--- /dev/null
+++ b/src/schema/rules/checks/anat.yaml
@@ -0,0 +1,42 @@
+# Rules for anatomical data that are not defined in tables
+---
+# 95
+T1wFileWithTooManyDimensions:
+ issue:
+ code: T1W_FILE_WITH_TOO_MANY_DIMENSIONS
+ message: |
+ _T1w.nii[.gz] files must have exactly three dimensions.
+ level: error
+ selectors:
+ - suffix == 'T1w'
+ - nifti_header != null
+ checks:
+ - nifti_header.dim[0] == 3
+
+PDT2Volumes:
+ issue:
+ code: PDT2_FILE_SHOULD_HAVE_TWO_VOLUMES
+ message: |
+ _PDT2.nii[.gz] files should be 4D images with exactly two volumes.
+ level: warning
+ selectors:
+ - suffix == 'PDT2'
+ - nifti_header != null
+ checks:
+ - nifti_header.dim[0] == 4
+ - nifti_header.dim[4] == 2
+
+PDT2Echos:
+ issue:
+ code: PDT2_ECHOS_SHOULD_MATCH_NIFTI_LENGTH
+ message: |
+ The EchoTime parameter for _PDT2.nii[.gz] files should have one value
+ per volume.
+ level: warning
+ selectors:
+ - suffix == 'PDT2'
+ - sidecar.EchoTime != null
+ - nifti_header.dim[0] == 4
+ checks:
+ - type(sidecar.EchoTime) == 'array'
+ - len(sidecar.EchoTime) == nifti_header.dim[4]
diff --git a/src/schema/rules/checks/asl.yaml b/src/schema/rules/checks/asl.yaml
index b2415796c2..cda02f4325 100644
--- a/src/schema/rules/checks/asl.yaml
+++ b/src/schema/rules/checks/asl.yaml
@@ -21,6 +21,7 @@ ASLLabelingDurationNiftiLength:
- suffix == "asl"
- '"LabelingDuration" in sidecar'
- type(sidecar.LabelingDuration) == 'array'
+ - type(nifti_header) != "null"
checks:
- length(sidecar.LabelingDuration) == nifti_header.dim[4]
@@ -34,7 +35,8 @@ ASLContextConsistent:
level: error
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
+ - type(associations.aslcontext) != "null"
+ - type(nifti_header) != "null"
checks:
- nifti_header.dim[4] == associations.aslcontext.n_rows
@@ -56,6 +58,7 @@ ASLFlipAngleNiftiLength:
- suffix == "asl"
- '"FlipAngle" in sidecar'
- type(sidecar.FlipAngle) == 'array'
+ - type(nifti_header) != "null"
checks:
- length(sidecar.FlipAngle) == nifti_header.dim[4]
@@ -75,11 +78,10 @@ ASLFlipAngleASLContextLength:
level: error
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
- - '"FlipAngle" in sidecar'
+ - type(associations.aslcontext) != "null"
- type(sidecar.FlipAngle) == 'array'
checks:
- - length(sidecar.FlipAngle) == aslcontext.n_rows
+ - length(sidecar.FlipAngle) == associations.aslcontext.n_rows
# 173
ASLPostLabelingDelayNiftiLength:
@@ -100,8 +102,9 @@ ASLPostLabelingDelayNiftiLength:
level: error
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
+ - type(associations.aslcontext) != "null"
- type(sidecar.PostLabelingDelay) == 'array'
+ - type(nifti_header) != "null"
checks:
- length(sidecar.PostLabelingDelay) == nifti_header.dim[4]
@@ -124,10 +127,10 @@ ASLPostLabelingDelayASLContextLength:
level: error
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
+ - type(associations.aslcontext) != "null"
- type(sidecar.PostLabelingDelay) == 'array'
checks:
- - length(sidecar.PostLabelingDelay) == aslcontext.n_rows
+ - length(sidecar.PostLabelingDelay) == associations.aslcontext.n_rows
# 175
ASLLabelingDurationASLContextLength:
@@ -149,11 +152,10 @@ ASLLabelingDurationASLContextLength:
level: error
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
- - '"LabelingDuration" in sidecar'
+ - type(associations.aslcontext) != "null"
- type(sidecar.LabelingDuration) == 'array'
checks:
- - length(sidecar.LabelingDuration) == aslcontext.n_rows
+ - length(sidecar.LabelingDuration) == associations.aslcontext.n_rows
# 177
ASLRepetitionTimePreparationASLContextLength:
@@ -170,11 +172,10 @@ ASLRepetitionTimePreparationASLContextLength:
level: error
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
- - '"RepetitionTimePreparation" in sidecar'
+ - type(associations.aslcontext) != "null"
- type(sidecar.RepetitionTimePreparation) == 'array'
checks:
- - length(sidecar.RepetitionTimePreparation) == aslcontext.n_rows
+ - length(sidecar.RepetitionTimePreparation) == associations.aslcontext.n_rows
# 180
ASLBackgroundSuppressionNumberPulses:
@@ -206,11 +207,48 @@ ASLTotalAcquiredVolumesASLContextLength:
level: warning
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
+ - type(associations.aslcontext) != "null"
- '"TotalAcquiredVolumes" in sidecar'
checks:
- aslcontext.n_rows == sidecar.TotalAcquiredVolumes
+# 184
+PostLabelingDelayGreater:
+ issue:
+ code: POST_LABELING_DELAY_GREATER
+ message: |
+ 'PostLabelingDelay' is greater than 10, are you sure it's expressed in seconds?
+ level: warning
+ selectors:
+ - suffix == "asl"
+ - sidecar.PostLabelingDelay != null
+ checks:
+ - sidecar.PostLabelingDelay <= 10
+
+# 186
+BolusCutOffDelayTimeGreater:
+ issue:
+ code: BOLUS_CUT_OFF_DELAY_TIME_GREATER
+ message: |
+ 'BolusCutOffDelayTime' is greater than 10, are you sure it's expressed in seconds?
+ level: warning
+ selectors:
+ - sidecar.BolusCutOffDelayTime != null
+ checks:
+ - sidecar.BolusCutOffDelayTime <= 10
+
+# 187
+LabelingDurationGreater:
+ issue:
+ code: LABELING_DURATION_GREATER
+ message: |
+ 'LabelingDuration' is greater than 10, are you sure it's expressed in seconds?
+ level: warning
+ selectors:
+ - sidecar.LabelingDuration != null
+ checks:
+ - sidecar.LabelingDuration <= 10
+
# 196
ASLEchoTimeASLContextLength:
issue:
@@ -222,11 +260,12 @@ ASLEchoTimeASLContextLength:
level: warning
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
- - '"EchoTime" in sidecar'
+ - type(associations.aslcontext) != "null"
- type(sidecar.EchoTime) == 'array'
checks:
- - length(sidecar.EchoTime) == aslcontext.n_rows
+ - length(sidecar.EchoTime) == associations.aslcontext.n_rows
+
+## 197 ECHO_TIME_ELEMENTS duplicates 165 and 196 (length(sidecar.EchoTime) == nifti_header.dim[4])
# 198
ASLM0TypeAbsentScan:
@@ -239,8 +278,8 @@ ASLM0TypeAbsentScan:
level: error
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
- - '"m0scan" in associations'
+ - type(associations.aslcontext) != "null"
+ - type(associations.m0scan) != "null"
- '"M0Type" in sidecar'
checks:
- sidecar.M0Type != "absent"
@@ -255,8 +294,8 @@ ASLM0TypeAbsentASLContext:
level: error
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
- - intersects(aslcontext.volume_type, ["m0scan"])
+ - type(associations.aslcontext) != "null"
+ - intersects(associations.aslcontext.volume_type, ["m0scan"])
- '"M0Type" in sidecar'
checks:
- sidecar.M0Type != "absent"
@@ -271,8 +310,7 @@ ASLM0TypeIncorrect:
level: error
selectors:
- suffix == "asl"
- - '"aslcontext" in associations'
- - '"M0Type" in sidecar'
+ - type(associations.aslcontext) != "null"
- sidecar.M0Type == "separate"
checks:
- - '"m0scan" in associations'
+ - type(associations.m0scan) != "null"
diff --git a/src/schema/rules/checks/dataset.yaml b/src/schema/rules/checks/dataset.yaml
new file mode 100644
index 0000000000..999de949cf
--- /dev/null
+++ b/src/schema/rules/checks/dataset.yaml
@@ -0,0 +1,91 @@
+# Rules for datasets
+# For now use `path == dataset_description.json` to ensure something only happens once
+---
+# 45
+SubjectFolders:
+ issue:
+ code: SUBJECT_FOLDERS
+ message: |
+ There are no subject directories (labeled "sub-*") in the root of this dataset.
+ level: error
+ selectors:
+ - path == 'dataset_description.json'
+ checks:
+ - length(dataset.subjects.sub_dirs) > 0
+
+# 49
+ParticipantIDMismtach:
+ issue:
+ code: PARTICIPANT_ID_MISMATCH
+ message: |
+ Participant labels found in this dataset did not match the values in participant_id column
+ found in the participants.tsv file.
+ level: error
+ selectors:
+ - path == 'participants.tsv'
+ checks:
+ - sorted(columns.participant_label) == sorted(dataset.subjects.sub_dirs)
+
+# 51
+PhenotypeSubjectsMissing:
+ issue:
+ code: PHENOTYPE_SUBJECTS_MISSING
+ message: |
+ A phenotype/ .tsv file lists subjects that were not found in the dataset.
+ level: error
+ selectors:
+ - path == 'dataset_description.json'
+ checks:
+ - sorted(dataset.subjects.phenotype) == sorted(dataset.subjects.sub_dirs)
+
+# 214
+SamplesTSVMissing:
+ issue:
+ code: SAMPLES_TSV_MISSING
+ message: |
+ The compulsory file /samples.tsv is missing. See Modality agnostic files section of the BIDS specification.
+ level: error
+ selectors:
+ - path == 'dataset_description.json'
+ - '"micr" in dataset.modalities'
+ checks:
+ - "'samples.tsv' in dataset.files"
+
+UnknownVersion:
+ issue:
+ code: UNKNOWN_BIDS_VERSION
+ message: |
+ The BIDSVersion field of dataset_description.json does not match a known release.
+ The BIDS Schema used for validation may be out of date.
+ level: warning
+ selectors:
+ - path == 'dataset_description.json'
+ checks:
+ - intersects([json.BIDSVersion], schema.meta.versions)
+
+SingleSourceAuthors:
+ issue:
+ code: AUTHORS_AND_CITATION_FILE_MUTUALLY_EXCLUSIVE
+ level: error
+ message: |
+ CITATION.cff file found. The "Authors" field of dataset_description.json
+ must be removed to avoid inconsistency.
+ selectors:
+ - path == 'CITATION.cff'
+ checks:
+ - '!("Authors" in dataset.dataset_description)'
+
+SingleSourceCitationFields:
+ issue:
+ code: SINGLE_SOURCE_CITATION_FIELDS
+ level: warning
+ message: |
+ CITATION.cff file found. The "HowToAckowledge", "License",
+ and "ReferencesAndLinks" fields of dataset_description.json
+ should be removed to avoid inconsistency.
+ selectors:
+ - path == 'CITATION.cff'
+ checks:
+ - '!("HowToAcknowledge" in dataset.dataset_description)'
+ - '!("License" in dataset.dataset_description)'
+ - '!("ReferencesAndLinks" in dataset.dataset_description)'
diff --git a/src/schema/rules/checks/dwi.yaml b/src/schema/rules/checks/dwi.yaml
index d5d00b6a5c..8f22ab6aae 100644
--- a/src/schema/rules/checks/dwi.yaml
+++ b/src/schema/rules/checks/dwi.yaml
@@ -12,6 +12,7 @@ DWIVolumeCount:
- suffix == "dwi"
- '"bval" in associations'
- '"bvec" in associations'
+ - type(nifti_header) != "null"
checks:
- associations.bval.n_cols == nifti_header.dim[4]
- associations.bvec.n_cols == nifti_header.dim[4]
@@ -24,7 +25,7 @@ DWIBvalRows:
'.bval' files should contain exactly one row of volumes.
level: error
selectors:
- - extension == "bval"
+ - extension == ".bval"
checks:
- data.n_rows == 1
@@ -36,7 +37,7 @@ DWIBvecRows:
'.bvec' files should contain exactly three rows of volumes.
level: error
selectors:
- - extension == "bvec"
+ - extension == ".bvec"
checks:
- data.n_rows == 3
@@ -49,6 +50,7 @@ DWIMissingBvec:
level: error
selectors:
- suffix == "dwi"
+ - match(extension, '^\.nii(\.gz)?$')
checks:
- '"bvec" in associations'
@@ -61,5 +63,6 @@ DWIMissingBval:
level: error
selectors:
- suffix == "dwi"
+ - match(extension, '^\.nii(\.gz)?$')
checks:
- '"bval" in associations'
diff --git a/src/schema/rules/checks/events.yaml b/src/schema/rules/checks/events.yaml
index d4a4972fd7..1492c03e2d 100644
--- a/src/schema/rules/checks/events.yaml
+++ b/src/schema/rules/checks/events.yaml
@@ -15,3 +15,15 @@ EventsMissing:
- suffix != "events"
checks:
- '"events" in associations'
+
+StimulusFileMissing:
+ issue:
+ code: STIMULUS_FILE_MISSING
+ message: |
+ A stimulus file was declared but not found in the dataset.
+ level: error
+ selectors:
+ - suffix == "events"
+ - columns.stim_file != null
+ checks:
+ - exists(columns.stim_file, "stimuli") == length(columns.stim_file)
diff --git a/src/schema/rules/checks/fmap.yaml b/src/schema/rules/checks/fmap.yaml
index 09d974a5d5..debddd03ea 100644
--- a/src/schema/rules/checks/fmap.yaml
+++ b/src/schema/rules/checks/fmap.yaml
@@ -1,9 +1,22 @@
# Rules for fieldmap data that are not defined in tables.
---
+# 83
+EchoTime12DifferenceUnreasonable:
+ issue:
+ code: ECHOTIME1_2_DIFFERENCE_UNREASONABLE
+ message: |
+ The value of (EchoTime2 - EchoTime1) should be within the range of 0.0001 - 0.01.
+ level: error
+ selectors:
+ - suffix == "phasediff"
+ checks:
+ - sidecar.EchoTime2 - sidecar.EchoTime1 >= 0.0001
+ - sidecar.EchoTime2 - sidecar.EchoTime1 <= 0.01
+
# 91
FmapFieldmapWithoutMagnitude:
issue:
- code: _FIELDMAP_WITHOUT_MAGNITUDE_FILE
+ code: FIELDMAP_WITHOUT_MAGNITUDE_FILE
message: |
'_fieldmap.nii[.gz]' file does not have accompanying '_magnitude.nii[.gz]' file.
level: error
@@ -23,3 +36,16 @@ FmapPhasediffWithoutMagnitude:
- suffix == "phasediff"
checks:
- '"magnitude1" in associations'
+
+# 94
+MagnitudeFileWithTooManyDimensions:
+ issue:
+ code: MAGNITUDE_FILE_WITH_TOO_MANY_DIMENSIONS
+ message: |
+ _magnitude1.nii[.gz] and _magnitude2.nii[.gz] files must have exactly three dimensions.
+ level: error
+ selectors:
+ - intersects([suffix], ['magnitude1', 'magnitude2'])
+ - nifti_header != null
+ checks:
+ - nifti_header.dim[0] == 3
diff --git a/src/schema/rules/checks/func.yaml b/src/schema/rules/checks/func.yaml
index 980444dc02..b17ea7a422 100644
--- a/src/schema/rules/checks/func.yaml
+++ b/src/schema/rules/checks/func.yaml
@@ -14,3 +14,116 @@ PhaseSuffixDeprecated:
- datatype == "func"
checks:
- suffix != "phase"
+
+# 2
+RepetitionTimeGreaterThan:
+ issue:
+ code: REPETITION_TIME_GREATER_THAN
+ message: |
+ 'RepetitionTime' is greater than 100 are you sure it's expressed in seconds?
+ level: warning
+ selectors:
+ - suffix == "bold"
+ - type(sidecar.RepetitionTime) != "null"
+ checks:
+ - sidecar.RepetitionTime <= 100
+
+# 12
+RepetitionTimeMismatch:
+ issue:
+ code: REPETITION_TIME_MISMATCH
+ message: |
+ Repetition time did not match between the scan's header and the associated JSON metadata file.
+ level: error
+ selectors:
+ - suffix == "bold"
+ - type(sidecar.RepetitionTime) != "null"
+ - type(nifti_header) != "null"
+ checks:
+ - sidecar.RepetitionTime == nifti_header.pixdim[4]
+
+# 54
+BoldNot4d:
+ issue:
+ code: BOLD_NOT_4D
+ message: |
+ Bold scans must be 4 dimensional.
+ level: error
+ selectors:
+ - suffix == "bold"
+ - type(nifti_header) != "null"
+ checks:
+ - nifti_header.dim[0] == 4
+
+# 66
+SliceTimingGreaterThanRepetitionTime:
+ issue:
+ code: SLICETIMING_VALUES_GREATER_THAN_REPETITION_TIME
+ message: |
+ SliceTiming values contains invalid value as it is greater than RepetitionTime.
+ SliceTiming values should be in seconds not milliseconds (common mistake).
+ level: error
+ selectors:
+ - suffix == "bold"
+ - type(sidecar.SliceTiming) != "null"
+ - type(sidecar.RepetitionTime) != "null"
+ checks:
+ - max(sidecar.SliceTiming) <= sidecar.RepetitionTime
+
+### The following rules implement the functional imaging acquisition timing rules
+### For the sake of informative errors, they are implemented in more rules than
+### strictly necessary
+### The selectors/checks are implemented with the least likely case in the selector
+### for efficiency.
+
+# 178
+VolumeTimingRepetitionTimeMutex:
+ issue:
+ code: VOLUME_TIMING_AND_REPETITION_TIME_MUTUALLY_EXCLUSIVE
+ message: |
+ The fields 'VolumeTiming' and 'RepetitionTime' for this file are mutually exclusive.
+ Choose 'RepetitionTime' when the same repetition time is used for all volumes,
+ or 'VolumeTiming' when variable times are used.
+ level: error
+ selectors:
+ - type(sidecar.VolumeTiming) != "null"
+ checks:
+ - type(sidecar.RepetitionTime) == "null"
+
+RepetitionTimeAcquisitionDurationMutex:
+ issue:
+ code: REPETITION_TIME_AND_ACQUISITION_DURATION_MUTUALLY_EXCLUSIVE
+ message: |
+ The fields 'RepetitionTime' and 'AcquisitionDuration' for this file are mutually exclusive.
+ To specify acquisition duration, use 'SliceTiming' or 'DelayTime'
+ (RepetitionTime - AcquisitionDuration).
+ level: error
+ selectors:
+ - type(sidecar.AcquistionDuration) != "null"
+ checks:
+ - type(sidecar.RepetitionTime) == "null"
+
+VolumeTimingDelayTimeMutex:
+ issue:
+ code: VOLUME_TIMING_AND_DELAY_TIME_MUTUALLY_EXCLUSIVE
+ message: |
+ The fields 'VolumeTiming' and 'DelayTime' for this file are mutually exclusive.
+ To specify acquisition duration, use 'AcquisitionDuration' or 'SliceTiming'.
+ level: error
+ selectors:
+ - type(sidecar.VolumeTiming) != "null"
+ checks:
+ - type(sidecar.DelayTime) == "null"
+
+SliceTimingAcquisitionDurationMutex:
+ issue:
+ code: SLICE_TIMING_AND_DURATION_MUTUALLY_EXCLUSIVE
+ message: |
+ 'SliceTiming' is defined for this file.
+ Neither 'DelayTime' nor 'AcquisitionDuration' may be defined in addition.
+ level: error
+ selectors:
+ - type(sidecar.SliceTiming) != "null"
+ checks:
+ - type(sidecar.AcquisitionDuration) == "null"
+ - type(sidecar.DelayTime) == "null"
diff --git a/src/schema/rules/checks/general.yaml b/src/schema/rules/checks/general.yaml
new file mode 100644
index 0000000000..054090b61f
--- /dev/null
+++ b/src/schema/rules/checks/general.yaml
@@ -0,0 +1,25 @@
+---
+# BIDS Validator Original Issue Code #74
+# Was DUPLICATE_NIFTI_FILES, but applies generally
+DuplicateFiles:
+ issue:
+ code: DUPLICATE_FILES
+ message: |
+ File exists with and without `.gz` extension
+ level: error
+ selectors:
+ - match(extension, '\.gz$')
+ checks:
+ - exists(substr(path, 0, length(path) - 3), "dataset") == 0
+
+ReadmeFileSmall:
+ issue:
+ code: README_FILE_SMALL
+ message: |
+ The recommended file /README is very small. Please consider expanding it
+ with additional information about the dataset.
+ level: warning
+ selectors:
+ - match(path, '^README')
+ checks:
+ - size > 150
diff --git a/src/schema/rules/checks/hints.yaml b/src/schema/rules/checks/hints.yaml
index 13959d4127..fe0fc7561e 100644
--- a/src/schema/rules/checks/hints.yaml
+++ b/src/schema/rules/checks/hints.yaml
@@ -1,4 +1,26 @@
---
+# This file is for warnings that are not specified by BIDS, but are frequently indicative
+# of an issue with a dataset.
+
+### Dataset level
+
+# 102
+TooFewAuthors:
+ issue:
+ code: TOO_FEW_AUTHORS
+ message: |
+ The Authors field of dataset_description.json should contain an array of fields -
+ with one author per field. This was triggered based on the presence of only one
+ author field. Please ignore if all contributors are already properly listed.
+ level: warning
+ selectors:
+ - path == '/dataset_description.json'
+ checks:
+ - length(json.Authors) > 1
+
+### Functional files
+
+# 85
SuspiciouslyLongBOLDDesign:
issue:
code: SUSPICIOUSLY_LONG_EVENT_DESIGN
@@ -9,9 +31,11 @@ SuspiciouslyLongBOLDDesign:
selectors:
- suffix == "bold"
- associations.events != null
+ - type(nifti_header) != "null"
checks:
- max(associations.events.onset) < nifti_header.pixdim[4] * nifti_header.dim[4]
+# 86
SuspiciouslyShortBOLDDesign:
issue:
code: SUSPICIOUSLY_SHORT_EVENT_DESIGN
@@ -22,5 +46,6 @@ SuspiciouslyShortBOLDDesign:
selectors:
- suffix == "bold"
- associations.events != null
+ - type(nifti_header) != "null"
checks:
- max(associations.events.onset) > nifti_header.pixdim[4] * nifti_header.dim[4] / 2
diff --git a/src/schema/rules/checks/micr.yaml b/src/schema/rules/checks/micr.yaml
new file mode 100644
index 0000000000..1476a0abb7
--- /dev/null
+++ b/src/schema/rules/checks/micr.yaml
@@ -0,0 +1,37 @@
+---
+# 221
+PixelSizeInconsistent:
+ issue:
+ code: PIXEL_SIZE_INCONSISTENT
+ message: |
+ PixelSize need to be consistent with PhysicalSizeX, PhysicalSizeY and PhysicalSizeZ OME metadata fields
+ level: error
+ selectors:
+ - ome != null
+ - sidecar.PixelSize != null
+ - sidecar.PixelSizeUnit != null
+ checks:
+ - |
+ ome.PhysicalSizeX * 10 ** (-3 * index(["mm", "um", "nm"], ome.PhysicalSizeXUnit))
+ == sidecar.PixelSize[0] * 10 ** (-3 * index(["mm", "um", "nm"], sidecar.PixelSizeUnit))
+ - |
+ ome.PhysicalSizeY * 10 ** (-3 * index(["mm", "um", "nm"], ome.PhysicalSizeYUnit))
+ == sidecar.PixelSize[1] * 10 ** (-3 * index(["mm", "um", "nm"], sidecar.PixelSizeUnit))
+ - |
+ ome.PhysicalSizeZ * 10 ** (-3 * index(["mm", "um", "nm"], ome.PhysicalSizeZUnit))
+ == sidecar.PixelSize[2] * 10 ** (-3 * index(["mm", "um", "nm"], sidecar.PixelSizeUnit))
+
+# 227
+InconsistentTiffExtension:
+ issue:
+ code: INCONSISTENT_TIFF_EXTENSION
+ message: |
+ Inconsistent TIFF file type and extension
+ level: error
+ selectors:
+ - tiff != null
+ - intersects([extension], ['.ome.tif', '.ome.btf'])
+ checks:
+ - tiff.version == 42 || tiff.version == 43
+ - (extension == '.ome.tif') == (tiff.version == 42)
+ - (extension == '.ome.btf') == (tiff.version == 43)
diff --git a/src/schema/rules/checks/mri.yaml b/src/schema/rules/checks/mri.yaml
index 4fd76f1716..983ccd686e 100644
--- a/src/schema/rules/checks/mri.yaml
+++ b/src/schema/rules/checks/mri.yaml
@@ -12,3 +12,119 @@ PhasePartUnits:
- '"Units" in sidecar'
checks:
- intersects([sidecar.Units], ["rad", "arbitrary"])
+
+# 3
+EchoTimeGreaterThan:
+ issue:
+ code: ECHO_TIME_GREATER_THAN
+ message: |
+ 'EchoTime' is greater than 1 are you sure it's expressed in seconds?
+ level: warning
+ selectors:
+ - modality == "MRI"
+ - sidecar.EchoTime
+ checks:
+ - sidecar.EchoTime <= 1
+
+# 5
+TotalReadoutTimeGreaterThan:
+ issue:
+ code: TOTAL_READOUT_TIME_GREATER_THAN
+ message: |
+ 'TotalReadoutTime' is greater than 10 are you sure it's expressed in seconds?
+ level: warning
+ selectors:
+ - modality == "MRI"
+ - sidecar.TotalReadoutTime
+ checks:
+ - sidecar.TotalReadoutTime <= 10
+
+# 76
+EffectiveEchoSpacingTooLarge:
+ issue:
+ code: EFFECTIVEECHOSPACING_TOO_LARGE
+ message: |
+ Abnormally high value of 'EffectiveEchoSpacing'.
+ level: error
+ selectors:
+ - modality == "MRI"
+ - type(sidecar.RepetitionTime) != "null"
+ - type(sidecar.EffectiveEchoSpacing) != "null"
+ - type(sidecar.PhaseEncodingDirection) != "null"
+ - type(nifti_header) != "null"
+ checks:
+ - |
+ sidecar.RepetitionTime >= (
+ sidecar.EffectiveEchoSpacing
+ * nifti_header.dim[index(["i", "j", "k"], sidecar.PhaseEncodingDirection[0])]
+ )
+
+# 87
+SliceTimingElements:
+ issue:
+ code: SLICETIMING_ELEMENTS
+ message: |
+ The number of elements in the SliceTiming array should match the 'k'
+ dimension of the corresponding NIfTI volume.
+ level: warning
+ selectors:
+ - modality == "MRI"
+ - type(sidecar.SliceTiming) != "null"
+ - type(nifti_header) != "null"
+ checks:
+ - length(sidecar.SliceTiming) == nifti_header.dim[3]
+
+# 93
+EESGreaterThanTRT:
+ issue:
+ code: EFFECTIVEECHOSPACING_LARGER_THAN_TOTALREADOUTTIME
+ message: |
+ EffectiveEchoSpacing should always be smaller than TotalReadoutTime.
+ level: error
+ selectors:
+ - modality == "MRI"
+ - sidecar.EffectiveEchoSpacing != null
+ - sidecar.TotalReadoutTime != null
+ checks:
+ - sidecar.EffectiveEchoSpacing < sidecar.TotalReadoutTime
+
+# 188
+VolumeTimingNotMonotonicallyIncreasing:
+ issue:
+ code: VOLUME_TIMING_NOT_MONOTONICALLY_INCREASING
+ message: |
+ 'VolumeTiming' is not monotonically increasing.
+ level: error
+ selectors:
+ - modality == "MRI"
+ - sidecar.VolumeTiming != null
+ checks:
+ - sorted(sidecar.VolumeTiming) == sidecar.VolumeTiming
+
+# 192
+BolusCutOffDelayTimeNotMonotonicallyIncreasing:
+ issue:
+ code: BOLUS_CUT_OFF_DELAY_TIME_NOT_MONOTONICALLY_INCREASING
+ message: |
+ 'BolusCutOffDelayTime' is not monotonically increasing.
+ level: error
+ selectors:
+ - modality == "MRI"
+ - sidecar.BolusCutoffDelayTime != null
+ checks:
+ - sorted(sidecar.BolusCutoffDelayTime) == sidecar.BolusCutoffDelayTime
+
+# 201
+RepetitionTimePreparationNotConsistent:
+ issue:
+ code: REPETITIONTIME_PREPARATION_NOT_CONSISTENT
+ message: |
+ The number of values for 'RepetitionTimePreparation' for this file does
+ not match the 4th dimension of the NIfTI header.
+ level: error
+ selectors:
+ - modality == "MRI"
+ - type(sidecar.RepetitionTimePreparation) == "array"
+ - type(nifti_header) != "null"
+ checks:
+ - length(sidecar.RepetitionTimePreparation) == nifti_header.dim[4]
diff --git a/src/schema/rules/checks/nifti.yaml b/src/schema/rules/checks/nifti.yaml
new file mode 100644
index 0000000000..9fdae953cc
--- /dev/null
+++ b/src/schema/rules/checks/nifti.yaml
@@ -0,0 +1,51 @@
+---
+# 40
+NiftiDimension:
+ issue:
+ code: NIFTI_DIMENSION
+ message: |
+ NIfTI file's header field for dimension information blank or too short.
+ level: warning
+ selectors:
+ - type(nifti_header) != "null"
+ checks:
+ - length(nifti_header.shape) > 0
+ - min(nifti_header.shape) > 0
+
+# 41
+NiftiUnit:
+ issue:
+ code: NIFTI_UNIT
+ message: |
+ NIfTI file's header field for unit information for x, y, z, and t dimensions empty or too short
+ level: warning
+ selectors:
+ - type(nifti_header) != "null"
+ checks:
+ - nifti_header.xyzt_units.xyz != 'unknown'
+ - nifti_header.dim[0] < 4 || nifti_header.xyzt_units.t != 'unknown'
+
+# 42
+NiftiPixdim:
+ issue:
+ code: NIFTI_PIXDIM
+ message: |
+ NIfTI file's header field for pixel dimension information empty or too short.
+ level: warning
+ selectors:
+ - type(nifti_header) != "null"
+ checks:
+ - min(nifti_header.voxel_sizes) > 0
+
+# 60
+XformCodes0:
+ issue:
+ code: SFORM_AND_QFORM_IN_IMAGE_HEADER_ARE_ZERO
+ message: |
+ sform_code and qform_code in the image header are 0.
+ The image/file will be considered invalid or assumed to be in LAS orientation.
+ level: warning
+ selectors:
+ - nifti_header != null
+ checks:
+ - nifti_header.qform_code != 0 || nifti_header.sform_code != 0
diff --git a/src/schema/rules/checks/nirs.yaml b/src/schema/rules/checks/nirs.yaml
index bbfa52cf7d..db9299eb07 100644
--- a/src/schema/rules/checks/nirs.yaml
+++ b/src/schema/rules/checks/nirs.yaml
@@ -44,14 +44,14 @@ ShortChannelCountReq:
checks:
- sidecar.ShortChannelCount == count(associations.channels.short_channel, true)
-OrientationComponent:
+Component:
selectors:
- datatype == "nirs"
- suffix == "channels"
- extension == ".tsv"
- intersect(columns.type, ["ACCEL", "GYRO", "MAGN"])
checks:
- - columns.orientation_component != null
+ - columns.component != null
RequiredChannels:
selectors:
diff --git a/src/schema/rules/checks/privacy.yaml b/src/schema/rules/checks/privacy.yaml
index e3024ee2e7..dd58320c0a 100644
--- a/src/schema/rules/checks/privacy.yaml
+++ b/src/schema/rules/checks/privacy.yaml
@@ -9,7 +9,22 @@ GzipHeaderFields:
level: warning
selectors:
- match(extension, ".gz$")
+ - gzip != null
checks:
- gzip.timestamp == 0
- gzip.filename == ""
- gzip.comment == ""
+
+CheckAge89:
+ issue:
+ code: AGE_89
+ message: |
+ As per section 164.514(C) of "The De-identification Standard" under HIPAA guidelines,
+ participants with age 89 or higher should be tagged as 89+. More information can be found at
+ https://www.hhs.gov/hipaa/for-professionals/privacy/special-topics/de-identification/#standard
+ level: warning
+ selectors:
+ - path == 'participants.tsv'
+ - columns.age != null
+ checks:
+ - max(columns.age) < 89
diff --git a/src/schema/rules/checks/references.yaml b/src/schema/rules/checks/references.yaml
new file mode 100644
index 0000000000..cbbc8f5454
--- /dev/null
+++ b/src/schema/rules/checks/references.yaml
@@ -0,0 +1,43 @@
+---
+SubjectRelativeIntendedFor:
+ selectors:
+ - datatype != "ieeg"
+ - type(sidecar.IntendedFor) != "null"
+ checks:
+ - exists(sidecar.IntendedFor, "bids-uri") || exists(sidecar.IntendedFor, "subject")
+
+DatasetRelativeIntendedFor:
+ selectors:
+ - datatype == "ieeg"
+ - type(sidecar.IntendedFor) != "null"
+ checks:
+ - exists(sidecar.IntendedFor, "bids-uri") || exists(sidecar.IntendedFor, "dataset")
+
+AssociatedEmptyRoom:
+ selectors:
+ - suffix == "meg"
+ - type(sidecar.AssociatedEmptyRoom) != "null"
+ checks:
+ - exists(sidecar.AssociatedEmptyRoom, "bids-uri") || exists(sidecar.AssociatedEmptyRoom, "dataset")
+
+Stimuli:
+ selectors:
+ - suffix == "events"
+ - extension == ".tsv"
+ - type(columns.stim_file) != "null"
+ checks:
+ - exists(columns.stim_file, "stimuli")
+
+Sources:
+ selectors:
+ - dataset.dataset_description.DatasetType == "derivatives"
+ - type(sidecar.Sources) != "null"
+ checks:
+ - exists(sidecar.Sources, "bids-uri") || exists(sidecar.Sources, "dataset")
+
+SpatialReferences:
+ selectors:
+ - dataset.dataset_description.DatasetType == "derivatives"
+ - type(sidecar.SpatialReference.URI) != "null"
+ checks:
+ - exists(sidecar.SpatialReference.URI, "bids-uri") || exists(sidecar.SpatialReference.URI, "dataset")
diff --git a/src/schema/rules/entities.yaml b/src/schema/rules/entities.yaml
index 08209723e5..09f240510b 100644
--- a/src/schema/rules/entities.yaml
+++ b/src/schema/rules/entities.yaml
@@ -5,6 +5,7 @@
- session
- sample
- task
+- tracksys
- acquisition
- ceagent
- tracer
diff --git a/src/schema/rules/errors.yaml b/src/schema/rules/errors.yaml
index 0a41808075..8e60654c58 100644
--- a/src/schema/rules/errors.yaml
+++ b/src/schema/rules/errors.yaml
@@ -1,6 +1,22 @@
---
# This file describes rules for broad "standard" errors in the specification.
+# BIDS Validator Original Issue Code #0
+InternalError:
+ code: INTERNAL_ERROR
+ message: |
+ Internal error. SOME VALIDATION STEPS MAY NOT HAVE OCCURRED.
+ level: error
+
+# BIDS Validator Original Issue Code #1
+NotIncluded:
+ code: NOT_INCLUDED
+ message: |
+ Files with such naming scheme are not part of BIDS specification. This error is
+ most commonly caused by typos in filenames that make them not BIDS compatible.
+ Please consult the specification and make sure your files are named correctly.
+ level: error
+
# BIDS Validator Original Issue Code #26
NiftiHeaderUnreadable:
code: NIFTI_HEADER_UNREADABLE
@@ -118,7 +134,7 @@ WrongNewLine:
selectors:
- extension == ".tsv"
-# BIDS Validator Original Issue Code #74
+# BIDS Validator Original Issue Code #88
MalformedBvec:
code: MALFORMED_BVEC
message: |
@@ -127,7 +143,7 @@ MalformedBvec:
selectors:
- extension == ".bvec"
-# BIDS Validator Original Issue Code #88
+# BIDS Validator Original Issue Code #89
MalformedBval:
code: MALFORMED_BVAL
message: |
@@ -136,7 +152,7 @@ MalformedBval:
selectors:
- extension == ".bval"
-# BIDS Validator Original Issue Code #89
+# BIDS Validator Original Issue Code #90
SidecarWithoutDatafile:
code: SIDECAR_WITHOUT_DATAFILE
message: |
@@ -145,7 +161,7 @@ SidecarWithoutDatafile:
selectors:
- extension == ".json"
-# BIDS Validator Original Issue Code #90
+# BIDS Validator Original Issue Code #97
MissingSession:
code: MISSING_SESSION
message: |
diff --git a/src/schema/rules/files/common/core.yaml b/src/schema/rules/files/common/core.yaml
index cacc3e0c41..2fad28f430 100644
--- a/src/schema/rules/files/common/core.yaml
+++ b/src/schema/rules/files/common/core.yaml
@@ -5,6 +5,9 @@
dataset_description:
level: required
path: dataset_description.json
+CITATION:
+ level: optional
+ path: CITATION.cff
README:
level: recommended
stem: README
diff --git a/src/schema/rules/files/deriv/imaging.yaml b/src/schema/rules/files/deriv/imaging.yaml
index f5d82d570d..e037dcc10c 100644
--- a/src/schema/rules/files/deriv/imaging.yaml
+++ b/src/schema/rules/files/deriv/imaging.yaml
@@ -99,7 +99,6 @@ anat_parametric_discrete_segmentation:
- .json
- .tsv
-
anat_nonparametric_discrete_segmentation:
$ref: rules.files.raw.anat.nonparametric
suffixes:
diff --git a/src/schema/rules/files/raw/anat.yaml b/src/schema/rules/files/raw/anat.yaml
index 54edaccc22..2e534a2817 100644
--- a/src/schema/rules/files/raw/anat.yaml
+++ b/src/schema/rules/files/raw/anat.yaml
@@ -44,7 +44,6 @@ parametric:
- T1rho
- MWFmap
- MTVmap
- - PDT2map
- Chimap
- S0map
- M0map
diff --git a/src/schema/rules/files/raw/channels.yaml b/src/schema/rules/files/raw/channels.yaml
index 88c8e79aa8..2c74cc405e 100644
--- a/src/schema/rules/files/raw/channels.yaml
+++ b/src/schema/rules/files/raw/channels.yaml
@@ -25,6 +25,15 @@ channels__meg:
$ref: rules.files.raw.channels.channels.entities
processing: optional
+# motion has an additional entity available
+channels__motion:
+ $ref: rules.files.raw.channels.channels
+ datatypes:
+ - motion
+ entities:
+ $ref: rules.files.raw.channels.channels.entities
+ tracksys: required
+
coordsystem:
suffixes:
- coordsystem
diff --git a/src/schema/rules/files/raw/meg.yaml b/src/schema/rules/files/raw/meg.yaml
index c308cd8b9b..4bf7a42549 100644
--- a/src/schema/rules/files/raw/meg.yaml
+++ b/src/schema/rules/files/raw/meg.yaml
@@ -39,6 +39,7 @@ calibration:
session: optional
acquisition:
level: required
+ # TODO: Add definitions for these values.
enum:
- calibration
@@ -54,6 +55,7 @@ crosstalk:
session: optional
acquisition:
level: required
+ # TODO: Add definitions for these values.
enum:
- crosstalk
diff --git a/src/schema/rules/files/raw/motion.yaml b/src/schema/rules/files/raw/motion.yaml
new file mode 100644
index 0000000000..5ac18a4e92
--- /dev/null
+++ b/src/schema/rules/files/raw/motion.yaml
@@ -0,0 +1,16 @@
+---
+motion:
+ suffixes:
+ - motion
+ extensions:
+ - .tsv
+ - .json
+ datatypes:
+ - motion
+ entities:
+ subject: required
+ session: optional
+ task: required
+ tracksys: required
+ acquisition: optional
+ run: optional
diff --git a/src/schema/rules/files/raw/perf.yaml b/src/schema/rules/files/raw/perf.yaml
index 61cbc24721..2964cff31e 100644
--- a/src/schema/rules/files/raw/perf.yaml
+++ b/src/schema/rules/files/raw/perf.yaml
@@ -22,7 +22,6 @@ aslcontext:
- aslcontext
extensions:
- .tsv
- - .json
datatypes:
- perf
entities:
@@ -38,6 +37,8 @@ asllabeling:
- asllabeling
extensions:
- .jpg
+ - .png
+ - .tif
datatypes:
- perf
entities:
diff --git a/src/schema/rules/files/raw/task.yaml b/src/schema/rules/files/raw/task.yaml
index 1767814eb9..cda1e45dec 100644
--- a/src/schema/rules/files/raw/task.yaml
+++ b/src/schema/rules/files/raw/task.yaml
@@ -51,6 +51,14 @@ events__mri:
reconstruction: optional
direction: optional
+events__motion:
+ $ref: rules.files.raw.task.events
+ datatypes:
+ - motion
+ entities:
+ $ref: rules.files.raw.task.events.entities
+ tracksys: optional
+
events__pet:
$ref: rules.files.raw.task.events
datatypes:
@@ -90,6 +98,14 @@ timeseries__meg:
$ref: rules.files.raw.task.timeseries.entities
processing: optional
+timeseries__motion:
+ $ref: rules.files.raw.task.timeseries
+ datatypes:
+ - motion
+ entities:
+ $ref: rules.files.raw.task.timeseries.entities
+ tracksys: optional
+
timeseries__pet:
$ref: rules.files.raw.task.timeseries
datatypes:
diff --git a/src/schema/rules/modalities.yaml b/src/schema/rules/modalities.yaml
index 9c9c15eb0d..2f290ca014 100644
--- a/src/schema/rules/modalities.yaml
+++ b/src/schema/rules/modalities.yaml
@@ -25,6 +25,9 @@ pet:
micr:
datatypes:
- micr
+motion:
+ datatypes:
+ - motion
nirs:
datatypes:
- nirs
diff --git a/src/schema/rules/sidecars/beh.yaml b/src/schema/rules/sidecars/beh.yaml
index e9221986f6..1221bf84d7 100644
--- a/src/schema/rules/sidecars/beh.yaml
+++ b/src/schema/rules/sidecars/beh.yaml
@@ -6,7 +6,7 @@
---
# Metadata for either beh or events files
-BEHTabularData:
+BEHTaskInformation:
selectors:
- intersects([suffix], ["beh", "events"])
fields:
@@ -15,6 +15,11 @@ BEHTabularData:
TaskDescription: recommended
CogAtlasID: recommended
CogPOID: recommended
+
+BEHInstitutionInformation:
+ selectors:
+ - intersects([suffix], ["beh", "events"])
+ fields:
InstitutionName: recommended
InstitutionAddress: recommended
InstitutionalDepartmentName: recommended
diff --git a/src/schema/rules/sidecars/derivatives/common_derivatives.yaml b/src/schema/rules/sidecars/derivatives/common_derivatives.yaml
index b4e44bd638..5982b2e01d 100644
--- a/src/schema/rules/sidecars/derivatives/common_derivatives.yaml
+++ b/src/schema/rules/sidecars/derivatives/common_derivatives.yaml
@@ -71,6 +71,7 @@ ImageDerivatives:
- dataset.dataset_description.DatasetType == "derivative"
- 'intersects([modality], ["mri", "pet"])'
- 'match(extension, "^\.nii(\.gz)?$")'
+ - '!intersects([suffix], ["dseg", "probseg", "mask"])'
fields:
SkullStripped: required
diff --git a/src/schema/rules/sidecars/dwi.yaml b/src/schema/rules/sidecars/dwi.yaml
index 921e5c8b83..381f5db039 100644
--- a/src/schema/rules/sidecars/dwi.yaml
+++ b/src/schema/rules/sidecars/dwi.yaml
@@ -1,9 +1,3 @@
-#
-# Groups of related metadata fields
-#
-# Assumptions: never need disjunction of selectors
-# Assumptions: top-to-bottom overrides is sufficient logic
-
---
# Multipart (split) DWI schemes
# NOTE: I don't think this can be schemafied, since it depends on owner intent.
@@ -12,7 +6,7 @@ MRIDiffusionMultipart:
- modality == "mri"
- datatype == "dwi"
fields:
- MultipartID: required
+ MultipartID: optional
# Other recommended metadata
MRIDiffusionOtherMetadata:
diff --git a/src/schema/rules/sidecars/eeg.yaml b/src/schema/rules/sidecars/eeg.yaml
index ecc13f7c13..793bd38000 100644
--- a/src/schema/rules/sidecars/eeg.yaml
+++ b/src/schema/rules/sidecars/eeg.yaml
@@ -5,30 +5,28 @@
# Assumptions: top-to-bottom overrides is sufficient logic
---
-EEGGeneric:
+EEGHardware:
selectors:
- modality == "eeg"
- datatype == "eeg"
- suffix == "eeg"
fields:
- TaskName:
- level: required
- description_addendum: |
- A recommended convention is to name resting state task using labels
- beginning with `rest`.
+ Manufacturer: recommended
+ ManufacturersModelName: recommended
+ SoftwareVersions: recommended
+ DeviceSerialNumber: recommended
-EEGRecommended:
+EEGTaskInformation:
selectors:
- modality == "eeg"
- datatype == "eeg"
- suffix == "eeg"
fields:
- InstitutionName: recommended
- InstitutionAddress: recommended
- InstitutionalDepartmentName: recommended
- Manufacturer: recommended
- ManufacturersModelName: recommended
- SoftwareVersions: recommended
+ TaskName:
+ level: required
+ description_addendum: |
+ A recommended convention is to name resting state task using labels
+ beginning with `rest`.
TaskDescription: recommended
Instructions:
level: recommended
@@ -37,7 +35,16 @@ EEGRecommended:
distinguishing between eyes open and eyes closed paradigms.
CogAtlasID: recommended
CogPOID: recommended
- DeviceSerialNumber: recommended
+
+EEGInstitutionInformation:
+ selectors:
+ - modality == "eeg"
+ - datatype == "eeg"
+ - suffix == "eeg"
+ fields:
+ InstitutionName: recommended
+ InstitutionAddress: recommended
+ InstitutionalDepartmentName: recommended
EEGRequired:
selectors:
@@ -54,7 +61,7 @@ EEGRequired:
PowerLineFrequency: required
SoftwareFilters: required
-EEGMoreRecommended:
+EEGRecommended:
selectors:
- modality == "eeg"
- datatype == "eeg"
@@ -66,7 +73,7 @@ EEGMoreRecommended:
ECGChannelCount: recommended
EMGChannelCount: recommended
EOGChannelCount: recommended
- MiscChannelCount: recommended
+ MISCChannelCount: recommended
TriggerChannelCount: recommended
RecordingDuration: recommended
RecordingType: recommended
diff --git a/src/schema/rules/sidecars/fmap.yaml b/src/schema/rules/sidecars/fmap.yaml
index ddb90f3949..adcc2ac692 100644
--- a/src/schema/rules/sidecars/fmap.yaml
+++ b/src/schema/rules/sidecars/fmap.yaml
@@ -34,22 +34,6 @@ MRIFieldmapPhaseDifferencePhasediff:
EchoTime1: required
EchoTime2: required
-MRIFieldmapPhaseDifferenceMagnitude1:
- selectors:
- - modality == "mri"
- - datatype == "fmap"
- - suffix == "magnitude1"
- fields:
- EchoTime1: required
-
-MRIFieldmapPhaseDifferenceMagnitude2:
- selectors:
- - modality == "mri"
- - datatype == "fmap"
- - suffix == "magnitude2"
- fields:
- EchoTime2: required
-
# Case 2: Two phase maps and two magnitude images
# NOTE: Need to check for presence of related files.
# For example, magnitude1 needs EchoTime__fmap only if phase1 file exists,
@@ -58,7 +42,7 @@ MRIFieldmapTwoPhase:
selectors:
- modality == "mri"
- datatype == "fmap"
- - intersects([suffix], ["phase1", "phase2", "magnitude1", "magnitude2"])
+ - intersects([suffix], ["phase1", "phase2"])
fields:
EchoTime__fmap: required
@@ -67,7 +51,7 @@ MRIFieldmapDirectFieldMapping:
selectors:
- modality == "mri"
- datatype == "fmap"
- - intersects([suffix], ["phase", "fieldmap"])
+ - suffix == "fieldmap"
fields:
Units:
level: required
diff --git a/src/schema/rules/sidecars/func.yaml b/src/schema/rules/sidecars/func.yaml
index 1c587f9549..1f487add7b 100644
--- a/src/schema/rules/sidecars/func.yaml
+++ b/src/schema/rules/sidecars/func.yaml
@@ -63,57 +63,6 @@ MRIFuncTimingParameters:
The field 'VolumeTiming' requires 'AcquisitionDuration' or 'SliceTiming' to be defined.
DelayAfterTrigger: recommended
-# The mutual exclusion table, spread across 5 definitions
-# NOTE: This introduces a prohibited level
-MRIFuncTimingParametersMutualExclusion1:
- selectors:
- - modality == "mri"
- - datatype == "func"
- - '"RepetitionTime" in sidecar'
- fields:
- AcquisitionDuration: prohibited
- VolumeTiming: prohibited
-
-MRIFuncTimingParametersMutualExclusion2:
- selectors:
- - modality == "mri"
- - datatype == "func"
- - '"SliceTiming" in sidecar'
- - '"VolumeTiming" in sidecar'
- fields:
- RepetitionTime: prohibited
- DelayTime: prohibited
-
-MRIFuncTimingParametersMutualExclusion3:
- selectors:
- - modality == "mri"
- - datatype == "func"
- - '"AcquisitionDuration" in sidecar'
- - '"VolumeTiming" in sidecar'
- fields:
- RepetitionTime: prohibited
- DelayTime: prohibited
-
-MRIFuncTimingParametersMutualExclusion4:
- selectors:
- - modality == "mri"
- - datatype == "func"
- - '"RepetitionTime" in sidecar'
- - '"SliceTiming" in sidecar'
- fields:
- AcquisitionDuration: prohibited
- VolumeTiming: prohibited
-
-MRIFuncTimingParametersMutualExclusion5:
- selectors:
- - modality == "mri"
- - datatype == "func"
- - '"RepetitionTime" in sidecar'
- - '"DelayTime" in sidecar'
- fields:
- AcquisitionDuration: prohibited
- VolumeTiming: prohibited
-
# fMRI task information
MRIFuncTaskInformation:
selectors:
diff --git a/src/schema/rules/sidecars/ieeg.yaml b/src/schema/rules/sidecars/ieeg.yaml
index 9dd2cb062e..799931e4f3 100644
--- a/src/schema/rules/sidecars/ieeg.yaml
+++ b/src/schema/rules/sidecars/ieeg.yaml
@@ -5,32 +5,30 @@
# Assumptions: top-to-bottom overrides is sufficient logic
---
-iEEGGeneric:
+iEEGHardware:
selectors:
- modality == "ieeg"
- datatype == "ieeg"
- suffix == "ieeg"
fields:
- TaskName:
- level: required
- description_addendum: |
- A recommended convention is to name resting state task using labels
- beginning with `rest`.
+ Manufacturer:
+ level: recommended
+ description_addendum: For example, `"TDT"`, `"Blackrock"`.
+ ManufacturersModelName: recommended
+ SoftwareVersions: recommended
+ DeviceSerialNumber: recommended
-iEEGRecommended:
+iEEGTaskInformation:
selectors:
- modality == "ieeg"
- datatype == "ieeg"
- suffix == "ieeg"
fields:
- InstitutionName: recommended
- InstitutionAddress: recommended
- InstitutionalDepartmentName: recommended
- Manufacturer:
- level: recommended
- description_addendum: For example, `"TDT"`, `"Blackrock"`.
- ManufacturersModelName: recommended
- SoftwareVersions: recommended
+ TaskName:
+ level: required
+ description_addendum: |
+ A recommended convention is to name resting state task using labels
+ beginning with `rest`.
TaskDescription: recommended
Instructions:
level: recommended
@@ -39,7 +37,16 @@ iEEGRecommended:
distinguishing between eyes open and eyes closed paradigms.
CogAtlasID: recommended
CogPOID: recommended
- DeviceSerialNumber: recommended
+
+iEEGInstitutionInformation:
+ selectors:
+ - modality == "ieeg"
+ - datatype == "ieeg"
+ - suffix == "ieeg"
+ fields:
+ InstitutionName: recommended
+ InstitutionAddress: recommended
+ InstitutionalDepartmentName: recommended
# Specific iEEG fields MUST be present
iEEGRequired:
@@ -58,7 +65,7 @@ iEEGRequired:
SoftwareFilters: required
# Specific iEEG fields SHOULD be present
-iEEGMoreRecommended:
+iEEGRecommended:
selectors:
- modality == "ieeg"
- datatype == "ieeg"
diff --git a/src/schema/rules/sidecars/meg.yaml b/src/schema/rules/sidecars/meg.yaml
index 41dae7a40f..891df1c035 100644
--- a/src/schema/rules/sidecars/meg.yaml
+++ b/src/schema/rules/sidecars/meg.yaml
@@ -8,27 +8,12 @@
# Magnetoencephalography
# Sidecar JSON (*_meg.json)
-MEGGeneric:
+MEGHardware:
selectors:
- modality == "meg"
- datatype == "meg"
- suffix == "meg"
fields:
- TaskName:
- level: required
- description_addendum: |
- A recommended convention is to name resting state task using labels
- beginning with `rest`.
-
-MEGRecommended:
- selectors:
- - modality == "meg"
- - datatype == "meg"
- - suffix == "meg"
- fields:
- InstitutionName: recommended
- InstitutionAddress: recommended
- InstitutionalDepartmentName: recommended
Manufacturer:
level: recommended
description_addendum: |
@@ -43,6 +28,19 @@ MEGRecommended:
See the [MEG Systems Appendix](SPEC_ROOT/appendices/meg-systems.md) for
preferred names.
SoftwareVersions: recommended
+ DeviceSerialNumber: recommended
+
+MEGTaskInformation:
+ selectors:
+ - modality == "meg"
+ - datatype == "meg"
+ - suffix == "meg"
+ fields:
+ TaskName:
+ level: required
+ description_addendum: |
+ A recommended convention is to name resting state task using labels
+ beginning with `rest`.
TaskDescription: recommended
Instructions:
level: recommended
@@ -51,7 +49,16 @@ MEGRecommended:
distinguishing between eyes open and eyes closed paradigms.
CogAtlasID: recommended
CogPOID: recommended
- DeviceSerialNumber: recommended
+
+MEGInstitutionInformation:
+ selectors:
+ - modality == "ieeg"
+ - datatype == "ieeg"
+ - suffix == "ieeg"
+ fields:
+ InstitutionName: recommended
+ InstitutionAddress: recommended
+ InstitutionalDepartmentName: recommended
# Specific MEG fields MUST be present
MEGRequired:
@@ -72,7 +79,7 @@ MEGRequired:
DigitizedHeadPoints: required
# Specific MEG fields SHOULD be present
-MEGMoreRecommended:
+MEGRecommended:
selectors:
- modality == "meg"
- datatype == "meg"
diff --git a/src/schema/rules/sidecars/micr.yaml b/src/schema/rules/sidecars/micr.yaml
index d3397c0007..1834e669c2 100644
--- a/src/schema/rules/sidecars/micr.yaml
+++ b/src/schema/rules/sidecars/micr.yaml
@@ -6,16 +6,23 @@
---
# Device Hardware
-MicroscopyDeviceHardware:
+MicroscopyHardware:
selectors:
- modality == "micr"
- datatype == "micr"
+ - suffix != "photo"
fields:
Manufacturer: recommended
ManufacturersModelName: recommended
DeviceSerialNumber: recommended
StationName: recommended
SoftwareVersions: recommended
+
+MicroscopyInstitutionInformation:
+ selectors:
+ - modality == "micr"
+ - datatype == "micr"
+ fields:
InstitutionName: recommended
InstitutionAddress: recommended
InstitutionalDepartmentName: recommended
@@ -25,6 +32,7 @@ MicroscopyImageAcquisition:
selectors:
- modality == "micr"
- datatype == "micr"
+ - suffix != "photo"
fields:
PixelSize: required
PixelSizeUnits: required
@@ -39,6 +47,7 @@ MicroscopySample:
selectors:
- modality == "micr"
- datatype == "micr"
+ - suffix != "photo"
fields:
BodyPart:
level: recommended
@@ -64,6 +73,7 @@ MicroscopyChunkTransformations:
selectors:
- modality == "micr"
- datatype == "micr"
+ - suffix != "photo"
- '"chunk" in entities'
fields:
ChunkTransformationMatrix:
@@ -74,6 +84,7 @@ MicroscopyChunkTransformationsMatrixAxis:
selectors:
- modality == "micr"
- datatype == "micr"
+ - suffix != "photo"
- '"chunk" in entities'
- '"ChunkTransformationMatrix" in sidecar'
fields:
diff --git a/src/schema/rules/sidecars/motion.yaml b/src/schema/rules/sidecars/motion.yaml
new file mode 100644
index 0000000000..981e0b3ad7
--- /dev/null
+++ b/src/schema/rules/sidecars/motion.yaml
@@ -0,0 +1,84 @@
+#
+# Groups of related metadata fields
+#
+# Assumptions: never need disjunction of selectors
+# Assumptions: top-to-bottom overrides is sufficient logic
+
+---
+motionHardware:
+ selectors:
+ - modality == "motion"
+ - datatype == "motion"
+ - suffix == "motion"
+ fields:
+ DeviceSerialNumber: recommended
+ Manufacturer: recommended
+ ManufacturersModelName: recommended
+ SoftwareVersions: recommended
+
+motionInstitutionInformation:
+ selectors:
+ - modality == "motion"
+ - datatype == "motion"
+ - suffix == "motion"
+ fields:
+ InstitutionName: recommended
+ InstitutionAddress: recommended
+ InstitutionalDepartmentName: recommended
+
+motionTaskInformation:
+ selectors:
+ - modality == "motion"
+ - datatype == "motion"
+ - suffix == "motion"
+ fields:
+ TaskName:
+ level: required
+ description_addendum: |
+ Task names for motion datasets usually contain information
+ about the specific motion task (for example, "`walking`").
+ TaskDescription: recommended
+ Instructions: recommended
+
+motionRequired:
+ selectors:
+ - modality == "motion"
+ - datatype == "motion"
+ - suffix == "motion"
+ fields:
+ SamplingFrequency:
+ level: required
+ description_addendum: |
+ This field refers to the nominal sampling frequency. For motion data one can use
+ "`SamplingFrequencyEffective`" if nominal and effective differ.
+ The sampling frequency of data channels that deviate from the main (nominal) sampling
+ frequency SHOULD be specified in the "`_tracksys-_channels.tsv`" file.
+
+motionRecommended:
+ selectors:
+ - modality == "motion"
+ - datatype == "motion"
+ - suffix == "motion"
+ fields:
+ ACCELChannelCount: recommended
+ ANGACCELChannelCount: recommended
+ GYROChannelCount: recommended
+ JNTANGChannelCount: recommended
+ LATENCYChannelCount: recommended
+ MAGNChannelCount: recommended
+ MISCChannelCount: recommended
+ MissingValues: recommended
+ MotionChannelCount: recommended
+ ORNTChannelCount: recommended
+ POSChannelCount: recommended
+ RotationOrder: recommended
+ RotationRule: recommended
+ SamplingFrequencyEffective:
+ level: recommended
+ description_addendum: |
+ If not available, the field takes value `n/a`.
+ SpatialAxes: recommended
+ SubjectArtefactDescription: recommended
+ TrackedPointsCount: recommended
+ TrackingSystemName: optional
+ VELChannelCount: recommended
diff --git a/src/schema/rules/sidecars/mri.yaml b/src/schema/rules/sidecars/mri.yaml
index 61ce836e2c..63e4a900df 100644
--- a/src/schema/rules/sidecars/mri.yaml
+++ b/src/schema/rules/sidecars/mri.yaml
@@ -6,7 +6,7 @@
---
# MRI Common metadata fields
-MRIScannerHardware:
+MRIHardware:
selectors:
- modality == "mri"
fields:
@@ -79,6 +79,7 @@ ASLMRISequenceSpecifics:
selectors:
- modality == "mri"
- datatype == "perf"
+ - suffix == "asl"
fields:
MRAcquisitionType: required
diff --git a/src/schema/rules/sidecars/nirs.yaml b/src/schema/rules/sidecars/nirs.yaml
index 0cb67d4397..08b02f3dbe 100644
--- a/src/schema/rules/sidecars/nirs.yaml
+++ b/src/schema/rules/sidecars/nirs.yaml
@@ -93,27 +93,46 @@ FiducialsCoordinateSystemDescriptionReq:
fields:
FiducialsCoordinateSystemDescription: required
-NirsBase:
+NirsHardware:
selectors:
- datatype == "nirs"
- suffix == "nirs"
fields:
- TaskName: required
- InstitutionName: recommended
- InstitutionAddress: recommended
Manufacturer: recommended
ManufacturersModelName: recommended
SoftwareVersions: recommended
- TaskDescription: recommended
- Instructions: recommended
- CogAtlasID: recommended
- CogPOID: recommended
DeviceSerialNumber: recommended
+
+NirsBase:
+ selectors:
+ - datatype == "nirs"
+ - suffix == "nirs"
+ fields:
RecordingDuration: recommended
HeadCircumference: recommended
HardwareFilters: recommended
SubjectArtefactDescription: recommended
+NirsTaskInformation:
+ selectors:
+ - datatype == "nirs"
+ - suffix == "nirs"
+ fields:
+ TaskName: required
+ TaskDescription: recommended
+ Instructions: recommended
+ CogAtlasID: recommended
+ CogPOID: recommended
+
+NirsInstitutionInformation:
+ selectors:
+ - datatype == "nirs"
+ - suffix == "nirs"
+ fields:
+ InstitutionName: recommended
+ InstitutionAddress: recommended
+ InstitutionalDepartmentName: recommended
+
NirsRequired:
selectors:
- datatype == "nirs"
diff --git a/src/schema/rules/sidecars/pet.yaml b/src/schema/rules/sidecars/pet.yaml
index feb1db9053..353389de71 100644
--- a/src/schema/rules/sidecars/pet.yaml
+++ b/src/schema/rules/sidecars/pet.yaml
@@ -1,6 +1,6 @@
---
# PET common metadata fields
-PETScannerHardware:
+PETHardware:
selectors:
- modality == "pet"
- suffix == "pet"
@@ -16,6 +16,15 @@ PETScannerHardware:
description_addendum: |
SI unit for radioactivity (Becquerel) should be used (for example, "Bq/mL").
Corresponds to DICOM Tag 0054, 1001 `Units`.
+ BodyPart:
+ level: recommended
+ description_addendum: Corresponds to DICOM Tag 0018, 0015 `Body Part Examined`.
+
+PETInstitutionInformation:
+ selectors:
+ - modality == "pet"
+ - suffix == "pet"
+ fields:
InstitutionName:
level: recommended
description_addendum: Corresponds to DICOM Tag 0008, 0080 `InstitutionName`.
@@ -25,9 +34,6 @@ PETScannerHardware:
InstitutionalDepartmentName:
level: recommended
description_addendum: Corresponds to DICOM Tag 0008, 1040 `Institutional Department Name`.
- BodyPart:
- level: recommended
- description_addendum: Corresponds to DICOM Tag 0018, 0015 `Body Part Examined`.
PETRadioChemistry:
selectors:
diff --git a/src/schema/rules/tabular_data/eeg.yaml b/src/schema/rules/tabular_data/eeg.yaml
index fd1e237425..ae31c5f6d9 100644
--- a/src/schema/rules/tabular_data/eeg.yaml
+++ b/src/schema/rules/tabular_data/eeg.yaml
@@ -6,11 +6,11 @@ EEGChannels:
- extension == ".tsv"
initial_columns:
- name__channels
- - type__eeg_channels
+ - type__channels
- units
columns:
name__channels: required
- type__eeg_channels: required
+ type__channels: required
units: required
description: optional
sampling_frequency: optional
diff --git a/src/schema/rules/tabular_data/ieeg.yaml b/src/schema/rules/tabular_data/ieeg.yaml
index 8009cf07ad..ad40a98098 100644
--- a/src/schema/rules/tabular_data/ieeg.yaml
+++ b/src/schema/rules/tabular_data/ieeg.yaml
@@ -6,8 +6,10 @@ iEEGChannels:
- extension == ".tsv"
initial_columns:
- name__channels
- - type__ieeg_channels
+ - type__channels
- units
+ - low_cutoff
+ - high_cutoff
columns:
name__channels:
level: required
@@ -15,7 +17,7 @@ iEEGChannels:
When a corresponding electrode is specified in `_electrodes.tsv`,
the name of that electrode MAY be specified here and the reference electrode
name MAY be provided in the `reference` column.
- type__ieeg_channels: required
+ type__channels: required
units: required
low_cutoff: required
high_cutoff: required
@@ -42,6 +44,7 @@ iEEGElectrodes:
- x
- y
- z
+ - size
columns:
name__electrodes: required
x: required
diff --git a/src/schema/rules/tabular_data/meg.yaml b/src/schema/rules/tabular_data/meg.yaml
index 0f0d6041cd..ff8e5a7596 100644
--- a/src/schema/rules/tabular_data/meg.yaml
+++ b/src/schema/rules/tabular_data/meg.yaml
@@ -6,11 +6,11 @@ MEGChannels:
- extension == ".tsv"
initial_columns:
- name__channels
- - type__meg_channels
+ - type__channels
- units
columns:
name__channels: required
- type__meg_channels: required
+ type__channels: required
units: required
description: optional
sampling_frequency: optional
diff --git a/src/schema/rules/tabular_data/motion.yaml b/src/schema/rules/tabular_data/motion.yaml
new file mode 100644
index 0000000000..cd7e809ae9
--- /dev/null
+++ b/src/schema/rules/tabular_data/motion.yaml
@@ -0,0 +1,24 @@
+---
+motionChannels:
+ selectors:
+ - datatype == "motion"
+ - suffix == "channels"
+ - extension == ".tsv"
+ initial_columns:
+ - name__channels
+ - component
+ - type__channels
+ - tracked_point__channels
+ - units__motion
+ columns:
+ name__channels: required
+ component: required
+ type__channels: required
+ tracked_point__channels: required
+ units__motion: required
+ placement__motion: recommended
+ description: optional
+ sampling_frequency: optional
+ status: optional
+ status_description: optional
+ additional_columns: allowed_if_defined
diff --git a/src/schema/rules/tabular_data/nirs.yaml b/src/schema/rules/tabular_data/nirs.yaml
index 95cd16c844..117116abd3 100644
--- a/src/schema/rules/tabular_data/nirs.yaml
+++ b/src/schema/rules/tabular_data/nirs.yaml
@@ -6,14 +6,14 @@ nirsChannels:
- extension == ".tsv"
initial_columns:
- name__channels
- - type__nirs_channels
+ - type__channels
- source__channels
- detector__channels
- wavelength_nominal
- units__nirs
columns:
name__channels: required
- type__nirs_channels: required
+ type__channels: required
source__channels: required
detector__channels: required
wavelength_nominal: required
@@ -21,7 +21,7 @@ nirsChannels:
sampling_frequency:
level: optional
level_addendum: required if `SamplingFrequency` is `n/a` in `_nirs.json`
- orientation_component:
+ component:
level: optional
level_addendum: required if `type` is `ACCEL`, `GYRO` or `MAGN`
wavelength_actual: optional
diff --git a/src/schema/rules/tabular_data/pet.yaml b/src/schema/rules/tabular_data/pet.yaml
index ec58aba2a7..b00d9f1e88 100644
--- a/src/schema/rules/tabular_data/pet.yaml
+++ b/src/schema/rules/tabular_data/pet.yaml
@@ -37,6 +37,7 @@ BloodMetabolite:
- suffix == "blood"
- extension == ".tsv"
- '"MetaboliteAvail" in sidecar'
+ - sidecar.MetaboliteAvail == true
columns:
metabolite_parent_fraction: required
metabolite_polar_fraction: recommended
diff --git a/src/schema/rules/tabular_data/task.yaml b/src/schema/rules/tabular_data/task.yaml
index 0b8c3a2166..1b07b24936 100644
--- a/src/schema/rules/tabular_data/task.yaml
+++ b/src/schema/rules/tabular_data/task.yaml
@@ -6,10 +6,8 @@ TaskEvents:
columns:
onset: required
duration: required
- sample: optional
trial_type: optional
response_time: optional
- value: optional
HED: optional
stim_file: optional
additional_columns: allowed
diff --git a/tools/add_contributors.py b/tools/add_contributors.py
new file mode 100644
index 0000000000..8c3891d26e
--- /dev/null
+++ b/tools/add_contributors.py
@@ -0,0 +1,567 @@
+"""Add new contributors listed in new_contributors.tsv to .tributors file
+
+The tributor file is then used to update
+- the CITATION.cff file
+- the .all-contributorsrc file
+- TODO: the table of contributors in the appendix of the spec
+
+Contrary to the typical .tributors file,
+the one here also centralizes the contributions
+that would otherwise be listed in the .all-contributorsrc file.
+
+This can also be used to update all files if new_contributors.tsv is empty.
+"""
+
+# TODO: handle the following cases
+# - ORCID
+# - affiliation
+# - getting avatars
+
+from __future__ import annotations
+
+import json
+import logging
+from collections import OrderedDict
+from pathlib import Path
+from typing import Optional
+
+import emoji
+import pandas as pd
+import requests
+import ruamel.yaml
+from cffconvert.cli.create_citation import create_citation
+from cffconvert.cli.validate_or_write_output import validate_or_write_output
+from rich.logging import RichHandler
+from rich.traceback import install
+
+INPUT_FILE = Path(__file__).parent / "new_contributors.tsv"
+
+LOG_LEVEL = "DEBUG" # 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'
+
+# Set to True to update the avatars
+# update with your github username and path to a file with github token
+UPDATE_AVATARS = False
+GH_USERNAME = "Remi-Gau"
+TOKEN_FILE = None
+RICH_STACKTRACE = False
+
+# Set to True to use some of the dummy data in the "new_contributors.tsv"
+TEST = True
+
+
+def logger(log_level="INFO") -> logging.Logger:
+ """Create log."""
+ # let rich print the traceback
+ FORMAT = "%(asctime)s - %(message)s"
+ if RICH_STACKTRACE:
+ install(show_locals=True)
+ logging.basicConfig(
+ level=log_level, format=FORMAT, datefmt="[%X]", handlers=[RichHandler()]
+ )
+ else:
+ logging.basicConfig(
+ level=log_level,
+ format=FORMAT,
+ datefmt="[%X]",
+ )
+ return logging.getLogger("rich")
+
+
+log = logger(log_level=LOG_LEVEL)
+
+yaml = ruamel.yaml.YAML()
+yaml.indent(mapping=2, sequence=4, offset=2)
+
+
+def root_dir() -> Path:
+ return Path(__file__).parent.parent
+
+
+def emoji_map(reverse=False) -> dict[str, str]:
+ # https://allcontributors.org/docs/en/emoji-key
+ if reverse:
+ tmp = emoji_map()
+ return {v: k for k, v in tmp.items()}
+
+ return {
+ "code": ":laptop:",
+ "doc": ":open_book:",
+ "ideas": ":thinking_face:",
+ "bug": ":bug:",
+ "example": ":light_bulb:",
+ "question": ":speech_balloon:",
+ "review": ":eyes:",
+ "plugin": ":electric_plug:",
+ "fundingFinding": ":magnifying_glass_tilted_left:",
+ "talk": ":loudspeaker:",
+ "design": ":artist_palette:",
+ "data": ":input_symbols:",
+ "tool": ":wrench:",
+ "projectManagement": ":tear-off_calendar:",
+ "test": ":warning:",
+ "eventOrganizing": ":clipboard:",
+ "infra": ":metro:",
+ "userTesting": ":notebook:",
+ "video": ":video_camera:",
+ "blog": ":memo:",
+ "content": ":fountain_pen:",
+ "tutorial": ":check_mark_button:",
+ "maintenance": ":construction:",
+ "financial": ":dollar_banknote:",
+ }
+
+
+def return_this_contributor(
+ df: pd.DataFrame, name: str, contribution_needed=True
+) -> dict[str, Optional[str]]:
+ """Get and validate the data for a given contributor from a panda dataframe"""
+ name = name.strip()
+
+ mask = df.name == name
+
+ github = df[mask].github.values[0]
+ if pd.isna(github) or not isinstance(github, (str)):
+ github = None
+
+ github_username = None
+ if github is not None:
+ github_username = github.replace("https://github.com/", "")
+ if github_username is None:
+ github_username = name.lower().replace(" ", "_")
+
+ contributions = df[mask].contributions.values[0]
+ log.debug(f"contributions for {name}: '{contributions}'")
+ if pd.isna(contributions):
+ contributions is None
+ if contribution_needed and contributions is None:
+ raise ValueError(f"Contributions for {name} not defined in input file.")
+ if contributions is not None:
+ contributions = listify_contributions(contributions)
+ validate_contributions(contributions, name)
+ contributions = canonicalize_contributions(contributions)
+ log.debug(f"kept contributions for {name}: {contributions}")
+
+ orcid = df[mask].orcid.values[0]
+ if pd.isna(orcid) or not isinstance(orcid, (str)):
+ orcid = None
+ if orcid is not None:
+ orcid = orcid.replace("http://", "https://")
+
+ website = df[mask].website.values[0]
+ affiliation = df[mask].affiliation.values[0]
+ email = df[mask].email.values[0]
+
+ this_contributor = {
+ "name": name,
+ "github": github,
+ "github_username": github_username,
+ "blog": website,
+ "affiliation": affiliation,
+ "orcid": orcid,
+ "email": email,
+ "contributions": contributions,
+ }
+
+ # light validation / clean up
+ for key, value in this_contributor.items():
+ if value is None:
+ continue
+ if not isinstance(this_contributor[key], (list)) and pd.isna(
+ this_contributor[key]
+ ):
+ this_contributor[key] = None
+ elif isinstance(this_contributor[key], (str)):
+ this_contributor[key] = this_contributor[key].strip()
+ elif all(pd.isna(x) for x in this_contributor[key]):
+ this_contributor[key] = None
+
+ tmp = this_contributor.copy()
+ for key in this_contributor:
+ if tmp[key] is None:
+ tmp.pop(key)
+
+ return tmp
+
+
+def listify_contributions(contributions: str):
+ contributions = [x.strip() for x in contributions.split(",")]
+ tmp = []
+ for contribution_ in contributions:
+ tmp.extend(iter(contribution_.split(" ")))
+ return tmp
+
+
+def validate_contributions(contributions: list, name: str):
+ allowed_contributions = list(emoji_map().keys())
+ allowed_emojis = [emoji.emojize(x) for x in list(emoji_map().values())]
+ allowed_contributions += allowed_emojis
+ if any(x for x in contributions if x.replace(":", "") not in allowed_contributions):
+ raise ValueError(
+ f"Contributions must be one of {allowed_contributions}.\n"
+ f" Got '{contributions}' for {name}."
+ )
+
+
+def canonicalize_contributions(contributions: list) -> list:
+ allowed_emojis = [emoji.emojize(x) for x in list(emoji_map().values())]
+ for contribution_ in contributions:
+ if contribution_ in allowed_emojis:
+ demoji = emoji.demojize(contribution_)
+ contributions[contributions.index(contribution_)] = emoji_map(
+ reverse=True
+ ).get(demoji)
+ for contribution_ in contributions:
+ contributions[contributions.index(contribution_)] = contribution_.replace(
+ ":", ""
+ )
+ contributions = sorted(list(set(contributions)))
+
+ return contributions
+
+
+def update_key(
+ contributor: dict[str, str], key: str, value: str | None
+) -> dict[str, str]:
+ """Update a key in a contributor dict if the value is not None."""
+ if value is None:
+ return contributor
+ log.info(f"updating {contributor['name']} - {key}")
+ if key == "contributions":
+ for contribution in value:
+ if contribution not in contributor[key]:
+ contributor[key].append(contribution)
+ else:
+ contributor[key] = value
+ return contributor
+
+
+"""TRIBUTORS"""
+
+
+def load_tributors(tributors_file: Path) -> dict:
+ """Load .tributors file."""
+ with open(tributors_file, "r", encoding="utf8") as tributors_file:
+ return json.load(tributors_file)
+
+
+def write_tributors(tributors_file: Path, tributors: dict[str, dict]) -> None:
+ """Write .tributors file."""
+ tributors = sort_tributors(tributors)
+ with open(tributors_file, "w", encoding="utf8") as output_file:
+ json.dump(tributors, output_file, indent=4, ensure_ascii=False)
+
+
+def return_missing_from_tributors(tributors_file: Path, names: list[str]) -> list[str]:
+ """Return list of names that are in the input file but not in the .tributors file."""
+ tributors = load_tributors(tributors_file)
+ tributors_names = [tributors[x]["name"] for x in tributors]
+ for i, name in enumerate(names):
+ names[i] = name.strip()
+ missing_from_tributors = set(names) - set(tributors_names)
+ return sorted(list(missing_from_tributors))
+
+
+def sort_tributors(tributors: dict[str, dict]) -> dict[str, dict]:
+ """Sort tributors alphabetically by name of contributor."""
+ for key in tributors:
+ tributors[key] = dict(OrderedDict(sorted(tributors[key].items())))
+ return dict(sorted(tributors.items(), key=lambda item: item[1]["name"]))
+
+
+def add_to_tributors(
+ tributors: dict[str, dict], this_contributor: dict[str, str]
+) -> dict[str, dict]:
+ """Add contributor to .tributors"""
+ name = this_contributor.get("name")
+
+ tributors_names = [tributors[x]["name"] for x in tributors]
+ if name in tributors_names:
+ return tributors
+
+ log.info(f"adding {name}")
+
+ user_login = this_contributor.get("github_username")
+ this_contributor.pop("github_username", None)
+
+ tributors[user_login] = this_contributor
+ return tributors
+
+
+def update_tributors(
+ tributors: dict[str, dict], this_contributor: dict[str, str]
+) -> dict[str, dict]:
+ tributors_names = [tributors[x]["name"] for x in tributors]
+
+ name = this_contributor["name"]
+
+ if name not in tributors_names:
+ return tributors
+
+ index_tributor = tributors_names.index(this_contributor["name"])
+ tributors_keys = list(tributors.keys())
+ key_tributor = tributors_keys[index_tributor]
+
+ for key, value in this_contributor.items():
+ if key == "github_username" or value is None:
+ continue
+
+ if key not in tributors[key_tributor]:
+ tributors[key_tributor] = update_key(
+ contributor=tributors[key_tributor],
+ key=key,
+ value=value,
+ )
+
+ if tributors[key_tributor][key] != value:
+ tributors[key_tributor] = update_key(
+ contributor=tributors[key_tributor],
+ key=key,
+ value=value,
+ )
+
+ return tributors
+
+
+"""ALCONTRIB"""
+
+
+def load_allcontrib(allcontrib_file: Path) -> None:
+ """Load .all-contributorsrc file."""
+ with open(allcontrib_file, "r", encoding="utf8") as input_file:
+ return json.load(input_file)
+
+
+def write_allcontrib(allcontrib_file: Path, allcontrib: dict) -> None:
+ """Write .all-contributorsrc file."""
+ allcontrib = sort_all_contrib(allcontrib)
+ with open(allcontrib_file, "w", encoding="utf8") as output_file:
+ json.dump(allcontrib, output_file, indent=4, ensure_ascii=False)
+
+
+def sort_all_contrib(allcontrib: dict) -> dict:
+ """Sort .all-contributorsrc file alphabetically by name of contributor."""
+ for i, contrib in enumerate(allcontrib["contributors"]):
+ allcontrib["contributors"][i] = dict(OrderedDict(sorted(contrib.items())))
+ allcontrib["contributors"] = sorted(
+ allcontrib["contributors"], key=lambda x: x["name"]
+ )
+ return allcontrib
+
+
+def update_allcontrib(allcontrib: dict, this_contributor: dict[str, str]) -> dict:
+ """Add a contributor if not in .all-contributorsrc, or update if already in."""
+ allcontrib_names = [x["name"] for x in allcontrib["contributors"]]
+
+ if this_contributor["name"] not in allcontrib_names:
+ log.info(f"adding {this_contributor['name']}")
+ allcontrib["contributors"].append(this_contributor)
+ return allcontrib
+
+ index_allcontrib = allcontrib_names.index(this_contributor["name"])
+
+ for key, value in this_contributor.items():
+ if key not in allcontrib["contributors"][index_allcontrib]:
+ allcontrib["contributors"][index_allcontrib] = update_key(
+ contributor=allcontrib["contributors"][index_allcontrib],
+ key=key,
+ value=value,
+ )
+
+ if allcontrib["contributors"][index_allcontrib][key] != value:
+ allcontrib["contributors"][index_allcontrib] = update_key(
+ contributor=allcontrib["contributors"][index_allcontrib],
+ key=key,
+ value=value,
+ )
+
+ return allcontrib
+
+
+def get_gh_avatar(gh_username: str, auth_username: str, auth_token: str) -> str:
+ """Return url of github avatar."""
+ avatar_url = None
+
+ if gh_username is None:
+ return avatar_url
+
+ log.info(f"getting avatar: {gh_username}")
+ url = f"https://api.github.com/users/{gh_username}"
+ response = requests.get(url, auth=(auth_username, auth_token))
+ if response.status_code == 200:
+ avatar_url = response.json()["avatar_url"]
+
+ return avatar_url
+
+
+def rename_keys_for_allcontrib(this_contributor: dict[str, str]) -> dict[str, str]:
+ """Rename some keys to adapt to all-contributors."""
+ renaming_map = {
+ "name": "name",
+ "avatar_url": "avatar_url",
+ "github_username": "login",
+ "login": "login",
+ "blog": "profile",
+ "contributions": "contributions",
+ }
+
+ renamed = {
+ renaming_map[key]: this_contributor[key]
+ for key in this_contributor
+ if key in renaming_map
+ }
+ return renamed
+
+
+"""CITATION.CFF"""
+
+
+def load_citation(citation_file: Path) -> dict:
+ """Load CITATION.CFF file."""
+ with open(citation_file, "r", encoding="utf8") as input_file:
+ return yaml.load(input_file)
+
+
+def write_citation(citation_file: Path, citation: dict) -> None:
+ """Write CITATION.CFF file."""
+ with open(citation_file, "w", encoding="utf8") as output_file:
+ return yaml.dump(citation, output_file)
+
+
+def return_author_list_for_cff(tributors_file: Path) -> list[dict[str, str]]:
+ """Create an dict to be used for the authors in the CITATION.CFF file."""
+ tributors = load_tributors(tributors_file)
+
+ author_list = []
+
+ for _, tributor in enumerate(tributors, start=1):
+ this_tributor = tributors[tributor]
+
+ name = this_tributor["name"]
+
+ # take as given name the first part of the name and anything ending with a dot
+ # suboptimal for people with multiple given names
+ given_names = name.split()[0]
+ str_index = 1
+ while str_index < len(name.split()) and name.split()[str_index].endswith("."):
+ given_names += f" {name.split()[str_index]}"
+ str_index += 1
+
+ new_contrib = {
+ "given-names": given_names,
+ }
+
+ if family_names := " ".join(name.split()[str_index:]):
+ new_contrib["family-names"] = family_names
+
+ if this_tributor.get("blog") is not None:
+ new_contrib["website"] = this_tributor["blog"]
+
+ if this_tributor.get("orcid") is not None:
+ new_contrib["orcid"] = "https://orcid.org/" + this_tributor["orcid"]
+
+ if this_tributor.get("affiliation") is not None:
+ new_contrib["affiliation"] = this_tributor["affiliation"]
+
+ if this_tributor.get("email") is not None:
+ new_contrib["email"] = this_tributor["email"]
+
+ author_list.append(new_contrib)
+
+ return author_list
+
+
+"""MAIN"""
+
+
+def main():
+ token = None
+ if TOKEN_FILE is not None:
+ with open(Path(TOKEN_FILE)) as f:
+ token = f.read().strip()
+
+ log.debug(f"Reading: {INPUT_FILE}")
+ df = pd.read_csv(INPUT_FILE, sep="\t", encoding="utf8")
+ log.debug(f"\n{df.head()}")
+
+ tributors_file = root_dir() / ".tributors"
+ allcontrib_file = root_dir() / ".all-contributorsrc"
+ citation_file = root_dir() / "CITATION.cff"
+
+ tributors = load_tributors(tributors_file)
+ tributors_names = [tributors[x]["name"] for x in tributors]
+
+ allcontrib = load_allcontrib(allcontrib_file)
+ allcontrib_names = [x["name"] for x in allcontrib["contributors"]]
+
+ citation = load_citation(citation_file)
+
+ # sanity checks to make sure no contributor was added manually
+ assert len(tributors_names) == len(set(tributors_names))
+ assert len(allcontrib_names) == len(set(allcontrib_names))
+ assert len(tributors_names) == len(allcontrib_names)
+ assert len(tributors_names) == len(citation["authors"])
+
+ new_contrib_names = df.name.to_list()
+
+ missing_from_tributors = return_missing_from_tributors(
+ tributors_file, new_contrib_names
+ )
+ if len(missing_from_tributors) != 0:
+ log.info("ADDING TO .tributors")
+ for name in missing_from_tributors:
+ if not TEST and name in (
+ "Margaret E. Heafield",
+ "Maria Salomea Skłodowska",
+ ):
+ log.info(f"skipping {name}")
+ continue
+ this_contributor = return_this_contributor(df, name)
+ add_to_tributors(tributors, this_contributor)
+
+ contributors_to_update = set(new_contrib_names) - set(missing_from_tributors)
+ if len(contributors_to_update) != 0:
+ log.info("UPDATING .tributors")
+ for name in contributors_to_update:
+ if not TEST and name in (
+ "Margaret E. Heafield",
+ "Maria Salomea Skłodowska",
+ ):
+ log.info(f"skipping {name}")
+ continue
+ this_contributor = return_this_contributor(
+ df=df, name=name, contribution_needed=False
+ )
+ tributors = update_tributors(tributors, this_contributor)
+
+ write_tributors(tributors_file, tributors)
+
+ log.info("UPDATING .all-contributorsrc")
+ for github_username in tributors:
+ this_contributor = tributors[github_username]
+ this_contributor["login"] = github_username
+ this_contributor = rename_keys_for_allcontrib(this_contributor)
+
+ if UPDATE_AVATARS:
+ avatar_url = get_gh_avatar(
+ this_contributor["github_username"], GH_USERNAME, token
+ )
+ this_contributor["avatar_url"] = avatar_url
+
+ allcontrib = update_allcontrib(allcontrib, this_contributor)
+
+ write_allcontrib(allcontrib_file, allcontrib)
+
+ log.info("UPDATING CITATION.cff")
+ citation = load_citation(citation_file)
+ citation["authors"] = return_author_list_for_cff(tributors_file)
+ write_citation(citation_file, citation)
+
+ log.info("VALIDATING CITATION.cff")
+ citation = create_citation(infile=citation_file, url=None)
+ validate_or_write_output(
+ outfile=None, outputformat=None, validate_only=True, citation=citation
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/entities_order.py b/tools/entities_order.py
index 32e55556b9..cb7e92c8d4 100644
--- a/tools/entities_order.py
+++ b/tools/entities_order.py
@@ -10,7 +10,6 @@
def main():
-
status_ok = True
entities_order = return_entities_order()
@@ -21,15 +20,12 @@ def main():
files_to_check = datatypes_schema_path.rglob("*.yaml")
for file_ in files_to_check:
-
print(f"Checking: {file_}")
with open(file_, "r") as f:
-
schema = yaml.safe_load(f)
for suffix_group in schema:
-
entities = list(schema[suffix_group]["entities"].keys())
if "$ref" in entities:
@@ -38,7 +34,6 @@ def main():
correct_order = sorted(entities, key=lambda x: entities_order.index(x))
if entities != correct_order:
-
status_ok = False
warnings.warn(
diff --git a/tools/examplecode/example.py b/tools/examplecode/example.py
index abaac4663b..efe8232c96 100644
--- a/tools/examplecode/example.py
+++ b/tools/examplecode/example.py
@@ -53,7 +53,6 @@ def _tree_body(self, directory, prefix=""):
entries_count = len(directory)
for index, entry in enumerate(directory):
-
# change connector if this is the last item in this directory
connector = self.ELBOW if index == entries_count - 1 else self.TEE
self._add_dictionary(
diff --git a/tools/filetree_example.ipynb b/tools/filetree_example.ipynb
index 81d7876d5e..05426ba9b7 100644
--- a/tools/filetree_example.ipynb
+++ b/tools/filetree_example.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
@@ -11,15 +11,30 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 2,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "```Text\n",
+ "├─ sub-01_part-mag_T1w.nii.gz \n",
+ "├─ sub-01_part-mag_T1w.json \n",
+ "├─ sub-01_part-phase_T1w.nii.gz comments can be added here\n",
+ "└─ sub-01_part-phase_T1w.json but padding them could be optimized\n",
+ "```\n",
+ "\n"
+ ]
+ }
+ ],
"source": [
"directory_list = {\n",
" \"sub-01_part-mag_T1w.nii.gz\" : \"\", # leave this value empty for files\n",
" \"sub-01_part-mag_T1w.json\" : \"\",\n",
" \"sub-01_part-phase_T1w.nii.gz\" : \" comments can be added here\",\n",
- " \"sub-01_part-phase_T1w.json\" : \" but padding them could be optimised\",\n",
+ " \"sub-01_part-phase_T1w.json\" : \" but padding them could be optimized\",\n",
"}\n",
"\n",
"tree = DirectoryTree(directory_list)\n",
@@ -29,9 +44,27 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 3,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "```Text\n",
+ "├─ sub-01\\\n",
+ "│ ├─ sub-01_part-mag_T1w.nii.gz \n",
+ "│ ├─ sub-01_part-mag_T1w.json \n",
+ "│ ├─ sub-01_part-phase_T1w.nii.gz \n",
+ "│ └─ sub-01_part-phase_T1w.json \n",
+ "└─ sub-02\\\n",
+ " └─ sub-02_part-mag_T1w.nii.gz \n",
+ "```\n",
+ "\n"
+ ]
+ }
+ ],
"source": [
"directory_dict = {\n",
" \"sub-01\": { # use nested dictionaries to represent directories\n",
@@ -52,9 +85,26 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 6,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "```Text\n",
+ "└─ sub-01\\\n",
+ " └─ anat\\\n",
+ " ├─ sub-01_part-mag_T1w.nii.gz \n",
+ " ├─ sub-01_part-mag_T1w.json \n",
+ " ├─ sub-01_part-phase_T1w.nii.gz \n",
+ " └─ sub-01_part-phase_T1w.json \n",
+ "```\n",
+ "\n"
+ ]
+ }
+ ],
"source": [
"nested_directory_dict = {\n",
" \"sub-01\" : {\n",
@@ -74,9 +124,32 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 5,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "```Text\n",
+ "└─ my_processed_data\\\n",
+ " ├─ code\\\n",
+ " │ ├─ processing_pipeline-1.0.0.img \n",
+ " │ ├─ hpc_submitter.sh \n",
+ " │ └─ ... \n",
+ " ├─ sourcedata\\\n",
+ " │ ├─ sub-01\\\n",
+ " │ ├─ sub-02\\\n",
+ " │ └─ ... \n",
+ " ├─ sub-01\\\n",
+ " ├─ sub-02\\\n",
+ " └─ ... \n",
+ "```\n",
+ "\n"
+ ]
+ }
+ ],
"source": [
"directory = { \n",
" \"my_processed_data\": {\n",
@@ -103,9 +176,33 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 4,
"metadata": {},
- "outputs": [],
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "```Text\n",
+ "├─ dataset_description.json \n",
+ "├─ sub-01\\\n",
+ "│ ├─ sessions.tsv \n",
+ "│ ├─ ses-01\\\n",
+ "│ │ └─ anat\\\n",
+ "│ │ ├─ sub-01_part-mag_T1w.nii.gz \n",
+ "│ │ ├─ sub-01_part-mag_T1w.json \n",
+ "│ │ ├─ sub-01_part-phase_T1w.nii.gz \n",
+ "│ │ └─ sub-01_part-phase_T1w.json \n",
+ "│ └─ scans.tsv \n",
+ "└─ ses-02\\\n",
+ " └─ func\\\n",
+ " └─ sub-01_bold.nii.gz \n",
+ "```\n",
+ "\n"
+ ]
+ }
+ ],
"source": [
"# you can also represent files and directories on the same level\n",
"\n",
@@ -134,6 +231,54 @@
"text = tree.generate()\n",
"print(text)"
]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n",
+ "```Text\n",
+ "└─ sub-\\\n",
+ " └─ [ses-]\\\n",
+ " └─ motion\\\n",
+ " ├─ sub-[_ses-]_task-[_tracksys-]_motion.tsv \n",
+ " ├─ sub-[_ses-]_task-_motion.json \n",
+ " ├─ sub-[_ses-]_task-_channels.tsv \n",
+ " ├─ sub-[_ses-]_task-_coordsystem.json \n",
+ " ├─ sub-[_ses-]_task-_events.tsv \n",
+ " └─ sub-[_ses-]_task-_events.json \n",
+ "```\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "# BIDS motion example recording data\n",
+ "\n",
+ "directory = {\n",
+ " \"sub-\" : {\n",
+ " \"[ses-]\" : {\n",
+ " \"motion\" : {\n",
+ " \"sub-[_ses-]_task-[_tracksys-]_motion.tsv\" : \"\",\n",
+ " \"sub-[_ses-]_task-_motion.json\" : \"\",\n",
+ " \"sub-[_ses-]_task-_channels.tsv\" : \"\",\n",
+ " \"sub-[_ses-]_task-_coordsystem.json\" : \"\",\n",
+ " \"sub-[_ses-]_task-_events.tsv\" : \"\",\n",
+ " \"sub-[_ses-]_task-_events.json\" : \"\",\n",
+ " }\n",
+ " }\n",
+ " }\n",
+ " }\n",
+ "\n",
+ "tree = DirectoryTree(directory)\n",
+ "text = tree.generate()\n",
+ "print(text)\n"
+ ]
}
],
"metadata": {
@@ -155,7 +300,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.8.3"
+ "version": "3.8.8"
}
},
"nbformat": 4,
diff --git a/tools/mkdocs_macros_bids/__init__.py b/tools/mkdocs_macros_bids/__init__.py
index d60725f372..87dd87a018 100644
--- a/tools/mkdocs_macros_bids/__init__.py
+++ b/tools/mkdocs_macros_bids/__init__.py
@@ -5,6 +5,7 @@
make_filetree_example,
make_metadata_table,
make_suffix_table,
+ render_text,
)
from .main import define_env
@@ -16,4 +17,5 @@
"make_suffix_table",
"make_metadata_table",
"make_filetree_example",
+ "render_text",
]
diff --git a/tools/mkdocs_macros_bids/macros.py b/tools/mkdocs_macros_bids/macros.py
index b39b7a691d..aa29f21b28 100644
--- a/tools/mkdocs_macros_bids/macros.py
+++ b/tools/mkdocs_macros_bids/macros.py
@@ -72,7 +72,10 @@ def make_filename_template(dstype="raw", src_path=None, pdf_format=False, **kwar
If False, the filename template will use HTML and include hyperlinks.
This works on the website.
Default is False.
- kwargs : dict
+
+ Other Parameters
+ ----------------
+ **kwargs : dict
Keyword arguments used to filter the schema.
Example kwargs that may be used include: "suffixes", "datatypes",
"extensions".
@@ -102,7 +105,13 @@ def make_entity_table(src_path=None, **kwargs):
Parameters
----------
- kwargs : dict
+ src_path : str or None
+ The file where this macro is called, which may be explicitly provided
+ by the "page.file.src_path" variable.
+
+ Other Parameters
+ ----------------
+ **kwargs : dict
Keyword arguments used to filter the schema.
Example kwargs that may be used include: "suffixes", "datatypes",
"extensions".
@@ -125,6 +134,12 @@ def make_entity_definitions(src_path=None):
"""Generate definitions and other relevant information for entities in the
specification.
+ Parameters
+ ----------
+ src_path : str or None
+ The file where this macro is called, which may be explicitly provided
+ by the "page.file.src_path" variable.
+
Returns
-------
text : str
@@ -144,7 +159,7 @@ def make_glossary(src_path=None):
Parameters
----------
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -169,7 +184,7 @@ def make_suffix_table(suffixes, src_path=None):
----------
suffixes : list of str
A list of the suffixes to include in the table.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -199,7 +214,7 @@ def make_metadata_table(field_info, src_path=None):
Until requirement levels can be codified in the schema,
this argument will be dictionary, with the field names as keys and
the requirement levels as values.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -224,7 +239,7 @@ def make_sidecar_table(table_name, src_path=None):
----------
table_name : str or list of str
Qualified name(s) in schema.rules.sidecars
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -249,7 +264,7 @@ def make_subobject_table(object_name, src_path=None):
----------
object_tuple : tuple of string
A tuple pointing to the object to render.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -283,7 +298,7 @@ def make_columns_table(table_name, src_path=None):
Until requirement levels can be codified in the schema,
this argument will be a dictionary, with the column names as keys and
the requirement levels as values.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -325,7 +340,7 @@ def define_common_principles(src_path=None):
Parameters
----------
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -343,10 +358,18 @@ def define_common_principles(src_path=None):
def define_allowed_top_directories(src_path=None):
-
if src_path is None:
src_path = _get_source_path()
schema_obj = schema.load_schema()
string = render.define_allowed_top_directories(schema_obj, src_path=src_path)
return string
+
+
+def render_text(key, src_path=None):
+ if src_path is None:
+ src_path = _get_source_path()
+
+ schema_obj = schema.load_schema()
+ string = render.render_text(schema_obj, key, src_path=src_path)
+ return string
diff --git a/tools/mkdocs_macros_bids/main.py b/tools/mkdocs_macros_bids/main.py
index 9b3c13f21d..30a93e6ce9 100644
--- a/tools/mkdocs_macros_bids/main.py
+++ b/tools/mkdocs_macros_bids/main.py
@@ -43,4 +43,7 @@ def define_env(env):
env.macro(macros.make_columns_table, "MACROS___make_columns_table")
env.macro(macros.make_filetree_example, "MACROS___make_filetree_example")
env.macro(macros.define_common_principles, "MACROS___define_common_principles")
- env.macro(macros.define_allowed_top_directories, "MACROS___define_allowed_top_directories")
+ env.macro(
+ macros.define_allowed_top_directories, "MACROS___define_allowed_top_directories"
+ )
+ env.macro(macros.render_text, "MACROS___render_text")
diff --git a/tools/new_contributors.tsv b/tools/new_contributors.tsv
new file mode 100644
index 0000000000..b4a5a2c11b
--- /dev/null
+++ b/tools/new_contributors.tsv
@@ -0,0 +1,3 @@
+name email github affiliation orcid contributions bio website
+Maria Salomea Skłodowska marie-curie@openscience.com MarieCurie Open-Science University, Earth 0320-4024-4203-5343 doc, ideas, :bug:, review Lorem ipsum https://example.com
+Margaret E. Heafield ideas code 📖 🚧 👀 🐛 📝
diff --git a/tools/print_contributors.py b/tools/print_contributors.py
new file mode 100644
index 0000000000..62f5c7cf92
--- /dev/null
+++ b/tools/print_contributors.py
@@ -0,0 +1,72 @@
+"""Update the table of contributors in the specification appendice.
+
+
+Takes the content from ".all-contributorsrc"
+to update the table of contributors names and contribution.
+
+"""
+
+from pathlib import Path
+
+import emoji
+from add_contributors import emoji_map, load_allcontrib, root_dir
+
+output_file = Path(__file__).parent.parent / "src" / "appendices" / "contributors.md"
+
+
+def contributor_table_header(max_name_length, max_contrib_length):
+ return f"""| name{" " * (max_name_length-4)} | contributions{" " * (max_contrib_length-13)} |
+| {"-" * max_name_length} | {"-"*max_contrib_length} |
+"""
+
+
+def create_line_contributor(
+ contributor: dict[str, str], max_name_length: int, max_contrib_length: int
+):
+ name = contributor["name"]
+
+ line = f"| {name}{' '*(max_name_length-len(name))} | "
+
+ nb_contrib = len(contributor["contributions"]) * 2
+ for contrib in contributor["contributions"]:
+ line += emoji.emojize(emoji_map()[contrib])
+
+ line += f"{' '*(max_contrib_length-nb_contrib)} |\n"
+
+ return line
+
+
+def main():
+ allcontrib_file = root_dir().joinpath(".all-contributorsrc")
+ allcontrib = load_allcontrib(allcontrib_file)
+
+ allcontrib_names = [x["name"] for x in allcontrib["contributors"]]
+
+ max_name_length = len(max(allcontrib_names, key=len))
+ max_contrib_length = (
+ max(len(x["contributions"]) for x in allcontrib["contributors"]) * 2
+ )
+
+ with open(output_file, "r", encoding="utf8") as f:
+ content = f.readlines()
+
+ with open(output_file, "w", encoding="utf8") as f:
+ for line in content:
+ if line.startswith("| name"):
+ break
+ f.write(line)
+
+ f.write(contributor_table_header(max_name_length, max_contrib_length))
+
+ for name in sorted(allcontrib_names):
+ index_allcontrib = allcontrib_names.index(name)
+ this_contrib = allcontrib["contributors"][index_allcontrib]
+ f.write(
+ create_line_contributor(
+ this_contrib, max_name_length, max_contrib_length
+ )
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/requirements.txt b/tools/requirements.txt
index f2293605cf..34b142a36a 100644
--- a/tools/requirements.txt
+++ b/tools/requirements.txt
@@ -1 +1,5 @@
requests
+emoji
+rich
+ruamel.yaml
+cffconvert
diff --git a/tools/schemacode/.readthedocs.yaml b/tools/schemacode/.readthedocs.yaml
new file mode 100644
index 0000000000..4d86d914fd
--- /dev/null
+++ b/tools/schemacode/.readthedocs.yaml
@@ -0,0 +1,14 @@
+version: 2
+
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.11"
+
+sphinx:
+ configuration: tools/schemacode/docs/conf.py
+
+python:
+ install:
+ - requirements: requirements.txt
+ - requirements: tools/schemacode/docs/requirements.txt
diff --git a/tools/schemacode/README.md b/tools/schemacode/README.md
index 36b15dcdf1..265d731ad0 100644
--- a/tools/schemacode/README.md
+++ b/tools/schemacode/README.md
@@ -1,6 +1,21 @@
# BIDS Schema Tools
-A Python library for working with the BIDS schema.
-
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![codecov](https://codecov.io/gh/bids-standard/bids-specification/branch/master/graph/badge.svg)](https://codecov.io/gh/bids-standard/bids-specification)
+[![Gentoo (::science)](https://repology.org/badge/version-for-repo/gentoo_ovl_science/bidsschematools.svg?header=Gentoo%20%28%3A%3Ascience%29)](https://repology.org/project/bidsschematools/versions)
+[![PyPI version fury.io](https://badge.fury.io/py/bidsschematools.svg)](https://pypi.org/project/bidsschematools/)
+
+A Python library (available after installation in the Python interpreter as `bidsschematools`)
+for working with the [Brain Imaging Data Structure (BIDS)](https://bids.neuroimaging.io/) schema.
+
+Features:
+* lightweight
+* reference schema parsing implementation used for schema testing
+* simple CLI bindings (e.g. `bst export`)
+
+If you have questions, you can post them in one of several channels where BIDS members are active:
+ - the [NeuroStars](https://neurostars.org/tags/bids) discourse forum
+ - the [BrainHack Mattermost](https://mattermost.brainhack.org),
+ for instant messaging (see also this [news item](https://bids.neuroimaging.io/2020/06/24/Join-the-BIDS-community-on-the-BrainHack-Mattermost.html))
+ - the [Google group](https://groups.google.com/forum/#!forum/bids-discussion),
+ for broader discussions surrounding BIDS
diff --git a/tools/schemacode/bidsschematools/__init__.py b/tools/schemacode/bidsschematools/__init__.py
index bc8c3100d5..d7ffa21ee6 100644
--- a/tools/schemacode/bidsschematools/__init__.py
+++ b/tools/schemacode/bidsschematools/__init__.py
@@ -1,13 +1,18 @@
-"""A Python package for working with the BIDS schema."""
-try:
- from importlib.resources import as_file, files
-except ImportError: # PY<3.9
- from importlib_resources import as_file, files
+"""A Python package for working with the BIDS schema.
+
+.. autodata:: __version__
+.. autodata:: __bids_version__
+"""
+
+try: # Prefer backport to leave consistency to dependency spec
+ from importlib_resources import files
+except ImportError:
+ from importlib.resources import files # type: ignore
version_file = files("bidsschematools.data") / "schema" / "SCHEMA_VERSION"
-with as_file(version_file) as f:
- __version__ = f.read_text().strip()
+__version__ = version_file.read_text().strip()
+"Schema version"
bids_version_file = files("bidsschematools.data") / "schema" / "BIDS_VERSION"
-with as_file(bids_version_file) as f:
- __bids_version__ = f.read_text().strip()
+__bids_version__ = bids_version_file.read_text().strip()
+"BIDS specification version"
diff --git a/tools/schemacode/bidsschematools/tests/conftest.py b/tools/schemacode/bidsschematools/conftest.py
similarity index 97%
rename from tools/schemacode/bidsschematools/tests/conftest.py
rename to tools/schemacode/bidsschematools/conftest.py
index 955e12c5dc..3154e3e5cf 100644
--- a/tools/schemacode/bidsschematools/tests/conftest.py
+++ b/tools/schemacode/bidsschematools/conftest.py
@@ -24,6 +24,7 @@
"pet003", # pet, anat
"qmri_tb1tfl", # fmap, _TB1TFL
"qmri_vfa", # derivatives
+ "ds000248", # .bidsignore
]
# Errors are described in the README of the respective datasets:
# https://github.com/bids-standard/bids-error-examples
@@ -82,7 +83,7 @@ def schema_dir():
"""Path to the schema housed in the bids-specification repo."""
from bidsschematools import utils
- bids_schema = utils.get_schema_path()
+ bids_schema = utils.get_bundled_schema_path()
return bids_schema
diff --git a/tools/schemacode/bidsschematools/data/__init__.py b/tools/schemacode/bidsschematools/data/__init__.py
index e69de29bb2..e275ce39b4 100644
--- a/tools/schemacode/bidsschematools/data/__init__.py
+++ b/tools/schemacode/bidsschematools/data/__init__.py
@@ -0,0 +1,99 @@
+"""Module containing data files, including the schema source files
+
+.. autofunction:: load_resource
+"""
+import atexit
+import os
+from contextlib import ExitStack
+from functools import cached_property
+from pathlib import Path
+from types import ModuleType
+from typing import Union
+
+try:
+ from functools import cache
+except ImportError: # PY38
+ from functools import lru_cache as cache
+
+try: # Prefer backport to leave consistency to dependency spec
+ from importlib_resources import as_file, files
+except ImportError:
+ from importlib.resources import as_file, files # type: ignore
+
+__all__ = ["load_resource"]
+
+
+class Loader:
+ """A loader for package files relative to a module
+
+ This class wraps :mod:`importlib.resources` to provide a getter
+ function with an interpreter-lifetime scope. For typical packages
+ it simply passes through filesystem paths as :class:`~pathlib.Path`
+ objects. For zipped distributions, it will unpack the files into
+ a temporary directory that is cleaned up on interpreter exit.
+
+ This loader accepts a fully-qualified module name or a module
+ object.
+
+ Expected usage::
+
+ '''Data package
+
+ .. autofunction:: load_data
+ '''
+
+ from bidsschematools.data import Loader
+
+ load_data = Loader(__package__)
+
+ :class:`~Loader` objects implement the :func:`callable` interface
+ and generate a docstring, and are intended to be treated and documented
+ as functions.
+ """
+
+ def __init__(self, anchor: Union[str, ModuleType]):
+ self._anchor = anchor
+ self.files = files(anchor)
+ self.exit_stack = ExitStack()
+ atexit.register(self.exit_stack.close)
+ # Allow class to have a different docstring from instances
+ self.__doc__ = self._doc
+
+ @cached_property
+ def _doc(self):
+ """Construct docstring for instances
+
+ Lists the public top-level paths inside the location, where
+ non-public means has a `.` or `_` prefix or is a 'tests'
+ directory.
+ """
+ top_level = sorted(
+ os.path.relpath(p, self.files) + "/"[: p.is_dir()]
+ for p in self.files.iterdir()
+ if p.name[0] not in (".", "_") and p.name != "tests"
+ )
+ doclines = [
+ f"Load package files relative to ``{self._anchor}``.",
+ "",
+ "This package contains the following (top-level) files/directories:",
+ "",
+ *(f"* ``{path}``" for path in top_level),
+ ]
+
+ return "\n".join(doclines)
+
+ @cache
+ def __call__(self, *segments) -> Path:
+ """Ensure data is available as a :class:`~pathlib.Path`.
+
+ Any temporary files that are created remain available throughout
+ the duration of the program, and are deleted when Python exits.
+
+ Results are cached so that multiple calls do not unpack the same
+ data multiple times, but the cache is sensitive to the specific
+ argument(s) passed.
+ """
+ return self.exit_stack.enter_context(as_file(self.files.joinpath(*segments)))
+
+
+load_resource = Loader(__package__)
diff --git a/tools/schemacode/bidsschematools/data/tests/test_rules.py b/tools/schemacode/bidsschematools/data/tests/test_rules.py
new file mode 100644
index 0000000000..c44634f814
--- /dev/null
+++ b/tools/schemacode/bidsschematools/data/tests/test_rules.py
@@ -0,0 +1,131 @@
+"""Simple validation tests on schema rules."""
+import warnings
+from collections.abc import Mapping
+
+import pytest
+
+
+def _dict_key_lookup(_dict, key, path=[]):
+ """Look up any uses of a key in a nested dictionary.
+
+ Adapted from https://stackoverflow.com/a/60377584/2589328.
+ """
+ results = []
+ if isinstance(_dict, Mapping):
+ if key in _dict:
+ results.append((path + [key], _dict[key]))
+
+ for k, v in _dict.items():
+ results.extend(_dict_key_lookup(v, key, path=path + [k]))
+
+ elif isinstance(_dict, list):
+ for index, item in enumerate(_dict):
+ results.extend(_dict_key_lookup(item, key, path=path + [index]))
+
+ return results
+
+
+@pytest.mark.validate_schema
+def test_rule_objects(schema_obj):
+ """Ensure that all objects referenced in the schema rules are defined in
+ its object portion.
+
+ This test currently fails because rules files reference object keys for some object types,
+ including entities, columns, and metadata fields,
+ but reference "name" or "value" elements of the object definitions for other object types,
+ including suffixes and extensions.
+ In the case of datatypes, the key and "value" field are always the same.
+
+ Some other object types, such as associated_data, common_principles, formats, modalities,
+ and top_level_files, are not checked in the rules at all.
+
+ Additionally, this test only checks rules that fit the keys.
+ """
+ OBJECT_TYPE_MAPPER = {
+ "metadata": "fields", # metadata in objects is referred to as fields in rules
+ }
+
+ not_found = [] # A list of undefined, but referenced, objects
+ for object_type in schema_obj.objects:
+ # "files" is both an object name and a grouping of rules
+ # The next line would be a false positive hit
+ if object_type == "files":
+ continue
+ # Find all uses of a given object type in the schema rules
+ type_instances_in_rules = _dict_key_lookup(
+ schema_obj.rules,
+ OBJECT_TYPE_MAPPER.get(object_type, object_type),
+ )
+ if not type_instances_in_rules:
+ continue
+
+ for type_instance in type_instances_in_rules:
+ path, instance = type_instance
+ is_list = True
+ if isinstance(instance, Mapping):
+ instance = list(instance)
+ is_list = False
+
+ for i_use, use in enumerate(instance):
+ if use == "derivatives":
+ # Skip derivatives dirs, because the dir is treated as a "use" instead.
+ continue
+ elif "[]" in use:
+ # Rules may reference metadata fields with lists.
+ # This test can't handle this yet, so skip.
+ continue
+ elif "{}" in use:
+ # Rules may reference sub-dictionaries in metadata fields.
+ # This test can't handle this yet, so skip.
+ continue
+
+ if object_type in ["extensions", "suffixes"]:
+ # Some object types are referenced via their "value" fields in the rules
+ object_values = [
+ schema_obj["objects"][object_type][k]["value"]
+ for k in schema_obj["objects"][object_type].keys()
+ ]
+ else:
+ # But other object types are referenced via their keys
+ object_values = list(schema_obj["objects"][object_type].keys())
+
+ # Build a list of items mentioned in rules, but not found in objects.
+ if use not in object_values:
+ temp_path = path[:]
+ if is_list:
+ temp_path[-1] += f"[{i_use}]"
+
+ not_found.append((temp_path, use))
+
+ if not_found:
+ not_found_string = "\n".join([f"{'.'.join(path)} == {val}" for path, val in not_found])
+ raise ValueError(not_found_string)
+
+
+@pytest.mark.validate_schema
+def test_entity_order(schema_obj):
+ """Check the order of the entities of the suffix group of each datatype
+ and lists those that are out of order.
+ """
+ status_ok = True
+
+ entities_order = schema_obj.rules.entities
+
+ for key, group in schema_obj.rules.files.items(level=2):
+ print(f"Checking {key}")
+ entities = list(group.get("entities", ()))
+ correct_order = sorted(entities, key=lambda x: entities_order.index(x))
+
+ if entities != correct_order:
+ status_ok = False
+ warnings.warn(
+ f"""\n\nfilename rule {key} has entities out-of-order:
+ - got: {entities}
+ - should be: {correct_order}
+ """
+ )
+
+ if not status_ok:
+ raise RuntimeError(
+ "Some suffix groups have their entities out of order. See warnings above."
+ )
diff --git a/tools/schemacode/bidsschematools/expressions.py b/tools/schemacode/bidsschematools/expressions.py
new file mode 100644
index 0000000000..c850d24551
--- /dev/null
+++ b/tools/schemacode/bidsschematools/expressions.py
@@ -0,0 +1,257 @@
+"""Parsing utilities for BIDS Schema expression language
+"""
+from functools import partial
+
+from pyparsing import (
+ Forward,
+ Literal,
+ Optional,
+ StringEnd,
+ StringStart,
+ Suppress,
+ common,
+ delimited_list,
+ one_of,
+ quoted_string,
+)
+
+
+def parse(expression_string: str) -> "ASTNode":
+ """Convert a BIDS schema expression into an abstract syntax tree
+
+ EBNF-ish grammar::
+
+ # In order of binding (loosest to tightest)
+ orOp :: '||'
+ andOp :: '&&'
+ notOp :: '!'
+ cmpOp :: '==' | '!=' | '<' | '<=' | '>' | '>=' | 'in'
+ addOp :: '+' | '-'
+ mulOp :: '*' | '/' | '%'
+ expOp :: '**'
+
+ item :: '(' test ')' | '[' [testList] '] | '{' '}' | NAME | NUMBER | STRING
+ trailer :: '(' [testList] ') | '[' test ']' | '.' NAME
+ atom :: item trailer*
+
+ factor :: atom [ expOp factor ]*
+ term :: factor [ mulOp factor ]*
+ expr :: term [ addOp term ]*
+
+ comparison :: expr [ compOp expr ]*
+ notTest :: not notTest | comparison
+ andTest :: notTest [ '&&' andTest ]*
+ test :: andTest [ '||' test ]*
+
+ testList :: test [ ',' test ]*
+
+ expression :: ^ test $
+ """
+ # Alternative: (possibly with 'null' as additional literal)
+ # This makes things like '1(a, b, c)' or '"string"[0]' syntax errors
+ #
+ # literal :: NUMBER | STRING
+ # item :: '(' test ')' | '[' testList '] | NAME
+ # atom :: literal | item trailer*
+
+ return expression.parse_string(expression_string)[0]
+
+
+orOp = Literal("||")
+andOp = Literal("&&")
+notOp = Literal("!")
+compOp = one_of(("==", "!=", "<", "<=", ">", ">=", "in"))
+addOp = one_of(("+", "-"))
+mulOp = one_of(("*", "/"))
+expOp = Literal("**")
+
+lpar, rpar = Suppress("("), Suppress(")")
+lsqr, rsqr = Suppress("["), Suppress("]")
+dot = Suppress(".")
+
+# Right-associative expressions need to be recursively defined
+factor = Forward()
+notTest = Forward()
+andTest = Forward()
+test = Forward()
+
+testlist = delimited_list(test)
+
+# Numbers and strings are base types, this could be expanded with bools and null
+# if it seems useful
+literal = common.number | quoted_string
+
+# Items are units that operations can be done on, including arithmetic, comparison,
+# function calls, index lookups and attribute lookups
+array = lsqr + Optional(testlist) + rsqr
+parenthetical = lpar + test + rpar
+# If object literals ever occur in real expressions, we'll need to define this
+obj_literal = Literal("{}")
+item = parenthetical | array | obj_literal | common.identifier | literal
+
+# Trailers are function calls, array indexes, and object attributes
+function_call = lpar + Optional(testlist) + rpar
+array_lookup = lsqr + test + rsqr
+object_lookup = dot + common.identifier
+trailer = function_call | array_lookup | object_lookup
+
+# An atom might have some lookups done, but now it can be part of an arithmetic
+# expression
+atom = item + (trailer)[...]
+
+# Arithmetic expressions
+factor <<= atom + (expOp + factor)[...] # Right-associative
+term = factor + (mulOp + factor)[...]
+expr = term + (addOp + term)[...]
+
+# Logic expressions (tests, to avoid name collision)
+comparison = expr + (compOp + expr)[...]
+notTest <<= notOp + notTest | comparison
+andTest <<= notTest + (andOp + andTest)[...] # Right-associative
+test <<= andTest + (orOp + test)[...] # Right-associative
+
+# Schema expressions must parse from start to finish
+expression = StringStart() + test + StringEnd()
+
+
+# With the grammar defined, we need the following syntax elements
+class ASTNode:
+ """AST superclass
+
+ Defines basic repr. Subclasses should define __str__ with enough
+ parentheses to make associations unambiguous.
+ """
+
+ def __repr__(self) -> str:
+ return f"<{self.__class__.__name__}: {self.__dict__}>"
+
+ def __str__(self) -> str:
+ raise NotImplementedError
+
+
+class RightOp(ASTNode):
+ """Right-associative unary operator"""
+
+ def __init__(self, tokens):
+ self.op, self.rh = tokens
+
+ def __str__(self):
+ return f"({self.op}{self.rh})"
+
+ @classmethod
+ def maybe(cls, tokens):
+ """Construct class only if not degenerate"""
+ if len(tokens) < 2:
+ return tokens
+ return cls(tokens)
+
+
+class BinOp(ASTNode):
+ """Binary operator"""
+
+ def __init__(self, tokens):
+ self.lh, self.op, self.rh = tokens
+
+ @classmethod
+ def maybe(cls, tokens):
+ """Construct class if not degenerate
+
+ * Right-associative: ``outer <<= inner + (op + outer)[...]``
+ * Left-associative: ``outer = inner + (op + inner)[...]``
+
+ In the right-associative case, we get ``[inner, op, BinOp(outer)]``,
+ so we loop once and are done.
+
+ In the left-associative case, we get ``[inner, op, inner, op, ...]``,
+ convert to ``[BinOp(inner, op, inner), op, inner, ...]``
+ """
+ while len(tokens) >= 3:
+ tokens = [cls(tokens[:3])] + tokens[3:]
+ return tokens
+
+ def __str__(self):
+ return f"({self.lh} {self.op} {self.rh})"
+
+
+class Function(ASTNode):
+ """Function call"""
+
+ def __init__(self, name, args):
+ self.name = name
+ self.args = args
+
+ def __str__(self):
+ arglist = ", ".join(map(str, self.args))
+ return f"{self.name}({arglist})"
+
+
+class Element(ASTNode):
+ """Array element lookup"""
+
+ def __init__(self, name, index):
+ self.name = name
+ self.index = index
+
+ def __str__(self):
+ return f"{self.name}[{self.index}]"
+
+
+class Property(ASTNode):
+ """Object property lookup"""
+
+ def __init__(self, name, field):
+ self.name = name
+ self.field = field
+
+ def __str__(self):
+ return f"({self.name}.{self.field})"
+
+
+class Array(ASTNode):
+ """Array literal"""
+
+ def __init__(self, tokens):
+ self.elements = list(tokens)
+
+ def __str__(self):
+ return str(self.elements)
+
+
+class Object(ASTNode):
+ """Object literal, unused outside tests, so degenerate"""
+
+ def __init__(self, tokens):
+ pass
+
+ def __str__(self):
+ return "{}"
+
+
+array.set_parse_action(Array)
+obj_literal.set_parse_action(Object)
+
+# Function calls and item lookups need to be constructed partially
+function_call.set_parse_action(lambda t: partial(Function, args=list(t)))
+array_lookup.set_parse_action(lambda t: partial(Element, index=t[0]))
+object_lookup.set_parse_action(lambda t: partial(Property, field=t[0]))
+
+
+# Once the atom is complete, we can build the left-associative tree by completing application
+def atomize(tokens):
+ item = tokens.pop(0)
+ for trailer in tokens:
+ item = trailer(item)
+ return item
+
+
+atom.set_parse_action(atomize)
+
+# Arithmetic expressions can all be degenerate, so use maybe to pass through
+factor.set_parse_action(BinOp.maybe)
+term.set_parse_action(BinOp.maybe)
+expr.set_parse_action(BinOp.maybe)
+
+comparison.set_parse_action(BinOp.maybe)
+notTest.set_parse_action(RightOp.maybe)
+andTest.set_parse_action(BinOp.maybe)
+test.set_parse_action(BinOp.maybe)
diff --git a/tools/schemacode/bidsschematools/render/__init__.py b/tools/schemacode/bidsschematools/render/__init__.py
index 77b787e4c4..506e6b4583 100644
--- a/tools/schemacode/bidsschematools/render/__init__.py
+++ b/tools/schemacode/bidsschematools/render/__init__.py
@@ -13,6 +13,7 @@
make_entity_definitions,
make_filename_template,
make_glossary,
+ render_text,
)
__all__ = [
@@ -27,4 +28,5 @@
"make_filename_template",
"define_common_principles",
"define_allowed_top_directories",
+ "render_text",
]
diff --git a/tools/schemacode/bidsschematools/render/tables.py b/tools/schemacode/bidsschematools/render/tables.py
index 8a3f15090e..90d091f5db 100644
--- a/tools/schemacode/bidsschematools/render/tables.py
+++ b/tools/schemacode/bidsschematools/render/tables.py
@@ -1,4 +1,6 @@
"""Functions for rendering portions of the schema as text."""
+from __future__ import annotations
+
import logging
import os
import typing as ty
@@ -44,7 +46,7 @@ def _make_object_table(
information to be added to the object's description).
table_type : str
The name of the field type. For example, "metadata".
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
tablefmt : string, optional
@@ -153,7 +155,7 @@ def _make_table_from_rule(
Qualified name(s) in schema.rules.tabular_data (for "columns" tables) or
schema.rules.sidecars (for "metadata" files).
Only one item may be provided for columns tables.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
tablefmt : string, optional
@@ -167,7 +169,7 @@ def _make_table_from_rule(
if isinstance(table_name, str):
table_name = [table_name]
- elements = {}
+ elements: dict[str, str | dict[str, str]] = {}
for table in table_name:
if table_type == "metadata":
table_schema = schema.rules.sidecars[table]
@@ -285,7 +287,7 @@ def make_entity_table(schema, tablefmt="github", src_path=None, **kwargs):
Directory containing schema, which is stored in yaml files.
tablefmt : string, optional
The target table format. The default is "github" (GitHub format).
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -356,7 +358,7 @@ def make_suffix_table(schema, suffixes, src_path=None, tablefmt="github"):
----------
schema : dict
suffixes : list of str
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
tablefmt : str
@@ -418,7 +420,7 @@ def make_sidecar_table(
The BIDS schema.
table_name : str or list of str
Qualified name(s) in schema.rules.sidecars
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
tablefmt : string, optional
@@ -456,7 +458,7 @@ def make_metadata_table(schema, field_info, src_path=None, tablefmt="github"):
and the second string is additional table-specific information
about the metadata field that will be appended to the field's base
definition from the schema.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
tablefmt : string, optional
@@ -513,7 +515,7 @@ def make_subobject_table(
The BIDS schema.
object_name : str
Qualified name in schema.objects
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
tablefmt : string, optional
@@ -565,7 +567,7 @@ def make_columns_table(
table_name : str
Qualified name in schema.rules.tabular_data.
Only one table may be provided in this function.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
tablefmt : string, optional
diff --git a/tools/schemacode/bidsschematools/render/text.py b/tools/schemacode/bidsschematools/render/text.py
index 81a3c8d910..6468007625 100644
--- a/tools/schemacode/bidsschematools/render/text.py
+++ b/tools/schemacode/bidsschematools/render/text.py
@@ -72,7 +72,14 @@ def _make_entity_definition(entity, entity_info):
text += f"**Format**: `{entity_info['name']}-<{entity_info.get('format', 'label')}>`"
text += "\n\n"
if "enum" in entity_info.keys():
- text += f"**Allowed values**: `{'`, `'.join(entity_info['enum'])}`"
+ allowed_values = []
+ for value in entity_info["enum"]:
+ if isinstance(value, str):
+ allowed_values.append(value)
+ else:
+ allowed_values.append(value["name"])
+
+ text += f"**Allowed values**: `{'`, `'.join(allowed_values)}`"
text += "\n\n"
description = entity_info["description"]
@@ -88,7 +95,7 @@ def make_glossary(schema, src_path=None):
schema : dict
The schema object, which is a dictionary with nested dictionaries and
lists stored within it.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -140,10 +147,14 @@ def make_glossary(schema, src_path=None):
for obj_key in sorted(all_objects.keys()):
obj = all_objects[obj_key]
obj_marker = obj["key"]
- obj_def = obj["definition"]
+ obj_def = obj.get("definition", None)
+ if obj_def is None:
+ raise ValueError(f"{obj_marker} has no definition.")
# Clean up the text description
- obj_desc = obj_def["description"]
+ obj_desc = obj_def.get("description", None)
+ if obj_desc is None:
+ raise ValueError(f"{obj_marker} has no description.")
# A backslash before a newline means continue a string
obj_desc = obj_desc.replace("\\\n", "")
# Two newlines should be respected
@@ -163,21 +174,25 @@ def make_glossary(schema, src_path=None):
elif obj["type"] == "format":
text += f"**Regular expression**: `{obj_def['pattern']}`\n\n"
+ keys_to_drop = ["description", "display_name", "name", "value", "pattern"]
if "enum" in obj_def.keys():
- allowed_vals = [f"`{enum}`" for enum in obj_def["enum"]]
- text += f"**Allowed values**: {', '.join(allowed_vals)}\n\n"
+ allowed_values = []
+ keys_to_drop.append("enum")
+ for value in obj_def["enum"]:
+ if isinstance(value, str):
+ allowed_values.append(value)
+ else:
+ allowed_values.append(value["name"])
+
+ text += f"**Allowed values**: `{'`, `'.join(allowed_values)}`\n\n"
text += f"**Description**:\n{obj_desc}\n\n"
- temp_obj_def = {
- k: v
- for k, v in obj_def.items()
- if k not in ("description", "display_name", "name", "value", "enum", "pattern")
- }
+ reduced_obj_def = {k: v for k, v in obj_def.items() if k not in keys_to_drop}
- if temp_obj_def:
- temp_obj_def = yaml.dump(temp_obj_def)
- text += f"**Schema information**:\n```yaml\n{temp_obj_def}\n```"
+ if reduced_obj_def:
+ reduced_obj_def = yaml.dump(reduced_obj_def)
+ text += f"**Schema information**:\n```yaml\n{reduced_obj_def}\n```"
# Spec internal links need to be replaced
text = text.replace("SPEC_ROOT", utils.get_relpath(src_path))
@@ -198,9 +213,18 @@ def _add_entity(filename_template, entity_pattern, requirement_level):
def _format_entity(entity, lt, gt):
fmt = entity.get("format")
if "enum" in entity:
- fmt = "|".join(entity["enum"])
+ allowed_values = []
+ for value in entity["enum"]:
+ if isinstance(value, str):
+ allowed_values.append(value)
+ else:
+ allowed_values.append(value["name"])
+
+ fmt = "|".join(allowed_values)
+
if fmt is None:
raise ValueError(f"entity missing format or enum fields: {entity}")
+
return f"{entity['name']}-{lt}{fmt}{gt}"
@@ -229,7 +253,7 @@ def make_filename_template(
schema : dict
The schema object, which is a dictionary with nested dictionaries and
lists stored within it.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
n_dupes_to_combine : int
@@ -241,7 +265,10 @@ def make_filename_template(
If False, the filename template will use HTML and include hyperlinks.
This works on the website.
Default is False.
- kwargs : dict
+
+ Other Parameters
+ ----------------
+ **kwargs : dict
Keyword arguments used to filter the schema.
Example kwargs that may be used include: "suffixes", "datatypes",
"extensions".
@@ -327,10 +354,11 @@ def make_filename_template(
heading=entity["name"],
pdf_format=pdf_format,
)
+ fmt = entity.get("format", "label")
entity["format"] = utils._link_with_html(
entity.get("format", "label"),
- html_path=f"{ENTITIES_PATH}.html",
- heading=entity.get("format", "label"),
+ html_path=f"{GLOSSARY_PATH}.html",
+ heading=f"{fmt}-common_principles",
pdf_format=pdf_format,
)
pattern = _format_entity(entity, lt, gt)
@@ -410,7 +438,21 @@ def make_filename_template(
def append_filename_template_legend(text, pdf_format=False):
- """Append a legend to filename templates."""
+ """Append a legend to filename templates.
+
+ Parameters
+ ----------
+ text : str
+ The text to append the legend to.
+
+ pdf_format : bool
+ Whether to format the legend for PDF output.
+
+ Returns
+ -------
+ str :
+ The text with the legend appended.
+ """
if pdf_format:
info_str = ""
else:
@@ -456,7 +498,7 @@ def define_common_principles(schema, src_path=None):
----------
schema : dict
The BIDS schema.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -489,7 +531,7 @@ def define_allowed_top_directories(schema, src_path=None) -> str:
----------
schema : dict
The BIDS schema.
- src_path : str | None
+ src_path : str or None
The file where this macro is called, which may be explicitly provided
by the "page.file.src_path" variable.
@@ -506,3 +548,34 @@ def define_allowed_top_directories(schema, src_path=None) -> str:
string += f"- `{dirname}`: {definition.description}"
return string.replace("SPEC_ROOT", utils.get_relpath(src_path))
+
+
+def render_text(schema, key: str, src_path=None):
+ """
+
+ Parameters
+ ----------
+ schema : dict
+ The BIDS schema.
+
+ object : str
+ The object to render the description for:
+ possible values correspond to the keys in schema["objects"].
+
+ key : str
+ The key of the object to render the description for:
+ possible values correspond to the keys in schema["objects"][object]
+
+ src_path : str or None
+ The file where this macro is called, which may be explicitly provided
+ by the "page.file.src_path" variable.
+
+ Returns
+ -------
+ desc : str
+ Description of the object.
+ """
+ text = schema.get(key)
+ if not isinstance(text, str):
+ raise ValueError(f"{key} does not refer to a text field")
+ return text.replace("SPEC_ROOT", utils.get_relpath(src_path))
diff --git a/tools/schemacode/bidsschematools/render/utils.py b/tools/schemacode/bidsschematools/render/utils.py
index 1dbddbff9b..a482b54492 100644
--- a/tools/schemacode/bidsschematools/render/utils.py
+++ b/tools/schemacode/bidsschematools/render/utils.py
@@ -159,6 +159,18 @@ def flatten_multiindexed_columns(df):
def get_link(string):
+ """Return a hyperlink to the JSON specification for a given JSON type.
+
+ Parameters
+ ----------
+ string : str
+ The JSON type to link to.
+
+ Returns
+ -------
+ url : str
+ The hyperlink to the JSON specification for the given JSON type.
+ """
refs = {
"array": "https://www.w3schools.com/js/js_json_arrays.asp",
"string": "https://www.w3schools.com/js/js_json_datatypes.asp",
@@ -185,7 +197,7 @@ def resolve_metadata_type(definition):
Returns
-------
- string : :obj:`str`
+ string : str
A string describing the valid value types for the metadata term.
"""
if "type" in definition.keys():
@@ -230,8 +242,7 @@ def describe_valid_values(definition):
Returns
-------
- :obj:`str`
- A sentence describing valid values for the object.
+ str : A sentence describing valid values for the object.
"""
description = ""
if "anyOf" in definition.keys():
@@ -243,11 +254,9 @@ def describe_valid_values(definition):
elif definition["type"] == "string":
if "enum" in definition.keys():
# Allow enums to be "objects" (dicts) or strings
- enum_values = [
- list(v.keys())[0] if isinstance(v, dict) else v for v in definition["enum"]
- ]
- enum_values = [f'`"{v}"`' for v in enum_values]
- description = f"Must be one of: {', '.join(enum_values)}."
+ enums = [list(v.keys())[0] if isinstance(v, dict) else v for v in definition["enum"]]
+ enums = [f'`"{v}"`' for v in enums]
+ description = f"Must be one of: {', '.join(enums)}."
elif definition["type"] in ("integer", "number"):
minstr = maxstr = minmaxstr = ""
@@ -294,6 +303,16 @@ def get_relpath(src_path):
def normalize_requirements(text):
+ """Normalize requirements wording in a string.
+
+ Parameters
+ ----------
+ text : str
+
+ Returns
+ -------
+ text : str
+ """
for level in ("optional", "recommended", "required", "deprecated"):
# Replace both "optional" and "Optional" with "OPTIONAL"
text = text.replace(level.title(), level).replace(level, level.upper())
@@ -301,6 +320,16 @@ def normalize_requirements(text):
def normalize_breaks(text):
+ """Normalize line breaks in a string, for new lines, escaped new lines and double new lines.
+
+ Parameters
+ ----------
+ text : str
+
+ Returns
+ -------
+ text : str
+ """
# A backslash before a newline means continue a string
text = text.replace("\\\n", "")
# Two newlines should be respected
@@ -320,6 +349,10 @@ def num2words(integer, to="ordinal"):
----------
integer : int
to : {"ordinal", "cardinal"}, optional
+
+ Returns
+ -------
+ word : str
"""
if to == "ordinal":
mapper = {
diff --git a/tools/schemacode/bidsschematools/rules.py b/tools/schemacode/bidsschematools/rules.py
new file mode 100644
index 0000000000..6dd837629f
--- /dev/null
+++ b/tools/schemacode/bidsschematools/rules.py
@@ -0,0 +1,246 @@
+"""Utilities for implementing ``schema.rules``
+
+This module is currently limited to constructing filename rules from
+``schema.rules.files``.
+"""
+import re
+import typing as ty
+from collections.abc import Mapping
+from functools import lru_cache
+
+import bidsschematools as bst
+import bidsschematools.types
+
+# The list of which entities create directories could be dynamically specified by the YAML, but for
+# now, it is not.
+# Ordering is important, as "subject" follows "session" alphabetically, but is hierarchically
+# above it.
+DIR_ENTITIES = ["subject", "session"]
+
+
+def _capture_regex(name, pattern, backref):
+ """Capture pattern to name or match back-reference to name
+
+ >>> _capture_regex("run", "[0-9]+", False)
+ '(?P[0-9]+)'
+ >>> _capture_regex("run", "[0-9]+", True)
+ '(?P=run)'
+ >>> re.match(_capture_regex("run", "[0-9]+", False), "123_").groupdict()
+ {'run': '123'}
+ """
+ return f"(?P={name})" if backref else f"(?P<{name}>{pattern})"
+
+
+def _optional_regex(regex, optional):
+ """Return an optional version of a regex if optional is True
+
+ A required regex is passed through unchanged:
+
+ >>> pattern = _optional_regex("xyz", False)
+ >>> pattern
+ 'xyz'
+ >>> re.match(pattern, "xyz").groups()
+ ()
+ >>> re.match(pattern, "") is None
+ True
+
+ An optional regex uses a non-capturing group, to avoid interfering
+ with existing groups
+
+ >>> pattern = _optional_regex("x(?P[a-z])z", True)
+ >>> pattern
+ '(?:x(?P[a-z])z)?'
+ >>> re.match(pattern, "xyz").groups()
+ ('y',)
+ >>> re.match(pattern, "xyz").groupdict()
+ {'name': 'y'}
+ >>> re.match(pattern, "").groups()
+ (None,)
+ >>> re.match(pattern, "").groupdict()
+ {'name': None}
+ """
+ return f"(?:{regex})?" if optional else regex
+
+
+@lru_cache()
+def _format_entity(entity, name, pattern, level, directory=False):
+ if directory and entity not in DIR_ENTITIES:
+ return ""
+
+ label = _capture_regex(entity, pattern, not directory and entity in DIR_ENTITIES)
+ post = "/" if directory else "_"
+
+ return _optional_regex(f"{name}-{label}{post}", level != "required")
+
+
+def _entity_rule(rule: Mapping, schema: bst.types.Namespace):
+ dir_regex = []
+ entity_regex = []
+ for ent in schema.rules.entities:
+ if ent not in rule["entities"]:
+ continue
+ ent_obj = rule["entities"][ent]
+ if isinstance(ent_obj, str):
+ ent_obj = {"level": ent_obj}
+ # Allow filename rule to override original entity fields
+ entity = {**schema.objects.entities[ent], **ent_obj}
+
+ if "enum" in entity:
+ allowed_values = []
+ for value in entity["enum"]:
+ if isinstance(value, str):
+ allowed_values.append(value)
+ else:
+ allowed_values.append(value["name"])
+
+ pattern = "|".join(allowed_values)
+ else:
+ pattern = schema.objects.formats[entity["format"]].pattern
+
+ dir_regex.append(
+ _format_entity(ent, entity["name"], pattern, entity["level"], directory=True)
+ )
+ entity_regex.append(_format_entity(ent, entity["name"], pattern, entity["level"]))
+
+ dtypes = set(rule.get("datatypes", ()))
+ optional_dtype = "" in dtypes
+ if optional_dtype:
+ dtypes.remove("")
+ if dtypes:
+ pattern = f"(?P{'|'.join(dtypes)})/"
+ if optional_dtype:
+ pattern = f"(?:{pattern})?"
+ dir_regex += pattern
+
+ # If we move to referring to suffixes by keys in the object table:
+ # suffixes = [schema.objects.suffixes[suffix].value for suffix in rule["suffixes"]]
+ suffixes = rule["suffixes"]
+ suffix_regex = f"(?P{'|'.join(suffixes)})"
+
+ # If we move to referring to extensions by keys in the object table:
+ # extensions = [schema.objects.extensions[ext].value for ext in rule["extensions"]]
+ extensions = rule["extensions"]
+ ext_match = "|".join(_sanitize_extension(ext) for ext in extensions)
+ ext_regex = f"(?P{ext_match})"
+
+ return {
+ "regex": "".join(dir_regex + entity_regex + [suffix_regex, ext_regex]),
+ "mandatory": False,
+ }
+
+
+def _split_inheritance_rules(rule: Mapping) -> ty.List[Mapping]:
+ """Break composite rules into main and sidecar rules
+
+ Implements the inheritance principle for file naming.
+ """
+ heritable_exts = {".tsv", ".json", ".bval", ".bvec"}
+ rule_exts = set(rule["extensions"])
+
+ main_exts = rule_exts - heritable_exts
+ # If a rule only has TSV or JSON files, entities can be
+ # made required
+ if not main_exts:
+ if ".tsv" in rule_exts:
+ main_exts = {".tsv"}
+ elif ".json" in rule_exts:
+ main_exts = {".json"}
+
+ sidecar_exts = rule_exts - main_exts
+ if not sidecar_exts:
+ return [rule]
+
+ sidecar_dtypes = [""] + rule.get("datatypes", [])
+ sidecar_entities = {ent: "optional" for ent in rule["entities"]}
+
+ main_rule = {**rule, **{"extensions": list(main_exts)}}
+ sidecar_rule = {
+ **rule,
+ **{
+ "extensions": list(sidecar_exts),
+ "datatypes": sidecar_dtypes,
+ "entities": sidecar_entities,
+ },
+ }
+
+ return [main_rule, sidecar_rule]
+
+
+def _sanitize_extension(ext: str) -> str:
+ if ext == ".*":
+ return r"\.[a-zA-Z0-9.]+"
+ return re.escape(ext)
+
+
+def _stem_rule(rule: bst.types.Namespace):
+ stem_regex = re.escape(rule.stem)
+ ext_match = "|".join(_sanitize_extension(ext) for ext in rule.extensions)
+ ext_regex = f"(?P{ext_match})"
+
+ return {"regex": stem_regex + ext_regex, "mandatory": rule.level == "required"}
+
+
+def _path_rule(rule: bst.types.Namespace):
+ return {"regex": re.escape(rule.path), "mandatory": rule.level == "required"}
+
+
+def regexify_filename_rules(
+ rule_group: bst.types.Namespace,
+ schema: bst.types.Namespace,
+ level: int,
+):
+ """Load schema rules into regular expressions
+
+ Parameters
+ ----------
+ rule_group : Namespace
+ The set of rules to load from the schema
+ schema : Namespace
+ A nested dictionary, as returned by `bidsschematools.schema.load_schema()`.
+ level : int
+ The depth in rule_group to look for rules
+
+ Returns
+ -------
+ rules : list of dict
+ A list of dictionaries, with keys including 'regex' and 'mandatory'.
+ """
+ regex_schema = []
+ for rule_template in rule_group.values(level=level):
+ # Simple rules, e.g. dataset_description.json, README
+ if "path" in rule_template:
+ regex_schema.append(_path_rule(rule_template))
+ elif "stem" in rule_template:
+ regex_schema.append(_stem_rule(rule_template))
+ else:
+ regex_schema.extend(
+ _entity_rule(rule, schema) for rule in _split_inheritance_rules(rule_template)
+ )
+
+ return regex_schema
+
+
+@lru_cache()
+def regexify_all(schema_dir=None):
+ """
+ Create full path regexes for all BIDS specification files.
+
+ Parameters
+ ----------
+ schema_dir : str, optional
+ A string pointing to a BIDS directory for which paths should be validated.
+
+ Returns
+ -------
+ all_regex : list of dict
+ A list of dictionaries, with keys including 'regex' and 'mandatory'.
+ my_schema : Mapping
+ Nested dictionaries representing the full schema.
+ """
+
+ schema = bst.schema.load_schema(schema_dir)
+ all_regex = []
+ for group in (schema.rules.files.common, schema.rules.files.raw):
+ all_regex.extend(regexify_filename_rules(group, schema, level=2))
+
+ return all_regex, schema
diff --git a/tools/schemacode/bidsschematools/schema.py b/tools/schemacode/bidsschematools/schema.py
index 57dd3ce956..22dac02cb6 100644
--- a/tools/schemacode/bidsschematools/schema.py
+++ b/tools/schemacode/bidsschematools/schema.py
@@ -19,17 +19,24 @@ class BIDSSchemaError(Exception):
"""Errors indicating invalid values in the schema itself"""
-def _get_entry_name(path):
- if path.suffix == ".yaml":
- return path.name[:-5] # no .yaml
- else:
- return path.name
+def _get_schema_version(schema_dir):
+ """
+ Determine schema version for given schema directory, based on file specification.
+ """
+
+ schema_version_path = os.path.join(schema_dir, "SCHEMA_VERSION")
+ with open(schema_version_path) as f:
+ schema_version = f.readline().rstrip()
+ return schema_version
-def _get_bids_version(bids_schema_dir):
- """Determine schema version, with directory name, file specification, and string fallback."""
+def _get_bids_version(schema_dir):
+ """
+ Determine BIDS version for given schema directory, with directory name, file specification,
+ and string fallback.
+ """
- bids_version_path = os.path.join(bids_schema_dir, "BIDS_VERSION")
+ bids_version_path = os.path.join(schema_dir, "BIDS_VERSION")
try:
with open(bids_version_path) as f:
bids_version = f.readline().rstrip()
@@ -37,18 +44,28 @@ def _get_bids_version(bids_schema_dir):
except FileNotFoundError:
# Maybe the directory encodes the version, as in:
# https://github.com/bids-standard/bids-schema
- _, bids_version = os.path.split(bids_schema_dir)
+ _, bids_version = os.path.split(schema_dir)
if not re.match(r"^.*?[0-9]*?\.[0-9]*?\.[0-9]*?.*?$", bids_version):
# Then we don't know, really.
- bids_version = bids_schema_dir
+ bids_version = schema_dir
return bids_version
def _find(obj, predicate):
- """Find objects in an arbitrary object that satisfy a predicate
+ """Find objects in an arbitrary object that satisfy a predicate.
Note that this does not cut branches, so every iterable sub-object
will be fully searched.
+
+ Parameters
+ ----------
+ obj : object
+ predicate : function
+
+ Returns
+ -------
+ generator
+ A generator of entries in ``obj`` that satisfy the predicate.
"""
try:
if predicate(obj):
@@ -67,17 +84,61 @@ def _find(obj, predicate):
def dereference(namespace, inplace=True):
- """Replace references in namespace with the contents of the referred object"""
+ """Replace references in namespace with the contents of the referred object.
+
+ Parameters
+ ----------
+ namespace : Namespace
+ Namespace for which to dereference.
+
+ inplace : bool, optional
+ Whether to modify the namespace in place or create a copy, by default True.
+
+ Returns
+ -------
+ namespace : Namespace
+ Dereferenced namespace
+ """
if not inplace:
namespace = deepcopy(namespace)
+
for struct in _find(namespace, lambda obj: "$ref" in obj):
- target = struct.pop("$ref")
- struct.update({**namespace[target], **struct})
+ target = namespace.get(struct["$ref"])
+ if isinstance(target, Mapping):
+ struct.pop("$ref")
+ struct.update({**target, **struct})
+
+ # At this point, any remaining refs are one-off objects in lists
+ for struct in _find(namespace, lambda obj: any("$ref" in sub for sub in obj)):
+ for i, item in enumerate(struct):
+ try:
+ target = item.pop("$ref")
+ except (AttributeError, KeyError):
+ pass
+ else:
+ struct[i] = namespace.get(target)
+
return namespace
def flatten_enums(namespace, inplace=True):
- """Replace enum collections with a single enum
+ """Replace enum collections with a single enum, merging enums contents.
+
+ The function helps reducing the complexity of the schema by assuming
+ that the values in the conditions (anyOf) are mutually exclusive.
+
+ Parameters
+ ----------
+ schema : dict
+ Schema in dictionary form to be flattened.
+
+ Returns
+ -------
+ schema : dict
+ Schema with flattened enums.
+
+ Examples
+ --------
>>> struct = {
... "anyOf": [
@@ -107,7 +168,7 @@ def load_schema(schema_path=None):
This function allows the schema, like BIDS itself, to be specified in
a hierarchy of directories and files.
- File names (minus extensions) and directory names become keys
+ Filenames (minus extensions) and directory names become keys
in the associative array (dict) of entries composed from content
of files and entire directories.
@@ -121,9 +182,14 @@ def load_schema(schema_path=None):
-------
dict
Schema in dictionary form.
+
+ Notes
+ -----
+ This function is cached, so it will only be called once per schema path.
"""
if schema_path is None:
- schema_path = utils.get_schema_path()
+ schema_path = utils.get_bundled_schema_path()
+ lgr.info("No schema path specified, defaulting to the bundled schema, `%s`.", schema_path)
schema = Namespace.from_directory(schema_path)
if not schema.objects:
raise ValueError(f"objects subdirectory path not found in {schema_path}")
@@ -133,10 +199,25 @@ def load_schema(schema_path=None):
dereference(schema)
flatten_enums(schema)
+ schema["bids_version"] = _get_bids_version(schema_path)
+ schema["schema_version"] = _get_schema_version(schema_path)
+
return schema
def export_schema(schema):
+ """Export the schema to JSON format.
+
+ Parameters
+ ----------
+ schema : dict
+ The schema object, in dictionary form.
+
+ Returns
+ -------
+ json : str
+ The schema serialized as a JSON string.
+ """
versioned = Namespace.build({"schema_version": __version__, "bids_version": __bids_version__})
versioned.update(schema)
return versioned.to_json()
@@ -150,7 +231,10 @@ def filter_schema(schema, **kwargs):
schema : dict
The schema object, which is a dictionary with nested dictionaries and
lists stored within it.
- kwargs : dict
+
+ Other Parameters
+ ----------------
+ **kwargs : dict
Keyword arguments used to filter the schema.
Example kwargs that may be used include: "suffixes", "datatypes",
"extensions".
diff --git a/tools/schemacode/bidsschematools/tests/__init__.py b/tools/schemacode/bidsschematools/tests/__init__.py
index e69de29bb2..a826cf2780 100644
--- a/tools/schemacode/bidsschematools/tests/__init__.py
+++ b/tools/schemacode/bidsschematools/tests/__init__.py
@@ -0,0 +1 @@
+"""Tests for bidsschematools."""
diff --git a/tools/schemacode/bidsschematools/tests/data/__init__.py b/tools/schemacode/bidsschematools/tests/data/__init__.py
index e69de29bb2..6415366aea 100644
--- a/tools/schemacode/bidsschematools/tests/data/__init__.py
+++ b/tools/schemacode/bidsschematools/tests/data/__init__.py
@@ -0,0 +1,9 @@
+"""Test data module
+
+.. autofunction:: load_test_data
+"""
+from ...data import Loader
+
+__all__ = ("load_test_data",)
+
+load_test_data = Loader(__package__)
diff --git a/tools/schemacode/bidsschematools/tests/test_expressions.py b/tools/schemacode/bidsschematools/tests/test_expressions.py
new file mode 100644
index 0000000000..5532ef38ba
--- /dev/null
+++ b/tools/schemacode/bidsschematools/tests/test_expressions.py
@@ -0,0 +1,79 @@
+import pytest
+from pyparsing.exceptions import ParseException
+
+from ..expressions import ASTNode, expression
+
+
+def test_schema_expressions(schema_obj):
+ # Basic smoke test. All of these expressions should parse without error.
+ for testexp in schema_obj.meta.expression_tests:
+ expression.parse_string(testexp["expression"])
+
+
+@pytest.mark.parametrize(
+ "expr, disambiguated",
+ [
+ ("a*x**2 + b*x + c == 0", "((((a * (x ** 2)) + (b * x)) + c) == 0)"),
+ ("a.b.c[0][1][2].d(3, 4)", "(((a.b).c)[0][1][2].d)(3, 4)"),
+ (
+ "nifti_header.pixdim[4] == sidecar.RepetitionTime",
+ "((nifti_header.pixdim)[4] == (sidecar.RepetitionTime))",
+ ),
+ (
+ """x.y * 10 ** (-3 * index(["a", "b", "c"], w.z))""",
+ """((x.y) * (10 ** (-3 * index(['"a"', '"b"', '"c"'], (w.z)))))""",
+ ),
+ ("a && b && c || d || e && f", "((a && (b && c)) || (d || (e && f)))"),
+ ("! a && b || ! c == d || e", "(((!a) && b) || ((!(c == d)) || e))"),
+ ],
+)
+def test_expression_associations(expr, disambiguated):
+ # The goal of this test is to use the str() function on the AST to
+ # reproduce the original expression, but with parentheses demonstrating
+ # associativity. For example `a.b.c` could be parsed `(a.b).c` or `a.(b.c)`.
+ parse_results = expression.parse_string(expr)
+ assert len(parse_results) == 1
+ ast = parse_results[0]
+ assert str(ast) == disambiguated
+
+
+def test_selectors(schema_obj):
+ # Selectors happen at the second level in a file
+ # For directories of files, need to look three down
+ for rules, level in (
+ (schema_obj.rules.checks, 3),
+ (schema_obj.rules.dataset_metadata, 2),
+ (schema_obj.rules.sidecars, 3),
+ (schema_obj.rules.tabular_data, 3),
+ (schema_obj.rules.errors, 2),
+ ):
+ keys = (key for key in rules.keys(level=level) if key.endswith("selectors"))
+ for key in keys:
+ for selector in rules[key]:
+ ast = expression.parse_string(selector)[0]
+ assert isinstance(ast, ASTNode)
+
+
+def test_checks(schema_obj):
+ checks = (
+ expr
+ for key, value in schema_obj.rules.checks.items(level=3)
+ if key.endswith("checks")
+ for expr in value
+ )
+ for check in checks:
+ ast = expression.parse_string(check)[0]
+ assert isinstance(ast, ASTNode)
+
+
+@pytest.mark.parametrize(
+ "expr",
+ (
+ "func(x, y",
+ "lhs == ",
+ "assignment = not.a.thing",
+ ),
+)
+def test_expected_failures(expr):
+ with pytest.raises(ParseException):
+ expression.parse_string(expr)
diff --git a/tools/schemacode/bidsschematools/tests/test_make_testdata.py b/tools/schemacode/bidsschematools/tests/test_make_testdata.py
index 537ef6c07b..5721b630d6 100644
--- a/tools/schemacode/bidsschematools/tests/test_make_testdata.py
+++ b/tools/schemacode/bidsschematools/tests/test_make_testdata.py
@@ -24,13 +24,14 @@ def test_make_archive(bids_examples, bids_error_examples):
Notes
-----
- * Due to intricacies arising from:
- (1) fixtures not working outside of pytest
- (2) implicit teardown leveraging tempdata removal (while held open by yield)
- (3) wrappers evaluating the yield statement
- (4) the desire to not download testdata twice for archive creation
- testdata archive creation is now inconspicuously posing as a test.
- * Archives will be generated under `/tmp/bidsschematools-testdata-SCHEMA_VERSION.tar.gz`
+ Due to intricacies arising from:
+
+ (1) fixtures not working outside of pytest
+ (2) implicit teardown leveraging tempdata removal (while held open by yield)
+ (3) wrappers evaluating the yield statement
+ (4) the desire to not download testdata twice for archive creation
+
+ testdata archive creation is now inconspicuously posing as a test.
"""
testdata_dir = files("bidsschematools.tests.data")
diff --git a/tools/schemacode/bidsschematools/tests/test_render_text.py b/tools/schemacode/bidsschematools/tests/test_render_text.py
index be9ceb132f..3d25fd8b32 100644
--- a/tools/schemacode/bidsschematools/tests/test_render_text.py
+++ b/tools/schemacode/bidsschematools/tests/test_render_text.py
@@ -1,6 +1,8 @@
"""Tests for the bidsschematools package."""
import os
+import pytest
+
from bidsschematools.render import text
@@ -65,12 +67,13 @@ def test_make_glossary(schema_obj, schema_dir):
def test_make_filename_template(schema_obj, schema_dir):
"""
Test whether:
- * the base hierarchy structure of mandatory subject and optional session is
- returned. This should be robust with respect to schema format.
- * each directory contains at least one possible pattern.
- This should be robust with respect to schema format.
- * all files under the datatype rules subdirectory have corresponding entries.
- This may need to be updated for schema hierarchy changes.
+
+ * The base hierarchy structure of mandatory subject and optional session is
+ returned. This should be robust with respect to schema format.
+ * Each directory contains at least one possible pattern.
+ This should be robust with respect to schema format.
+ * All files under the datatype rules subdirectory have corresponding entries.
+ This may need to be updated for schema hierarchy changes.
"""
filename_template = text.make_filename_template("raw", schema_obj, pdf_format=True)
@@ -130,3 +133,17 @@ def test_define_allowed_top_directories(schema_obj):
"""smoke test for allowed top directories."""
test_str = text.define_allowed_top_directories(schema_obj)
assert isinstance(test_str, str)
+
+
+def test_render_text(schema_obj):
+ test_str = text.render_text(
+ schema_obj, key="objects.files.dataset_description.description", src_path=None
+ )
+ assert (
+ test_str == "The file `dataset_description.json` is a JSON file describing the dataset.\n"
+ )
+
+
+def test_render_text_errors(schema_obj):
+ with pytest.raises(ValueError, match="does not refer to a text field"):
+ text.render_text(schema_obj, key="dataset_description", src_path=None)
diff --git a/tools/schemacode/bidsschematools/tests/test_rules.py b/tools/schemacode/bidsschematools/tests/test_rules.py
index c44634f814..693c4b24fe 100644
--- a/tools/schemacode/bidsschematools/tests/test_rules.py
+++ b/tools/schemacode/bidsschematools/tests/test_rules.py
@@ -1,131 +1,117 @@
-"""Simple validation tests on schema rules."""
-import warnings
-from collections.abc import Mapping
+from bidsschematools import rules
+
+from ..types import Namespace
+
+
+def test_entity_rule(schema_obj):
+ # Simple
+ rule = Namespace.build(
+ {
+ "datatypes": ["anat"],
+ "entities": {"subject": "required", "session": "optional"},
+ "suffixes": ["T1w"],
+ "extensions": [".nii"],
+ }
+ )
+ assert rules._entity_rule(rule, schema_obj) == {
+ "regex": (
+ r"sub-(?P[0-9a-zA-Z]+)/"
+ r"(?:ses-(?P[0-9a-zA-Z]+)/)?"
+ r"(?Panat)/"
+ r"sub-(?P=subject)_"
+ r"(?:ses-(?P=session)_)?"
+ r"(?PT1w)"
+ r"(?P\.nii)"
+ ),
+ "mandatory": False,
+ }
-import pytest
+ # Sidecar entities are optional
+ rule = Namespace.build(
+ {
+ "datatypes": ["anat", ""],
+ "entities": {"subject": "optional", "session": "optional"},
+ "suffixes": ["T1w"],
+ "extensions": [".json"],
+ }
+ )
+ assert rules._entity_rule(rule, schema_obj) == {
+ "regex": (
+ r"(?:sub-(?P[0-9a-zA-Z]+)/)?"
+ r"(?:ses-(?P[0-9a-zA-Z]+)/)?"
+ r"(?:(?Panat)/)?"
+ r"(?:sub-(?P=subject)_)?"
+ r"(?:ses-(?P=session)_)?"
+ r"(?PT1w)"
+ r"(?P\.json)"
+ ),
+ "mandatory": False,
+ }
-def _dict_key_lookup(_dict, key, path=[]):
- """Look up any uses of a key in a nested dictionary.
+def test_split_inheritance_rules():
+ rule = {
+ "datatypes": ["anat"],
+ "entities": {"subject": "required", "session": "optional"},
+ "suffixes": ["T1w"],
+ "extensions": [".nii", ".json"],
+ }
- Adapted from https://stackoverflow.com/a/60377584/2589328.
- """
- results = []
- if isinstance(_dict, Mapping):
- if key in _dict:
- results.append((path + [key], _dict[key]))
+ main, sidecar = rules._split_inheritance_rules(rule)
+ assert main == {
+ "datatypes": ["anat"],
+ "entities": {"subject": "required", "session": "optional"},
+ "suffixes": ["T1w"],
+ "extensions": [".nii"],
+ }
+ assert sidecar == {
+ "datatypes": ["", "anat"],
+ "entities": {"subject": "optional", "session": "optional"},
+ "suffixes": ["T1w"],
+ "extensions": [".json"],
+ }
- for k, v in _dict.items():
- results.extend(_dict_key_lookup(v, key, path=path + [k]))
+ # Can't split again
+ (main2,) = rules._split_inheritance_rules(main)
+ assert main2 == {
+ "datatypes": ["anat"],
+ "entities": {"subject": "required", "session": "optional"},
+ "suffixes": ["T1w"],
+ "extensions": [".nii"],
+ }
- elif isinstance(_dict, list):
- for index, item in enumerate(_dict):
- results.extend(_dict_key_lookup(item, key, path=path + [index]))
- return results
+def test_stem_rule():
+ rule = Namespace.build({"stem": "README", "level": "required", "extensions": ["", ".md"]})
+ assert rules._stem_rule(rule) == {
+ "regex": r"README(?P|\.md)",
+ "mandatory": True,
+ }
+ rule = Namespace.build(
+ {"stem": "participants", "level": "optional", "extensions": [".tsv", ".json"]}
+ )
+ assert rules._stem_rule(rule) == {
+ "regex": r"participants(?P\.tsv|\.json)",
+ "mandatory": False,
+ }
-@pytest.mark.validate_schema
-def test_rule_objects(schema_obj):
- """Ensure that all objects referenced in the schema rules are defined in
- its object portion.
- This test currently fails because rules files reference object keys for some object types,
- including entities, columns, and metadata fields,
- but reference "name" or "value" elements of the object definitions for other object types,
- including suffixes and extensions.
- In the case of datatypes, the key and "value" field are always the same.
+def test_path_rule():
+ rule = Namespace.build({"path": "dataset_description.json", "level": "required"})
+ assert rules._path_rule(rule) == {
+ "regex": r"dataset_description\.json",
+ "mandatory": True,
+ }
- Some other object types, such as associated_data, common_principles, formats, modalities,
- and top_level_files, are not checked in the rules at all.
+ rule = Namespace.build({"path": "LICENSE", "level": "optional"})
+ assert rules._path_rule(rule) == {"regex": "LICENSE", "mandatory": False}
- Additionally, this test only checks rules that fit the keys.
- """
- OBJECT_TYPE_MAPPER = {
- "metadata": "fields", # metadata in objects is referred to as fields in rules
- }
- not_found = [] # A list of undefined, but referenced, objects
- for object_type in schema_obj.objects:
- # "files" is both an object name and a grouping of rules
- # The next line would be a false positive hit
- if object_type == "files":
- continue
- # Find all uses of a given object type in the schema rules
- type_instances_in_rules = _dict_key_lookup(
- schema_obj.rules,
- OBJECT_TYPE_MAPPER.get(object_type, object_type),
- )
- if not type_instances_in_rules:
- continue
-
- for type_instance in type_instances_in_rules:
- path, instance = type_instance
- is_list = True
- if isinstance(instance, Mapping):
- instance = list(instance)
- is_list = False
-
- for i_use, use in enumerate(instance):
- if use == "derivatives":
- # Skip derivatives dirs, because the dir is treated as a "use" instead.
- continue
- elif "[]" in use:
- # Rules may reference metadata fields with lists.
- # This test can't handle this yet, so skip.
- continue
- elif "{}" in use:
- # Rules may reference sub-dictionaries in metadata fields.
- # This test can't handle this yet, so skip.
- continue
-
- if object_type in ["extensions", "suffixes"]:
- # Some object types are referenced via their "value" fields in the rules
- object_values = [
- schema_obj["objects"][object_type][k]["value"]
- for k in schema_obj["objects"][object_type].keys()
- ]
- else:
- # But other object types are referenced via their keys
- object_values = list(schema_obj["objects"][object_type].keys())
-
- # Build a list of items mentioned in rules, but not found in objects.
- if use not in object_values:
- temp_path = path[:]
- if is_list:
- temp_path[-1] += f"[{i_use}]"
-
- not_found.append((temp_path, use))
-
- if not_found:
- not_found_string = "\n".join([f"{'.'.join(path)} == {val}" for path, val in not_found])
- raise ValueError(not_found_string)
-
-
-@pytest.mark.validate_schema
-def test_entity_order(schema_obj):
- """Check the order of the entities of the suffix group of each datatype
- and lists those that are out of order.
- """
- status_ok = True
-
- entities_order = schema_obj.rules.entities
-
- for key, group in schema_obj.rules.files.items(level=2):
- print(f"Checking {key}")
- entities = list(group.get("entities", ()))
- correct_order = sorted(entities, key=lambda x: entities_order.index(x))
-
- if entities != correct_order:
- status_ok = False
- warnings.warn(
- f"""\n\nfilename rule {key} has entities out-of-order:
- - got: {entities}
- - should be: {correct_order}
- """
- )
-
- if not status_ok:
- raise RuntimeError(
- "Some suffix groups have their entities out of order. See warnings above."
- )
+def test_regexify_all():
+ schema_all, _ = rules.regexify_all()
+
+ # Check if expected keys are present in all entries
+ for entry in schema_all:
+ assert "regex" in entry
+ assert "mandatory" in entry
diff --git a/tools/schemacode/bidsschematools/tests/test_schema.py b/tools/schemacode/bidsschematools/tests/test_schema.py
index c5a1652d01..b0703361a3 100644
--- a/tools/schemacode/bidsschematools/tests/test_schema.py
+++ b/tools/schemacode/bidsschematools/tests/test_schema.py
@@ -6,15 +6,12 @@
from bidsschematools import __bids_version__, schema, types
+from ..data import load_resource
+
def test__get_bids_version(tmp_path):
# Is the version being read in correctly?
- schema_path = os.path.join(
- os.path.abspath(os.path.dirname(__file__)),
- os.pardir,
- "data",
- "schema",
- )
+ schema_path = str(load_resource("schema"))
bids_version = schema._get_bids_version(schema_path)
assert bids_version == __bids_version__
@@ -312,3 +309,36 @@ def test_dereferencing():
}
},
}
+
+ orig = {
+ "objects": {
+ "enums": {
+ "left": {"value": "L"},
+ "right": {"value": "R"},
+ },
+ "entities.hemisphere": {
+ "name": "hemi",
+ "enum": [
+ {"$ref": "objects.enums.left.value"},
+ {"$ref": "objects.enums.right.value"},
+ ],
+ },
+ },
+ }
+
+ sch = types.Namespace.build(orig)
+ dereffed = schema.dereference(sch)
+ assert dereffed == {
+ "objects": {
+ "enums": {
+ "left": {"value": "L"},
+ "right": {"value": "R"},
+ },
+ "entities": {
+ "hemisphere": {
+ "name": "hemi",
+ "enum": ["L", "R"],
+ },
+ },
+ },
+ }
diff --git a/tools/schemacode/bidsschematools/tests/test_validator.py b/tools/schemacode/bidsschematools/tests/test_validator.py
index 17005ca67d..fd808fb709 100644
--- a/tools/schemacode/bidsschematools/tests/test_validator.py
+++ b/tools/schemacode/bidsschematools/tests/test_validator.py
@@ -3,119 +3,14 @@
import pytest
-from .. import validator
-from ..types import Namespace
-from .conftest import BIDS_ERROR_SELECTION, BIDS_SELECTION
+from bidsschematools.conftest import BIDS_ERROR_SELECTION, BIDS_SELECTION
+from bidsschematools.validator import select_schema_path, validate_bids
-
-def test_path_rule():
- rule = Namespace.build({"path": "dataset_description.json", "level": "required"})
- assert validator._path_rule(rule) == {
- "regex": r"dataset_description\.json",
- "mandatory": True,
- }
-
- rule = Namespace.build({"path": "LICENSE", "level": "optional"})
- assert validator._path_rule(rule) == {"regex": "LICENSE", "mandatory": False}
-
-
-def test_stem_rule():
- rule = Namespace.build({"stem": "README", "level": "required", "extensions": ["", ".md"]})
- assert validator._stem_rule(rule) == {
- "regex": r"README(?P|\.md)",
- "mandatory": True,
- }
-
- rule = Namespace.build(
- {"stem": "participants", "level": "optional", "extensions": [".tsv", ".json"]}
- )
- assert validator._stem_rule(rule) == {
- "regex": r"participants(?P\.tsv|\.json)",
- "mandatory": False,
- }
-
-
-def test_entity_rule(schema_obj):
- # Simple
- rule = Namespace.build(
- {
- "datatypes": ["anat"],
- "entities": {"subject": "required", "session": "optional"},
- "suffixes": ["T1w"],
- "extensions": [".nii"],
- }
- )
- assert validator._entity_rule(rule, schema_obj) == {
- "regex": (
- r"sub-(?P[0-9a-zA-Z]+)/"
- r"(?:ses-(?P[0-9a-zA-Z]+)/)?"
- r"(?Panat)/"
- r"sub-(?P=subject)_"
- r"(?:ses-(?P=session)_)?"
- r"(?PT1w)"
- r"(?P\.nii)"
- ),
- "mandatory": False,
- }
-
- # Sidecar entities are optional
- rule = Namespace.build(
- {
- "datatypes": ["anat", ""],
- "entities": {"subject": "optional", "session": "optional"},
- "suffixes": ["T1w"],
- "extensions": [".json"],
- }
- )
- assert validator._entity_rule(rule, schema_obj) == {
- "regex": (
- r"(?:sub-(?P[0-9a-zA-Z]+)/)?"
- r"(?:ses-(?P[0-9a-zA-Z]+)/)?"
- r"(?:(?Panat)/)?"
- r"(?:sub-(?P=subject)_)?"
- r"(?:ses-(?P=session)_)?"
- r"(?PT1w)"
- r"(?P\.json)"
- ),
- "mandatory": False,
- }
-
-
-def test_split_inheritance_rules():
- rule = {
- "datatypes": ["anat"],
- "entities": {"subject": "required", "session": "optional"},
- "suffixes": ["T1w"],
- "extensions": [".nii", ".json"],
- }
-
- main, sidecar = validator.split_inheritance_rules(rule)
- assert main == {
- "datatypes": ["anat"],
- "entities": {"subject": "required", "session": "optional"},
- "suffixes": ["T1w"],
- "extensions": [".nii"],
- }
- assert sidecar == {
- "datatypes": ["", "anat"],
- "entities": {"subject": "optional", "session": "optional"},
- "suffixes": ["T1w"],
- "extensions": [".json"],
- }
-
- # Can't split again
- (main2,) = validator.split_inheritance_rules(main)
- assert main2 == {
- "datatypes": ["anat"],
- "entities": {"subject": "required", "session": "optional"},
- "suffixes": ["T1w"],
- "extensions": [".nii"],
- }
+from ..data import load_resource
+from .data import load_test_data
def test_inheritance_examples():
- from bidsschematools.validator import validate_bids
-
correct_inheritance = [
"/lala/sub-01/ses-test/sub-01_ses-test_task-sometask_bold.json",
"/lala/sub-01/sub-01_task-sometask_bold.json",
@@ -128,30 +23,12 @@ def test_inheritance_examples():
result = validate_bids(
correct_inheritance + incorrect_inheritance,
- accept_dummy_paths=True,
+ dummy_paths=True,
)
assert result["path_tracking"] == incorrect_inheritance
-def test_load_all():
- from bidsschematools.validator import load_all
-
- # schema_path = "/usr/share/bids-schema/1.7.0/"
- schema_path = os.path.join(
- os.path.abspath(os.path.dirname(__file__)),
- os.pardir,
- "data",
- "schema",
- )
- schema_all, _ = load_all(schema_path)
-
- # Check if expected keys are present in all entries
- for entry in schema_all:
- assert "regex" in list(entry.keys())
- assert "mandatory" in list(entry.keys())
-
-
def test_write_report(tmp_path):
from bidsschematools.validator import write_report
@@ -194,20 +71,11 @@ def test_write_report(tmp_path):
"rawdata/sub-EXC022/anat/sub-EXC022_ses-MRI_flip-1_VFA.nii.gz"
]
- report_path = os.path.join(
- tmp_path,
- "output_bids_validator_xs_write.log",
- )
- expected_report_path = os.path.join(
- os.path.abspath(os.path.dirname(__file__)),
- "data/expected_bids_validator_xs_write.log",
- )
- write_report(validation_result, report_path=report_path)
- with open(report_path, "r") as f:
- report_text = f.read()
- with open(expected_report_path, "r") as f:
- expected_report_text = f.read()
- assert report_text == expected_report_text
+ report_path = tmp_path / "output_bids_validator_xs_write.log"
+ write_report(validation_result, report_path=str(report_path))
+
+ expected_report_path = load_test_data("expected_bids_validator_xs_write.log")
+ assert report_path.read_text() == expected_report_path.read_text()
@pytest.mark.skipif(
@@ -216,15 +84,10 @@ def test_write_report(tmp_path):
)
@pytest.mark.parametrize("dataset", BIDS_SELECTION)
def test_bids_datasets(bids_examples, tmp_path, dataset):
- from bidsschematools.validator import validate_bids
-
- schema_path = "{module_path}/data/schema/"
-
# Validate per dataset:
target = os.path.join(bids_examples, dataset)
result = validate_bids(
target,
- schema_version=schema_path,
)
# Have all files been validated?
assert len(result["path_tracking"]) == 0
@@ -235,10 +98,6 @@ def test_bids_datasets(bids_examples, tmp_path, dataset):
reason="no network",
)
def test_validate_bids(bids_examples, tmp_path):
- from bidsschematools.validator import validate_bids
-
- schema_path = "{module_path}/data/schema/"
-
# Create input for file list based validation
selected_dir = os.path.join(bids_examples, BIDS_SELECTION[0])
selected_paths = []
@@ -246,27 +105,21 @@ def test_validate_bids(bids_examples, tmp_path):
for f in files:
selected_path = os.path.join(root, f)
selected_paths.append(selected_path)
- # Do version fallback work?
- result = validate_bids(selected_paths, schema_version=None)
+ # Does version fallback work?
+ result = validate_bids(selected_paths, schema_path=False)
# Does default log path specification work?
- result = validate_bids(selected_paths, schema_version=schema_path, report_path=True)
+ result = validate_bids(selected_paths, report_path=True)
# Does custom log path specification work?
result = validate_bids(
selected_paths,
- schema_version=schema_path,
report_path=os.path.join(tmp_path, "test_bids.log"),
)
# Have all files been validated?
assert len(result["path_tracking"]) == 0
# Is the schema version recorded correctly?
- schema_path = os.path.join(
- os.path.abspath(os.path.dirname(__file__)),
- os.pardir,
- "data",
- "schema",
- )
+ schema_path = load_resource("schema")
with open(os.path.join(schema_path, "BIDS_VERSION")) as f:
expected_version = f.readline().rstrip()
assert result["bids_version"] == expected_version
@@ -279,16 +132,12 @@ def test_validate_bids(bids_examples, tmp_path):
def test_broken_json_dataset(bids_examples, tmp_path):
"""Perhaps this can be integrated into
https://github.com/bids-standard/bids-error-examples ."""
- from bidsschematools.validator import validate_bids
dataset = "asl003"
dataset_path = os.path.join(bids_examples, dataset)
dataset_json = os.path.join(dataset_path, "dataset_description.json")
- broken_json = os.path.join(
- os.path.abspath(os.path.dirname(__file__)),
- "data/broken_dataset_description.json",
- )
+ broken_json = load_test_data("broken_dataset_description.json")
shutil.copyfile(broken_json, dataset_json)
# No assert, will simply raise JSON reader error if not catching it properly.
@@ -302,16 +151,65 @@ def test_broken_json_dataset(bids_examples, tmp_path):
os.environ.get("SCHEMACODE_TESTS_NONETWORK") is not None,
reason="no network",
)
-@pytest.mark.parametrize("dataset", BIDS_ERROR_SELECTION)
-def test_error_datasets(bids_error_examples, dataset):
+def test_exclude_files(bids_examples, tmp_path):
+ from bidsschematools.validator import validate_bids
+
+ dataset = "asl003"
+ dataset_reference = os.path.join(bids_examples, dataset)
+ tmp_path = str(tmp_path)
+ shutil.copytree(dataset_reference, tmp_path, dirs_exist_ok=True)
+
+ # Create non-BIDS non-dotfile
+ archive_file_name = "dandiset.yaml"
+ archive_file_path = os.path.join(tmp_path, archive_file_name)
+ with open(archive_file_path, "w") as f:
+ f.write(" \n")
+
+ # Does it fail, as it should (more like a failsafe assertion)
+ result = validate_bids(tmp_path)
+ assert len(result["path_tracking"]) == 1
+
+ # Does the parameter work?
+ result = validate_bids(tmp_path, exclude_files=[archive_file_name])
+ assert len(result["path_tracking"]) == 0
+
+
+@pytest.mark.skipif(
+ os.environ.get("SCHEMACODE_TESTS_NONETWORK") is not None,
+ reason="no network",
+)
+def test_accept_non_bids_dir(bids_examples, tmp_path):
from bidsschematools.validator import validate_bids
- schema_path = "{module_path}/data/schema/"
+ dataset = "asl003"
+ dataset_reference = os.path.join(bids_examples, dataset)
+ tmp_path = str(tmp_path)
+ shutil.copytree(dataset_reference, tmp_path, dirs_exist_ok=True)
+
+ # remove `dataset_description.json`
+ os.remove(os.path.join(tmp_path, "dataset_description.json"))
+
+ # Does it fail, as it should (more like a failsafe assertion)
+ with pytest.raises(
+ ValueError,
+ match="None of the files in the input list are part of a BIDS dataset. Aborting.",
+ ):
+ _ = validate_bids(tmp_path)
+
+ # Does the parameter work?
+ result = validate_bids(tmp_path, accept_non_bids_dir=True)
+ assert len(result["path_tracking"]) == 0
+
+@pytest.mark.skipif(
+ os.environ.get("SCHEMACODE_TESTS_NONETWORK") is not None,
+ reason="no network",
+)
+@pytest.mark.parametrize("dataset", BIDS_ERROR_SELECTION)
+def test_error_datasets(bids_error_examples, dataset):
target = os.path.join(bids_error_examples, dataset)
result = validate_bids(
target,
- schema_version=schema_path,
report_path=True,
)
# Are there non-validated files?
@@ -320,16 +218,34 @@ def test_error_datasets(bids_error_examples, dataset):
def test_gitdir(bids_examples, tmp_path):
"""Maybe better handled in example data?"""
- from distutils.dir_util import copy_tree
-
- from bidsschematools.validator import validate_bids
selected_dir = os.path.join(bids_examples, BIDS_SELECTION[0])
tmp_path = str(tmp_path)
- copy_tree(selected_dir, tmp_path)
+ shutil.copytree(selected_dir, tmp_path, dirs_exist_ok=True)
os.makedirs(os.path.join(tmp_path, ".git"))
with open(os.path.join(tmp_path, ".git", "config"), "w") as temp_file:
temp_file.write("")
result = validate_bids(tmp_path)
assert len(result["path_tracking"]) == 0
+
+
+def test_select_schema_path(bids_examples, tmp_path):
+ dataset = "asl003"
+ dataset_path = os.path.join(bids_examples, dataset)
+
+ # Does fallback to None work without any `raise`?
+ schema_path = select_schema_path(dataset_path)
+ assert schema_path is None
+
+
+def test_bids_schema_versioncheck(monkeypatch):
+ """Test incompatible version."""
+ import bidsschematools as bst
+
+ from ..utils import get_bundled_schema_path
+
+ schema_dir = get_bundled_schema_path()
+ assert bst.validator._bids_schema_versioncheck(schema_dir)
+ monkeypatch.setattr(bst, "__version__", "99.99.99")
+ assert not bst.validator._bids_schema_versioncheck(schema_dir)
diff --git a/tools/schemacode/bidsschematools/types/__init__.py b/tools/schemacode/bidsschematools/types/__init__.py
index b0bffeb806..79c8eb8996 100644
--- a/tools/schemacode/bidsschematools/types/__init__.py
+++ b/tools/schemacode/bidsschematools/types/__init__.py
@@ -1 +1,3 @@
+"""Types and data structures used in bidsschematools."""
+
from .namespace import Namespace
diff --git a/tools/schemacode/bidsschematools/types/namespace.py b/tools/schemacode/bidsschematools/types/namespace.py
index c637a44cd3..b59e8bb586 100644
--- a/tools/schemacode/bidsschematools/types/namespace.py
+++ b/tools/schemacode/bidsschematools/types/namespace.py
@@ -1,4 +1,9 @@
-"""Namespace types"""
+"""Namespace types
+
+The purpose of the :class:`~Namespace` type is to make a directory of
+YAML files available as a single dictionary and allow attribute (``.``)
+lookups.
+"""
import json
import typing as ty
from collections.abc import ItemsView, KeysView, Mapping, MutableMapping, ValuesView
@@ -19,6 +24,16 @@ def _expand_dots(entry: ty.Tuple[str, ty.Any]) -> ty.Tuple[str, ty.Any]:
def expand(element):
"""Expand a dict, recursively, to replace dots in keys with recursive dictionaries
+ Parameters
+ ----------
+ element : dict
+ The dictionary to expand
+
+ Returns
+ -------
+ dict :
+ The expanded dictionary
+
Examples
--------
>>> expand({"a": 1, "b.c": 2, "d": [{"e": 3, "f.g": 4}]})
diff --git a/tools/schemacode/bidsschematools/utils.py b/tools/schemacode/bidsschematools/utils.py
index a848eed8b1..308fd981f6 100644
--- a/tools/schemacode/bidsschematools/utils.py
+++ b/tools/schemacode/bidsschematools/utils.py
@@ -1,9 +1,10 @@
"""Utility functions for the bids-specification schema."""
import logging
-import os.path as op
+from . import data
-def get_schema_path():
+
+def get_bundled_schema_path():
"""Get the path to the schema directory.
Returns
@@ -11,7 +12,7 @@ def get_schema_path():
str
Absolute path to the directory containing schema-related files.
"""
- return op.abspath(op.join(op.dirname(__file__), "data", "schema"))
+ return str(data.load_resource("schema"))
def get_logger(name=None):
diff --git a/tools/schemacode/bidsschematools/validator.py b/tools/schemacode/bidsschematools/validator.py
index 5eba1c730a..34621a8c60 100644
--- a/tools/schemacode/bidsschematools/validator.py
+++ b/tools/schemacode/bidsschematools/validator.py
@@ -1,31 +1,141 @@
+"""A partial implementation of schema-based validation in Python."""
+
import datetime
+import fnmatch
import json
import os
import re
-import typing as ty
-from collections.abc import Mapping
from copy import deepcopy
-from functools import lru_cache
from pathlib import Path
import bidsschematools as bst
+import bidsschematools.rules
import bidsschematools.schema
import bidsschematools.types
import bidsschematools.utils
lgr = bst.utils.get_logger()
-# The list of which entities create directories could be dynamically specified by the YAML, but for
-# now, it is not.
-# Ordering is important, as "subject" follows "session" alphabetically, but is hierarchically
-# above it.
-DIR_ENTITIES = ["subject", "session"]
+VALIDATOR_SCHEMA_COMPATIBILITY_LEVEL = "minor"
+
+
+def _bids_schema_versioncheck(schema_dir, compatibility=VALIDATOR_SCHEMA_COMPATIBILITY_LEVEL):
+ """
+ Use the `SCHEMA_VERSION` descriptor file to determine whether a selected schema directory is
+ compatible with the validator.
+
+ Parameters
+ ----------
+ schema_dir : str
+ A string which specifies a path.
+ compatibility : "major" or "minor", optional
+ Compatibility range of the validator. Major means "M.*" are considered compatible, minor
+ means "M.m.*" are considered compatible.
+
+ Returns
+ -------
+ bool:
+ Whether the schema is compatible with the validator.
+ """
+
+ if compatibility not in ("major", "minor"):
+ raise ValueError("Schema compatibility needs to be set to either “major” or “minor”.")
+
+ schema_version_file = os.path.join(schema_dir, "SCHEMA_VERSION")
+ try:
+ with open(schema_version_file, "r") as f:
+ schema_version = f.readlines()[0].strip()
+ except FileNotFoundError:
+ lgr.warning(
+ "The selected schema directory, `%s`, does not contain a SCHEMA_VERSION file. "
+ "Cannot ascertain compatibility. Attempting to query the BIDS reference for "
+ "compatible versions.",
+ schema_dir,
+ )
+ else:
+ nparts = 1 if compatibility == "major" else 2
+ return schema_version.split(".", nparts)[:-1] == bst.__version__.split(".", nparts)[:-1]
+ lgr.warning(
+ "The selected schema `%s`, has a schema version (`%s`) which is "
+ "incompatible with the validator. Attempting to query the BIDS reference "
+ "for compatible versions.",
+ schema_dir,
+ schema_version,
+ )
+ return False
+
+
+def _find_bids_root(in_paths, accept_non_bids_dir):
+ """
+ Return BIDS root for a list of paths.
+ Raise error if more than one root is found and, optionally, if none is found.
+ """
+
+ dataset_descriptions = []
+ for in_path in in_paths:
+ in_path = os.path.abspath(os.path.expanduser(in_path))
+ dataset_description = _find_dataset_description(in_path)
+ if dataset_description and dataset_description not in dataset_descriptions:
+ dataset_descriptions.append(dataset_description)
+ if len(dataset_descriptions) > 1:
+ raise ValueError(
+ f"You have selected files belonging to {len(dataset_descriptions)} "
+ "different datasets. Please run the validator once per dataset."
+ )
+ elif len(dataset_descriptions) == 0:
+ if accept_non_bids_dir:
+ lgr.warning(
+ "None of the files in the input list are part of a BIDS dataset. Proceeding."
+ )
+ return ""
+ else:
+ raise ValueError(
+ "None of the files in the input list are part of a BIDS dataset. Aborting."
+ )
+ return os.path.dirname(dataset_descriptions[0])
+
+
+def _find_dataset_description(my_path):
+ my_path = Path(my_path)
+ for path in (my_path, *my_path.parents):
+ candidate = path / "dataset_description.json"
+ if candidate.is_file():
+ return candidate
+ return None
+
+
+def _get_directory_suffixes(my_schema):
+ """Query schema for suffixes which identify directory entities.
+
+ Parameters
+ ----------
+ my_schema : dict
+ Nested directory as produced by `bidsschematools.schema.load_schema()`.
+
+ Returns
+ -------
+ list of str
+ Directory pseudofile suffixes excluding trailing slashes.
+
+ Notes
+ -----
+ * Yes this seems super-awkward to do explicitly, after all, the trailing slash is
+ already in so it should automagically work, but no:
+ - Subdirectory names need to be dynamically excluded from validation input.
+ - Backslash directory delimiters are still in use, which is regrettable.
+ """
+ return [
+ ext.value[:-1]
+ for ext in my_schema.objects.extensions.values()
+ if len(ext.value) > 1 and ext.value.endswith("/")
+ ]
def _get_paths(
bids_paths,
pseudofile_suffixes=[],
- accept_dummy_paths=False,
+ dummy_paths=False,
+ exclude_files=[],
):
"""
Get all paths from a list of directories, excluding hidden subdirectories from distribution.
@@ -38,35 +148,43 @@ def _get_paths(
pseudofile_suffixes : list of str
Directory suffixes prompting the validation of the directory name and limiting further
directory walk.
- accept_dummy_paths : bool, optional
+ dummy_paths : bool, optional
Whether to accept path strings which do not correspond to either files or directories.
+ exclude_files : list, optional
+ Files to exclude from listing.
+ Dot files (`.*`) do not need to be explicitly listed, as these are excluded by default.
Notes
-----
- * Figure out how to return paths from BIDS root.
* Deduplicate paths (if input dirs are subsets of other input dirs), might best be done at the
- very end.
+ very end. This is only relevant for poor usage (e.g. passing parent *and* child
+ directories), and has thus far not caused problems, but it would be good to brace for
+ that.
+ * The `dataset_description.json` and `.bidsignore` handling should be split out of the main
+ loop, since we now have BIDS root detection called before file detection. This is
+ non-critical however, since the topdown `os.walk` makes sure top-level files are detected
+ first.
"""
- # `.bidsignore` is not, in fact, a BIDS file, as per:
- # https://github.com/bids-standard/bids-specification/issues/980
- # Perhaps this should be parameterized for downstream flexibility and not having to keep track
- # of downstream nuisance files here.
- exclude_files = [
- "dandiset.yaml",
- ]
path_list = []
+ bidsignore_list = []
bids_root_found = False
for bids_path in bids_paths:
- if not accept_dummy_paths:
+ if not dummy_paths:
bids_path = os.path.abspath(os.path.expanduser(bids_path))
if os.path.isdir(bids_path):
for root, dirs, file_names in os.walk(bids_path, topdown=True):
if "dataset_description.json" in file_names:
if bids_root_found:
+ # No nested BIDS.
dirs[:] = []
file_names[:] = []
else:
+ try:
+ with open(os.path.join(root, ".bidsignore")) as f:
+ bidsignore_list = f.read().splitlines()
+ except FileNotFoundError:
+ pass
bids_root_found = True
if root.endswith(tuple(pseudofile_suffixes)):
# Add the directory name to the validation paths list.
@@ -74,249 +192,190 @@ def _get_paths(
# Do not index the contents of the directory.
dirs[:] = []
file_names[:] = []
- # will break if BIDS ever puts meaningful data under `/.{dandi,datalad,git}*/`
if os.path.basename(root).startswith("."):
dirs[:] = []
file_names[:] = []
for file_name in file_names:
if file_name in exclude_files or file_name.startswith("."):
continue
+ if bidsignore_list:
+ ignored = False
+ for ignore_expression in bidsignore_list:
+ ignored = _bidsignore_check(ignore_expression, file_name, root)
+ if ignored:
+ break
+ if ignored:
+ continue
file_path = os.path.join(root, file_name)
- # This will need to be replaced with bids root finding.
path_list.append(Path(file_path).as_posix())
- elif os.path.isfile(bids_path) or accept_dummy_paths:
+ elif os.path.isfile(bids_path) or dummy_paths:
path_list.append(Path(bids_path).as_posix())
else:
raise FileNotFoundError(
f"The input path `{bids_path}` could not be located. If this is a string "
"intended for path validation which does not correspond to an actual "
- "path, please set the `accept_dummy_paths` parameter to True."
+ "path, please set the `dummy_paths` parameter to True."
)
return path_list
-def _capture_regex(name, pattern, backref):
- """Capture pattern to name or match back-reference to name
-
- >>> _capture_regex("run", "[0-9]+", False)
- '(?P[0-9]+)'
- >>> _capture_regex("run", "[0-9]+", True)
- '(?P=run)'
- >>> re.match(_capture_regex("run", "[0-9]+", False), "123_").groupdict()
- {'run': '123'}
- """
- return f"(?P={name})" if backref else f"(?P<{name}>{pattern})"
-
-
-def _optional_regex(regex, optional):
- """Return an optional version of a regex if optional is True
-
- A required regex is passed through unchanged:
-
- >>> pattern = _optional_regex("xyz", False)
- >>> pattern
- 'xyz'
- >>> re.match(pattern, "xyz").groups()
- ()
- >>> re.match(pattern, "") is None
- True
-
- An optional regex uses a non-capturing group, to avoid interfering
- with existing groups
-
- >>> pattern = _optional_regex("x(?P[a-z])z", True)
- >>> pattern
- '(?:x(?P[a-z])z)?'
- >>> re.match(pattern, "xyz").groups()
- ('y',)
- >>> re.match(pattern, "xyz").groupdict()
- {'name': 'y'}
- >>> re.match(pattern, "").groups()
- (None,)
- >>> re.match(pattern, "").groupdict()
- {'name': None}
+def _bidsignore_check(ignore_expression, file_name, file_root):
"""
- return f"(?:{regex})?" if optional else regex
+ Check whether a file is set to be ignored as per `.bidsignore`
+ Parameters
+ ----------
+ ignore_expression : str
+ A string following git-wildcard conventions (including `**/`).
+ file_name : str
+ A string which represents a filename.
+ file_root : str
+ The directory containing the file specifiled in `file_name`.
-@lru_cache()
-def _format_entity(entity, name, pattern, level, directory=False):
- if directory and entity not in DIR_ENTITIES:
- return ""
+ Returns
+ -------
+ bool:
+ Whether the file should be ignored.
- label = _capture_regex(entity, pattern, not directory and entity in DIR_ENTITIES)
- post = "/" if directory else "_"
+ Notes
+ -----
+ * We cannot use `glob` since that would preempt working with theoretical or non-local
+ paths, therefore we use `fnmatch`.
+ * `fnmatch` does not support `**/` matching as that is an optional convention from e.g.
+ globstar and git, and not part of the standard Unix shell. As we formalize `.bidsignore`
+ we may choose drop it, since we already treat simple filenames as to-be-ignored in
+ all directories, and with BIDS having only up to 4 hierarchical levels, the utility of
+ other usage is limited and expansion to `..*/*..` would only mean at maximum a duplication
+ of entries.
+ """
- return _optional_regex(f"{name}-{label}{post}", level != "required")
+ if ignore_expression.startswith("**/"):
+ ignore_expression = ignore_expression.lstrip("**/")
+ elif any(i in ignore_expression for i in ["/", "\\"]):
+ file_name = os.path.join(file_root, file_name)
+ return fnmatch.fnmatch(file_name, ignore_expression)
-def split_inheritance_rules(rule: Mapping) -> ty.List[Mapping]:
- """Break composite rules into main and sidecar rules
- Implements the inheritance principle for file naming.
+def log_errors(validation_result):
"""
- heritable_exts = {".tsv", ".json", ".bval", ".bvec"}
- rule_exts = set(rule["extensions"])
-
- main_exts = rule_exts - heritable_exts
- # If a rule only has TSV or JSON files, entities can be
- # made required
- if not main_exts:
- if ".tsv" in rule_exts:
- main_exts = {".tsv"}
- elif ".json" in rule_exts:
- main_exts = {".json"}
-
- sidecar_exts = rule_exts - main_exts
- if not sidecar_exts:
- return [rule]
-
- sidecar_dtypes = [""] + rule.get("datatypes", [])
- sidecar_entities = {ent: "optional" for ent in rule.get("entities")}
-
- main_rule = {**rule, **{"extensions": list(main_exts)}}
- sidecar_rule = {
- **rule,
- **{
- "extensions": list(sidecar_exts),
- "datatypes": sidecar_dtypes,
- "entities": sidecar_entities,
- },
- }
-
- return [main_rule, sidecar_rule]
-
-
-def _path_rule(rule: bst.types.Namespace):
- return {"regex": re.escape(rule.path), "mandatory": rule.level == "required"}
-
-
-def _sanitize_extension(ext: str) -> str:
- if ext == ".*":
- return r"\.[a-zA-Z0-9.]+"
- return re.escape(ext)
-
-
-def _stem_rule(rule: bst.types.Namespace):
- stem_regex = re.escape(rule.stem)
- ext_match = "|".join(_sanitize_extension(ext) for ext in rule.extensions)
- ext_regex = f"(?P{ext_match})"
-
- return {"regex": stem_regex + ext_regex, "mandatory": rule.level == "required"}
-
-
-def _entity_rule(rule: Mapping, schema: bst.types.Namespace):
- dir_regex = []
- entity_regex = []
- for ent in schema.rules.entities:
- if ent not in rule["entities"]:
- continue
- ent_obj = rule["entities"][ent]
- if isinstance(ent_obj, str):
- ent_obj = {"level": ent_obj}
- # Allow filename rule to override original entity fields
- entity = {**schema.objects.entities[ent], **ent_obj}
-
- if "enum" in entity:
- pattern = "|".join(entity["enum"])
- else:
- pattern = schema.objects.formats[entity["format"]].pattern
-
- dir_regex.append(
- _format_entity(ent, entity["name"], pattern, entity["level"], directory=True)
- )
- entity_regex.append(_format_entity(ent, entity["name"], pattern, entity["level"]))
-
- dtypes = set(rule.get("datatypes", ()))
- optional_dtype = "" in dtypes
- if optional_dtype:
- dtypes.remove("")
- if dtypes:
- pattern = f"(?P{'|'.join(dtypes)})/"
- if optional_dtype:
- pattern = f"(?:{pattern})?"
- dir_regex += pattern
-
- # If we move to referring to suffixes by keys in the object table:
- # suffixes = [schema.objects.suffixes[suffix].value for suffix in rule["suffixes"]]
- suffixes = rule["suffixes"]
- suffix_regex = f"(?P{'|'.join(suffixes)})"
-
- # If we move to referring to extensions by keys in the object table:
- # extensions = [schema.objects.extensions[ext].value for ext in rule["extensions"]]
- extensions = rule["extensions"]
- ext_match = "|".join(_sanitize_extension(ext) for ext in extensions)
- ext_regex = f"(?P{ext_match})"
-
- return {
- "regex": "".join(dir_regex + entity_regex + [suffix_regex, ext_regex]),
- "mandatory": False,
- }
-
-
-def load_filename_rules(
- rule_group: bst.types.Namespace,
- schema: bst.types.Namespace,
- level: int,
-):
- """Load schema rules into regular expressions
+ Raise errors for validation result.
Parameters
----------
- rule_group : Namespace
- The set of rules to load from the schema
- schema : Namespace
- A nested dictionary, as returned by `bidsschematools.schema.load_schema()`.
- level : int
- The depth in rule_group to look for rules
-
- Returns
- -------
- rules : list of dict
- A list of dictionaries, with keys including 'regex' and 'mandatory'.
+ validation_result : dict
+ A dictionary as returned by `validate_all()` with keys including "schema_tracking",
+ "path_tracking", "path_listing", and, optionally "itemwise".
+ The "itemwise" value, if present, should be a list of dictionaries, with keys including
+ "path", "regex", and "match".
"""
- regex_schema = []
- for rule_template in rule_group.values(level=level):
- # Simple rules, e.g. dataset_description.json, README
- if "path" in rule_template:
- regex_schema.append(_path_rule(rule_template))
- elif "stem" in rule_template:
- regex_schema.append(_stem_rule(rule_template))
- else:
- regex_schema.extend(
- _entity_rule(rule, schema) for rule in split_inheritance_rules(rule_template)
- )
-
- return regex_schema
+ total_file_count = len(validation_result["path_listing"])
+ validated_files_count = total_file_count - len(validation_result["path_tracking"])
+ errorless = True
+ for i in validation_result["path_tracking"]:
+ lgr.warning("The `%s` file was not matched by any regex schema entry.", i)
+ errorless = False
+ if validated_files_count == 0:
+ lgr.error("No valid BIDS files were found.")
+ errorless = False
+ else:
+ # No use reporting this separately if no BIDS files were found
+ for entry in validation_result["schema_tracking"]:
+ if entry["mandatory"]:
+ lgr.error(
+ "The `%s` regex pattern file required by BIDS was not found.",
+ entry["regex"],
+ )
+ errorless = False
+ if errorless:
+ lgr.info("SUCCESS: All files are BIDS valid and no BIDS-required files are missing.")
-@lru_cache()
-def load_all(
- schema_dir,
+def select_schema_path(
+ bids_version=None,
+ bids_root=None,
+ bids_reference_root=None,
):
"""
- Create full path regexes for all BIDS specification files.
+ Select schema directory, according to a priority logic whereby the schema path is
+ either:
+
+ (1) a concatenation of `bids_reference_root` and `bids_version`, if the latter is
+ specified, and the BIDS version schema is compatible with the validator,
+ (2) a concatenation of `bids_reference_root` the detected version specification
+ inside the BIDS root directory, if such a directory is provided and the BIDS version
+ schema is compatible with the validator.
+ (3) `None`, expanded to the bundled schema supplied with the validator by
+ `bst.utils.get_bundled_schema_path`.
Parameters
----------
- schema_dir : str, optional
- A string pointing to a BIDS directory for which paths should be validated.
+ bids_root : str or None, optional
+ The path to the BIDS root for the paths to be validated.
+ bids_reference_root : str, optional
+ Path where schema versions are stored, and which contains directories named exactly
+ according to the respective schema version, e.g. "1.7.0".
+ bids_version : str or None, optional
+ BIDS version desired for validation.
+ If empty, the `dataset_description.json` fie will be queried for the dataset schema
+ version.
+
Returns
-------
- all_regex : list of dict
- A list of dictionaries, with keys including 'regex' and 'mandatory'.
- my_schema : Mapping
- Nested dictionaries representing the full schema.
+ str
+ A string which is a path to the selected schema directory.
+
+ Notes
+ -----
+ * This is a purely aspirational function, and is pre-empted by logic inside
+ `bst.validator.validate_bids()`, and further contingent on better schema stability and
+ ongoing work in: https://github.com/bids-standard/bids-schema
+ * The default `bids_reference_root` value is based on the FHS and ideally should be enforced.
+ Alternatively this could be handled by an environment variable, though that also requires
+ enforcement on the package distribution side.
"""
- schema = bst.schema.load_schema(schema_dir)
- all_regex = []
- for group in (schema.rules.files.common, schema.rules.files.raw):
- all_regex.extend(load_filename_rules(group, schema, level=2))
+ if bids_reference_root is None:
+ lgr.warning("No BIDS reference root provided.")
+ return None
- return all_regex, schema
+ bids_reference_root = os.path.abspath(os.path.expanduser(bids_reference_root))
+
+ schema_dir = False
+ if bids_root and not bids_version:
+ dataset_description = os.path.join(bids_root, "dataset_description.json")
+ with open(dataset_description) as f:
+ try:
+ dataset_info = json.load(f)
+ except json.decoder.JSONDecodeError:
+ lgr.error(
+ "The `%s` file cannot be loaded. Please check whether it is valid JSON.",
+ dataset_description,
+ )
+ else:
+ try:
+ bids_version = dataset_info["BIDSVersion"]
+ except KeyError:
+ lgr.warning("BIDSVersion is not specified in `dataset_description.json`.")
+ if bids_version:
+ schema_dir = os.path.join(bids_reference_root, bids_version)
+ if _bids_schema_versioncheck(schema_dir):
+ return schema_dir
+
+ try:
+ for schema_dir_candidate in os.listdir(bids_reference_root):
+ schema_dir = os.path.join(bids_reference_root, schema_dir_candidate)
+ if _bids_schema_versioncheck(schema_dir):
+ return schema_dir
+ except FileNotFoundError:
+ pass
+ lgr.warning(
+ "No suitable schema could be found in the BIDS reference root (`%s`).",
+ bids_reference_root,
+ )
+ return None
def validate_all(
@@ -334,7 +393,7 @@ def validate_all(
one and only one BIDS directory root (i.e. nested datasets should be validated
separately).
regex_schema : list of dict
- A list of dictionaries as generated by `load_all()`.
+ A list of dictionaries as generated by `regexify_all()`.
Returns
-------
@@ -352,18 +411,21 @@ def validate_all(
groups as well.
"""
- tracking_schema = deepcopy(regex_schema)
tracking_paths = deepcopy(paths_list)
+ tracking_schema = []
itemwise_results = []
matched = False
match_listing = []
for target_path in paths_list:
lgr.debug("Checking file `%s`.", target_path)
lgr.debug("Trying file types:")
- for regex_entry in tracking_schema:
- target_regex = regex_entry["regex"]
+ for regex_entry in regex_schema:
+ target_regex = r"(?:.*/)?" + regex_entry["regex"]
+ # We need to record the actual expressions we query.
+ _regex_entry = deepcopy(regex_entry)
+ _regex_entry.update({"regex": target_regex})
lgr.debug("\t* `%s`, with pattern: `%`", target_path, target_regex)
- matched = re.match(r"(?:.*/)?" + target_regex, target_path)
+ matched = re.match(target_regex, target_path)
itemwise_result = {}
itemwise_result["path"] = target_path
itemwise_result["regex"] = target_regex
@@ -377,12 +439,13 @@ def validate_all(
if matched:
tracking_paths.remove(target_path)
# Might be fragile since it relies on where the loop broke:
- if regex_entry["mandatory"]:
- tracking_schema.remove(regex_entry)
+ if not regex_entry["mandatory"]:
+ tracking_schema.append(_regex_entry)
match_entry = matched.groupdict()
match_entry["path"] = target_path
match_listing.append(match_entry)
else:
+ tracking_schema.append(_regex_entry)
lgr.debug(
"The `%s` file could not be matched to any regex schema entry.",
target_path,
@@ -476,206 +539,16 @@ def write_report(
lgr.info("BIDS validation log written to %s", report_path)
-def _find_dataset_description(my_path):
- candidate = os.path.join(my_path, "dataset_description.json")
- # Windows support... otherwise we could do `if my_path == "/"`.
- if my_path == "/" or not any(i in my_path for i in ["/", "\\"]):
- return None
- if os.path.isfile(candidate):
- return candidate
- else:
- level_up = os.path.dirname(my_path.rstrip("/\\"))
- return _find_dataset_description(level_up)
-
-
-def select_schema_dir(
- bids_paths,
- schema_reference_root,
- schema_version,
- schema_min_version,
-):
- """
- Select schema directory, according to a fallback logic whereby the schema path is
- either (1) `schema_version` if the value is a path, (2) a concatenation of
- `schema_reference_root` and `schema_version`, (3) a concatenation of the detected
- version specification from a `dataset_description.json` file if one is found in
- parents of the input path, (4) `schema_min_version` if no other version can be found
- or if the detected version from `dataset_description.json` is smaller than
- `schema_min_version`.
-
- Parameters
- ----------
- bids_paths : list of str
- Paths to be validated.
- Entries in this list will be used to crawl the directory tree upwards until a
- dataset_description.json file is found.
- schema_reference_root : str, optional
- Path where schema versions are stored, and which contains directories named exactly
- according to the respective schema version, e.g. "1.7.0".
- If the path starts with the string "{module_path}" it will be expanded relative to the
- module path.
- schema_version : str or None
- Version of BIDS schema, or path to schema.
- If a path is given, this will be expanded and used directly, not concatenated with
- `schema_reference_root`.
- If the path starts with the string "{module_path}" it will be expanded relative to the
- module path.
- If None, the `dataset_description.json` fie will be queried for the dataset schema version.
- schema_min_version : str
- Minimal version to use UNLESS the schema version is manually specified.
- If the version is auto-detected and the version is smaller than schema_min_version,
- schema_min_version will be selected instead.
-
-
- Returns
- -------
-
- """
- # Expand module_path
- module_path = os.path.abspath(os.path.dirname(__file__))
- if schema_reference_root.startswith("{module_path}"):
- schema_reference_root = schema_reference_root.format(module_path=module_path)
- schema_reference_root = os.path.abspath(os.path.expanduser(schema_reference_root))
-
- # Handle path schema specification
- if schema_version:
- if "/" in schema_version:
- schema_dir = schema_version
- if schema_version.startswith("{module_path}"):
- schema_dir = schema_version.format(module_path=module_path)
- schema_dir = os.path.abspath(os.path.expanduser(schema_dir))
- return schema_dir
- schema_dir = os.path.join(schema_reference_root, schema_version)
- return schema_dir
-
- dataset_descriptions = []
- for bids_path in bids_paths:
- bids_path = os.path.abspath(os.path.expanduser(bids_path))
- dataset_description = _find_dataset_description(bids_path)
- if dataset_description and dataset_description not in dataset_descriptions:
- dataset_descriptions.append(dataset_description)
- if len(dataset_descriptions) > 1:
- raise ValueError(
- f"You have selected files belonging to {len(dataset_descriptions)} "
- "different datasets. Please run the validator once per dataset."
- )
- if dataset_descriptions:
- dataset_description = dataset_descriptions[0]
- with open(dataset_description) as f:
- try:
- dataset_info = json.load(f)
- except json.decoder.JSONDecodeError:
- lgr.error(
- "The `%s` file could not be loaded. "
- "Please check whether the file is valid JSON. "
- "Falling back to the `%s` BIDS version.",
- dataset_description,
- schema_min_version,
- )
- schema_version = schema_min_version
- else:
- try:
- schema_version = dataset_info["BIDSVersion"]
- except KeyError:
- lgr.warning(
- "BIDSVersion is not specified in "
- "`dataset_description.json`. "
- "Falling back to `%s`.",
- schema_min_version,
- )
- schema_version = schema_min_version
- if not schema_version:
- lgr.warning(
- "No BIDSVersion could be found for the dataset. Falling back to `%s`.",
- schema_min_version,
- )
- schema_version = schema_min_version
- elif schema_min_version:
- if schema_version < schema_min_version:
- lgr.warning(
- "BIDSVersion `%s` is less than the minimal working "
- "`%s`. "
- "Falling back to `%s`. "
- "To force the usage of earlier versions specify them explicitly "
- "when calling the validator.",
- schema_version,
- schema_min_version,
- schema_min_version,
- )
- schema_version = schema_min_version
- schema_dir = os.path.join(schema_reference_root, schema_version)
- if os.path.isdir(schema_dir):
- return schema_dir
- else:
- raise ValueError(
- f"The expected schema directory {schema_dir} does not exist on the system. "
- "Please ensure the file exists or manually specify a schema version for "
- "which the bidsschematools files are available on your system."
- )
-
-
-def log_errors(validation_result):
- """
- Raise errors for validation result.
-
- Parameters
- ----------
- validation_result : dict
- A dictionary as returned by `validate_all()` with keys including "schema_tracking",
- "path_tracking", "path_listing", and, optionally "itemwise".
- The "itemwise" value, if present, should be a list of dictionaries, with keys including
- "path", "regex", and "match".
- """
- total_file_count = len(validation_result["path_listing"])
- validated_files_count = total_file_count - len(validation_result["path_tracking"])
- if validated_files_count == 0:
- lgr.error("No valid BIDS files were found.")
- for entry in validation_result["schema_tracking"]:
- if entry["mandatory"]:
- lgr.error(
- "The `%s` regex pattern file required by BIDS was not found.",
- entry["regex"],
- )
- for i in validation_result["path_tracking"]:
- lgr.warning("The `%s` file was not matched by any regex schema entry.", i)
-
-
-def _get_directory_suffixes(my_schema):
- """Query schema for suffixes which identify directory entities.
-
- Parameters
- ----------
- my_schema : dict
- Nested directory as produced by `bidsschematools.schema.load_schema()`.
-
- Returns
- -------
- list of str
- Directory pseudofile suffixes excluding trailing slashes.
-
- Notes
- -----
- * Yes this seems super-awkward to do explicitly, after all, the trailing slash is
- already in so it should automagically work, but no:
- - Subdirectory names need to be dynamically excluded from validation input.
- - Backslash directory delimiters are still in use, which is regrettable.
- """
- pseudofile_suffixes = []
- for i in my_schema["objects"]["extensions"].values():
- i_value = i["value"]
- if i_value.endswith("/") and i_value != "/":
- pseudofile_suffixes.append(i_value[:-1])
- return pseudofile_suffixes
-
-
def validate_bids(
in_paths,
- accept_dummy_paths=False,
- schema_reference_root="{module_path}/data/",
- schema_version=None,
+ dummy_paths=False,
+ bids_reference_root=None,
+ schema_path=None,
+ bids_version=None,
report_path=False,
suppress_errors=False,
- schema_min_version="schema",
+ accept_non_bids_dir=False,
+ exclude_files=[],
):
"""
Validate paths according to BIDS schema.
@@ -684,27 +557,28 @@ def validate_bids(
----------
in_paths : str or list of str
Paths which to validate, may be individual files or directories.
- accept_dummy_paths : bool, optional
+ dummy_paths : bool, optional
Whether to accept path strings which do not correspond to either files or directories.
- schema_reference_root : str, optional
+ bids_reference_root : str, optional
Path where schema versions are stored, and which contains directories named exactly
according to the respective schema version, e.g. "1.7.0".
- If the path starts with the string "{module_path}" it will be expanded relative to the
- module path.
- schema_version : str or None, optional
- Version of BIDS schema, or path to schema.
- If a path is given, this will be expanded and used directly, not concatenated with
- `schema_reference_root`.
- If the path starts with the string "{module_path}" it will be expanded relative to the
- module path.
- If None, the `dataset_description.json` fie will be queried for the dataset schema version.
+ Currently this is untested.
+ bids_version : str or None, optional
+ Version of BIDS schema, or path to schema. This supersedes the specification detected in
+ `dataset_description.json` and is itself superseded if `schema_path` is specified.
+ schema_path : str or None, optional
+ If a path is given, this will be expanded and used directly, ignoring all other BIDS
+ version specification logic. This is not relative to `bids_reference_root`.
report_path : bool or str, optional
If `True` a log will be written using the standard output path of `.write_report()`.
If string, the string will be used as the output path.
If the variable evaluates as False, no log will be written.
- schema_min_version : str, optional
- Minimal working schema version, used by the `bidsschematools.select_schema_dir()` function
- only if no schema version is found or a lower schema version is specified by the dataset.
+ accept_non_bids_dir : bool, optional
+ exclude_files : str, optional
+ Files which will not be indexed for validation, use this if your data is in an archive
+ standard which requires the presence of archive-specific files (e.g. DANDI requiring
+ `dandiset.yaml`).
+ Dot files (`.*`) do not need to be explicitly listed, as these are excluded by default.
Returns
-------
@@ -721,8 +595,7 @@ def validate_bids(
from bidsschematools import validator
bids_paths = '~/.data2/datalad/000026/rawdata'
- schema_version='{module_path}/data/schema/'
- validator.validate_bids(bids_paths, schema_version=schema_version)
+ validator.validate_bids(bids_paths)
Notes
-----
@@ -734,27 +607,39 @@ def validate_bids(
if isinstance(in_paths, str):
in_paths = [in_paths]
- bids_schema_dir = select_schema_dir(
- in_paths,
- schema_reference_root,
- schema_version,
- schema_min_version=schema_min_version,
- )
- regex_schema, my_schema = load_all(bids_schema_dir)
+ # Are we dealing with real paths?
+ if dummy_paths:
+ bids_root = None
+ else:
+ bids_root = _find_bids_root(in_paths, accept_non_bids_dir)
+
+ # Select schema path:
+ if not schema_path:
+ schema_path = select_schema_path(
+ bids_version,
+ bids_root,
+ bids_reference_root=bids_reference_root,
+ )
+
+ regex_schema, my_schema = bst.rules.regexify_all(schema_path)
pseudofile_suffixes = _get_directory_suffixes(my_schema)
+
+ # Get list of all paths since inputs can be directories.
bids_paths = _get_paths(
in_paths,
- accept_dummy_paths=accept_dummy_paths,
+ dummy_paths=dummy_paths,
pseudofile_suffixes=pseudofile_suffixes,
+ exclude_files=exclude_files,
)
+
+ # Go!
validation_result = validate_all(
bids_paths,
regex_schema,
)
# Record schema version.
- bids_version = bst.schema._get_bids_version(bids_schema_dir)
- validation_result["bids_version"] = bids_version
+ validation_result["bids_version"] = my_schema["bids_version"]
log_errors(validation_result)
diff --git a/tools/schemacode/docs/.gitignore b/tools/schemacode/docs/.gitignore
new file mode 100644
index 0000000000..8c6492ca8e
--- /dev/null
+++ b/tools/schemacode/docs/.gitignore
@@ -0,0 +1 @@
+api/
diff --git a/tools/schemacode/docs/Makefile b/tools/schemacode/docs/Makefile
index b7b8584379..a9d2648525 100644
--- a/tools/schemacode/docs/Makefile
+++ b/tools/schemacode/docs/Makefile
@@ -5,7 +5,7 @@
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
-SOURCEDIR =
+SOURCEDIR =
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
diff --git a/tools/schemacode/docs/_static/black_logo.svg b/tools/schemacode/docs/_static/black_logo.svg
new file mode 120000
index 0000000000..820d232aa6
--- /dev/null
+++ b/tools/schemacode/docs/_static/black_logo.svg
@@ -0,0 +1 @@
+../../../../BIDS_logo/BIDS_logo_black.svg
\ No newline at end of file
diff --git a/tools/schemacode/docs/_static/white_logo.svg b/tools/schemacode/docs/_static/white_logo.svg
new file mode 120000
index 0000000000..04ac6f3d6e
--- /dev/null
+++ b/tools/schemacode/docs/_static/white_logo.svg
@@ -0,0 +1 @@
+../../../../BIDS_logo/BIDS_logo_white.svg
\ No newline at end of file
diff --git a/tools/schemacode/docs/_templates/module.rst b/tools/schemacode/docs/_templates/module.rst
new file mode 100644
index 0000000000..3cab176d37
--- /dev/null
+++ b/tools/schemacode/docs/_templates/module.rst
@@ -0,0 +1,68 @@
+{{ fullname | escape | underline}}
+
+.. automodule:: {{ fullname }}
+
+{% block modules %}
+{% if modules %}
+.. rubric:: Modules
+
+.. autosummary::
+ :toctree:
+ :template: module.rst
+ :recursive:
+{% for item in modules %}
+ {{ item }}
+{%- endfor %}
+{% endif %}
+{% endblock %}
+
+.. currentmodule:: {{ fullname }}
+
+{% block attributes %}
+{% if attributes %}
+.. rubric:: {{ _('Module Attributes') }}
+
+{% for item in attributes %}
+.. autoattribute:: {{ item }}
+
+{%- endfor %}
+{% endif %}
+{% endblock %}
+
+{% block classes %}
+{% if classes %}
+.. rubric:: {{ _('Classes') }}
+
+{% for item in classes %}
+
+.. autoclass:: {{ item }}
+ :members:
+ :show-inheritance:
+ :inherited-members:
+ :special-members: __call__, __add__, __mul__
+
+{%- endfor %}
+{% endif %}
+{% endblock %}
+
+{% block functions %}
+{% if functions %}
+.. rubric:: {{ _('Functions') }}
+
+{% for item in functions %}
+.. autofunction:: {{ item }}
+
+{%- endfor %}
+{% endif %}
+{% endblock %}
+
+{% block exceptions %}
+{% if exceptions %}
+.. rubric:: {{ _('Exceptions') }}
+
+{% for item in exceptions %}
+.. autoexception:: {{ item }}
+
+{%- endfor %}
+{% endif %}
+{% endblock %}
diff --git a/tools/schemacode/docs/api.rst b/tools/schemacode/docs/api.rst
new file mode 100644
index 0000000000..964088bef4
--- /dev/null
+++ b/tools/schemacode/docs/api.rst
@@ -0,0 +1,9 @@
+Schema Tools API
+================
+
+.. autosummary::
+ :toctree: api
+ :template: module.rst
+ :recursive:
+
+ bidsschematools
diff --git a/tools/schemacode/docs/bidsschematools.rst b/tools/schemacode/docs/bidsschematools.rst
deleted file mode 100644
index 5965ad4b9b..0000000000
--- a/tools/schemacode/docs/bidsschematools.rst
+++ /dev/null
@@ -1,41 +0,0 @@
-.. bidsschematools:
-
-render
-------
-
-.. automodule:: bidsschematools.render
- :members:
- :undoc-members:
- :show-inheritance:
-
-schema
-------
-
-.. automodule:: bidsschematools.schema
- :members:
- :undoc-members:
- :show-inheritance:
-
-utils
------
-
-.. automodule:: bidsschematools.utils
- :members:
- :undoc-members:
- :show-inheritance:
-
-validator
----------
-
-.. automodule:: bidsschematools.validator
- :members:
- :undoc-members:
- :show-inheritance:
-
-types
------
-
-.. automodule:: bidsschematools.types
- :members:
- :undoc-members:
- :show-inheritance:
diff --git a/tools/schemacode/docs/conf.py b/tools/schemacode/docs/conf.py
index 2cf1172ed8..2a93891819 100644
--- a/tools/schemacode/docs/conf.py
+++ b/tools/schemacode/docs/conf.py
@@ -10,18 +10,16 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
-import os
-import pathlib
-import sys
+# import os
+# import pathlib
+# import sys
-schemacode_path = pathlib.Path(os.path.abspath("bidsschematools"))
-sys.path.insert(0, str(schemacode_path))
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
-project = "bidsschematools"
+project = "BIDS Schema Tools"
copyright = "2022, bids-specification"
author = "bids-specification"
@@ -32,13 +30,18 @@
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
+ "sphinx.ext.intersphinx",
"sphinx.ext.duration",
"sphinx.ext.doctest",
- "sphinx_rtd_theme",
"sphinx.ext.coverage",
"sphinx.ext.napoleon",
+ "sphinx.ext.autodoc",
+ "sphinx.ext.autosummary",
+ "myst_parser",
]
+autosummary_generate = True
+
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
@@ -48,14 +51,28 @@
exclude_patterns = ["_build", "tests"]
+autosummary_mock_imports = [
+ "pytest",
+ # Mock internal modules to avoid building docs
+ "bidsschematools.conftest",
+]
+
+intersphinx_mapping = {"python": ("https://docs.python.org/3", None)}
+
+
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
-html_theme = "sphinx_rtd_theme"
+html_theme = "furo"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-# html_static_path = ['_static']
+html_static_path = ["_static"]
+
+html_theme_options = {
+ "light_logo": "black_logo.svg",
+ "dark_logo": "white_logo.svg",
+}
diff --git a/tools/schemacode/docs/description.md b/tools/schemacode/docs/description.md
new file mode 120000
index 0000000000..93e16a2fab
--- /dev/null
+++ b/tools/schemacode/docs/description.md
@@ -0,0 +1 @@
+../../../src/schema/README.md
\ No newline at end of file
diff --git a/tools/schemacode/docs/index.rst b/tools/schemacode/docs/index.rst
index e344ad2340..b6176e0249 100644
--- a/tools/schemacode/docs/index.rst
+++ b/tools/schemacode/docs/index.rst
@@ -1,10 +1,14 @@
-Welcome to bidsschematools's documentation!
-===========================================
+.. include:: ../README.md
+ :parser: myst_parser.sphinx_
+
+Contents
+========
.. toctree::
- modules
:maxdepth: 2
- :caption: Contents:
+
+ description
+ api
Indices and tables
diff --git a/tools/schemacode/docs/modules.rst b/tools/schemacode/docs/modules.rst
deleted file mode 100644
index 6515d1311c..0000000000
--- a/tools/schemacode/docs/modules.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-bidsschematools
-===============
-
-.. toctree::
- :maxdepth: 4
-
- bidsschematools
\ No newline at end of file
diff --git a/tools/schemacode/docs/requirements.txt b/tools/schemacode/docs/requirements.txt
new file mode 100644
index 0000000000..58f4969304
--- /dev/null
+++ b/tools/schemacode/docs/requirements.txt
@@ -0,0 +1,4 @@
+sphinx
+furo
+myst_parser
+pyparsing
diff --git a/tools/schemacode/setup.cfg b/tools/schemacode/setup.cfg
index d8bddf2529..27159dee84 100644
--- a/tools/schemacode/setup.cfg
+++ b/tools/schemacode/setup.cfg
@@ -13,13 +13,13 @@ classifiers =
Intended Audience :: Science/Research
Topic :: Scientific/Engineering :: Information Analysis
License :: OSI Approved :: MIT License
- Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
+ Programming Language :: Python :: 3.11
[options]
-python_requires = >=3.7
+python_requires = >=3.8
install_requires =
click
pyyaml
@@ -36,6 +36,8 @@ render =
tabulate
pandas
markdown-it-py
+expressions =
+ pyparsing
tests =
codecov
coverage[toml]
@@ -48,6 +50,7 @@ all =
%(doc)s
%(render)s
%(tests)s
+ %(expressions)s
[options.package_data]
bidsschematools =
@@ -55,6 +58,7 @@ bidsschematools =
data/schema/SCHEMA_VERSION
data/schema/**/*.yaml
tests/data/**/*
+ tests/data/**/.bidsignore
[options.entry_points]
console_scripts =