Repair a claim that does not derive
Use this when a rule should derive a row and does not, and you want to know
which fact is missing. gnd why-not names the first step of the rule that
left no bindings, and the values that reached it. You add that fact and ask
again, until the row derives and gnd witness proves it.
Files on GitHub
quoted_claims/model.orm.json: the modelquoted_claims/gap.pop.json: the population, with two facts missingquoted_claims/claim.json: the claimed rowquoted_claims/extracted.json: a value a source document supplies- this page's Markdown source
The claim
An answer says Ada Byrne said quotation q-17. The model certifies a quotation
as said by a person when three facts hold: the quotation appears in a work,
the person wrote that work, and an editor reviewed it.
The population records where each quotation appears, and nothing else. Copy the example:
python -c "import gnd, pathlib, shutil; shutil.copytree(pathlib.Path(gnd.__file__).parent / 'examples/quoted_claims', 'quoted_claims', dirs_exist_ok=True)"
gnd validate quoted_claims/model.orm.json
gnd check passes: the population covers the claim's fact type, asserts
none of its rows, and none derive. That is consistent, and it does not tell you why the
claim is missing.
gnd check quoted_claims/model.orm.json quoted_claims/gap.pop.json --format json \
| python -c "import json, sys; r = json.load(sys.stdin); print(r['exit_code'], [d['verdict'] for d in r['derivations']])"
Ask why the claim is not derived
gnd why-not quoted_claims/model.orm.json quoted_claims/gap.pop.json \
--fact-type certified --row quoted_claims/claim.json | python -m json.tool
{
"clauses": [
{
"blocker": {
"asserted_not_read": false,
"derivation": "base",
"fact_type": "authored",
"ground": true,
"index": 1,
"kind": "atom",
"near_miss_count": 0,
"near_miss_truncated": false,
"near_misses": "redacted",
"roles": {
"person": {
"bound": true,
"variable": "$p"
},
"work": {
"bound": true,
"variable": "$w"
}
},
"sample_count": 1,
"samples": "redacted"
},
"clause_index": 0,
"descent": null,
"descent_stopped": "not-derived",
"steps": [
{
"count": 1,
"fact_type": "quote",
"index": 0,
"kind": "atom"
},
{
"count": 0,
"fact_type": "authored",
"index": 1,
"kind": "atom"
}
],
"steps_after_blocker": "not-evaluated"
}
],
"exit_code": 0,
"fact_type": "certified",
"mode": "row",
"order": "declared",
"reason": null,
"schema_version": "gnd-why-not/2",
"status": "blocked",
"unavailable": null,
"view": "redacted",
"work": {
"binding_cells": 3,
"joins": 1,
"pre_joins": 2,
"probes": 4
}
}
The rule's first step found one binding, and the second, authored, found
none. Both of its roles are bound, so the missing fact is fully determined.
The default view hides the values. Ask for them with --raw:
gnd why-not quoted_claims/model.orm.json quoted_claims/gap.pop.json \
--fact-type certified --row quoted_claims/claim.json --raw \
| python -c "import json, sys; b = json.load(sys.stdin)['clauses'][0]['blocker']; print(b['fact_type'], b['samples'])"
Add the first fact and ask again
The missing fact is authored(Field Notes, Ada Byrne). Add it and ask again:
python -c "
import json
p = json.load(open('quoted_claims/gap.pop.json'))
p['facts']['authored'].append({'work': 'Field Notes', 'person': 'Ada Byrne'})
json.dump(p, open('quoted_claims/once.pop.json', 'w'))
"
gnd why-not quoted_claims/model.orm.json quoted_claims/once.pop.json \
--fact-type certified --row quoted_claims/claim.json --raw \
| python -c "import json, sys; b = json.load(sys.stdin)['clauses'][0]['blocker']; print(b['fact_type'], b['roles'], b['samples'])"
reviewed {'reviewer': {'bound': False, 'variable': '$r'}, 'work': {'bound': True, 'variable': '$w'}} [{'work': 'Field Notes'}]
Now the rule stops one step later, at reviewed. Its work role is bound,
but no binding reaches reviewer: the payload cannot say who reviewed the
work. That value has to come from a source, here extracted.json.
Add the second fact and prove the claim
python -c "
import json
p = json.load(open('quoted_claims/once.pop.json'))
reviewer = json.load(open('quoted_claims/extracted.json'))['reviewed']['reviewer']
p['facts']['reviewed'].append({'work': 'Field Notes', 'reviewer': reviewer})
json.dump(p, open('quoted_claims/twice.pop.json', 'w'))
"
gnd why-not quoted_claims/model.orm.json quoted_claims/twice.pop.json \
--fact-type certified --row quoted_claims/claim.json \
| python -c "import json, sys; print(json.load(sys.stdin)['status'])"
gnd witness quoted_claims/model.orm.json quoted_claims/twice.pop.json \
--targets quoted_claims/claim.json \
| python -c "import json, sys; print(json.load(sys.stdin)['results'][0]['status'])"
Keep the population consistent
The row now derives, but the population still covers that fact type and asserts
no rows, so gnd check would report the derived extension incomplete. Assert
the derived row, and the check passes again:
python -c "
import json
p = json.load(open('quoted_claims/twice.pop.json'))
p['facts']['certified'] = [{'quote': 'q-17', 'person': 'Ada Byrne'}]
json.dump(p, open('quoted_claims/repaired.pop.json', 'w'))
"
gnd check quoted_claims/model.orm.json quoted_claims/repaired.pop.json --format json \
| python -c "import json, sys; r = json.load(sys.stdin); print(r['exit_code'], [d['verdict'] for d in r['derivations']])"
When the values are spelled differently
Sometimes the fact is there, spelled another way. In spelling-gap.pop.json
someone recorded the work as FIELD NOTES. To GND that is a different value
from Field Notes, so the claim is blocked at authored exactly as before.
The answer's near-misses show the difference:
gnd why-not quoted_claims/model.orm.json quoted_claims/spelling-gap.pop.json \
--fact-type certified --row quoted_claims/claim.json --raw \
| python -c "import json, sys; b = json.load(sys.stdin)['clauses'][0]['blocker']; print(b['fact_type'], b['near_miss_count']); print(b['near_misses'])"
authored 1
[{'candidate': {'person': 'Ada Byrne', 'work': 'FIELD NOTES'}, 'roles': ['work'], 'sample': {'person': 'Ada Byrne', 'work': 'Field Notes'}}]
The binding for Field Notes met a row for FIELD NOTES by the same person.
roles names the role that differs. The population also has Field notes by
Bo Lin, which is not listed: its other bound role, person, differs too.
Near-misses compare strings after case folding and removing punctuation and
extra spaces; the why-not reference
defines the rule.
Why-not does not decide that the two spellings name one work. If they do, correct the row to the spelling the rule expects, and ask again:
python -c "
import json
p = json.load(open('quoted_claims/spelling-gap.pop.json'))
for row in p['facts']['authored']:
if row == {'work': 'FIELD NOTES', 'person': 'Ada Byrne'}:
row['work'] = 'Field Notes'
json.dump(p, open('quoted_claims/respelled.pop.json', 'w'))
"
gnd why-not quoted_claims/model.orm.json quoted_claims/respelled.pop.json \
--fact-type certified --row quoted_claims/claim.json --raw \
| python -c "import json, sys; b = json.load(sys.stdin)['clauses'][0]['blocker']; print(b['fact_type'], b['near_miss_count'])"
The claim now stops at reviewed, with no near-misses: that fact is missing,
and you add it as in the previous section.
What the answer does not tell you
- Why-not says which fact is missing, not whether it is true. Add a fact only when a source supports it.
- A step with an unbound role needs a value from somewhere else, as
reviewerdid here. - A near-miss is a candidate, not a match. Correct a spelling only when you know both name the same thing.
- Each answer covers the rule as written, in declared order. The why-not reference defines the steps, the exit codes and the limits.