Make great documentation goal 1.2, and plan the README's reorganisation and the nested-template gaps #33

Open
lilleman wants to merge 966 commits from worktree-todo-relative-dates-exact-counts into main
6 changed files with 39 additions and 25 deletions
Showing only changes of commit 1afcbc1a83 - Show all commits
+2 -2
View File
@@ -69,8 +69,8 @@ replacement, and each removed path, column or flag.
- `geo.SE` and `geo.US`: five linked tables per country, `region`, `municipality`,
`locality`, `postal-code` and `street`, weighted by population and address counts
and built from SCB, GeoNames, Trafikverket NVDB and the US Census Bureau, and an
`address` record binding one street and reading its locality, postal code and region
through it. `sv_SE.address` and `en_US.address` bind those records once, so
`address` record binding one locality and drawing its street and postal code inside
it. `sv_SE.address` and `en_US.address` bind those records once, so
`en_US.address.street` no longer carries `name` and `suffix`, and a locale folder
loads only beside `geo`.
- `{date(from,to,'layout')}` and `{time('layout')}`: a second between two days, or
+4 -5
View File
@@ -238,8 +238,7 @@ too, so a person's prefix never contradicts it. A `personnummer`'s birth number,
for a man, which no real person is ever given.
A person of a chosen sex is assembled from the tables — `sex[f].first-name` beside
`last-name` — while a shipped `personnummer` draws its own sex, apart from any `person`
beside it. That test series is also small: a personnummer
`last-name`. Skatteverket's test series is also small: a personnummer
is one of about 70,000 values, a day in 1930–2025 against the two birth numbers, so a
fixture past a few hundred rows repeats one and a `UNIQUE` column needs a category of
your own. `sv_SE.date` and `en_US.date` are uniform over 1970-01-01 to 2029-12-31,
@@ -249,9 +248,9 @@ your own. `sv_SE.date` and `en_US.date` are uniform over 1970-01-01 to 2029-12-3
inside a sex.
A `geo` folder holds one tree per country under its alpha-2 code: five
[linked tables](#linked-tables) named alike, and an `address` record binding one street and
reading its locality, postal code and region through it, which the locale's `address`
binds once and prints whole.
[linked tables](#linked-tables) named alike, and an `address` record binding one locality
and drawing its street and postal code inside it, which the locale's `address` binds once
and prints whole.
| Table | `geo.SE` | `geo.US` | Weight |
|-------|----------|----------|--------|
+3 -3
View File
@@ -1,6 +1,6 @@
{
"format": "{street} {street-number}\n{postal-code} {locality}",
"street": "{.street as s}{s.name}",
"street": "{.locality as l}{l.street.name}",
"street-number": [
"{int(1,9)}",
"{int(10,99)}",
@@ -9,6 +9,6 @@
{ "format": "{int(10,99)}{upper(1)}", "weight": 0.1 },
{ "format": "{int(100,999)}{upper(1)}", "weight": 0.05 }
],
"postal-code": "{s..locality.postal-code.code}",
"locality": "{s..locality.name}"
"postal-code": "{l.postal-code.code}",
"locality": "{l.name}"
}
+4 -4
View File
@@ -1,13 +1,13 @@
{
"format": "{street-number} {street}\n{locality}, {region} {postal-code}",
"street": "{.street as s}{s.name}",
"street": "{.locality as l}{l.street.name}",
"street-number": [
"{int(10,99)}",
{ "format": "{int(100,999)}", "weight": 2 },
{ "format": "{int(1000,9999)}", "weight": 0.5 },
{ "format": "{int(10,99)}{int(100,999)}", "weight": 0.3 }
],
"locality": "{s..locality.name}",
"region": "{s..locality..municipality..region.abbr}",
"postal-code": "{s..locality.postal-code.code}"
"locality": "{l.name}",
"region": "{l..municipality..region.abbr}",
"postal-code": "{l.postal-code.code}"
}
+24 -9
View File
@@ -268,9 +268,10 @@ func TestShippedPersonNames(t *testing.T) {
seen := map[string]bool{}
for i := 0; i < 2000; i++ {
seen[fake(t, f, locale+".sex[f].first-name")] = true
first, sex := fake(t, f, locale+".person.first"), fake(t, f, locale+".person.sex")
if first == "" || sex == "" {
t.Fatalf("%s.person lacks a first name or a sex", locale)
got := fakeTemplate(t, f, `{/`+locale+`.person.first}|{/`+locale+`.person.sex}`)
first, sex, _ := strings.Cut(got, "|")
if v := fake(t, f, locale+".sex["+sex+"].first-name["+first+"]"); v != first {
t.Fatalf("%s: %q: %q is not a %s name", locale, got, first, sex)
}
}
if len(seen) < 300 || !seen["Anna"] && locale == "sv_SE" || !seen["Mary"] && locale == "en_US" {
@@ -339,22 +340,36 @@ func TestShippedSwedishAddress(t *testing.T) {
// one address category are two addresses.
func TestShippedAddressIsOneNamedPick(t *testing.T) {
f := newGenerator(t, "data", WithSeed(3))
for _, c := range []struct{ locale, country, template string }{
{"sv_SE", "SE", "{a.street}|{a.postal-code}|{a.locality}||{b.street}"},
{"en_US", "US", "{a.street}|{a.postal-code}|{a.locality}|{a.region}|{b.street}"},
for _, c := range []struct{ locale, country, template, largest string }{
{"sv_SE", "SE", "{a.street}|{a.postal-code}|{a.locality}||{b.street}", "Stockholm"},
{"en_US", "US", "{a.street}|{a.postal-code}|{a.locality}|{a.region}|{b.street}", "New York"},
} {
places := geoPlaces(t, c.country)
apart := false
for i := 0; i < 300; i++ {
apart, localities := false, map[string]int{}
for i := 0; i < 2000; i++ {
got := fakeTemplate(t, f, `{/`+c.locale+`.address as a}{/`+c.locale+`.address as b}`+c.template)
parts := strings.Split(got, "|")
if !places[strings.Join(parts[:4], "|")] {
t.Fatalf("%s.address: %q is no street, postal code, locality and region of one place", c.locale, got)
}
apart = apart || parts[0] != parts[4]
localities[parts[2]]++
region := "{/" + c.locale + ".address.region}"
if c.country == "SE" {
region = ""
}
got = fakeTemplate(t, f, `{/`+c.locale+`.address.street}|{/`+c.locale+`.address.postal-code}|{/`+c.locale+`.address.locality}|`+region)
if !places[got] {
t.Fatalf("%s.address: field paths %q are no street, postal code, locality and region of one place", c.locale, got)
}
}
if !apart {
t.Errorf("%s: two names bound to address drew one street in 300 renders, want two addresses", c.locale)
t.Errorf("%s: two names bound to address drew one street in 2000 renders, want two addresses", c.locale)
}
for l, n := range localities {
if n > localities[c.largest] {
t.Errorf("%s.address drew %s %d times and %s %d, want the locality weighted by population", c.locale, l, n, c.largest, localities[c.largest])
}
}
}
}
+2 -2
View File
@@ -861,8 +861,8 @@ name is an error listing both keys; select by type instead.
A Luhn-valid personnummer over a random birth number may be a living person's; 238 and
239 after any date are blocked from assignment, so the shipped `personnummer` and
`samordningsnummer` use those. They sit in a `birth-number` table under `sex` rather
than as a column of it: the render's shared draw of the family is what makes the number
and the name agree on sex, and `sex` stays one shape across locales instead of
than as a column of it, so a pick of a sex can read the number, which `todo.md` item 28
has `personnummer` do to agree with a person's sex, and `sex` stays one shape across locales instead of
collecting every sex-keyed id fact. A samordningsnummer's day, the birthday plus 60, is
drawn from 61 to 88, valid in every month, rather than computed from the date drawn.