studio2ped 0.1.0__tar.gz → 0.2.0__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.0/CHANGELOG.md +67 -0
  2. studio2ped-0.2.0/MANIFEST.in +4 -0
  3. studio2ped-0.2.0/PKG-INFO +242 -0
  4. studio2ped-0.2.0/README.md +219 -0
  5. {studio2ped-0.1.0 → studio2ped-0.2.0}/pyproject.toml +11 -9
  6. studio2ped-0.2.0/studio2ped/__init__.py +10 -0
  7. {studio2ped-0.1.0 → studio2ped-0.2.0}/studio2ped/cli.py +17 -4
  8. {studio2ped-0.1.0 → studio2ped-0.2.0}/studio2ped/converter.py +346 -47
  9. studio2ped-0.2.0/studio2ped.egg-info/PKG-INFO +242 -0
  10. studio2ped-0.2.0/studio2ped.egg-info/SOURCES.txt +30 -0
  11. studio2ped-0.2.0/studio2ped.egg-info/requires.txt +3 -0
  12. studio2ped-0.2.0/studio2ped_standalone.py +1081 -0
  13. studio2ped-0.2.0/tests/data/cf-cousins.studio.json +508 -0
  14. studio2ped-0.2.0/tests/data/expected/cf-cousins.mped +16 -0
  15. studio2ped-0.2.0/tests/data/expected/features.mped +29 -0
  16. studio2ped-0.2.0/tests/data/expected/lynch.mped +22 -0
  17. studio2ped-0.2.0/tests/data/expected/okafor.mped +18 -0
  18. studio2ped-0.2.0/tests/data/expected/price.mped +18 -0
  19. studio2ped-0.2.0/tests/data/features.studio.json +1044 -0
  20. studio2ped-0.2.0/tests/data/lynch.studio.json +712 -0
  21. studio2ped-0.2.0/tests/data/okafor.studio.json +589 -0
  22. studio2ped-0.2.0/tests/data/price.studio.json +582 -0
  23. studio2ped-0.2.0/tests/test_standalone.py +45 -0
  24. studio2ped-0.2.0/tests/test_studio2ped.py +235 -0
  25. studio2ped-0.2.0/tests/test_version.py +13 -0
  26. studio2ped-0.2.0/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.0}/LICENSE +0 -0
  33. {studio2ped-0.1.0 → studio2ped-0.2.0}/setup.cfg +0 -0
  34. {studio2ped-0.1.0 → studio2ped-0.2.0}/studio2ped/__main__.py +0 -0
  35. {studio2ped-0.1.0 → studio2ped-0.2.0}/studio2ped.egg-info/dependency_links.txt +0 -0
  36. {studio2ped-0.1.0 → studio2ped-0.2.0}/studio2ped.egg-info/entry_points.txt +0 -0
  37. {studio2ped-0.1.0 → studio2ped-0.2.0}/studio2ped.egg-info/top_level.txt +0 -0
