sslabdata 3.1.0__tar.gz → 5.0.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.
- {sslabdata-3.1.0/sslabdata.egg-info → sslabdata-5.0.0}/PKG-INFO +52 -9
- {sslabdata-3.1.0 → sslabdata-5.0.0}/README.md +51 -8
- {sslabdata-3.1.0 → sslabdata-5.0.0}/SPEC.md +64 -25
- {sslabdata-3.1.0 → sslabdata-5.0.0}/pyproject.toml +4 -4
- sslabdata-5.0.0/schema/input/v2/collaborators.schema.json +24 -0
- sslabdata-5.0.0/schema/input/v2/lab.schema.json +71 -0
- sslabdata-5.0.0/schema/input/v2/people.schema.json +51 -0
- sslabdata-5.0.0/schema/input/v2/projects.schema.json +34 -0
- sslabdata-5.0.0/schema/v6/output.schema.json +481 -0
- sslabdata-5.0.0/schema/v7/output.schema.json +485 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/__init__.py +3 -2
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/diagnostics.py +2 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/loaders.py +3 -2
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/models.py +24 -2
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/parsers/bibtex.py +91 -4
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/templates/init/bib/publications.bib +4 -1
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/templates/init/collaborators.yaml +1 -1
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/templates/init/lab.yaml +1 -1
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/templates/init/people.yaml +2 -1
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/templates/init/projects.yaml +1 -1
- {sslabdata-3.1.0 → sslabdata-5.0.0/sslabdata.egg-info}/PKG-INFO +52 -9
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata.egg-info/SOURCES.txt +6 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/LICENSE +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/MANIFEST.in +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/schema/__init__.py +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/schema/input/v1/collaborators.schema.json +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/schema/input/v1/lab.schema.json +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/schema/input/v1/people.schema.json +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/schema/input/v1/projects.schema.json +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/schema/v3/output.schema.json +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/schema/v4/output.schema.json +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/schema/v5/output.schema.json +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/setup.cfg +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/assembler.py +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/cli.py +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/config.py +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/exporters.py +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/parsers/__init__.py +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/parsers/latex.py +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata/resolver.py +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata.egg-info/dependency_links.txt +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata.egg-info/entry_points.txt +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata.egg-info/requires.txt +0 -0
- {sslabdata-3.1.0 → sslabdata-5.0.0}/sslabdata.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sslabdata
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
4
4
|
Summary: Renderer-agnostic academic lab data assembler: BibTeX + YAML → structured data
|
|
5
5
|
Author: Siddhartha Srinivasa
|
|
6
6
|
License-Expression: MIT
|
|
@@ -37,6 +37,11 @@ Dynamic: license-file
|
|
|
37
37
|
|
|
38
38
|
# sslabdata
|
|
39
39
|
|
|
40
|
+
[](https://pypi.org/project/sslabdata/)
|
|
41
|
+
[](https://pypi.org/project/sslabdata/)
|
|
42
|
+
[](https://github.com/siddhss5/sslabdata/blob/main/LICENSE)
|
|
43
|
+
[](https://github.com/siddhss5/sslabdata/actions/workflows/test.yml)
|
|
44
|
+
|
|
40
45
|
sslabdata compiles BibTeX and a little YAML into one schema-specified document —
|
|
41
46
|
works, people, projects and the links between them — that any website, CV or
|
|
42
47
|
script can read.
|
|
@@ -56,10 +61,11 @@ sslabdata --config lab.yaml --output lab.yml
|
|
|
56
61
|
order the lists are in, which fields are derived, when the version changes.
|
|
57
62
|
- [`CHANGELOG.md`](https://github.com/siddhss5/sslabdata/blob/main/CHANGELOG.md) — what changed at each release, and what it
|
|
58
63
|
replaced.
|
|
59
|
-
- [`schema/
|
|
64
|
+
- [`schema/v6/output.schema.json`](https://github.com/siddhss5/sslabdata/blob/main/schema/v6/output.schema.json) — the
|
|
60
65
|
document's JSON Schema. Published versions are immutable and live at their
|
|
61
|
-
own paths; [`schema/v3/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v3/output.schema.json)
|
|
62
|
-
[`schema/v4/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v4/output.schema.json)
|
|
66
|
+
own paths; [`schema/v3/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v3/output.schema.json),
|
|
67
|
+
[`schema/v4/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v4/output.schema.json) and
|
|
68
|
+
[`schema/v5/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v5/output.schema.json) are still there.
|
|
63
69
|
- [`tests/COVERAGE.md`](https://github.com/siddhss5/sslabdata/blob/main/tests/COVERAGE.md) — every input case sslabdata
|
|
64
70
|
supports, and every case it does not, with the fixture and test for each.
|
|
65
71
|
|
|
@@ -182,6 +188,7 @@ nothing else:
|
|
|
182
188
|
| `doi`, `isbn`, `issn`, `eprint` + `archivePrefix` (or `eprinttype`) | `identifiers`, an open map from scheme to a list of identifiers, plus the links built from them. An `eprint`'s scheme is the repository `archivePrefix` or `eprinttype` named, lower-cased, so that field needs no property of its own — and an `eprint` in a repository other than arXiv gets no arXiv link |
|
|
183
189
|
| `abstract` | `abstract` |
|
|
184
190
|
| `note` | `note` |
|
|
191
|
+
| `award` | `awards`, a list of `{name, year}` (see below). `note` is never read for awards |
|
|
185
192
|
| `url` | A link of kind `video` when its host is YouTube or Vimeo (or a subdomain of either), otherwise of kind `url` |
|
|
186
193
|
| `video` | A link of kind `video`, whatever its host, so `url` can hold the work's website |
|
|
187
194
|
| `pdf` | The work's one link of kind `pdf`. An entry without it gets `pdf_base_url` plus its citation key, when `pdf_base_url` is set |
|
|
@@ -192,6 +199,26 @@ The entry is also re-serialized into a `bibtex` field, so fields sslabdata does
|
|
|
192
199
|
not interpret are still carried. It is a re-serialization, not a copy
|
|
193
200
|
([`SPEC.md` §5](https://github.com/siddhss5/sslabdata/blob/main/SPEC.md#5-input-versus-derived)).
|
|
194
201
|
|
|
202
|
+
### Awards
|
|
203
|
+
|
|
204
|
+
A paper's awards go in its `award` field. Several are separated by `and`, as
|
|
205
|
+
the names in `author` are, and braces keep an `and` inside one name. An award
|
|
206
|
+
may start with the year it was given, as `YYYY:` and a space, when that is not
|
|
207
|
+
the paper's year:
|
|
208
|
+
|
|
209
|
+
```bibtex
|
|
210
|
+
award = {2026: Test of Time Award and {Best Systems and Software Paper Award}}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Each award becomes `{name, year}` in the work's `awards`, in the order
|
|
214
|
+
written, with its name converted from LaTeX as `title` is. An award without a
|
|
215
|
+
year takes the work's `year`, or `null` when the work has none. `awards` is
|
|
216
|
+
`[]` for a work with no `award` field. An empty award, or a prefix that looks
|
|
217
|
+
like a year and is not four digits, a colon and a space, is reported
|
|
218
|
+
(`BIB-AWARD-EMPTY`, `BIB-AWARD-YEAR-MALFORMED`); a malformed prefix stays in
|
|
219
|
+
the name. Awards a person holds, such as fellowships, are not part of the
|
|
220
|
+
document.
|
|
221
|
+
|
|
195
222
|
### The `project` tag
|
|
196
223
|
|
|
197
224
|
sslabdata adds one custom BibTeX field, `project`, to link a paper to a research
|
|
@@ -205,6 +232,7 @@ project:
|
|
|
205
232
|
year = {2024},
|
|
206
233
|
eprint = {2406.99812},
|
|
207
234
|
archivePrefix = {arXiv},
|
|
235
|
+
award = {Best Paper Award},
|
|
208
236
|
project = {homebot}
|
|
209
237
|
}
|
|
210
238
|
```
|
|
@@ -227,6 +255,9 @@ author names to people:
|
|
|
227
255
|
website: "https://example.org/people/bbrown"
|
|
228
256
|
co_advisor: "Peggy Park"
|
|
229
257
|
start_year: 2021
|
|
258
|
+
bio: |
|
|
259
|
+
Bob Brown is a PhD student advised by Alice Adams and Peggy Park.
|
|
260
|
+
He works on shared control for assistive robot arms.
|
|
230
261
|
|
|
231
262
|
- id: "iingram"
|
|
232
263
|
name: "Ivan Ingram"
|
|
@@ -243,6 +274,16 @@ author names to people:
|
|
|
243
274
|
`id` and `name` are required. `role` is any non-empty string, so any lab's
|
|
244
275
|
roles fit; `status` is `current` (the default) or `alumni`.
|
|
245
276
|
|
|
277
|
+
`bio` is a short biography in plain text, not Markdown or HTML: it is
|
|
278
|
+
emitted as written and a renderer escapes it. Line breaks are kept as YAML
|
|
279
|
+
reads them, so a block scalar (`|`) keeps each line, and a renderer may treat
|
|
280
|
+
a blank line as a paragraph break. A person without one gets `bio: null`; an
|
|
281
|
+
empty string is emitted as written, as any other person field is. Keep the
|
|
282
|
+
structured facts in their own fields: nothing reads `degree`, years or
|
|
283
|
+
`current_position` out of a bio, so an alumni line such as "PhD 2022, now
|
|
284
|
+
Research Scientist at Example Robotics" is for the renderer to build from
|
|
285
|
+
those fields.
|
|
286
|
+
|
|
246
287
|
### External co-authors (optional, `data/collaborators.yaml`)
|
|
247
288
|
|
|
248
289
|
A list of co-authors outside the lab whose spellings you want grouped
|
|
@@ -278,24 +319,26 @@ deciding which URLs are safe to render is the renderer's job.
|
|
|
278
319
|
### Checking inputs in an editor
|
|
279
320
|
|
|
280
321
|
Each input file has a JSON Schema in
|
|
281
|
-
[`schema/input/
|
|
322
|
+
[`schema/input/v2/`](https://github.com/siddhss5/sslabdata/tree/main/schema/input/v2):
|
|
282
323
|
`lab.schema.json`, `people.schema.json`, `projects.schema.json` and
|
|
283
324
|
`collaborators.schema.json`. The wheel installs them under
|
|
284
|
-
`sslabdata/schema/input/
|
|
325
|
+
`sslabdata/schema/input/v2/`. An editor can use them to check and complete
|
|
285
326
|
the files as you write; `--validate` stays the check, and also reports what
|
|
286
327
|
a schema cannot see, such as a repeated id or a missing file. With the YAML
|
|
287
328
|
language server (the VS Code YAML extension, among others), name the schema
|
|
288
329
|
in a comment at the top of the file:
|
|
289
330
|
|
|
290
331
|
```yaml
|
|
291
|
-
# yaml-language-server: $schema=https://raw.githubusercontent.com/siddhss5/sslabdata/input-schema-
|
|
332
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/siddhss5/sslabdata/input-schema-v2/schema/input/v2/people.schema.json
|
|
292
333
|
- id: "aadams"
|
|
293
334
|
name: "Alice Adams"
|
|
294
335
|
role: "professor"
|
|
295
336
|
```
|
|
296
337
|
|
|
297
|
-
The URL is the schema's `$id`, served from the `input-schema-
|
|
338
|
+
The URL is the schema's `$id`, served from the `input-schema-v2` tag, which is
|
|
298
339
|
never moved ([`SPEC.md` §6](https://github.com/siddhss5/sslabdata/blob/main/SPEC.md#6-version-policy)).
|
|
340
|
+
A file that names v1 still validates, because v1 allows keys it does not
|
|
341
|
+
list, but only v2 checks and completes `bio`.
|
|
299
342
|
Use `lab.schema.json`, `projects.schema.json` or `collaborators.schema.json`
|
|
300
343
|
in the same way for the other files.
|
|
301
344
|
|
|
@@ -349,7 +392,7 @@ pip install jsonschema
|
|
|
349
392
|
python -c "
|
|
350
393
|
import json, yaml, jsonschema
|
|
351
394
|
from importlib.resources import files
|
|
352
|
-
schema = json.loads(files('sslabdata.schema').joinpath('
|
|
395
|
+
schema = json.loads(files('sslabdata.schema').joinpath('v6/output.schema.json').read_text())
|
|
353
396
|
jsonschema.Draft202012Validator(schema).validate(yaml.safe_load(open('lab.yml')))
|
|
354
397
|
print('valid')
|
|
355
398
|
"
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# sslabdata
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/sslabdata/)
|
|
4
|
+
[](https://pypi.org/project/sslabdata/)
|
|
5
|
+
[](https://github.com/siddhss5/sslabdata/blob/main/LICENSE)
|
|
6
|
+
[](https://github.com/siddhss5/sslabdata/actions/workflows/test.yml)
|
|
7
|
+
|
|
3
8
|
sslabdata compiles BibTeX and a little YAML into one schema-specified document —
|
|
4
9
|
works, people, projects and the links between them — that any website, CV or
|
|
5
10
|
script can read.
|
|
@@ -19,10 +24,11 @@ sslabdata --config lab.yaml --output lab.yml
|
|
|
19
24
|
order the lists are in, which fields are derived, when the version changes.
|
|
20
25
|
- [`CHANGELOG.md`](https://github.com/siddhss5/sslabdata/blob/main/CHANGELOG.md) — what changed at each release, and what it
|
|
21
26
|
replaced.
|
|
22
|
-
- [`schema/
|
|
27
|
+
- [`schema/v6/output.schema.json`](https://github.com/siddhss5/sslabdata/blob/main/schema/v6/output.schema.json) — the
|
|
23
28
|
document's JSON Schema. Published versions are immutable and live at their
|
|
24
|
-
own paths; [`schema/v3/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v3/output.schema.json)
|
|
25
|
-
[`schema/v4/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v4/output.schema.json)
|
|
29
|
+
own paths; [`schema/v3/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v3/output.schema.json),
|
|
30
|
+
[`schema/v4/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v4/output.schema.json) and
|
|
31
|
+
[`schema/v5/`](https://github.com/siddhss5/sslabdata/blob/main/schema/v5/output.schema.json) are still there.
|
|
26
32
|
- [`tests/COVERAGE.md`](https://github.com/siddhss5/sslabdata/blob/main/tests/COVERAGE.md) — every input case sslabdata
|
|
27
33
|
supports, and every case it does not, with the fixture and test for each.
|
|
28
34
|
|
|
@@ -145,6 +151,7 @@ nothing else:
|
|
|
145
151
|
| `doi`, `isbn`, `issn`, `eprint` + `archivePrefix` (or `eprinttype`) | `identifiers`, an open map from scheme to a list of identifiers, plus the links built from them. An `eprint`'s scheme is the repository `archivePrefix` or `eprinttype` named, lower-cased, so that field needs no property of its own — and an `eprint` in a repository other than arXiv gets no arXiv link |
|
|
146
152
|
| `abstract` | `abstract` |
|
|
147
153
|
| `note` | `note` |
|
|
154
|
+
| `award` | `awards`, a list of `{name, year}` (see below). `note` is never read for awards |
|
|
148
155
|
| `url` | A link of kind `video` when its host is YouTube or Vimeo (or a subdomain of either), otherwise of kind `url` |
|
|
149
156
|
| `video` | A link of kind `video`, whatever its host, so `url` can hold the work's website |
|
|
150
157
|
| `pdf` | The work's one link of kind `pdf`. An entry without it gets `pdf_base_url` plus its citation key, when `pdf_base_url` is set |
|
|
@@ -155,6 +162,26 @@ The entry is also re-serialized into a `bibtex` field, so fields sslabdata does
|
|
|
155
162
|
not interpret are still carried. It is a re-serialization, not a copy
|
|
156
163
|
([`SPEC.md` §5](https://github.com/siddhss5/sslabdata/blob/main/SPEC.md#5-input-versus-derived)).
|
|
157
164
|
|
|
165
|
+
### Awards
|
|
166
|
+
|
|
167
|
+
A paper's awards go in its `award` field. Several are separated by `and`, as
|
|
168
|
+
the names in `author` are, and braces keep an `and` inside one name. An award
|
|
169
|
+
may start with the year it was given, as `YYYY:` and a space, when that is not
|
|
170
|
+
the paper's year:
|
|
171
|
+
|
|
172
|
+
```bibtex
|
|
173
|
+
award = {2026: Test of Time Award and {Best Systems and Software Paper Award}}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Each award becomes `{name, year}` in the work's `awards`, in the order
|
|
177
|
+
written, with its name converted from LaTeX as `title` is. An award without a
|
|
178
|
+
year takes the work's `year`, or `null` when the work has none. `awards` is
|
|
179
|
+
`[]` for a work with no `award` field. An empty award, or a prefix that looks
|
|
180
|
+
like a year and is not four digits, a colon and a space, is reported
|
|
181
|
+
(`BIB-AWARD-EMPTY`, `BIB-AWARD-YEAR-MALFORMED`); a malformed prefix stays in
|
|
182
|
+
the name. Awards a person holds, such as fellowships, are not part of the
|
|
183
|
+
document.
|
|
184
|
+
|
|
158
185
|
### The `project` tag
|
|
159
186
|
|
|
160
187
|
sslabdata adds one custom BibTeX field, `project`, to link a paper to a research
|
|
@@ -168,6 +195,7 @@ project:
|
|
|
168
195
|
year = {2024},
|
|
169
196
|
eprint = {2406.99812},
|
|
170
197
|
archivePrefix = {arXiv},
|
|
198
|
+
award = {Best Paper Award},
|
|
171
199
|
project = {homebot}
|
|
172
200
|
}
|
|
173
201
|
```
|
|
@@ -190,6 +218,9 @@ author names to people:
|
|
|
190
218
|
website: "https://example.org/people/bbrown"
|
|
191
219
|
co_advisor: "Peggy Park"
|
|
192
220
|
start_year: 2021
|
|
221
|
+
bio: |
|
|
222
|
+
Bob Brown is a PhD student advised by Alice Adams and Peggy Park.
|
|
223
|
+
He works on shared control for assistive robot arms.
|
|
193
224
|
|
|
194
225
|
- id: "iingram"
|
|
195
226
|
name: "Ivan Ingram"
|
|
@@ -206,6 +237,16 @@ author names to people:
|
|
|
206
237
|
`id` and `name` are required. `role` is any non-empty string, so any lab's
|
|
207
238
|
roles fit; `status` is `current` (the default) or `alumni`.
|
|
208
239
|
|
|
240
|
+
`bio` is a short biography in plain text, not Markdown or HTML: it is
|
|
241
|
+
emitted as written and a renderer escapes it. Line breaks are kept as YAML
|
|
242
|
+
reads them, so a block scalar (`|`) keeps each line, and a renderer may treat
|
|
243
|
+
a blank line as a paragraph break. A person without one gets `bio: null`; an
|
|
244
|
+
empty string is emitted as written, as any other person field is. Keep the
|
|
245
|
+
structured facts in their own fields: nothing reads `degree`, years or
|
|
246
|
+
`current_position` out of a bio, so an alumni line such as "PhD 2022, now
|
|
247
|
+
Research Scientist at Example Robotics" is for the renderer to build from
|
|
248
|
+
those fields.
|
|
249
|
+
|
|
209
250
|
### External co-authors (optional, `data/collaborators.yaml`)
|
|
210
251
|
|
|
211
252
|
A list of co-authors outside the lab whose spellings you want grouped
|
|
@@ -241,24 +282,26 @@ deciding which URLs are safe to render is the renderer's job.
|
|
|
241
282
|
### Checking inputs in an editor
|
|
242
283
|
|
|
243
284
|
Each input file has a JSON Schema in
|
|
244
|
-
[`schema/input/
|
|
285
|
+
[`schema/input/v2/`](https://github.com/siddhss5/sslabdata/tree/main/schema/input/v2):
|
|
245
286
|
`lab.schema.json`, `people.schema.json`, `projects.schema.json` and
|
|
246
287
|
`collaborators.schema.json`. The wheel installs them under
|
|
247
|
-
`sslabdata/schema/input/
|
|
288
|
+
`sslabdata/schema/input/v2/`. An editor can use them to check and complete
|
|
248
289
|
the files as you write; `--validate` stays the check, and also reports what
|
|
249
290
|
a schema cannot see, such as a repeated id or a missing file. With the YAML
|
|
250
291
|
language server (the VS Code YAML extension, among others), name the schema
|
|
251
292
|
in a comment at the top of the file:
|
|
252
293
|
|
|
253
294
|
```yaml
|
|
254
|
-
# yaml-language-server: $schema=https://raw.githubusercontent.com/siddhss5/sslabdata/input-schema-
|
|
295
|
+
# yaml-language-server: $schema=https://raw.githubusercontent.com/siddhss5/sslabdata/input-schema-v2/schema/input/v2/people.schema.json
|
|
255
296
|
- id: "aadams"
|
|
256
297
|
name: "Alice Adams"
|
|
257
298
|
role: "professor"
|
|
258
299
|
```
|
|
259
300
|
|
|
260
|
-
The URL is the schema's `$id`, served from the `input-schema-
|
|
301
|
+
The URL is the schema's `$id`, served from the `input-schema-v2` tag, which is
|
|
261
302
|
never moved ([`SPEC.md` §6](https://github.com/siddhss5/sslabdata/blob/main/SPEC.md#6-version-policy)).
|
|
303
|
+
A file that names v1 still validates, because v1 allows keys it does not
|
|
304
|
+
list, but only v2 checks and completes `bio`.
|
|
262
305
|
Use `lab.schema.json`, `projects.schema.json` or `collaborators.schema.json`
|
|
263
306
|
in the same way for the other files.
|
|
264
307
|
|
|
@@ -312,7 +355,7 @@ pip install jsonschema
|
|
|
312
355
|
python -c "
|
|
313
356
|
import json, yaml, jsonschema
|
|
314
357
|
from importlib.resources import files
|
|
315
|
-
schema = json.loads(files('sslabdata.schema').joinpath('
|
|
358
|
+
schema = json.loads(files('sslabdata.schema').joinpath('v6/output.schema.json').read_text())
|
|
316
359
|
jsonschema.Draft202012Validator(schema).validate(yaml.safe_load(open('lab.yml')))
|
|
317
360
|
print('valid')
|
|
318
361
|
"
|
|
@@ -7,7 +7,7 @@ them.
|
|
|
7
7
|
This file states the parts of that contract a JSON Schema cannot express:
|
|
8
8
|
what the strings in the document are, what order the lists are in, what an
|
|
9
9
|
absent key means, which fields are computed, when the version changes, and
|
|
10
|
-
how a repeated `@string` macro resolves. `schema/
|
|
10
|
+
how a repeated `@string` macro resolves. `schema/v7/output.schema.json`
|
|
11
11
|
states the rest.
|
|
12
12
|
|
|
13
13
|
Everything here is normative unless it carries a `Target` note. A `Target`
|
|
@@ -16,8 +16,8 @@ names the issue that will make it true. Until that issue lands, the rule is
|
|
|
16
16
|
the intent and the note is the fact. What changed at each release, and what
|
|
17
17
|
it replaced, is in [`CHANGELOG.md`](CHANGELOG.md), not here.
|
|
18
18
|
|
|
19
|
-
- Applies to: `schema_version`
|
|
20
|
-
version
|
|
19
|
+
- Applies to: `schema_version` 7 (`sslabdata.models.SCHEMA_VERSION`), package
|
|
20
|
+
version 5.0.0 (`sslabdata.__version__`).
|
|
21
21
|
|
|
22
22
|
### How this file cites the code
|
|
23
23
|
|
|
@@ -25,7 +25,7 @@ Every rule below is grounded in a named part of the code rather than a line
|
|
|
25
25
|
number, because line numbers rot silently: a function such as
|
|
26
26
|
`sslabdata.parsers.bibtex.parse_all_works()`, a method such as
|
|
27
27
|
`Person.to_dict()`, a module-level constant such as `TEXT_FIELDS`, a JSON
|
|
28
|
-
Pointer into `schema/
|
|
28
|
+
Pointer into `schema/v7/output.schema.json` such as `/$defs/person/required`, or
|
|
29
29
|
a `tests/COVERAGE.md` row key such as `config.people_file.missing`. A bare
|
|
30
30
|
statement is cited by its enclosing function.
|
|
31
31
|
|
|
@@ -201,7 +201,7 @@ without depending on English wording. Codes obey three rules:
|
|
|
201
201
|
| **Fatal at load** | `Error loading configuration: <CODE> …` (`Error: <CODE> …` for `CONFIG-NOT-FOUND`) on standard error; exits `1` before anything is compiled, so there is no report. | The same. | `CONFIG-BIB-FILE-ABSOLUTE`, `CONFIG-BIB-FILE-OUTSIDE-BIB-DIR`, `CONFIG-NOT-A-MAPPING`, `CONFIG-KEY-MISSING`, `CONFIG-TYPE-INVALID`, `CONFIG-VALUE-NOT-JSON`, `CONFIG-KEY-REPEATED`, `CONFIG-NOT-FOUND`, `CONFIG-UNREADABLE` |
|
|
202
202
|
| **Fatal** | Listed under `Bibliography errors` and counted; exits `1`. | Written to standard error unprefixed; exits `1`, and `--output` writes nothing. | `BIB-CROSSREF-UNSUPPORTED`, `BIB-ENCODING-INVALID`, `CONFIG-FILE-NOT-FOUND`, `CONFIG-PATH-WRONG-KIND`, `PEOPLE-YAML-INVALID`, `PEOPLE-NOT-A-LIST`, `PEOPLE-FIELD-MISSING`, `PROJECTS-YAML-INVALID`, `PROJECTS-NOT-A-LIST`, `PROJECTS-FIELD-MISSING`, `COLLABORATORS-YAML-INVALID`, `COLLABORATORS-NOT-A-LIST`, `COLLABORATORS-FIELD-MISSING`, `RECORD-KEY-REPEATED`, `OUTPUT-WRITE-FAILED`, `INIT-FILE-EXISTS`, `INIT-PATH-WRONG-KIND`, `INIT-PATH-OUTSIDE-DIR`, `INIT-WRITE-FAILED` |
|
|
203
203
|
| **Validation error** | Listed under `Bibliography errors` and counted; exits `1`. | Prefixed `Warning: ` on standard error; the run continues and exits `0`. | `BIB-DUPLICATE-KEY`, `RESOLVE-PROJECT-UNKNOWN`, `PEOPLE-ID-DUPLICATE`, `PROJECTS-ID-DUPLICATE` |
|
|
204
|
-
| **Warning** | Listed under `Warnings`; not counted, and does not change the exit code. | Prefixed `Warning: ` on standard error; the run continues. | `BIB-YEAR-MISSING`, `BIB-YEAR-INVALID`, `BIB-DOI-INVALID`, `BIB-OTHERS-NOT-LAST`, `BIB-STRING-UNDEFINED`, `BIB-SYNTAX-ERROR`, `BIB-BRACE-MISMATCH`, `BIB-COMMENTED-COMMAND-READ`, `BIB-VENUE-MISSING`, `BIB-ENTRY-TYPE-UNSUPPORTED`, `LATEX-COMMAND-UNKNOWN`, `ID-GROUPING-SPANS-SPELLINGS`, `ID-GROUPING-INITIALS-AMBIGUOUS`, `RESOLVE-AMBIGUOUS-NAME`, `RESOLVE-SUGGESTION`, `RESOLVE-COLLABORATOR-ALIAS-IS-MEMBER`, `PEOPLE-ALIAS-AMBIGUOUS`, `PEOPLE-ROLE-INVALID`, `PEOPLE-STATUS-INVALID`, `PROJECTS-STATUS-INVALID`, `CONFIG-LAB-NAME-MISSING`, `CONFIG-KEY-UNKNOWN`, `RECORD-KEY-UNKNOWN`, `RECORD-TYPE-INVALID`, `CONFIG-BIB-FILES-MISSING`, `BIB-PARSER-MESSAGE`, `LATEX-CONVERSION-FAILED`, `LINK-SCHEME-UNSUPPORTED`, `TEXT-CONTROL-CHARACTER`, `BIB-WRITE-BACK-FAILED`, `BIB-STRING-REDEFINED`, `ID-GROUPING-AMBIGUOUS-DECLARED`, `RESOLVE-UNRESOLVED-NAME` |
|
|
204
|
+
| **Warning** | Listed under `Warnings`; not counted, and does not change the exit code. | Prefixed `Warning: ` on standard error; the run continues. | `BIB-YEAR-MISSING`, `BIB-YEAR-INVALID`, `BIB-DOI-INVALID`, `BIB-AWARD-EMPTY`, `BIB-AWARD-YEAR-MALFORMED`, `BIB-OTHERS-NOT-LAST`, `BIB-STRING-UNDEFINED`, `BIB-SYNTAX-ERROR`, `BIB-BRACE-MISMATCH`, `BIB-COMMENTED-COMMAND-READ`, `BIB-VENUE-MISSING`, `BIB-ENTRY-TYPE-UNSUPPORTED`, `LATEX-COMMAND-UNKNOWN`, `ID-GROUPING-SPANS-SPELLINGS`, `ID-GROUPING-INITIALS-AMBIGUOUS`, `RESOLVE-AMBIGUOUS-NAME`, `RESOLVE-SUGGESTION`, `RESOLVE-COLLABORATOR-ALIAS-IS-MEMBER`, `PEOPLE-ALIAS-AMBIGUOUS`, `PEOPLE-ROLE-INVALID`, `PEOPLE-STATUS-INVALID`, `PROJECTS-STATUS-INVALID`, `CONFIG-LAB-NAME-MISSING`, `CONFIG-KEY-UNKNOWN`, `RECORD-KEY-UNKNOWN`, `RECORD-TYPE-INVALID`, `CONFIG-BIB-FILES-MISSING`, `BIB-PARSER-MESSAGE`, `LATEX-CONVERSION-FAILED`, `LINK-SCHEME-UNSUPPORTED`, `TEXT-CONTROL-CHARACTER`, `BIB-WRITE-BACK-FAILED`, `BIB-STRING-REDEFINED`, `ID-GROUPING-AMBIGUOUS-DECLARED`, `RESOLVE-UNRESOLVED-NAME` |
|
|
205
205
|
|
|
206
206
|
The same code always carries the same class. What varies with the mode is
|
|
207
207
|
how the run reacts to it, which is why the class is not in the code, and
|
|
@@ -249,6 +249,8 @@ Codes in use:
|
|
|
249
249
|
| `CONFIG-LAB-NAME-MISSING` | The `lab` header declares no `name`. A `lab` that is not a mapping at all is a different condition and is not reported under this code. A warning. |
|
|
250
250
|
| `BIB-YEAR-INVALID` | An entry's `year` is present but is not an unsigned run of the ASCII digits `0`–`9` (`sslabdata.parsers.bibtex.YEAR_DIGITS`), such as `in press`, or `-5`, `+2020`, `2_020` and full-width `2020`, which Python's `int()` would read as numbers. The work is emitted with `year: null` and sorts last, as one with no year does. A warning. |
|
|
251
251
|
| `BIB-DOI-INVALID` | An entry's `doi` is a DOI resolver URL with nothing after it, such as `https://doi.org/` (`sslabdata.parsers.bibtex.DOI_RESOLVERS`), so it names no DOI. Located at `<file>:<key>:doi`, naming the value. The work gets no DOI identifier and no link of kind `doi`, rather than an empty one; the entry is kept. A `doi` that is empty or blank is read as absent, and is not reported. A warning. |
|
|
252
|
+
| `BIB-AWARD-EMPTY` | An entry's `award` field names no award: the field is empty or whitespace alone, or one of its awards is empty — two `and`s with nothing between them, an `and` that opens or closes the list, or a name that is empty once converted from LaTeX, such as `{}` — or is a year prefix with no name after it, such as `2026:` (`sslabdata.parsers.bibtex.parse_awards()`). Located at `<file>:<key>:award`; the prose names the award's place in the list, and the award as written when it has a year. The empty award is dropped and the others are kept, so an empty field gives `awards: []`. A warning. |
|
|
253
|
+
| `BIB-AWARD-YEAR-MALFORMED` | An award in an entry's `award` field starts with what reads as a year but is not the prefix `YYYY:` — four ASCII digits, a colon and whitespace (`sslabdata.parsers.bibtex.AWARD_YEAR`): digits of any kind and a colon, such as `26: …`, `2026:…` with no space, or full-width `2026: …`, or four digits and a space with no colon, `2026 …` (`AWARD_YEAR_LIKE`). Located at `<file>:<key>:award`, naming the award as written and the part that is not a prefix. No year is read from it: **the text is kept as part of the name**, and the award takes the work's `year`. A prefix inside braces, `{2026}: …`, is text the author protected and is not reported. A warning. |
|
|
252
254
|
| `LINK-SCHEME-UNSUPPORTED` | A link in `work.links` whose URL scheme is not `http`, `https` or `mailto` (`sslabdata.parsers.bibtex.LINK_SCHEMES`), such as `javascript:`, `data:` or `vbscript:`, which a renderer escaping the document (§2) should refuse to turn into a link. The scheme is the one Python's URL parser reads (`urllib.parse.urlsplit()`, in `sslabdata.parsers.bibtex.url_scheme()`) once surrounding whitespace is off, compared without regard to case; the parser drops a tab or line break anywhere in the URL first, as a browser does, so `java<tab>script:` is `javascript`. A URL with no scheme is a relative path and is not reported; that includes a protocol-relative `//host/path`, which takes the scheme of the page that links it. A scheme of one letter is a Windows drive, read by Windows rules as `sslabdata.config` reads one (`PureWindowsPath`), and not a scheme: `C:/papers`, `C:\papers` and the drive-relative `c:papers` are local paths and are not reported. No registered scheme is one letter long. Every link is checked, whether the entry wrote it (`url`, `video`, `pdf`) or sslabdata built it (`doi`, `arxiv`, and `pdf` from `pdf_base_url`). Located at `<file>:<key>:<field>`, the field the link came from, or at `<file>:<key>:` for the PDF link `pdf_base_url` builds, which no field of the entry wrote; the prose names the link's kind, which for `url` can be `video`, and the URL. Once per link. A person's `website` and `photo`, a project's `website` and `image`, and everything under `lab` are fields, not links, and are not checked. **The link is emitted unchanged**: this code only reports it. A warning. |
|
|
253
255
|
| `BIB-OTHERS-NOT-LAST` | An `author` or `editor` list has `and others` somewhere other than at its end, the one place BibTeX reads it as "et al.". It names nobody there either, so it is dropped as a terminal one is, and the names around it are kept in their order, with `position` counting only them. Located at `<file>:<key>:author` or `:editor`, once per list however many times it occurs. A terminal `and others` is not reported. A warning. |
|
|
254
256
|
| `BIB-STRING-UNDEFINED` | A field value names an `@string` macro that nothing defined earlier in the same file. Located at the entry and field that use it, and naming the macro. It is read as empty, as BibTeX reads it, and the entry and its neighbours are kept. A macro used inside another `@string` definition is located at the file alone. A warning. |
|
|
@@ -273,7 +275,7 @@ Codes in use:
|
|
|
273
275
|
| `PROJECTS-ID-DUPLICATE` | Two projects declare one `id`. Located at the second. Both are kept. A validation error. |
|
|
274
276
|
| `PROJECTS-STATUS-INVALID` | A project's `status` is present and is not `active` or `completed`. A missing status reads as `active`, and so does one that is not a string. A warning. |
|
|
275
277
|
| `RECORD-KEY-UNKNOWN` | A person, project or collaborator record holds a key sslabdata does not read (`sslabdata.loaders.PERSON_KEYS`, `PROJECT_KEYS`, `COLLABORATOR_KEYS`), such as `hobby` or a misspelt `webiste`. One code for all three files. Located at `<people_file>:<id>:<key>`, `<projects_file>:<id>:<key>` or `<collaborators_file>:<collaborator name>:<key>`, once per key. The key is ignored and never emitted; the record is kept. Only a record that is loaded is checked, so a record missing a required field reports that alone. A warning. |
|
|
276
|
-
| `RECORD-TYPE-INVALID` | An optional field of a person, project or collaborator record has a value of the wrong type, other than the `role` and `status` that have codes of their own. A person's `photo`, `website`, `email`, `co_advisor`, `degree`, `thesis_title` and `
|
|
278
|
+
| `RECORD-TYPE-INVALID` | An optional field of a person, project or collaborator record has a value of the wrong type, other than the `role` and `status` that have codes of their own. A person's `photo`, `website`, `email`, `co_advisor`, `degree`, `thesis_title`, `current_position` and `bio`, and a project's `description`, `website` and `image`, are strings; a person's `start_year` and `end_year` are integers, not booleans; a person's or collaborator's `aliases` is a list of non-empty strings. One code for all three files, located at `<file>:<id>:<field>` (a collaborator's name for its id). The value is read as empty, so it is emitted as `null`, and a wrong `aliases` declares none; the record is kept. Only a record that is loaded is checked. A warning. |
|
|
277
279
|
| `RECORD-KEY-REPEATED` | A key is given twice in one mapping of a people, projects or collaborators file, which YAML alone would read as its last value, silently dropping the first. Every YAML file sslabdata reads is read with one loader that finds these (`sslabdata.config.YAMLLoader`). Keys are compared as YAML reads them, so `1` and `0x1` are one key. A merge key (`<<`) is not a repeat: the keys it merges in are overridden by the mapping's own, as YAML merge keys are defined. One code for all three files. Located at `<file>:<id>:<key>` (a collaborator's name for its id), with the path below the record's top level joined by `.` and a list member's index in brackets; the record is named by nothing when the repeated key is its `id` (a collaborator's `name`), and a repeat outside any record is located at `<file>::<path>`. The prose names the key and both lines. Every repeat is reported; the record is not loaded, and the run is fatal, as for a missing field: which value was meant is not sslabdata's to guess. |
|
|
278
280
|
| `CONFIG-NOT-A-MAPPING` | `lab.yaml` is not a mapping of keys, or is empty. Fatal at load. |
|
|
279
281
|
| `CONFIG-KEY-MISSING` | A required key is absent: `bib_dir`, or the `name` or `category` of a `bib_files` entry (`lab.yaml:bib_files:name`). Fatal at load. |
|
|
@@ -359,7 +361,7 @@ What each mode puts in the array:
|
|
|
359
361
|
|
|
360
362
|
**The Python API is convenience only.** Public: the names in `sslabdata.__all__`
|
|
361
363
|
— `assemble`, `AssemblyResult`, `AssemblyError`, the models `LabData`,
|
|
362
|
-
`Work`, `Author`, `Contributor`, `Venue`, `Link`, `Person`, `Project`,
|
|
364
|
+
`Work`, `Award`, `Author`, `Contributor`, `Venue`, `Link`, `Person`, `Project`,
|
|
363
365
|
`Collaborator`, the config loader `LabDataConfig` with `BibFile`, and the exporters
|
|
364
366
|
`export_to_yaml` and `export_to_json`, and the exception
|
|
365
367
|
`ConfigurationError`.
|
|
@@ -521,7 +523,8 @@ the URL, and in math it is escaped to `\&` or `\%`. Exactly the fields in
|
|
|
521
523
|
`publisher`, `address`, `organization`, `archivePrefix` and `eprinttype` —
|
|
522
524
|
applied in `entry_fields()`. Name
|
|
523
525
|
parts are converted the same way, in `person_name_parts()`, for authors and
|
|
524
|
-
editors alike
|
|
526
|
+
editors alike, and so is each award's name, once `award` is split into its
|
|
527
|
+
awards and each award's year prefix is read (`parse_awards()`).
|
|
525
528
|
|
|
526
529
|
Being converted is not the same as being emitted. Of these fields, `title`,
|
|
527
530
|
`abstract`, `note`, `type`, `series`, `publisher`, `address` and
|
|
@@ -538,10 +541,20 @@ converts nor checks them:
|
|
|
538
541
|
- **The person and project strings supplied in YAML.**
|
|
539
542
|
`sslabdata.loaders.load_people()` and `load_projects()` perform no conversion
|
|
540
543
|
of any kind — they check the types of a record's fields, a person's `role` and that a
|
|
541
|
-
`status` is one they know, but emit every string as written — so a person's `name`, `role`, `current_position
|
|
542
|
-
`thesis_title`, and a project's `title` or `description`, are copied
|
|
544
|
+
`status` is one they know, but emit every string as written — so a person's `name`, `role`, `current_position`,
|
|
545
|
+
`thesis_title` or `bio`, and a project's `title` or `description`, are copied
|
|
543
546
|
straight from `people.yaml` and `projects.yaml`. Not every YAML string is
|
|
544
547
|
emitted — `aliases` and the configuration paths are not; see heading 3.
|
|
548
|
+
"As written" is the string YAML reads, after control characters are
|
|
549
|
+
removed and NFC is applied as for every input string (above), and nothing
|
|
550
|
+
else: an empty or whitespace-only string is emitted as such, not as
|
|
551
|
+
`null`, and line breaks are kept. That matters most for a person's `bio`,
|
|
552
|
+
the one of these meant to run over several lines: a literal block scalar
|
|
553
|
+
(`|`) keeps each line break and the final one, a folded one (`>`) joins
|
|
554
|
+
lines with a space and keeps a blank line as a line break, and `\n` in a
|
|
555
|
+
double-quoted string is a line break. It is plain text like the rest — not
|
|
556
|
+
Markdown and not HTML — and a renderer escapes it, and may read a blank
|
|
557
|
+
line as a paragraph break.
|
|
545
558
|
- **`work.category`**, which comes from the `category` of the `bib_files`
|
|
546
559
|
entry in `lab.yaml`, not from the `.bib` file
|
|
547
560
|
(`sslabdata.config.LabDataConfig.from_yaml()`, then
|
|
@@ -581,6 +594,7 @@ rule does not apply to the input itself — only to whatever it produces.
|
|
|
581
594
|
| `pdf` | Becomes the work's one link of kind `pdf`, with `origin: input`, in place of the one `pdf_base_url` would give (`build_links()`). Empty or whitespace-only is read as absent. |
|
|
582
595
|
| `author` | Parsed into the `authors` list (`parse_author_list()`); the name parts are converted under heading 1. |
|
|
583
596
|
| `editor` | Parsed into the `editors` list (`parse_editor_list()`), resolved by the same machinery, and excluded from `person.work_ids`, from a project's people and from `collaborators`. |
|
|
597
|
+
| `award` | Parsed into the `awards` list (`parse_awards()`), and emitted nowhere else: `note` is never read for awards, and an award in `note` stays there as text. The field is split into awards as BibTeX splits `author` into names: on `and`, in any case, with whitespace on both sides, outside braces, which are counted as BibTeX counts them, so `{Systems and Software Award}` is one award. An `and` that opens or closes the list, or follows another, separates an empty award, which is dropped and reported (`BIB-AWARD-EMPTY`). Each award may then start with the year it was given, four ASCII digits, a colon and whitespace, `2026: Test of Time Award` (`AWARD_YEAR`), read before the name is converted from LaTeX under heading 1 and trimmed; it is that award's `year`, which need not be the work's. An award without one takes the work's `year`, or `null` when the work has none. A prefix that reads as a year and is not one, `26:` or `2026 …`, is kept in the name and reported (`BIB-AWARD-YEAR-MALFORMED`). |
|
|
584
598
|
| `year` | Emitted as the integer `year` — not a string — or `null` with a `BIB-YEAR-MISSING` diagnostic when the entry supplied none, or with `BIB-YEAR-INVALID` when it is not written in the digits `0`–`9` alone. It drives the works order (§3). |
|
|
585
599
|
| `crossref` | **Rejected, on presence rather than on value.** An entry carrying the field is an error under `BIB-CROSSREF-UNSUPPORTED`, whatever is inside it: an empty `crossref = {}` is a field the entry carries, and letting it through would put the silent path back under a different spelling. The entry is not emitted and the run fails in every mode (`parse_all_works()`). No field of any entry is filled in from any other entry. |
|
|
586
600
|
| `journal`, `booktitle`, `school`, `institution` | Converted under heading 1, then consumed by `build_venue()` into `venue.name`, with the `venue.kind` each implies. |
|
|
@@ -657,6 +671,7 @@ accident.
|
|
|
657
671
|
| `works` | `year` **descending**, with works that have no year **last**. Ties keep *read order* (below). The sort is the final statement of `sslabdata.parsers.bibtex.parse_all_works()`. |
|
|
658
672
|
| `work.authors` | The order the `author` field wrote them (`sslabdata.parsers.bibtex.parse_author_list()`). A terminal `and others` is BibTeX's "et al." and is dropped rather than emitted as an author; one anywhere else is dropped too, and reported as `BIB-OTHERS-NOT-LAST`. `position` is that order, 1-based, and counts only the names that reach the document. |
|
|
659
673
|
| `work.editors` | The order the `editor` field wrote them, read the same way (`parse_editor_list()`). |
|
|
674
|
+
| `work.awards` | The order the `award` field wrote them, with each empty award dropped (`parse_awards()`). |
|
|
660
675
|
| `work.project_ids` | The order the `project` field wrote them, comma-separated, whitespace trimmed, empty entries dropped (`sslabdata.parsers.bibtex.parse_project_ids()`). |
|
|
661
676
|
| `people` | The order of `people_file`. sslabdata does not sort people (`sslabdata.loaders.load_people()`, called by `sslabdata.assembler.assemble()`). |
|
|
662
677
|
| `projects` | The order of `projects_file`, likewise (`sslabdata.loaders.load_projects()`). |
|
|
@@ -752,7 +767,7 @@ unconditionally, and by `LabData.to_dict()`, which always emits `lab`.
|
|
|
752
767
|
itself and each entity type as **closed**: `/additionalProperties` and each
|
|
753
768
|
of `/$defs/authorship`, `/$defs/editorship`, `/$defs/work`, `/$defs/person`,
|
|
754
769
|
`/$defs/project`, `/$defs/collaborator`, `/$defs/venue`, `/$defs/link`,
|
|
755
|
-
`/$defs/resolution`, and the `source` and `generator` objects, set
|
|
770
|
+
`/$defs/resolution`, `/$defs/award`, and the `source` and `generator` objects, set
|
|
756
771
|
`additionalProperties: false`. Within those, a property that is not declared
|
|
757
772
|
cannot appear, and "absent" always means a declared property with no value.
|
|
758
773
|
|
|
@@ -783,6 +798,7 @@ sslabdata's own output as input, and a wrong derivation becomes permanent.
|
|
|
783
798
|
| `work.source.file` | Input — the `name` of the `bib_files` entry the file was listed under, **never an absolute path**. A *relative* directory is fine and is passed through as written: `sub/journal.bib` is a name under `bib_dir`; one that leaves `bib_dir` is rejected under `CONFIG-BIB-FILE-OUTSIDE-BIB-DIR`. The guarantee is kept by rejecting the input rather than by rewriting it, which would quietly discard that directory, and is enforced under `CONFIG-BIB-FILE-ABSOLUTE` (§1, *`sslabdata.ConfigurationError`*). |
|
|
784
799
|
| `work.entry_type` | Input — the BibTeX entry type, **lowercased** by `entry_fields()`. Of it and `bib_id`, it is the only one that is case-folded. |
|
|
785
800
|
| `work.title`, `abstract`, `note` | Input — BibTeX fields, converted from LaTeX to text (§2). `note` additionally has trailing `.` and whitespace trimmed (`sslabdata.parsers.bibtex.extract_note()`). |
|
|
801
|
+
| `work.awards` | Input — the BibTeX `award` field, split into awards, each `{name, year}`: `name` converted from LaTeX, and `year` the year the award was given, from its `YYYY:` prefix, else the work's `year`, else `null` (`sslabdata.parsers.bibtex.parse_awards()`, §2). `[]` when the entry writes none. These are a work's awards only; an award a person holds, such as a fellowship, is not in the document. |
|
|
786
802
|
| `work.year` | Input — the BibTeX `year`, as an integer; `null` when the entry supplied none, with a diagnostic (`entry_year()`). |
|
|
787
803
|
| `work.category` | Input — the `category` of the `bib_files` entry the file was listed under, not anything in the `.bib` file (`sslabdata.config.BibFile`, read by `parse_all_works()`). |
|
|
788
804
|
| `work.venue` | **Derived** — the first of `journal`, `booktitle`, `school` and `institution` the entry wrote, as `name`, with the `kind` that field and the entry type imply; a preprint's repository when the entry has only an `eprint`; `null` when it names no container (`sslabdata.parsers.bibtex.build_venue()`). See below. |
|
|
@@ -799,7 +815,7 @@ sslabdata's own output as input, and a wrong derivation becomes permanent.
|
|
|
799
815
|
| `author.resolution` | **Derived** — `status` over `resolved`, `unresolved` and `ambiguous`, and `method` `exact`, or `null` when nothing matched (`resolve_authors()`). Both are open strings; `fuzzy` is never emitted. |
|
|
800
816
|
| `author.equal_contribution` | **Derived** — whether the entry wrote a `*` marker on any part of the name (`sslabdata.parsers.bibtex.marks_equal_contribution()`). |
|
|
801
817
|
| `work.editors[*]` | The same, minus `collaborator_key` and `equal_contribution`. An editor that matched nobody is simply `person_id: null` (`parse_editor_list()`). |
|
|
802
|
-
| `person.*` except the two below | Input — the fields of `people_file` (`sslabdata.loaders.load_people()`). `aliases` is read for matching and is **not** emitted. `status` is `current` or `alumni`, and `current` when absent; `role` is open, any non-empty string (`PEOPLE-STATUS-INVALID`, `PEOPLE-ROLE-INVALID`). |
|
|
818
|
+
| `person.*` except the two below | Input — the fields of `people_file` (`sslabdata.loaders.load_people()`). `aliases` is read for matching and is **not** emitted. `status` is `current` or `alumni`, and `current` when absent; `role` is open, any non-empty string (`PEOPLE-STATUS-INVALID`, `PEOPLE-ROLE-INVALID`). `bio` is a short biography in plain text with its line breaks as written (§2), and `null` when absent; it is carried and never read: nothing in the document is derived from it, and no `degree`, year or `current_position` is read out of one. |
|
|
803
819
|
| `person.work_ids` | **Derived** — back-links over authorships (`sslabdata.resolver.compute_backlinks()`). Editors are not authorships and are not listed. |
|
|
804
820
|
| `project.id`, `title`, `description`, `website`, `image`, `status` | Input — the fields of `projects_file` (`sslabdata.loaders.load_projects()`). `status` is one of `active` and `completed`, and `active` when absent (`PROJECTS-STATUS-INVALID`). `image` is a URL or a site path, the same kind of value as a person's `photo`, carried as plain text: deciding which URLs are safe to render is the renderer's job. |
|
|
805
821
|
| `project.work_ids`, `people_ids` | **Derived** — back-links, and the people reached through them (`compute_backlinks()`). |
|
|
@@ -1114,13 +1130,14 @@ meaning and guarantees, and each of them is a bump.
|
|
|
1114
1130
|
has been published is never edited. Version `N`'s schema stays reachable, byte
|
|
1115
1131
|
for byte, at its own path after version `N+1` ships, so a consumer pinned to
|
|
1116
1132
|
`N` keeps a stable target. `schema/v3/output.schema.json`,
|
|
1117
|
-
`schema/v4/output.schema.json
|
|
1133
|
+
`schema/v4/output.schema.json`, `schema/v5/output.schema.json`,
|
|
1134
|
+
`schema/v6/output.schema.json` and `schema/v7/output.schema.json` are those
|
|
1118
1135
|
paths, and `tests/COVERAGE.md` row `output.versioned_schema` asserts that the
|
|
1119
|
-
older
|
|
1136
|
+
older four are unchanged byte for byte and still say `3`, `4`, `5` and `6`.
|
|
1120
1137
|
|
|
1121
|
-
**The `$id` is a pinned tag URL.**
|
|
1122
|
-
`https://raw.githubusercontent.com/siddhss5/sslabdata/schema-
|
|
1123
|
-
The rule that makes it a contract rather than a guess: **the `schema-
|
|
1138
|
+
**The `$id` is a pinned tag URL.** v7's `$id` is
|
|
1139
|
+
`https://raw.githubusercontent.com/siddhss5/sslabdata/schema-v7/schema/v7/output.schema.json`.
|
|
1140
|
+
The rule that makes it a contract rather than a guess: **the `schema-v7` tag
|
|
1124
1141
|
is created when this version ships and is never moved.** A branch URL such as
|
|
1125
1142
|
`blob/main` is not usable — it serves an HTML page rather than the schema, so
|
|
1126
1143
|
no consumer can ever have resolved v3's `$id` — and this repository publishes
|
|
@@ -1135,11 +1152,11 @@ rule.
|
|
|
1135
1152
|
descriptions inside those files, name the repository `labdata`, and the files
|
|
1136
1153
|
are left byte for byte as published rather than rewritten. v4's raw `$id`
|
|
1137
1154
|
resolves, because GitHub redirects `labdata` to `sslabdata`. v3's `blob/main`
|
|
1138
|
-
`$id` does not resolve, as above.
|
|
1139
|
-
`sslabdata`.
|
|
1155
|
+
`$id` does not resolve, as above. The `$id`s, titles and descriptions of v5,
|
|
1156
|
+
v6 and v7 say `sslabdata`.
|
|
1140
1157
|
|
|
1141
1158
|
**The input schemas are versioned apart from the document.**
|
|
1142
|
-
`schema/input/
|
|
1159
|
+
`schema/input/v2/` holds a JSON Schema for each input file: `lab.schema.json`,
|
|
1143
1160
|
`people.schema.json`, `projects.schema.json` and `collaborators.schema.json`.
|
|
1144
1161
|
They describe what the loaders accept; the loaders stay the authority, and
|
|
1145
1162
|
the diagnostic codes above report what a schema cannot see. The input format
|
|
@@ -1147,9 +1164,13 @@ and the document change for different reasons, so the input schemas carry
|
|
|
1147
1164
|
their own version rather than `schema_version`. They follow the same rules:
|
|
1148
1165
|
a published input schema is never edited, any change to one is a new
|
|
1149
1166
|
version at a new path, and its `$id` is a pinned tag URL,
|
|
1150
|
-
`https://raw.githubusercontent.com/siddhss5/sslabdata/input-schema-
|
|
1151
|
-
whose `input-schema-
|
|
1152
|
-
moved.
|
|
1167
|
+
`https://raw.githubusercontent.com/siddhss5/sslabdata/input-schema-v2/schema/input/v2/<file>.schema.json`,
|
|
1168
|
+
whose `input-schema-v2` tag is created when this version ships and is never
|
|
1169
|
+
moved. A version holds all four files, so a change to one of them versions
|
|
1170
|
+
the set: v2 adds a person's `bio` to `people.schema.json`, and its other three
|
|
1171
|
+
files differ from v1's only in the tag their `$id`s and descriptions name. `schema/input/v1/`, served
|
|
1172
|
+
from the `input-schema-v1` tag, stays byte for byte as published; the wheel
|
|
1173
|
+
installs only the current version.
|
|
1153
1174
|
|
|
1154
1175
|
**Version history.** What each `schema_version` changed is in
|
|
1155
1176
|
[`CHANGELOG.md`](CHANGELOG.md).
|
|
@@ -1243,12 +1264,30 @@ So each rejected type is rejected for a stated reason, and each has a home:
|
|
|
1243
1264
|
| Teaching and courses | A prose page in your site repository, or the institution's course catalogue. |
|
|
1244
1265
|
| Press and media coverage | A typed link on the work it covers: `links` is an open map from kind to links, so #27 adds the kind without a version bump. |
|
|
1245
1266
|
| Galleries, photos and videos | Your site repository; a video already reaches the document as a link of kind `video`. |
|
|
1246
|
-
| Awards and honours |
|
|
1267
|
+
| Awards and honours | A paper's awards are an attribute of the work, `work.awards`, read from its `award` field (§5). An award a person holds, such as a fellowship or an endowed chair, belongs in your site repository. |
|
|
1247
1268
|
| Funding and grants | Your site repository. Nothing in the document depends on it. |
|
|
1248
1269
|
| Software and datasets | Not a separate collection — they are kinds of *work*, added by #31. |
|
|
1249
1270
|
| Alumni | Not a collection — a `status` on a person (`sslabdata.models.Person.status`). |
|
|
1250
1271
|
| Robots, platforms, facilities | Your site repository; one of 27 surveyed sites had such a page. |
|
|
1251
1272
|
|
|
1273
|
+
**An attribute of an entity the document already has is not an entity.**
|
|
1274
|
+
The evidence above, and the question #60 asks of a candidate — would
|
|
1275
|
+
sslabdata do anything with it beyond carrying it — decide which *entities*
|
|
1276
|
+
the document has: collections with records of their own, ids, references
|
|
1277
|
+
and an order, each of which the compiler has to check and a renderer has to
|
|
1278
|
+
understand. A field
|
|
1279
|
+
on a person, work or project raises none of that. Its owner is already
|
|
1280
|
+
admitted, the field has one value of one type, and the only work it asks
|
|
1281
|
+
for is what every input string gets: control characters removed, NFC and
|
|
1282
|
+
the text rule (§2). A person already carries attributes sslabdata does
|
|
1283
|
+
nothing else with — `photo`, `website`, `thesis_title`, `current_position` —
|
|
1284
|
+
and a person's `bio` is one more. Such a field is judged on two questions
|
|
1285
|
+
instead: does it belong to that entity rather than to a page of the site,
|
|
1286
|
+
and is it the only place the fact can live, so that it does not copy a
|
|
1287
|
+
structured field into text that will drift from it. A bio passes both; an
|
|
1288
|
+
alumni line such as "PhD 2012, now at Example Robotics" fails the second,
|
|
1289
|
+
because `degree`, `end_year` and `current_position` already hold it.
|
|
1290
|
+
|
|
1252
1291
|
**There is no generic extension mechanism and no `collections` escape hatch.**
|
|
1253
1292
|
What one would carry is mostly prose, and its one real service — catching
|
|
1254
1293
|
references that point at nothing — is delivered by #58 without the document
|
|
@@ -1260,7 +1299,7 @@ vocabularies, not over arbitrary content.
|
|
|
1260
1299
|
|
|
1261
1300
|
## 9. What this file is not
|
|
1262
1301
|
|
|
1263
|
-
It does not list the document's fields; `schema/
|
|
1302
|
+
It does not list the document's fields; `schema/v7/output.schema.json` does.
|
|
1264
1303
|
It does not describe renderers such as
|
|
1265
1304
|
[sslabdata-site](https://github.com/siddhss5/sslabdata-site), which are
|
|
1266
1305
|
downstream consumers in their own repositories. It does not describe the input formats `lab.yaml`,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "sslabdata"
|
|
3
|
-
version = "
|
|
3
|
+
version = "5.0.0"
|
|
4
4
|
description = "Renderer-agnostic academic lab data assembler: BibTeX + YAML → structured data"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -58,8 +58,8 @@ requires = ["setuptools>=77"]
|
|
|
58
58
|
build-backend = "setuptools.build_meta"
|
|
59
59
|
|
|
60
60
|
# The current output schema and input schemas are public wheel data, installed
|
|
61
|
-
# at sslabdata/schema/
|
|
62
|
-
# sslabdata/schema/input/
|
|
61
|
+
# at sslabdata/schema/v7/output.schema.json and
|
|
62
|
+
# sslabdata/schema/input/v2/*.schema.json. The package directory is mapped onto
|
|
63
63
|
# the repository's schema/ directory so each frozen file has one copy and one
|
|
64
64
|
# path; earlier versions stay in the sdist only. schema/__init__.py makes the
|
|
65
65
|
# mapped directory a regular package, which editable installs need to resolve.
|
|
@@ -71,7 +71,7 @@ packages = ["sslabdata", "sslabdata.parsers", "sslabdata.schema"]
|
|
|
71
71
|
"sslabdata.schema" = "schema"
|
|
72
72
|
|
|
73
73
|
[tool.setuptools.package-data]
|
|
74
|
-
"sslabdata.schema" = ["
|
|
74
|
+
"sslabdata.schema" = ["v7/output.schema.json", "input/v2/*.schema.json"]
|
|
75
75
|
# The starting point `sslabdata init` copies.
|
|
76
76
|
"sslabdata" = ["templates/init/*.yaml", "templates/init/bib/*.bib"]
|
|
77
77
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/siddhss5/sslabdata/input-schema-v2/schema/input/v2/collaborators.schema.json",
|
|
4
|
+
"title": "sslabdata collaborators file",
|
|
5
|
+
"description": "The file lab.yaml names as collaborators_file: co-authors outside the lab whose spellings are grouped together, one record each. A record never makes anyone a lab member. It describes what sslabdata's loader accepts; the loader stays the authority, and reports each problem under a code in SPEC.md, Diagnostic codes. A record key not listed here is reported as RECORD-KEY-UNKNOWN and ignored, so it is allowed. Published input schemas are immutable: this one is served from the input-schema-v2 tag, which is created when this version ships and is never moved (SPEC.md §6).",
|
|
6
|
+
"type": ["array", "null"],
|
|
7
|
+
"items": { "$ref": "#/$defs/collaborator" },
|
|
8
|
+
"$defs": {
|
|
9
|
+
"nonEmptyString": { "type": "string", "pattern": "\\S" },
|
|
10
|
+
"collaborator": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"required": ["name"],
|
|
13
|
+
"additionalProperties": true,
|
|
14
|
+
"properties": {
|
|
15
|
+
"name": { "$ref": "#/$defs/nonEmptyString" },
|
|
16
|
+
"aliases": {
|
|
17
|
+
"description": "Other spellings of the name, grouped with it.",
|
|
18
|
+
"type": ["array", "null"],
|
|
19
|
+
"items": { "$ref": "#/$defs/nonEmptyString" }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|