studio2ped 0.1.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. studio2ped-0.2.1/CHANGELOG.md +88 -0
  2. studio2ped-0.2.1/MANIFEST.in +4 -0
  3. studio2ped-0.2.1/PKG-INFO +244 -0
  4. studio2ped-0.2.1/README.md +221 -0
  5. {studio2ped-0.1.0 → studio2ped-0.2.1}/pyproject.toml +11 -9
  6. studio2ped-0.2.1/studio2ped/__init__.py +10 -0
  7. {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped/cli.py +17 -4
  8. {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped/converter.py +370 -53
  9. studio2ped-0.2.1/studio2ped.egg-info/PKG-INFO +244 -0
  10. studio2ped-0.2.1/studio2ped.egg-info/SOURCES.txt +30 -0
  11. studio2ped-0.2.1/studio2ped.egg-info/requires.txt +3 -0
  12. studio2ped-0.2.1/studio2ped_standalone.py +1099 -0
  13. studio2ped-0.2.1/tests/data/cf-cousins.studio.json +508 -0
  14. studio2ped-0.2.1/tests/data/expected/cf-cousins.mped +16 -0
  15. studio2ped-0.2.1/tests/data/expected/features.mped +29 -0
  16. studio2ped-0.2.1/tests/data/expected/lynch.mped +22 -0
  17. studio2ped-0.2.1/tests/data/expected/okafor.mped +18 -0
  18. studio2ped-0.2.1/tests/data/expected/price.mped +18 -0
  19. studio2ped-0.2.1/tests/data/features.studio.json +1044 -0
  20. studio2ped-0.2.1/tests/data/lynch.studio.json +712 -0
  21. studio2ped-0.2.1/tests/data/okafor.studio.json +589 -0
  22. studio2ped-0.2.1/tests/data/price.studio.json +582 -0
  23. studio2ped-0.2.1/tests/test_standalone.py +45 -0
  24. studio2ped-0.2.1/tests/test_studio2ped.py +288 -0
  25. studio2ped-0.2.1/tests/test_version.py +13 -0
  26. studio2ped-0.2.1/tools/make_standalone.py +76 -0
  27. studio2ped-0.1.0/PKG-INFO +0 -157
  28. studio2ped-0.1.0/README.md +0 -131
  29. studio2ped-0.1.0/studio2ped/__init__.py +0 -7
  30. studio2ped-0.1.0/studio2ped.egg-info/PKG-INFO +0 -157
  31. studio2ped-0.1.0/studio2ped.egg-info/SOURCES.txt +0 -12
  32. {studio2ped-0.1.0 → studio2ped-0.2.1}/LICENSE +0 -0
  33. {studio2ped-0.1.0 → studio2ped-0.2.1}/setup.cfg +0 -0
  34. {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped/__main__.py +0 -0
  35. {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped.egg-info/dependency_links.txt +0 -0
  36. {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped.egg-info/entry_points.txt +0 -0
  37. {studio2ped-0.1.0 → studio2ped-0.2.1}/studio2ped.egg-info/top_level.txt +0 -0
@@ -0,0 +1,88 @@
1
+ # Changelog
2
+
3
+ ## 0.2.1
4
+
5
+ - A parent marked G is a gestational carrier, and is not written as a genetic
6
+ parent. Since the 2025 correction to the pedigree nomenclature (Steinhaus
7
+ French, Bennett and Resta, *J Genet Couns* 2025;34(2):e2020), G marks a
8
+ gestational carrier, and Pedigree Studio draws G where it used to draw S.
9
+ S, which older drawings use, still means the same.
10
+ - The note says "gestational carrier" and gives the mark (G or S), not
11
+ "surrogate": the corrected nomenclature keeps "surrogate" for a woman who
12
+ gives the egg and carries the pregnancy, who is a genetic parent.
13
+ - A symbol divided into thirds, which Pedigree Studio now draws for three
14
+ conditions, gives each of its colours as a condition the person has, as
15
+ quarters do.
16
+ - The "no children" and "infertility" symbols no longer count when a row is
17
+ numbered. The nomenclature numbers individuals (Bennett et al., *J Genet
18
+ Couns* 2022;31(6):1238-48) and these symbols are not people, so Pedigree
19
+ Studio now gives them no number, and studio2ped numbers only people too:
20
+ someone to the right of one of these symbols in the same row has a number
21
+ one lower than before (II-3 rather than II-4), and a row with nothing but
22
+ these symbols is not a generation.
23
+
24
+ ## 0.2.0
25
+
26
+ - People are now identified by their pedigree numbers (I-1, II-3 and so on),
27
+ worked out exactly as Pedigree Studio shows them and as studio2ga4gh 0.4
28
+ writes them, so the same person has the same identifier in the PED file and
29
+ in the GA4GH files. Version 0.1.0 took identifiers from the text under each
30
+ symbol, which usually holds a name and an age (`Alice_50`), sometimes a
31
+ condition (`Bowel_cancer`), and is not always unique: in the Lynch example
32
+ two cousins labelled 26 got the same identifier, which `mpedtools validate`
33
+ rejects.
34
+ - `--ids` chooses the identifiers: `numbers` (the default), `drawing` (the
35
+ labels the drawing shows under its numbering setting: numbers, letters or
36
+ labels typed in), `studio` (Pedigree Studio's own ids) or `text` (0.1.0's
37
+ identifiers). Whatever the choice, an identifier that would be shared by two
38
+ people, be empty or be `0` (which PED reads as "no parent") is replaced by
39
+ the person's pedigree number, and the command says so.
40
+ - The "no children" and "infertility" symbols are no longer written as
41
+ people. Pedigree Studio stores them as children of the couple, so 0.1.0 put
42
+ them in the file as individuals of unknown sex (29 individuals for the 27
43
+ people in the features example). They are left out now, with a note naming
44
+ the couple.
45
+ - Adoptive parents and surrogates are no longer written as parents. A child
46
+ joined to a couple by a dashed line (an adoption) and a parent marked S (a
47
+ gestational carrier) are not genetic relations, and a linkage program reads
48
+ the father and mother columns as genetic parents, so 0.1.0's files gave
49
+ wrong ones. Those parents are now 0, with a note, which is also what
50
+ studio2ga4gh's PED-style pedigree gives: a solid line from birth parents
51
+ still counts, and so does a donor (D).
52
+ - A symbol standing for several people (a number such as 3 in the symbol) is
53
+ still written as one person, but the command now says so.
54
+ - An ontology id written after a condition's name in the colour key, as
55
+ studio2ga4gh reads it (`Condition X [MONDO:0009061]`), is left out of the
56
+ MPED column name, which is now `Condition_X` instead of
57
+ `Condition_X_MONDO0009061`. Only the ids of ontologies count (the HPO,
58
+ Mondo, OMIM, Orphanet, SNOMED CT and the others studio2ga4gh can declare):
59
+ anything else in brackets, such as `Hypertension (BP:160/95)`, stays part of
60
+ the name. Two conditions that would give the same column name are told apart
61
+ with `_2`, `_3`.
62
+ - Without `-o`, the output is named after the input without `.studio.json`:
63
+ `okafor.studio.json` gives `okafor.mped`, where 0.1.0 wrote
64
+ `okafor.studio.mped`.
65
+ - Within each generation, people are listed in the order of the drawing,
66
+ from left to right, rather than in the order of Pedigree Studio's internal
67
+ ids.
68
+ - Session files are read as UTF-8 (with or without a byte-order mark) and the
69
+ PED files written as UTF-8, whatever the computer's own setting, so names
70
+ with accents no longer fail on Windows.
71
+ - `-d` makes the output folder if it does not exist. In 0.1.0 the README's own
72
+ example (`-d ./output/`) failed unless the folder was already there, and
73
+ the error said that the input file could not be found.
74
+ - `studio2ped_standalone.py` is made from the package by
75
+ `tools/make_standalone.py`, and a test checks that it is up to date and
76
+ writes exactly what the package writes.
77
+ - New in the Python interface: `convert(..., ids=...)`,
78
+ `convert_file(..., ids=...)`, `individual_ids`, `pedigree_numbers` and
79
+ `ID_CHOICES`; `ConversionResult` has `notes` and `ids`.
80
+ - `--version`, tests run on Python 3.9 to 3.14, and releases are published to
81
+ PyPI from GitHub.
82
+
83
+ Commands written for 0.1.0 still run unchanged. To get 0.1.0's identifiers,
84
+ add `--ids text`.
85
+
86
+ ## 0.1.0
87
+
88
+ Released on PyPI on 20 May 2026.
@@ -0,0 +1,4 @@
1
+ include CHANGELOG.md
2
+ include studio2ped_standalone.py
3
+ include tools/make_standalone.py
4
+ recursive-include tests *.py *.json *.mped *.ped
@@ -0,0 +1,244 @@
1
+ Metadata-Version: 2.4
2
+ Name: studio2ped
3
+ Version: 0.2.1
4
+ Summary: Convert Pedigree Studio session JSON to PED/MPED pedigree files
5
+ Author-email: Tim Hearn <tjh70@cam.ac.uk>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/comparativechrono/studio2ped
8
+ Project-URL: Issues, https://github.com/comparativechrono/studio2ped/issues
9
+ Project-URL: Changelog, https://github.com/comparativechrono/studio2ped/blob/main/CHANGELOG.md
10
+ Keywords: pedigree,genetics,ped,family-tree,clinical-genetics,pedigree-studio
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Intended Audience :: Healthcare Industry
14
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
15
+ Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
16
+ Classifier: Programming Language :: Python :: 3
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Provides-Extra: test
21
+ Requires-Dist: pytest>=7; extra == "test"
22
+ Dynamic: license-file
23
+
24
+ # studio2ped
25
+
26
+ Convert families drawn in [Pedigree Studio](https://github.com/comparativechrono/PedigreeStudio)
27
+ into standard PED pedigree files, or into extended MPED (multi-phenotype PED)
28
+ files when more than one condition is recorded.
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pip install studio2ped
34
+ ```
35
+
36
+ It needs Python 3.9 or later and nothing else. You can also use the
37
+ standalone script, `studio2ped_standalone.py`, without installing anything.
38
+
39
+ ## Usage
40
+
41
+ ### Command line
42
+
43
+ ```bash
44
+ # Basic conversion: writes okafor.ped, or okafor.mped if the family needs MPED
45
+ studio2ped okafor.studio.json
46
+
47
+ # Specify output name and directory
48
+ studio2ped okafor.studio.json -o my_pedigree -d ./output/
49
+
50
+ # Identify people by the labels the drawing shows, instead of pedigree numbers
51
+ studio2ped okafor.studio.json --ids drawing
52
+
53
+ # Using the standalone script
54
+ python studio2ped_standalone.py okafor.studio.json
55
+ ```
56
+
57
+ The command prints a short summary, the files it wrote and notes on anything
58
+ you should know: symbols that are not people and were left out, identifiers
59
+ that had to be changed, and so on.
60
+
61
+ Commands written for version 0.1.0 still work. The difference you will notice
62
+ is that people are now identified by their pedigree numbers; add `--ids text`
63
+ to get the identifiers 0.1.0 used (see [Identifying people](#identifying-people)).
64
+
65
+ ### Python API
66
+
67
+ ```python
68
+ import json
69
+ import studio2ped
70
+
71
+ with open("okafor.studio.json", encoding="utf-8") as f:
72
+ session = json.load(f)
73
+
74
+ result = studio2ped.convert(session, "okafor") # ids="drawing", "studio" or "text" to choose identifiers
75
+ print(result.summary)
76
+ for note in result.notes:
77
+ print("Note:", note)
78
+ for filename, content in result.files:
79
+ print(f"--- {filename} ---")
80
+ print(content)
81
+
82
+ # From file to file
83
+ result = studio2ped.convert_file("okafor.studio.json")
84
+ for filepath, _ in result.files:
85
+ print(f"Written: {filepath}")
86
+ ```
87
+
88
+ ## What it does
89
+
90
+ The converter reads a Pedigree Studio session file and:
91
+
92
+ 1. **Detects separate pedigrees**: if the canvas holds several unconnected
93
+ families, each is written to a file of its own.
94
+ 2. **Works out the family structure** from the partnership and child lines,
95
+ not from where the symbols are, so a rearranged pedigree converts correctly.
96
+ 3. **Leaves out symbols that are not people.** Pedigree Studio stores the "no
97
+ children" bar and the "infertility" double bar as children of the couple;
98
+ they are not written as individuals, and a note names the couple.
99
+ 4. **Gives genetic parents only.** The father and mother columns of a PED
100
+ file are genetic parents. A child joined to a couple by a dashed line (an
101
+ adoption) gets 0 for that couple, and so does the place of a parent marked
102
+ G (a gestational carrier), with a note in each case; a parent marked D (a
103
+ donor) is a genetic parent. Drawings made before the 2025 correction to the
104
+ pedigree nomenclature mark a gestational carrier S, which is read the same
105
+ way. studio2ga4gh's PED-style pedigree gives the same.
106
+ 5. **Extracts phenotypes** from the colour key: solid fills, half fills,
107
+ symbols in thirds or quarters, and shading patterns are all recognised.
108
+ 6. **Detects carrier notation**: centre dots (●) and half-filled symbols mark
109
+ carriers.
110
+ 7. **Chooses the format**:
111
+ - **one phenotype** → a standard `.ped` file (6 columns);
112
+ - **several phenotypes**, or **carrier notation alongside a phenotype** → an
113
+ extended `.mped` file.
114
+
115
+ ## Identifying people
116
+
117
+ | `--ids` | Each person is identified by |
118
+ |---|---|
119
+ | `numbers` (the default) | their pedigree number as Pedigree Studio shows it: the generation in Roman numerals and the position from the left, such as `II-3` |
120
+ | `drawing` | the label the drawing shows under its numbering setting: pedigree numbers, letters (`II-C`, or `A`, `B`, `C` …) or labels typed in |
121
+ | `studio` | Pedigree Studio's own id, such as `p-3` |
122
+ | `text` | what version 0.1.0 used: the label typed in if there is one, otherwise the first line of the text under the symbol, otherwise Pedigree Studio's id |
123
+
124
+ Pedigree numbers are the ones studio2ga4gh writes into the GA4GH files, so a
125
+ person can be followed from one file to the other. The text under a symbol
126
+ usually holds a name and an age ("Alice, 50"), so `text`, and `drawing` when
127
+ the drawing has labels typed in, can put names into the file: use them only
128
+ when that is acceptable for wherever the file is going.
129
+
130
+ Identifiers can't contain spaces, so spaces become underscores and other
131
+ punctuation is dropped: "J. Mensah" becomes `J_Mensah`. studio2ga4gh makes its
132
+ ids from labels the same way, so with the same choice a person has the same
133
+ identifier in the PED file and in the GA4GH files. No two people may share an
134
+ identifier, and `0` means "no parent" in a PED file, so if a label would be
135
+ shared by two people (two cousins both labelled "26", say), would be empty or
136
+ would be `0`, the person is given their pedigree number instead, and a note
137
+ says so.
138
+
139
+ Pedigree numbers follow the drawing, as in Pedigree Studio: a new generation
140
+ starts 80 pixels or more below the first person of the generation above, and
141
+ each generation is numbered from the left. Only people are numbered: the "no
142
+ children" and "infertility" symbols are not people, so they have no number
143
+ and do not count in their row.
144
+
145
+ ## Output formats
146
+
147
+ ### Standard PED (`.ped`)
148
+
149
+ The standard 6-column format used by PLINK, GATK and most genetics tools:
150
+
151
+ ```
152
+ # Family: FAM1
153
+ # FamID IndID FatherID MotherID Sex Phenotype
154
+ FAM1 I-1 0 0 1 1
155
+ FAM1 I-2 0 0 2 1
156
+ FAM1 II-1 I-1 I-2 2 2
157
+ ```
158
+
159
+ Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected. The columns
160
+ are separated by tabs.
161
+
162
+ ### Extended MPED (`.mped`)
163
+
164
+ A multi-phenotype extension for pedigrees in which more than one condition is
165
+ tracked. It adds a header line naming the phenotype columns, and a carrier
166
+ status:
167
+
168
+ ```
169
+ # MPED v1 Breast_cancer Carrier
170
+ # FamID IndID FatherID MotherID Sex Breast_cancer Carrier
171
+ FAM1 I-1 0 0 1 1 1
172
+ FAM1 I-2 0 0 2 3 3
173
+ FAM1 II-1 I-1 I-2 2 2 1
174
+ ```
175
+
176
+ Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected, `3` = carrier.
177
+
178
+ The format is easy to read by program: the first line declares the format and
179
+ names the phenotype columns, the second is a header for people to read, and
180
+ the data lines have the same tab-separated columns as standard PED, with one
181
+ more column for each phenotype.
182
+
183
+ Column names are the condition names from the colour key, with spaces turned
184
+ into underscores. An ontology id written in brackets after a name, as
185
+ studio2ga4gh reads it ("Breast cancer [MONDO:0007254]"), is left out of the
186
+ column name, which is then `Breast_cancer`, and a note says which id it had.
187
+ Only the ids of ontologies studio2ga4gh can declare count (the HPO, Mondo,
188
+ OMIM, Orphanet, SNOMED CT and so on): anything else in brackets, such as
189
+ "Hypertension (BP:160/95)", stays part of the name.
190
+ If two conditions would give the same column name, the second becomes
191
+ `Breast_cancer_2`.
192
+
193
+ ## Phenotype detection
194
+
195
+ The converter maps Pedigree Studio's markings to phenotype status using the
196
+ colour key:
197
+
198
+ | Marking | Colour key entry | Status |
199
+ |---------------|-------------------|--------|
200
+ | Solid colour fill | `#rrggbb` | Affected (2) |
201
+ | Full shading (stripes/dots) | `shading:pattern:full` | Affected (2) |
202
+ | Half colour fill (one side only) | `#rrggbb` | Carrier (3) |
203
+ | Half shading | `shading:pattern:half-left` | Carrier (3) |
204
+ | Thirds or quarters (any part) | `#rrggbb` | Affected (2) |
205
+ | Centre dot (●) | — | Carrier (3) |
206
+ | No fill | — | Unaffected (1) |
207
+
208
+ Phenotype names are the text typed into the colour key (for example "Breast
209
+ cancer"). A colour with no text in the key is ignored.
210
+
211
+ A centre dot gives 3 in the Carrier column, and in the column of the condition
212
+ the person is drawn with; on a symbol with no fill it is the first condition
213
+ in the key.
214
+
215
+ ## Multiple pedigrees
216
+
217
+ If a drawing holds several unconnected families (common when several are
218
+ drawn on the same canvas), each is written to a file of its own:
219
+
220
+ ```
221
+ okafor_1.mped # the first family
222
+ okafor_2.mped # the second family
223
+ ```
224
+
225
+ The colour key belongs to the whole drawing, so every family is written in the
226
+ same format: all `.ped` or all `.mped`.
227
+
228
+ ## The standalone script
229
+
230
+ `studio2ped_standalone.py` is the whole package in one file, for computers
231
+ where installing a package is awkward; it takes the same options as the
232
+ `studio2ped` command. It is made from the package by
233
+ `python tools/make_standalone.py`, and a test checks that the two agree.
234
+
235
+ ## Development
236
+
237
+ ```bash
238
+ pip install -e ".[test]"
239
+ pytest
240
+ ```
241
+
242
+ ## Licence
243
+
244
+ MIT
@@ -0,0 +1,221 @@
1
+ # studio2ped
2
+
3
+ Convert families drawn in [Pedigree Studio](https://github.com/comparativechrono/PedigreeStudio)
4
+ into standard PED pedigree files, or into extended MPED (multi-phenotype PED)
5
+ files when more than one condition is recorded.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install studio2ped
11
+ ```
12
+
13
+ It needs Python 3.9 or later and nothing else. You can also use the
14
+ standalone script, `studio2ped_standalone.py`, without installing anything.
15
+
16
+ ## Usage
17
+
18
+ ### Command line
19
+
20
+ ```bash
21
+ # Basic conversion: writes okafor.ped, or okafor.mped if the family needs MPED
22
+ studio2ped okafor.studio.json
23
+
24
+ # Specify output name and directory
25
+ studio2ped okafor.studio.json -o my_pedigree -d ./output/
26
+
27
+ # Identify people by the labels the drawing shows, instead of pedigree numbers
28
+ studio2ped okafor.studio.json --ids drawing
29
+
30
+ # Using the standalone script
31
+ python studio2ped_standalone.py okafor.studio.json
32
+ ```
33
+
34
+ The command prints a short summary, the files it wrote and notes on anything
35
+ you should know: symbols that are not people and were left out, identifiers
36
+ that had to be changed, and so on.
37
+
38
+ Commands written for version 0.1.0 still work. The difference you will notice
39
+ is that people are now identified by their pedigree numbers; add `--ids text`
40
+ to get the identifiers 0.1.0 used (see [Identifying people](#identifying-people)).
41
+
42
+ ### Python API
43
+
44
+ ```python
45
+ import json
46
+ import studio2ped
47
+
48
+ with open("okafor.studio.json", encoding="utf-8") as f:
49
+ session = json.load(f)
50
+
51
+ result = studio2ped.convert(session, "okafor") # ids="drawing", "studio" or "text" to choose identifiers
52
+ print(result.summary)
53
+ for note in result.notes:
54
+ print("Note:", note)
55
+ for filename, content in result.files:
56
+ print(f"--- {filename} ---")
57
+ print(content)
58
+
59
+ # From file to file
60
+ result = studio2ped.convert_file("okafor.studio.json")
61
+ for filepath, _ in result.files:
62
+ print(f"Written: {filepath}")
63
+ ```
64
+
65
+ ## What it does
66
+
67
+ The converter reads a Pedigree Studio session file and:
68
+
69
+ 1. **Detects separate pedigrees**: if the canvas holds several unconnected
70
+ families, each is written to a file of its own.
71
+ 2. **Works out the family structure** from the partnership and child lines,
72
+ not from where the symbols are, so a rearranged pedigree converts correctly.
73
+ 3. **Leaves out symbols that are not people.** Pedigree Studio stores the "no
74
+ children" bar and the "infertility" double bar as children of the couple;
75
+ they are not written as individuals, and a note names the couple.
76
+ 4. **Gives genetic parents only.** The father and mother columns of a PED
77
+ file are genetic parents. A child joined to a couple by a dashed line (an
78
+ adoption) gets 0 for that couple, and so does the place of a parent marked
79
+ G (a gestational carrier), with a note in each case; a parent marked D (a
80
+ donor) is a genetic parent. Drawings made before the 2025 correction to the
81
+ pedigree nomenclature mark a gestational carrier S, which is read the same
82
+ way. studio2ga4gh's PED-style pedigree gives the same.
83
+ 5. **Extracts phenotypes** from the colour key: solid fills, half fills,
84
+ symbols in thirds or quarters, and shading patterns are all recognised.
85
+ 6. **Detects carrier notation**: centre dots (●) and half-filled symbols mark
86
+ carriers.
87
+ 7. **Chooses the format**:
88
+ - **one phenotype** → a standard `.ped` file (6 columns);
89
+ - **several phenotypes**, or **carrier notation alongside a phenotype** → an
90
+ extended `.mped` file.
91
+
92
+ ## Identifying people
93
+
94
+ | `--ids` | Each person is identified by |
95
+ |---|---|
96
+ | `numbers` (the default) | their pedigree number as Pedigree Studio shows it: the generation in Roman numerals and the position from the left, such as `II-3` |
97
+ | `drawing` | the label the drawing shows under its numbering setting: pedigree numbers, letters (`II-C`, or `A`, `B`, `C` …) or labels typed in |
98
+ | `studio` | Pedigree Studio's own id, such as `p-3` |
99
+ | `text` | what version 0.1.0 used: the label typed in if there is one, otherwise the first line of the text under the symbol, otherwise Pedigree Studio's id |
100
+
101
+ Pedigree numbers are the ones studio2ga4gh writes into the GA4GH files, so a
102
+ person can be followed from one file to the other. The text under a symbol
103
+ usually holds a name and an age ("Alice, 50"), so `text`, and `drawing` when
104
+ the drawing has labels typed in, can put names into the file: use them only
105
+ when that is acceptable for wherever the file is going.
106
+
107
+ Identifiers can't contain spaces, so spaces become underscores and other
108
+ punctuation is dropped: "J. Mensah" becomes `J_Mensah`. studio2ga4gh makes its
109
+ ids from labels the same way, so with the same choice a person has the same
110
+ identifier in the PED file and in the GA4GH files. No two people may share an
111
+ identifier, and `0` means "no parent" in a PED file, so if a label would be
112
+ shared by two people (two cousins both labelled "26", say), would be empty or
113
+ would be `0`, the person is given their pedigree number instead, and a note
114
+ says so.
115
+
116
+ Pedigree numbers follow the drawing, as in Pedigree Studio: a new generation
117
+ starts 80 pixels or more below the first person of the generation above, and
118
+ each generation is numbered from the left. Only people are numbered: the "no
119
+ children" and "infertility" symbols are not people, so they have no number
120
+ and do not count in their row.
121
+
122
+ ## Output formats
123
+
124
+ ### Standard PED (`.ped`)
125
+
126
+ The standard 6-column format used by PLINK, GATK and most genetics tools:
127
+
128
+ ```
129
+ # Family: FAM1
130
+ # FamID IndID FatherID MotherID Sex Phenotype
131
+ FAM1 I-1 0 0 1 1
132
+ FAM1 I-2 0 0 2 1
133
+ FAM1 II-1 I-1 I-2 2 2
134
+ ```
135
+
136
+ Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected. The columns
137
+ are separated by tabs.
138
+
139
+ ### Extended MPED (`.mped`)
140
+
141
+ A multi-phenotype extension for pedigrees in which more than one condition is
142
+ tracked. It adds a header line naming the phenotype columns, and a carrier
143
+ status:
144
+
145
+ ```
146
+ # MPED v1 Breast_cancer Carrier
147
+ # FamID IndID FatherID MotherID Sex Breast_cancer Carrier
148
+ FAM1 I-1 0 0 1 1 1
149
+ FAM1 I-2 0 0 2 3 3
150
+ FAM1 II-1 I-1 I-2 2 2 1
151
+ ```
152
+
153
+ Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected, `3` = carrier.
154
+
155
+ The format is easy to read by program: the first line declares the format and
156
+ names the phenotype columns, the second is a header for people to read, and
157
+ the data lines have the same tab-separated columns as standard PED, with one
158
+ more column for each phenotype.
159
+
160
+ Column names are the condition names from the colour key, with spaces turned
161
+ into underscores. An ontology id written in brackets after a name, as
162
+ studio2ga4gh reads it ("Breast cancer [MONDO:0007254]"), is left out of the
163
+ column name, which is then `Breast_cancer`, and a note says which id it had.
164
+ Only the ids of ontologies studio2ga4gh can declare count (the HPO, Mondo,
165
+ OMIM, Orphanet, SNOMED CT and so on): anything else in brackets, such as
166
+ "Hypertension (BP:160/95)", stays part of the name.
167
+ If two conditions would give the same column name, the second becomes
168
+ `Breast_cancer_2`.
169
+
170
+ ## Phenotype detection
171
+
172
+ The converter maps Pedigree Studio's markings to phenotype status using the
173
+ colour key:
174
+
175
+ | Marking | Colour key entry | Status |
176
+ |---------------|-------------------|--------|
177
+ | Solid colour fill | `#rrggbb` | Affected (2) |
178
+ | Full shading (stripes/dots) | `shading:pattern:full` | Affected (2) |
179
+ | Half colour fill (one side only) | `#rrggbb` | Carrier (3) |
180
+ | Half shading | `shading:pattern:half-left` | Carrier (3) |
181
+ | Thirds or quarters (any part) | `#rrggbb` | Affected (2) |
182
+ | Centre dot (●) | — | Carrier (3) |
183
+ | No fill | — | Unaffected (1) |
184
+
185
+ Phenotype names are the text typed into the colour key (for example "Breast
186
+ cancer"). A colour with no text in the key is ignored.
187
+
188
+ A centre dot gives 3 in the Carrier column, and in the column of the condition
189
+ the person is drawn with; on a symbol with no fill it is the first condition
190
+ in the key.
191
+
192
+ ## Multiple pedigrees
193
+
194
+ If a drawing holds several unconnected families (common when several are
195
+ drawn on the same canvas), each is written to a file of its own:
196
+
197
+ ```
198
+ okafor_1.mped # the first family
199
+ okafor_2.mped # the second family
200
+ ```
201
+
202
+ The colour key belongs to the whole drawing, so every family is written in the
203
+ same format: all `.ped` or all `.mped`.
204
+
205
+ ## The standalone script
206
+
207
+ `studio2ped_standalone.py` is the whole package in one file, for computers
208
+ where installing a package is awkward; it takes the same options as the
209
+ `studio2ped` command. It is made from the package by
210
+ `python tools/make_standalone.py`, and a test checks that the two agree.
211
+
212
+ ## Development
213
+
214
+ ```bash
215
+ pip install -e ".[test]"
216
+ pytest
217
+ ```
218
+
219
+ ## Licence
220
+
221
+ MIT
@@ -1,13 +1,14 @@
1
1
  [build-system]
2
- requires = ["setuptools>=61.0"]
2
+ requires = ["setuptools>=77"]
3
3
  build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "studio2ped"
7
- version = "0.1.0"
7
+ version = "0.2.1"
8
8
  description = "Convert Pedigree Studio session JSON to PED/MPED pedigree files"
9
9
  readme = "README.md"
10
- license = {text = "MIT"}
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
11
12
  requires-python = ">=3.9"
12
13
  classifiers = [
13
14
  "Development Status :: 4 - Beta",
@@ -15,25 +16,26 @@ classifiers = [
15
16
  "Intended Audience :: Healthcare Industry",
16
17
  "Topic :: Scientific/Engineering :: Bio-Informatics",
17
18
  "Topic :: Scientific/Engineering :: Medical Science Apps.",
18
- "License :: OSI Approved :: MIT License",
19
19
  "Programming Language :: Python :: 3",
20
- "Programming Language :: Python :: 3.9",
21
- "Programming Language :: Python :: 3.10",
22
- "Programming Language :: Python :: 3.11",
23
- "Programming Language :: Python :: 3.12",
24
- "Programming Language :: Python :: 3.13",
25
20
  ]
26
21
  keywords = ["pedigree", "genetics", "ped", "family-tree", "clinical-genetics", "pedigree-studio"]
27
22
  authors = [
28
23
  {name = "Tim Hearn", email = "tjh70@cam.ac.uk"},
29
24
  ]
30
25
 
26
+ [project.optional-dependencies]
27
+ test = ["pytest>=7"]
28
+
31
29
  [project.scripts]
32
30
  studio2ped = "studio2ped.cli:main"
33
31
 
34
32
  [project.urls]
35
33
  Homepage = "https://github.com/comparativechrono/studio2ped"
36
34
  Issues = "https://github.com/comparativechrono/studio2ped/issues"
35
+ Changelog = "https://github.com/comparativechrono/studio2ped/blob/main/CHANGELOG.md"
37
36
 
38
37
  [tool.setuptools.packages.find]
39
38
  include = ["studio2ped*"]
39
+
40
+ [tool.pytest.ini_options]
41
+ testpaths = ["tests"]
@@ -0,0 +1,10 @@
1
+ """studio2ped — Convert Pedigree Studio session JSON to PED/MPED pedigree files."""
2
+
3
+ __version__ = "0.2.1"
4
+
5
+ from .converter import ( # noqa: E402
6
+ ID_CHOICES, ConversionResult, convert, convert_file, individual_ids, parse_session, pedigree_numbers,
7
+ )
8
+
9
+ __all__ = ["convert", "convert_file", "parse_session", "individual_ids", "pedigree_numbers", "ID_CHOICES",
10
+ "ConversionResult", "__version__"]
@@ -5,7 +5,8 @@ from __future__ import annotations
5
5
  import argparse
6
6
  import sys
7
7
 
8
- from .converter import convert_file
8
+ from . import __version__
9
+ from .converter import ID_CHOICES, convert_file
9
10
 
10
11
 
11
12
  def main(argv: list[str] | None = None) -> int:
@@ -21,7 +22,7 @@ def main(argv: list[str] | None = None) -> int:
21
22
  "-o", "--output-prefix",
22
23
  default=None,
23
24
  dest="output_prefix",
24
- help="Base name for output files (default: input filename stem)",
25
+ help="Base name for output files (default: the input file's name without .studio.json)",
25
26
  )
26
27
  parser.add_argument(
27
28
  "-d", "--output-dir",
@@ -29,6 +30,15 @@ def main(argv: list[str] | None = None) -> int:
29
30
  dest="output_dir",
30
31
  help="Directory for output files (default: same as input file)",
31
32
  )
33
+ parser.add_argument(
34
+ "--ids",
35
+ choices=ID_CHOICES,
36
+ default="numbers",
37
+ help="How people are identified: pedigree numbers such as II-3 (numbers, the default), the labels "
38
+ "the drawing shows (drawing), Pedigree Studio's own ids (studio), or version 0.1.0's "
39
+ "identifiers taken from the text under each symbol, which are often names (text)",
40
+ )
41
+ parser.add_argument("--version", action="version", version=f"studio2ped {__version__}")
32
42
 
33
43
  args = parser.parse_args(argv)
34
44
 
@@ -37,13 +47,16 @@ def main(argv: list[str] | None = None) -> int:
37
47
  args.input,
38
48
  output_prefix=args.output_prefix,
39
49
  output_dir=args.output_dir,
50
+ ids=args.ids,
40
51
  )
41
52
  print(result.summary)
42
53
  for filepath, _ in result.files:
43
54
  print(f" Written: {filepath}")
55
+ for note in result.notes:
56
+ print(f" Note: {note}")
44
57
  return 0
45
- except FileNotFoundError:
46
- print(f"Error: file not found: {args.input}", file=sys.stderr)
58
+ except FileNotFoundError as e:
59
+ print(f"Error: file not found: {e.filename or args.input}", file=sys.stderr)
47
60
  return 1
48
61
  except Exception as e:
49
62
  print(f"Error: {e}", file=sys.stderr)