@@ -0,0 +1,67 @@
1
+ # Changelog
2
+
3
+ ## 0.2.0
4
+
5
+ - People are now identified by their pedigree numbers (I-1, II-3 and so on),
6
+ worked out exactly as Pedigree Studio shows them and as studio2ga4gh 0.4
7
+ writes them, so the same person has the same identifier in the PED file and
8
+ in the GA4GH files. Version 0.1.0 took identifiers from the text under each
9
+ symbol, which usually holds a name and an age (`Alice_50`), sometimes a
10
+ condition (`Bowel_cancer`), and is not always unique: in the Lynch example
11
+ two cousins labelled 26 got the same identifier, which `mpedtools validate`
12
+ rejects.
13
+ - `--ids` chooses the identifiers: `numbers` (the default), `drawing` (the
14
+ labels the drawing shows under its numbering setting: numbers, letters or
15
+ labels typed in), `studio` (Pedigree Studio's own ids) or `text` (0.1.0's
16
+ identifiers). Whatever the choice, an identifier that would be shared by two
17
+ people, be empty or be `0` (which PED reads as "no parent") is replaced by
18
+ the person's pedigree number, and the command says so.
19
+ - The "no children" and "infertility" symbols are no longer written as
20
+ people. Pedigree Studio stores them as children of the couple, so 0.1.0 put
21
+ them in the file as individuals of unknown sex (29 individuals for the 27
22
+ people in the features example). They are left out now, with a note naming
23
+ the couple.
24
+ - Adoptive parents and surrogates are no longer written as parents. A child
25
+ joined to a couple by a dashed line (an adoption) and a parent marked S (a
26
+ gestational carrier) are not genetic relations, and a linkage program reads
27
+ the father and mother columns as genetic parents, so 0.1.0's files gave
28
+ wrong ones. Those parents are now 0, with a note, which is also what
29
+ studio2ga4gh's PED-style pedigree gives: a solid line from birth parents
30
+ still counts, and so does a donor (D).
31
+ - A symbol standing for several people (a number such as 3 in the symbol) is
32
+ still written as one person, but the command now says so.
33
+ - An ontology id written after a condition's name in the colour key, as
34
+ studio2ga4gh reads it (`Condition X [MONDO:0009061]`), is left out of the
35
+ MPED column name, which is now `Condition_X` instead of
36
+ `Condition_X_MONDO0009061`. Only the ids of ontologies count (the HPO,
37
+ Mondo, OMIM, Orphanet, SNOMED CT and the others studio2ga4gh can declare):
38
+ anything else in brackets, such as `Hypertension (BP:160/95)`, stays part of
39
+ the name. Two conditions that would give the same column name are told apart
40
+ with `_2`, `_3`.
41
+ - Without `-o`, the output is named after the input without `.studio.json`:
42
+ `okafor.studio.json` gives `okafor.mped`, where 0.1.0 wrote
43
+ `okafor.studio.mped`.
44
+ - Within each generation, people are listed in the order of the drawing,
45
+ from left to right, rather than in the order of Pedigree Studio's internal
46
+ ids.
47
+ - Session files are read as UTF-8 (with or without a byte-order mark) and the
48
+ PED files written as UTF-8, whatever the computer's own setting, so names
49
+ with accents no longer fail on Windows.
50
+ - `-d` makes the output folder if it does not exist. In 0.1.0 the README's own
51
+ example (`-d ./output/`) failed unless the folder was already there, and
52
+ the error said that the input file could not be found.
53
+ - `studio2ped_standalone.py` is made from the package by
54
+ `tools/make_standalone.py`, and a test checks that it is up to date and
55
+ writes exactly what the package writes.
56
+ - New in the Python interface: `convert(..., ids=...)`,
57
+ `convert_file(..., ids=...)`, `individual_ids`, `pedigree_numbers` and
58
+ `ID_CHOICES`; `ConversionResult` has `notes` and `ids`.
59
+ - `--version`, tests run on Python 3.9 to 3.14, and releases are published to
60
+ PyPI from GitHub.
61
+
62
+ Commands written for 0.1.0 still run unchanged. To get 0.1.0's identifiers,
63
+ add `--ids text`.
64
+
65
+ ## 0.1.0
66
+
67
+ 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,242 @@
1
+ Metadata-Version: 2.4
2
+ Name: studio2ped
3
+ Version: 0.2.0
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
+ S (a gestational carrier), with a note in each case; a parent marked D (a
103
+ donor) is a genetic parent. studio2ga4gh's PED-style pedigree gives the
104
+ same.
105
+ 5. **Extracts phenotypes** from the colour key: solid fills, half fills,
106
+ quartered fills and shading patterns are all recognised.
107
+ 6. **Detects carrier notation**: centre dots (●) and half-filled symbols mark
108
+ carriers.
109
+ 7. **Chooses the format**:
110
+ - **one phenotype** → a standard `.ped` file (6 columns);
111
+ - **several phenotypes**, or **carrier notation alongside a phenotype** → an
112
+ extended `.mped` file.
113
+
114
+ ## Identifying people
115
+
116
+ | `--ids` | Each person is identified by |
117
+ |---|---|
118
+ | `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` |
119
+ | `drawing` | the label the drawing shows under its numbering setting: pedigree numbers, letters (`II-C`, or `A`, `B`, `C` …) or labels typed in |
120
+ | `studio` | Pedigree Studio's own id, such as `p-3` |
121
+ | `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 |
122
+
123
+ Pedigree numbers are the ones studio2ga4gh writes into the GA4GH files, so a
124
+ person can be followed from one file to the other. The text under a symbol
125
+ usually holds a name and an age ("Alice, 50"), so `text`, and `drawing` when
126
+ the drawing has labels typed in, can put names into the file: use them only
127
+ when that is acceptable for wherever the file is going.
128
+
129
+ Identifiers can't contain spaces, so spaces become underscores and other
130
+ punctuation is dropped: "J. Mensah" becomes `J_Mensah`. studio2ga4gh makes its
131
+ ids from labels the same way, so with the same choice a person has the same
132
+ identifier in the PED file and in the GA4GH files. No two people may share an
133
+ identifier, and `0` means "no parent" in a PED file, so if a label would be
134
+ shared by two people (two cousins both labelled "26", say), would be empty or
135
+ would be `0`, the person is given their pedigree number instead, and a note
136
+ says so.
137
+
138
+ Pedigree numbers follow the drawing, as in Pedigree Studio: a new generation
139
+ starts 80 pixels or more below the first symbol of the generation above, and
140
+ the "no children" and "infertility" symbols are counted in their row, even
141
+ though they are not written to the file.
142
+
143
+ ## Output formats
144
+
145
+ ### Standard PED (`.ped`)
146
+
147
+ The standard 6-column format used by PLINK, GATK and most genetics tools:
148
+
149
+ ```
150
+ # Family: FAM1
151
+ # FamID IndID FatherID MotherID Sex Phenotype
152
+ FAM1 I-1 0 0 1 1
153
+ FAM1 I-2 0 0 2 1
154
+ FAM1 II-1 I-1 I-2 2 2
155
+ ```
156
+
157
+ Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected. The columns
158
+ are separated by tabs.
159
+
160
+ ### Extended MPED (`.mped`)
161
+
162
+ A multi-phenotype extension for pedigrees in which more than one condition is
163
+ tracked. It adds a header line naming the phenotype columns, and a carrier
164
+ status:
165
+
166
+ ```
167
+ # MPED v1 Breast_cancer Carrier
168
+ # FamID IndID FatherID MotherID Sex Breast_cancer Carrier
169
+ FAM1 I-1 0 0 1 1 1
170
+ FAM1 I-2 0 0 2 3 3
171
+ FAM1 II-1 I-1 I-2 2 2 1
172
+ ```
173
+
174
+ Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected, `3` = carrier.
175
+
176
+ The format is easy to read by program: the first line declares the format and
177
+ names the phenotype columns, the second is a header for people to read, and
178
+ the data lines have the same tab-separated columns as standard PED, with one
179
+ more column for each phenotype.
180
+
181
+ Column names are the condition names from the colour key, with spaces turned
182
+ into underscores. An ontology id written in brackets after a name, as
183
+ studio2ga4gh reads it ("Breast cancer [MONDO:0007254]"), is left out of the
184
+ column name, which is then `Breast_cancer`, and a note says which id it had.
185
+ Only the ids of ontologies studio2ga4gh can declare count (the HPO, Mondo,
186
+ OMIM, Orphanet, SNOMED CT and so on): anything else in brackets, such as
187
+ "Hypertension (BP:160/95)", stays part of the name.
188
+ If two conditions would give the same column name, the second becomes
189
+ `Breast_cancer_2`.
190
+
191
+ ## Phenotype detection
192
+
193
+ The converter maps Pedigree Studio's markings to phenotype status using the
194
+ colour key:
195
+
196
+ | Marking | Colour key entry | Status |
197
+ |---------------|-------------------|--------|
198
+ | Solid colour fill | `#rrggbb` | Affected (2) |
199
+ | Full shading (stripes/dots) | `shading:pattern:full` | Affected (2) |
200
+ | Half colour fill (one side only) | `#rrggbb` | Carrier (3) |
201
+ | Half shading | `shading:pattern:half-left` | Carrier (3) |
202
+ | Quartered fill (any quarter) | `#rrggbb` | Affected (2) |
203
+ | Centre dot (●) | — | Carrier (3) |
204
+ | No fill | — | Unaffected (1) |
205
+
206
+ Phenotype names are the text typed into the colour key (for example "Breast
207
+ cancer"). A colour with no text in the key is ignored.
208
+
209
+ A centre dot gives 3 in the Carrier column, and in the column of the condition
210
+ the person is drawn with; on a symbol with no fill it is the first condition
211
+ in the key.
212
+
213
+ ## Multiple pedigrees
214
+
215
+ If a drawing holds several unconnected families (common when several are
216
+ drawn on the same canvas), each is written to a file of its own:
217
+
218
+ ```
219
+ okafor_1.mped # the first family
220
+ okafor_2.mped # the second family
221
+ ```
222
+
223
+ The colour key belongs to the whole drawing, so every family is written in the
224
+ same format: all `.ped` or all `.mped`.
225
+
226
+ ## The standalone script
227
+
228
+ `studio2ped_standalone.py` is the whole package in one file, for computers
229
+ where installing a package is awkward; it takes the same options as the
230
+ `studio2ped` command. It is made from the package by
231
+ `python tools/make_standalone.py`, and a test checks that the two agree.
232
+
233
+ ## Development
234
+
235
+ ```bash
236
+ pip install -e ".[test]"
237
+ pytest
238
+ ```
239
+
240
+ ## Licence
241
+
242
+ MIT
@@ -0,0 +1,219 @@
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
+ S (a gestational carrier), with a note in each case; a parent marked D (a
80
+ donor) is a genetic parent. studio2ga4gh's PED-style pedigree gives the
81
+ same.
82
+ 5. **Extracts phenotypes** from the colour key: solid fills, half fills,
83
+ quartered fills and shading patterns are all recognised.
84
+ 6. **Detects carrier notation**: centre dots (●) and half-filled symbols mark
85
+ carriers.
86
+ 7. **Chooses the format**:
87
+ - **one phenotype** → a standard `.ped` file (6 columns);
88
+ - **several phenotypes**, or **carrier notation alongside a phenotype** → an
89
+ extended `.mped` file.
90
+
91
+ ## Identifying people
92
+
93
+ | `--ids` | Each person is identified by |
94
+ |---|---|
95
+ | `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` |
96
+ | `drawing` | the label the drawing shows under its numbering setting: pedigree numbers, letters (`II-C`, or `A`, `B`, `C` …) or labels typed in |
97
+ | `studio` | Pedigree Studio's own id, such as `p-3` |
98
+ | `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 |
99
+
100
+ Pedigree numbers are the ones studio2ga4gh writes into the GA4GH files, so a
101
+ person can be followed from one file to the other. The text under a symbol
102
+ usually holds a name and an age ("Alice, 50"), so `text`, and `drawing` when
103
+ the drawing has labels typed in, can put names into the file: use them only
104
+ when that is acceptable for wherever the file is going.
105
+
106
+ Identifiers can't contain spaces, so spaces become underscores and other
107
+ punctuation is dropped: "J. Mensah" becomes `J_Mensah`. studio2ga4gh makes its
108
+ ids from labels the same way, so with the same choice a person has the same
109
+ identifier in the PED file and in the GA4GH files. No two people may share an
110
+ identifier, and `0` means "no parent" in a PED file, so if a label would be
111
+ shared by two people (two cousins both labelled "26", say), would be empty or
112
+ would be `0`, the person is given their pedigree number instead, and a note
113
+ says so.
114
+
115
+ Pedigree numbers follow the drawing, as in Pedigree Studio: a new generation
116
+ starts 80 pixels or more below the first symbol of the generation above, and
117
+ the "no children" and "infertility" symbols are counted in their row, even
118
+ though they are not written to the file.
119
+
120
+ ## Output formats
121
+
122
+ ### Standard PED (`.ped`)
123
+
124
+ The standard 6-column format used by PLINK, GATK and most genetics tools:
125
+
126
+ ```
127
+ # Family: FAM1
128
+ # FamID IndID FatherID MotherID Sex Phenotype
129
+ FAM1 I-1 0 0 1 1
130
+ FAM1 I-2 0 0 2 1
131
+ FAM1 II-1 I-1 I-2 2 2
132
+ ```
133
+
134
+ Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected. The columns
135
+ are separated by tabs.
136
+
137
+ ### Extended MPED (`.mped`)
138
+
139
+ A multi-phenotype extension for pedigrees in which more than one condition is
140
+ tracked. It adds a header line naming the phenotype columns, and a carrier
141
+ status:
142
+
143
+ ```
144
+ # MPED v1 Breast_cancer Carrier
145
+ # FamID IndID FatherID MotherID Sex Breast_cancer Carrier
146
+ FAM1 I-1 0 0 1 1 1
147
+ FAM1 I-2 0 0 2 3 3
148
+ FAM1 II-1 I-1 I-2 2 2 1
149
+ ```
150
+
151
+ Phenotype codes: `0` = unknown, `1` = unaffected, `2` = affected, `3` = carrier.
152
+
153
+ The format is easy to read by program: the first line declares the format and
154
+ names the phenotype columns, the second is a header for people to read, and
155
+ the data lines have the same tab-separated columns as standard PED, with one
156
+ more column for each phenotype.
157
+
158
+ Column names are the condition names from the colour key, with spaces turned
159
+ into underscores. An ontology id written in brackets after a name, as
160
+ studio2ga4gh reads it ("Breast cancer [MONDO:0007254]"), is left out of the
161
+ column name, which is then `Breast_cancer`, and a note says which id it had.
162
+ Only the ids of ontologies studio2ga4gh can declare count (the HPO, Mondo,
163
+ OMIM, Orphanet, SNOMED CT and so on): anything else in brackets, such as
164
+ "Hypertension (BP:160/95)", stays part of the name.
165
+ If two conditions would give the same column name, the second becomes
166
+ `Breast_cancer_2`.
167
+
168
+ ## Phenotype detection
169
+
170
+ The converter maps Pedigree Studio's markings to phenotype status using the
171
+ colour key:
172
+
173
+ | Marking | Colour key entry | Status |
174
+ |---------------|-------------------|--------|
175
+ | Solid colour fill | `#rrggbb` | Affected (2) |
176
+ | Full shading (stripes/dots) | `shading:pattern:full` | Affected (2) |
177
+ | Half colour fill (one side only) | `#rrggbb` | Carrier (3) |
178
+ | Half shading | `shading:pattern:half-left` | Carrier (3) |
179
+ | Quartered fill (any quarter) | `#rrggbb` | Affected (2) |
180
+ | Centre dot (●) | — | Carrier (3) |
181
+ | No fill | — | Unaffected (1) |
182
+
183
+ Phenotype names are the text typed into the colour key (for example "Breast
184
+ cancer"). A colour with no text in the key is ignored.
185
+
186
+ A centre dot gives 3 in the Carrier column, and in the column of the condition
187
+ the person is drawn with; on a symbol with no fill it is the first condition
188
+ in the key.
189
+
190
+ ## Multiple pedigrees
191
+
192
+ If a drawing holds several unconnected families (common when several are
193
+ drawn on the same canvas), each is written to a file of its own:
194
+
195
+ ```
196
+ okafor_1.mped # the first family
197
+ okafor_2.mped # the second family
198
+ ```
199
+
200
+ The colour key belongs to the whole drawing, so every family is written in the
201
+ same format: all `.ped` or all `.mped`.
202
+
203
+ ## The standalone script
204
+
205
+ `studio2ped_standalone.py` is the whole package in one file, for computers
206
+ where installing a package is awkward; it takes the same options as the
207
+ `studio2ped` command. It is made from the package by
208
+ `python tools/make_standalone.py`, and a test checks that the two agree.
209
+
210
+ ## Development
211
+
212
+ ```bash
213
+ pip install -e ".[test]"
214
+ pytest
215
+ ```
216
+
217
+ ## Licence
218
+
219
+ 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.0"
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.0"
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)