Compare commits
1 Commits
main
..
4c8f30406e
| Author | SHA1 | Date | |
|---|---|---|---|
| 4c8f30406e |
@@ -16,54 +16,12 @@ jobs:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
- name: Relevant changes
|
||||
id: changes
|
||||
run: |
|
||||
if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then
|
||||
git fetch --no-tags origin "$GITHUB_BASE_REF"
|
||||
base="origin/$GITHUB_BASE_REF"
|
||||
else
|
||||
base='${{ github.event.before }}'
|
||||
fi
|
||||
if git diff --quiet "$base"...HEAD -- '*.go' go.mod go.sum Dockerfile .dockerignore .github data testdata README.md; then
|
||||
echo "code=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "code=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
echo "base=$base" >> "$GITHUB_OUTPUT"
|
||||
- name: CHANGELOG entry for data changes
|
||||
run: |
|
||||
base='${{ steps.changes.outputs.base }}'
|
||||
if ! git diff --quiet "$base"...HEAD -- data testdata/shipped_shape.txt && git diff --quiet "$base"...HEAD -- CHANGELOG.md; then
|
||||
echo "::error::the shipped data changed without a CHANGELOG.md entry (see the README's Versioning)"
|
||||
exit 1
|
||||
fi
|
||||
# `docker build` streams the context to the daemon. A compose bind-mount of
|
||||
# `.` mounts an empty host dir instead, because the job is itself a container.
|
||||
- name: Latest supported Go
|
||||
if: ${{ steps.changes.outputs.code == 'true' }}
|
||||
run: docker build .
|
||||
# Runs even when the step above failed, so a red build says whether the
|
||||
# failure is version-specific.
|
||||
- name: Lowest supported Go
|
||||
if: ${{ !cancelled() && steps.changes.outputs.code == 'true' }}
|
||||
if: ${{ !cancelled() }}
|
||||
run: docker build --build-arg GO_VERSION=1.22.12 .
|
||||
|
||||
release:
|
||||
name: Gitea release from CHANGELOG.md
|
||||
needs: test
|
||||
if: github.event_name == 'push'
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- run: python3 release-tooling/publish_release.py
|
||||
env:
|
||||
GITEA_API_URL: ${{ github.api_url }}
|
||||
GITEA_REPOSITORY: ${{ github.repository }}
|
||||
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SHA: ${{ github.sha }}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
.claude
|
||||
*.out
|
||||
__pycache__/
|
||||
data-import/cache/
|
||||
todo.md
|
||||
@@ -1,27 +1,18 @@
|
||||
# Rules
|
||||
|
||||
- Go runs only through `docker compose run --rm <test|vet|fmt|build|cyclo>`; the merge gate is `docker build .` plus CI's changelog check.
|
||||
- Tests first, in their own commit; the implementation follows in the next. A re-pin of seeded output or of `testdata/shipped_shape.txt` is its own commit.
|
||||
- A change under `data/` or to the shape pin adds its `CHANGELOG.md` entry under `Unreleased` in the same PR, and so does a change to a flag, an exit code, an exported name, a fence, a builtin or the lowest Go; what is major is the README's Versioning table.
|
||||
- Go runs only through `docker compose run --rm <test|vet|fmt|build>`; the merge gate is `docker build .`.
|
||||
- Tests first, in their own commit; the implementation follows in the next. A re-pin of seeded output is its own commit.
|
||||
- One-line commit messages: no ticket prefix, no repo name, no authorship trailers.
|
||||
- Merges are fast-forward only, which `tea pr merge` cannot spell — every style it
|
||||
offers fails with "is it still open?", as does a merge whose required checks are
|
||||
still pending, so read the checks before believing the style is the problem. Use
|
||||
`tea api /repos/{owner}/{repo}/pulls/<n>/merge -f Do=fast-forward-only`.
|
||||
- Gitea queues Actions runs that `/repos/{owner}/{repo}/actions/tasks` does not
|
||||
list, so an empty task list says nothing about whether CI ran; read
|
||||
`/repos/{owner}/{repo}/commits/<sha>/status`.
|
||||
- README goal 2 is gated at 7.0 on the `comprehension-panel` skill, and below it
|
||||
nothing else merges — no feature, no category, no data — bar a security fix and a
|
||||
dependency bump. `todo.md` carries the round: the nine-seat findings run at depth 1,
|
||||
a PR per item it names, then the run again, until the score passes. At or above 7.0
|
||||
every pull request scores with the four-seat run and answers it in one run. No merge
|
||||
lowers the last score.
|
||||
- Hard tabs. No comment by default; delete a restatement, a rationale, history, or a file preamble.
|
||||
- One spelling per result: reject the other at `New`, and let the error name the spelling to use.
|
||||
- A standing choice a reader would relitigate goes under Decisions in the README, not in a comment.
|
||||
- Prose naming a source states what that source states: read the register's own field
|
||||
before paraphrasing it, and name the register you read before claiming none
|
||||
publishes a spelling.
|
||||
- A README example is a `json` block that loads and renders as a category; `readme_test.go` runs every one.
|
||||
- Cyclomatic complexity is gated at 14: the table-shaped dispatches (`linkParent`, `compileTemplate`, `renderEdges`) sit at it and stay whole; a function that would pass it is decomposed.
|
||||
- Cyclomatic complexity is gated at 14: the table-shaped dispatches (`eachToken`, `calc.factor`, `walkPath`) sit at 13–14 and stay whole; anything else that reaches 14 is decomposed.
|
||||
|
||||
# Deferred
|
||||
|
||||
- **Shipped-data de-duplication (2026-09-02).** `email.json`'s `local` is a
|
||||
drifted copy of `username.json`, and no shipped file yet uses a held path, an
|
||||
operand or a reference. Fixed in the data fill before the first tag, when the
|
||||
shipped set is rewritten anyway; premise: nothing depends on the shipped data's
|
||||
shape until then. Not raised in review before that.
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
# Changelog
|
||||
|
||||
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). A release's
|
||||
`Breaking` section comes first and names each rejected spelling with its
|
||||
replacement, and each removed path, column or flag.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- First release: the CLI, the library and the shipped data set.
|
||||
- Table categories: a category JSON naming a `rows` TSV beside it, with the options
|
||||
`key`, `name`, `weight` and `parent`; a path selects a row by key or name,
|
||||
`misc.territory[SE]`, and descends to a linked table by name; linked tables draw
|
||||
consistently within one render and draw group. `rows` is an option, so no
|
||||
template may carry a field of that name. A `name` without a `key` resolves
|
||||
inside the table's `parent`. Refused at `New`: a `name` without a `key` or a
|
||||
`parent`, a name repeating inside one parent row, a name spelling another row's key, a table named like a column of any
|
||||
table above it, a table whose format or cell references its own family, and,
|
||||
within one render and draw group, a path drawing a table another path selects a
|
||||
row of, or two paths pinning different rows of one table.
|
||||
- `New` refuses a root choice of templates sharing one format and one set of string
|
||||
fields, naming the rows TSV to write instead.
|
||||
- `misc.territory`, `misc.currency`, `misc.language`, `misc.httpstatus` and
|
||||
`misc.mimetype` are tables. `misc.territory` is every ISO 3166-1 territory that has
|
||||
a capital, a currency and a TLD, with the columns `calling-code`, `capital`,
|
||||
`country`, `currency`, `flag`, `languages`, `numeric` and `tld` added, `country`
|
||||
holding the alpha-2 code of the sovereign state it belongs to and its own where it
|
||||
is one; `misc.currency` the
|
||||
current ISO 4217 currencies with a minor unit, with `decimals` and `numeric`
|
||||
added, and its symbols from CLDR; `misc.language` every ISO 639-2 entry carrying a
|
||||
639-1 code, with a `code3` column holding its 639-2/T code; `misc.httpstatus` each
|
||||
code the IANA registry lists with a plain reason phrase; and `misc.mimetype` each
|
||||
type mime-db records as IANA-registered and gives a filename extension.
|
||||
`DATA-LICENSES.md` lists each source.
|
||||
- `misc.timezone`, `misc.car` and `misc.useragent` are tables too. `misc.timezone`
|
||||
is every zone tzdb `zone.tab` gives a shipped territory, with `offset` holding the
|
||||
zone's standard UTC offset and `territory` linking to `misc.territory`, so
|
||||
`misc.territory[SE].timezone` draws `Europe/Stockholm`; zones of a territory
|
||||
`misc.territory` does not ship, Antarctica's among them, and the constant `UTC`
|
||||
are gone; spell that one as the text `"UTC"`. A `weight` column carries the
|
||||
population GeoNames records in each zone, so a draw favours the zones people live
|
||||
in. `misc.useragent` is the top-user-agents desktop and mobile lists with `browser`,
|
||||
`device` and `os` columns. `misc.car` keeps its makes and models in a `make` and a
|
||||
`model` column.
|
||||
- `misc.httpmethod`, `misc.protocol` and `misc.port` are IANA's registries as tables.
|
||||
`misc.httpmethod` is the eight methods RFC 9110 defines and PATCH, carrying
|
||||
`safe` and `idempotent` as `true` or `false`; `misc.protocol` the keyword, its
|
||||
`number` and its `name`, selectable by either; `misc.port` renders a TCP port number,
|
||||
with the IANA `service` name beside it, keeping the first service the registry
|
||||
describes for a port so a number selects one row.
|
||||
- `misc.tld` is the IANA root zone as a table: every delegated TLD keyed with its
|
||||
leading dot, `.se` and `.xn--p1ai`, the register's `type` beside it, and a `unicode`
|
||||
column holding the form the register displays, so `misc.tld[.рф]` selects the row
|
||||
`.xn--p1ai` renders. A delegation the register marks "Not assigned" does not ship.
|
||||
- `misc.loglevel` is the eight syslog severities: the `keyword` a configuration writes,
|
||||
`err` and `info`, keyed by the numerical `code` a PRI encodes and selectable by
|
||||
either, with the `severity` RFC 5424 spells beside it.
|
||||
- `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 over one consistent draw of them. `sv_SE.address` and
|
||||
`en_US.address` read those records, 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
|
||||
within one, in a single-quoted Go layout, drawn in UTC; `from` may equal `to`.
|
||||
`sv_SE.date`, `en_US.date`, `sv_SE.time` and `en_US.time` render through them, so
|
||||
`date.year`, `date.month`, `date.day`, `time.hour`, `time.minute`, `time.minute.t`,
|
||||
`time.sec` and `time.ampm` are no longer paths — a part of a date is now its own
|
||||
`{date(…,'2006')}`. `misc.datetime` is an RFC 3339 instant.
|
||||
- `sex`, `first-name` and `last-name` tables in `sv_SE` and `en_US`, weighted by
|
||||
bearers from SCB, the SSA and the Census Bureau; `first-name` links to `sex`, and a
|
||||
name both sexes carry is a row under each. `person` reads them, so its columns are
|
||||
`first`, `last`, `prefix` and `sex`; `person.femalefirst` and `person.malefirst` are
|
||||
no longer paths — draw `sex[f].first-name` and `sex[m].first-name` instead.
|
||||
`en_US.title` links to `sex` as well, so `en_US.person.prefix` draws `Mr` or `Ms`
|
||||
without contradicting the record's `sex`; `sv_SE.title` is an unsexed table of the
|
||||
same shape.
|
||||
- `sv_SE.personnummer` and `sv_SE.samordningsnummer`, Skatteverket's test series
|
||||
from a `sv_SE.birth-number` table under `sex`, in place of `sv_SE.ssn`, whose
|
||||
`ssn.mmdd`, `ssn.mmdd.m` and `ssn.mmdd.d` go with it. `en_US.ssn` now draws the
|
||||
ranges the SSA assigns and carries the columns `area`, `group` and `serial`, so
|
||||
`--format csv en_US.ssn` writes a header where it used to fail; `en_US.itin` is new
|
||||
and carries the same three. `person.prefix` is null where a person has no title,
|
||||
where it used to be an empty string, so `--format sql` writes `NULL` and a `string`
|
||||
struct field reading it becomes `*string`.
|
||||
- `ErrNoColumns` is exported, so a caller can tell the one record fence a path can
|
||||
answer from the rest.
|
||||
- An error names a spelling that runs: a layout is named single-quoted and free of
|
||||
its own quotes, a row of a table with no key is named as the path that selects it,
|
||||
`sv_SE.sex[f].first-name[Kim]`, and a category with no columns names the record
|
||||
that gives it one.
|
||||
@@ -1,31 +0,0 @@
|
||||
# Data licenses
|
||||
|
||||
Every shipped dataset, its source, its licence and the attribution it asks for. A
|
||||
`data-import/` script rebuilds each sourced table, run as the README's
|
||||
[Development](README.md#development) section says; a curated one is hand-written.
|
||||
|
||||
| Table | Source | Licence | Attribution | Rebuild |
|
||||
|-------|--------|---------|-------------|---------|
|
||||
| `geo/SE/locality.tsv`, `postal-code.tsv` | [GeoNames](https://www.geonames.org/) postal codes for SE; populations from SCB tätorter 2023 | [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/); CC0 1.0 | "Postal codes from GeoNames, www.geonames.org" | `data-import/geo-se.py` |
|
||||
| `geo/SE/region.tsv`, `municipality.tsv` | [SCB](https://www.scb.se/) län and kommun codes 2026 and population 2024 | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) | none required | `data-import/geo-se.py` |
|
||||
| `geo/SE/street.tsv` | [Trafikverket NVDB](https://www.trafikverket.se/) Gatunamn, through the open API | CC0 1.0 | none required | `data-import/geo-se.py` |
|
||||
| `geo/US/region.tsv`, `municipality.tsv`, `locality.tsv` | [Census Bureau](https://www.census.gov/) Gazetteer 2026 and population estimates 2025 | [public domain](https://www.usa.gov/government-works) | none required | `data-import/geo-us.py` |
|
||||
| `geo/US/postal-code.tsv`, `street.tsv` | Census Bureau ZCTA to place relationships 2020 and TIGER/Line 2025 address ranges and feature names | public domain | none required | `data-import/geo-us.py` |
|
||||
| `sv_SE/first-name.tsv`, `last-name.tsv` | [SCB](https://www.scb.se/) names with at least two bearers, 31 December 2022 | CC0 1.0 | "Källa: SCB" | `data-import/names-se.py` |
|
||||
| `en_US/first-name.tsv` | [SSA](https://www.ssa.gov/oact/babynames/) baby names, births 1930 to 2020, through [hackerb9/ssa-baby-names](https://github.com/hackerb9/ssa-baby-names) | public domain | none required | `data-import/names-us.py` |
|
||||
| `en_US/last-name.tsv` | Census Bureau surnames occurring 100 or more times, 2010 | public domain | none required | `data-import/names-us.py` |
|
||||
| `sv_SE/sex.tsv`, `sv_SE/birth-number.tsv`, `sv_SE/title.tsv`, `en_US/sex.tsv`, `en_US/title.tsv`, `misc/car.tsv` | curated; Skatteverket's test birth numbers are facts, and `car.tsv` waits for an international source (goal 12) | — | — | — |
|
||||
| `misc/currency.tsv` | [datasets/currency-codes](https://github.com/datasets/currency-codes); symbols from [Unicode CLDR](https://github.com/unicode-org/cldr) `en.xml` and `root.xml` | PDDL 1.0; [Unicode License v3](https://www.unicode.org/license.txt) | CLDR: "Copyright © 1991-2025 Unicode, Inc. Unicode and the Unicode Logo are registered trademarks of Unicode, Inc. in the United States and other countries." | `data-import/currency.py` |
|
||||
| `misc/httpmethod.tsv` | [IANA HTTP Method Registry](https://www.iana.org/assignments/http-methods/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/httpmethod.py` |
|
||||
| `misc/httpstatus.tsv` | [IANA HTTP Status Code Registry](https://www.iana.org/assignments/http-status-codes/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/httpstatus.py` |
|
||||
| `misc/language.tsv` | [datasets/language-codes](https://github.com/datasets/language-codes), the [Library of Congress](https://www.loc.gov/standards/iso639-2/) ISO 639-2 register | [PDDL 1.0](https://opendatacommons.org/licenses/pddl/1-0/) | none required | `data-import/language.py` |
|
||||
| `misc/loglevel.tsv` | [RFC 5424](https://www.rfc-editor.org/rfc/rfc5424.txt) Table 2, the syslog message severities; keywords from the [POSIX](https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/syslog.h.html) `<syslog.h>` severity macros | facts | none required | `data-import/loglevel.py` |
|
||||
| `misc/mimetype.tsv` | [mime-db](https://github.com/jshttp/mime-db), the IANA media type registry with filename extensions | [MIT](https://github.com/jshttp/mime-db/blob/master/LICENSE) | "Copyright (c) 2014 Jonathan Ong, Copyright (c) 2015-2022 Douglas Christopher Wilson" | `data-import/mimetype.py` |
|
||||
| `misc/port.tsv` | [IANA Service Name and Transport Protocol Port Number Registry](https://www.iana.org/assignments/service-names-port-numbers/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/port.py` |
|
||||
| `misc/protocol.tsv` | [IANA Protocol Numbers](https://www.iana.org/assignments/protocol-numbers/) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/protocol.py` |
|
||||
| `misc/territory.tsv` | [datasets/country-codes](https://github.com/datasets/country-codes) | [PDDL 1.0](https://opendatacommons.org/licenses/pddl/1-0/) | none required | `data-import/territory.py` |
|
||||
| `misc/timezone.tsv` | [IANA tzdb](https://www.iana.org/time-zones) 2026d `zone.tab` and the standard offset of each zone; weights from [GeoNames](https://www.geonames.org/) `cities15000` populations | [public domain](https://data.iana.org/time-zones/tzdb/LICENSE); [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) | "Populations from GeoNames, www.geonames.org" | `data-import/timezone.py` |
|
||||
| `misc/tld.tsv` | [IANA Root Zone Database](https://www.iana.org/domains/root/db), checked against the root zone's [TLD list](https://data.iana.org/TLD/tlds-alpha-by-domain.txt) | [public domain](https://www.iana.org/help/licensing-terms) | none required | `data-import/tld.py` |
|
||||
| `misc/useragent.tsv` | [top-user-agents](https://github.com/microlinkhq/top-user-agents) desktop and mobile lists | [MIT](https://github.com/microlinkhq/top-user-agents/blob/master/LICENSE.md) | "Copyright © 2020 Kiko Beats" | `data-import/useragent.py` |
|
||||
|
||||
Every other category is hand-written under [`data/`](data), MIT like the code.
|
||||
+6
-6
@@ -26,12 +26,12 @@ func benchPath(b *testing.B, dir, path string) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPerson(b *testing.B) { benchPath(b, "data", "sv_SE.person") }
|
||||
func BenchmarkAddress(b *testing.B) { benchPath(b, "data", "sv_SE.address") }
|
||||
func BenchmarkWord(b *testing.B) { benchPath(b, "data", "sv_SE.word") }
|
||||
func BenchmarkCreditcard(b *testing.B) { benchPath(b, "data", "misc.creditcard") }
|
||||
func BenchmarkPersonnummer(b *testing.B) { benchPath(b, "data", "sv_SE.personnummer") }
|
||||
func BenchmarkUUIDv7(b *testing.B) { benchPath(b, "data", "misc.uuid") }
|
||||
func BenchmarkPerson(b *testing.B) { benchPath(b, "data/sv_SE", "person") }
|
||||
func BenchmarkAddress(b *testing.B) { benchPath(b, "data/sv_SE", "address") }
|
||||
func BenchmarkWord(b *testing.B) { benchPath(b, "data/sv_SE", "word") }
|
||||
func BenchmarkCreditcard(b *testing.B) { benchPath(b, "data/misc", "creditcard") }
|
||||
func BenchmarkSSN(b *testing.B) { benchPath(b, "data/sv_SE", "ssn") }
|
||||
func BenchmarkUUIDv7(b *testing.B) { benchPath(b, "data/misc", "uuid") }
|
||||
|
||||
func tmpData(b *testing.B, name, body string) string {
|
||||
b.Helper()
|
||||
|
||||
+211
-75
@@ -2,19 +2,19 @@ package fejkdata
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// maxLen caps sample output lengths (hex, nanoid, base64, digits, upper, lower)
|
||||
// and maxDecimals float/calc decimal places, so a fat-fingered or overflowing
|
||||
// argument fails at New instead of trying to allocate gigabytes — or panicking —
|
||||
// at render.
|
||||
// maxLen caps sample output lengths (hex, nanoid, base64) and the renders a repeat
|
||||
// multiplies to along any path; maxDecimals caps float/calc decimal places. So a
|
||||
// fat-fingered or overflowing argument fails at New instead of trying to allocate
|
||||
// gigabytes — or panicking — at render.
|
||||
const (
|
||||
maxLen = 1 << 20
|
||||
maxLen = 1 << 20 // 1,048,576 chars/bytes
|
||||
maxDecimals = 1024
|
||||
)
|
||||
|
||||
@@ -30,11 +30,9 @@ var builtins = map[string]builtin{
|
||||
"ulid": {arity: 0, prep: sample(ulid)},
|
||||
"nanoid": {arity: 1, check: posIntArg, prep: chars(nanoidAlphabet)},
|
||||
"hex": {arity: 1, check: posIntArg, prep: chars(hexDigits)},
|
||||
"digits": {arity: 1, check: posIntArg, prep: chars("0123456789"), number: func(token string, a []string) proven {
|
||||
return printing(token, DataTypeString, bounded(0, math.Pow(10, float64(atoi(a[0])))-1, true))
|
||||
}},
|
||||
"upper": {arity: 1, check: posIntArg, prep: chars("ABCDEFGHIJKLMNOPQRSTUVWXYZ")},
|
||||
"lower": {arity: 1, check: posIntArg, prep: chars("abcdefghijklmnopqrstuvwxyz")},
|
||||
"digits": {arity: 1, check: posIntArg, prep: chars("0123456789")},
|
||||
"upper": {arity: 1, check: posIntArg, prep: chars("ABCDEFGHIJKLMNOPQRSTUVWXYZ")},
|
||||
"lower": {arity: 1, check: posIntArg, prep: chars("abcdefghijklmnopqrstuvwxyz")},
|
||||
"base64": {arity: 1, check: posIntArg, prep: func(a []string) callFn {
|
||||
n := atoi(a[0])
|
||||
return func(s *session, _ string, _ []string) string {
|
||||
@@ -44,23 +42,17 @@ var builtins = map[string]builtin{
|
||||
"int": {arity: 2, check: intRangeArgs, prep: func(a []string) callFn {
|
||||
lo, span := atoi(a[0]), atoi(a[1])-atoi(a[0])+1
|
||||
return func(s *session, _ string, _ []string) string { return strconv.Itoa(lo + s.IntN(span)) }
|
||||
}, number: func(token string, a []string) proven {
|
||||
return printing(token, DataTypeInteger, bounded(float64(atoi(a[0])), float64(atoi(a[1])), true))
|
||||
}},
|
||||
"float": {arity: 3, check: floatArgs, prep: func(a []string) callFn {
|
||||
lo, hi, dp := atof(a[0]), atof(a[1]), atoi(a[2])
|
||||
return func(s *session, _ string, _ []string) string {
|
||||
return formatFloat(lo+s.Float64()*(hi-lo), dp)
|
||||
return strconv.FormatFloat(lo+s.Float64()*(hi-lo), 'f', dp, 64)
|
||||
}
|
||||
}, number: func(token string, a []string) proven {
|
||||
return printedNumber(token, bounded(atof(a[0]), atof(a[1]), false), atoi(a[2]))
|
||||
}},
|
||||
"iban": {arity: 1, check: ibanArg, prep: func(a []string) callFn {
|
||||
cc := a[0]
|
||||
return func(s *session, _ string, _ []string) string { return iban(s, cc) }
|
||||
}},
|
||||
"date": {arity: -1, check: dateArgs, prep: datePrep},
|
||||
"time": {arity: -1, check: timeArg, prep: timePrep},
|
||||
"calc": {arity: -1, check: checkCalc, prep: calcPrep, operands: calcOperands},
|
||||
"lowercase": {arity: 1, check: transformArg, prep: transformPrep(strings.ToLower), operands: transformOperand},
|
||||
"uppercase": {arity: 1, check: transformArg, prep: transformPrep(strings.ToUpper), operands: transformOperand},
|
||||
@@ -76,8 +68,6 @@ var builtins = map[string]builtin{
|
||||
return func(s *session, _ string, _ []string) string {
|
||||
return strconv.FormatUint(s.next(key), 10)
|
||||
}
|
||||
}, number: func(token string, _ []string) proven {
|
||||
return printing(token, DataTypeInteger, bounded(1, math.MaxInt64, true))
|
||||
}},
|
||||
}
|
||||
|
||||
@@ -89,11 +79,13 @@ func derive(f func(emitted string) string) func([]string) callFn {
|
||||
return func(_ *session, emitted string, _ []string) string { return f(emitted) }
|
||||
}
|
||||
}
|
||||
|
||||
func sample(f func(rng) string) func([]string) callFn {
|
||||
return func([]string) callFn {
|
||||
return func(s *session, _ string, _ []string) string { return f(s) }
|
||||
}
|
||||
}
|
||||
|
||||
func chars(alphabet string) func([]string) callFn {
|
||||
return func(a []string) callFn {
|
||||
n := atoi(a[0])
|
||||
@@ -103,13 +95,94 @@ func chars(alphabet string) func([]string) callFn {
|
||||
|
||||
const hexDigits = "0123456789abcdef"
|
||||
|
||||
// formatFloat prints v to dp decimals, -1 for the shortest form, and a zero unsigned.
|
||||
func formatFloat(v float64, dp int) string {
|
||||
s := strconv.FormatFloat(v, 'f', dp, 64)
|
||||
if strings.HasPrefix(s, "-") && strings.Trim(s, "-0.") == "" {
|
||||
return s[1:]
|
||||
// transforms are the builtins that rewrite one operand's value; they nest, so
|
||||
// {lowercase(ascii(x))} folds then lowers.
|
||||
var transforms = map[string]func(string) string{
|
||||
"ascii": asciiFold,
|
||||
"lowercase": strings.ToLower,
|
||||
"uppercase": strings.ToUpper,
|
||||
}
|
||||
|
||||
// unwrapTransform peels nested transform calls off an operand arg, returning the
|
||||
// field it finally names and the transforms to apply, innermost last.
|
||||
func unwrapTransform(arg string) (leaf string, chain []func(string) string, err error) {
|
||||
for {
|
||||
name, args, isCall := funcCall(arg)
|
||||
if !isCall {
|
||||
return arg, chain, nil
|
||||
}
|
||||
fn, isTransform := transforms[name]
|
||||
if !isTransform {
|
||||
return "", nil, fmt.Errorf("%s(%s) is not a transform, so it cannot be an operand", name, strings.Join(args, ","))
|
||||
}
|
||||
if len(args) != 1 {
|
||||
return "", nil, fmt.Errorf("%s takes 1 arg, got %d", name, len(args))
|
||||
}
|
||||
chain = append(chain, fn)
|
||||
arg = args[0]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func transformArg(fields map[string]node, a []string) error {
|
||||
leaf, _, err := unwrapTransform(a[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isRef(leaf) {
|
||||
_, _, err := refShape(leaf)
|
||||
return err
|
||||
}
|
||||
return checkArm(leaf, fields)
|
||||
}
|
||||
|
||||
func transformOperand(a []string) []string {
|
||||
leaf, _, err := unwrapTransform(a[0])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return []string{leaf}
|
||||
}
|
||||
|
||||
func transformPrep(outer func(string) string) func([]string) callFn {
|
||||
return func(a []string) callFn {
|
||||
_, chain, err := unwrapTransform(a[0])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("fejkdata: transform arg %q reached prep unvalidated: %v", a[0], err))
|
||||
}
|
||||
return func(_ *session, _ string, operands []string) string {
|
||||
v := operands[0]
|
||||
for i := len(chain) - 1; i >= 0; i-- {
|
||||
v = chain[i](v)
|
||||
}
|
||||
return outer(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// asciiFolds maps the Latin letters with diacritics or ligatures to ASCII.
|
||||
var asciiFolds = map[rune]string{
|
||||
'À': "A", 'Á': "A", 'Â': "A", 'Ã': "A", 'Ä': "A", 'Å': "A", 'Æ': "AE", 'Ç': "C",
|
||||
'È': "E", 'É': "E", 'Ê': "E", 'Ë': "E", 'Ì': "I", 'Í': "I", 'Î': "I", 'Ï': "I",
|
||||
'Ð': "D", 'Ñ': "N", 'Ò': "O", 'Ó': "O", 'Ô': "O", 'Õ': "O", 'Ö': "O", 'Ø': "O",
|
||||
'Ù': "U", 'Ú': "U", 'Û': "U", 'Ü': "U", 'Ý': "Y", 'Þ': "Th", 'ß': "ss", 'Œ': "OE",
|
||||
'à': "a", 'á': "a", 'â': "a", 'ã': "a", 'ä': "a", 'å': "a", 'æ': "ae", 'ç': "c",
|
||||
'è': "e", 'é': "e", 'ê': "e", 'ë': "e", 'ì': "i", 'í': "i", 'î': "i", 'ï': "i",
|
||||
'ð': "d", 'ñ': "n", 'ò': "o", 'ó': "o", 'ô': "o", 'õ': "o", 'ö': "o", 'ø': "o",
|
||||
'ù': "u", 'ú': "u", 'û': "u", 'ü': "u", 'ý': "y", 'þ': "th", 'ÿ': "y", 'œ': "oe",
|
||||
}
|
||||
|
||||
// asciiFold rewrites s to ASCII: folded Latin letters stay, any other non-ASCII
|
||||
// rune is dropped.
|
||||
func asciiFold(s string) string {
|
||||
var b strings.Builder
|
||||
for _, r := range s {
|
||||
if r < unicode.MaxASCII {
|
||||
b.WriteRune(r)
|
||||
} else {
|
||||
b.WriteString(asciiFolds[r])
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// atoi parses an arg a builtin's check already validated. It panics rather than
|
||||
@@ -131,6 +204,7 @@ func atof(s string) float64 {
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func randBytes(r rng, n int) []byte {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
@@ -138,6 +212,7 @@ func randBytes(r rng, n int) []byte {
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func randChars(r rng, n int, alphabet string) string {
|
||||
b := make([]byte, n)
|
||||
for i := range b {
|
||||
@@ -146,86 +221,54 @@ func randChars(r rng, n int, alphabet string) string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// plainInt parses an integer arg written the one way: no sign, no leading zero.
|
||||
func plainInt(s string) (int, error) {
|
||||
n, err := strconv.Atoi(s)
|
||||
if errors.Is(err, strconv.ErrRange) {
|
||||
return 0, fmt.Errorf("%q is past the integer range: %w", s, err)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%q is not an integer", s)
|
||||
}
|
||||
if strconv.Itoa(n) != s {
|
||||
return 0, fmt.Errorf("%q is not a plain integer; write %d", s, n)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
func posIntArg(_ map[string]node, a []string) error {
|
||||
n, err := plainInt(a[0])
|
||||
if errors.Is(err, strconv.ErrRange) {
|
||||
return fmt.Errorf("count %q exceeds the maximum %d", a[0], maxLen)
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("count %w", err)
|
||||
}
|
||||
if n < 1 {
|
||||
return fmt.Errorf("count %q must be positive", a[0])
|
||||
n, err := strconv.Atoi(a[0])
|
||||
if err != nil || n < 1 {
|
||||
return fmt.Errorf("count %q must be a positive integer", a[0])
|
||||
}
|
||||
if n > maxLen {
|
||||
return fmt.Errorf("count %d exceeds the maximum %d", n, maxLen)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func intRangeArgs(_ map[string]node, a []string) error {
|
||||
lo, err := plainInt(a[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("int(min,max): min %w", err)
|
||||
}
|
||||
hi, err := plainInt(a[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("int(min,max): max %w", err)
|
||||
lo, e1 := strconv.Atoi(a[0])
|
||||
hi, e2 := strconv.Atoi(a[1])
|
||||
if e1 != nil || e2 != nil {
|
||||
return fmt.Errorf("int(min,max) needs integer args, got %q,%q", a[0], a[1])
|
||||
}
|
||||
if lo > hi {
|
||||
return fmt.Errorf("int(min,max): min %d > max %d", lo, hi)
|
||||
}
|
||||
if lo == hi {
|
||||
return fmt.Errorf("int(%d,%d) is the constant %d; write it as text", lo, hi, lo)
|
||||
}
|
||||
if uint64(hi)-uint64(lo) >= uint64(math.MaxInt64) { // span hi-lo+1 would overflow int -> IntN panic
|
||||
return fmt.Errorf("int(min,max): range %d..%d is too wide", lo, hi)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func floatArgs(_ map[string]node, a []string) error {
|
||||
lo, e1 := strconv.ParseFloat(a[0], 64)
|
||||
hi, e2 := strconv.ParseFloat(a[1], 64)
|
||||
if e1 != nil || e2 != nil {
|
||||
return fmt.Errorf("float(min,max,dp) needs numeric bounds, got %q,%q", a[0], a[1])
|
||||
}
|
||||
dp, err := plainInt(a[2])
|
||||
if err != nil {
|
||||
return fmt.Errorf("float(min,max,dp): decimals %w", err)
|
||||
}
|
||||
if math.IsNaN(lo) || math.IsNaN(hi) || math.IsInf(lo, 0) || math.IsInf(hi, 0) {
|
||||
return fmt.Errorf("float(min,max,dp) needs finite bounds, got %q,%q", a[0], a[1])
|
||||
dp, e3 := strconv.Atoi(a[2])
|
||||
if e1 != nil || e2 != nil || e3 != nil {
|
||||
return fmt.Errorf("float(min,max,dp) needs numeric args, got %q,%q,%q", a[0], a[1], a[2])
|
||||
}
|
||||
if lo > hi {
|
||||
return fmt.Errorf("float(min,max,dp): min %v > max %v", lo, hi)
|
||||
}
|
||||
if dp < 0 || dp > maxDecimals {
|
||||
return fmt.Errorf("float(min,max,dp): decimals %d out of range 0..%d", dp, maxDecimals)
|
||||
}
|
||||
if lo == hi {
|
||||
return fmt.Errorf("float(%s,%s,%d) is the constant %q; write it as text", a[0], a[1], dp, strconv.FormatFloat(lo, 'f', dp, 64))
|
||||
}
|
||||
if math.IsInf(hi-lo, 0) { // an overflowing span would render as "+Inf"
|
||||
return fmt.Errorf("float(min,max,dp): range %v..%v is too wide", lo, hi)
|
||||
}
|
||||
if dp < 0 || dp > maxDecimals {
|
||||
return fmt.Errorf("float(min,max,dp): decimals %d out of range 0..%d", dp, maxDecimals)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func seqArg(_ map[string]node, a []string) error {
|
||||
if len(a) > 1 {
|
||||
return fmt.Errorf("seq takes at most one name, got %d", len(a))
|
||||
return fmt.Errorf("seq takes at most one name, got %d args", len(a))
|
||||
}
|
||||
if len(a) == 1 && a[0] == "" {
|
||||
return fmt.Errorf("seq name must not be empty")
|
||||
@@ -276,3 +319,96 @@ func ulid(r rng) string {
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// luhnCheck returns the Luhn check digit (0-9) over the digits of s; non-digit
|
||||
// runes are skipped. Doubling runs from the rightmost digit, so the result is
|
||||
// correct whatever the payload length.
|
||||
func luhnCheck(s string) int {
|
||||
sum, double := 0, true
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
continue
|
||||
}
|
||||
d := int(c - '0')
|
||||
if double {
|
||||
if d *= 2; d > 9 {
|
||||
d -= 9
|
||||
}
|
||||
}
|
||||
double = !double
|
||||
sum += d
|
||||
}
|
||||
return (10 - sum%10) % 10
|
||||
}
|
||||
|
||||
// mod11Check returns the weighted mod-11 check character over the digits of s
|
||||
// (weights 2..7 cycling from the right). A would-be value of 10 emits 'X', as in
|
||||
// ISBN-10 / ISO 7064; non-digits are skipped.
|
||||
func mod11Check(s string) string {
|
||||
sum, w := 0, 2
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
continue
|
||||
}
|
||||
sum += int(c-'0') * w
|
||||
if w++; w > 7 {
|
||||
w = 2
|
||||
}
|
||||
}
|
||||
if chk := (11 - sum%11) % 11; chk != 10 {
|
||||
return string(rune('0' + chk))
|
||||
}
|
||||
return "X"
|
||||
}
|
||||
|
||||
// eanCheck returns the EAN-13 / UPC-A / ISBN-13 / GTIN check digit over the
|
||||
// digits of s: weights 3 and 1 alternating from the rightmost digit, mod 10.
|
||||
func eanCheck(s string) string {
|
||||
sum, w := 0, 3
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
continue
|
||||
}
|
||||
sum += int(c-'0') * w
|
||||
w = 4 - w // 3 <-> 1
|
||||
}
|
||||
return string(rune('0' + (10-sum%10)%10))
|
||||
}
|
||||
|
||||
// ibanLen maps a supported country code to the full IBAN length. The check digits
|
||||
// sit between the country code and the BBAN, so — unlike luhn/ean — iban can't be
|
||||
// a left-to-right derivation; it generates the whole value instead.
|
||||
var ibanLen = map[string]int{"BE": 16, "DE": 22, "DK": 18, "ES": 24, "FI": 18, "NO": 15, "SE": 24}
|
||||
|
||||
func ibanArg(_ map[string]node, a []string) error {
|
||||
if _, ok := ibanLen[a[0]]; !ok {
|
||||
return fmt.Errorf("iban(%q): unsupported country code", a[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// iban generates a structurally valid IBAN for cc: a numeric BBAN of the right
|
||||
// length, then mod-97 check digits. Real bank/branch structure isn't modelled —
|
||||
// the result passes length and checksum validation, which is what fake data needs.
|
||||
func iban(r rng, cc string) string {
|
||||
bban := make([]byte, ibanLen[cc]-4)
|
||||
for i := range bban {
|
||||
bban[i] = byte('0' + r.IntN(10))
|
||||
}
|
||||
rem := 0
|
||||
feed := func(d int) { rem = (rem*10 + d) % 97 }
|
||||
for _, c := range bban {
|
||||
feed(int(c - '0'))
|
||||
}
|
||||
for i := 0; i < len(cc); i++ { // letters A-Z -> 10..35, fed as two digits
|
||||
v := int(cc[i]-'A') + 10
|
||||
feed(v / 10)
|
||||
feed(v % 10)
|
||||
}
|
||||
feed(0)
|
||||
feed(0)
|
||||
return fmt.Sprintf("%s%02d%s", cc, 98-rem, bban)
|
||||
}
|
||||
|
||||
@@ -4,9 +4,7 @@ import (
|
||||
"encoding/base64"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestBuiltinIDGenerators(t *testing.T) {
|
||||
@@ -43,7 +41,6 @@ func TestBuiltinSamplesReproducible(t *testing.T) {
|
||||
`"{uuid()}"`, `"{ulid()}"`,
|
||||
`"{nanoid(12)}"`, `"{int(1,1000000)}"`,
|
||||
`"{float(0,1,6)}"`, `"{base64(12)}"`, `"{iban(SE)}"`,
|
||||
`"{date(2000-01-01,2020-12-31,'2006-01-02 15:04:05')}"`, `"{time('15:04:05')}"`,
|
||||
} {
|
||||
if a, b := mustRender(t, engine(7), tmpl), mustRender(t, engine(7), tmpl); a != b {
|
||||
t.Fatalf("%s not reproducible: %q != %q", tmpl, a, b)
|
||||
@@ -72,9 +69,6 @@ func TestBuiltinFloat(t *testing.T) {
|
||||
if got := mustRender(t, f, `"{float(1,2,3)}"`); !re.MatchString(got) {
|
||||
t.Fatalf("float(1,2,3) = %q, want d.ddd in [1,2]", got)
|
||||
}
|
||||
if got := mustRender(t, f, `"{float(-1,1,0)}"`); got == "-0" {
|
||||
t.Fatalf("float(-1,1,0) = %q, want a zero printed unsigned", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,135 +230,3 @@ func TestClassBuiltinArgs(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuiltinDateAndTime pins the two clock-free samples: date draws a second in
|
||||
// [from 00:00:00, to 23:59:59], both days reachable, and renders it in the quoted Go
|
||||
// layout, commas and English names included; time draws a second within one day.
|
||||
func TestBuiltinDateAndTime(t *testing.T) {
|
||||
f := engine(1)
|
||||
seen := map[string]bool{}
|
||||
for i := 0; i < 500; i++ {
|
||||
got := mustRender(t, f, `"{date(1990-01-01,1990-12-31,'2006-01-02')}"`)
|
||||
if d, err := time.Parse("2006-01-02", got); err != nil || d.Year() != 1990 {
|
||||
t.Fatalf("date = %q, want a 1990 calendar date (err %v)", got, err)
|
||||
}
|
||||
seen[got] = true
|
||||
}
|
||||
if len(seen) < 200 {
|
||||
t.Fatalf("date drew %d distinct days of 365 in 500, want a uniform spread", len(seen))
|
||||
}
|
||||
lo, hi := false, false
|
||||
for i := 0; i < 200; i++ {
|
||||
got := mustRender(t, f, `"{date(2020-02-28,2020-02-29,'2006-01-02')}"`)
|
||||
if got != "2020-02-28" && got != "2020-02-29" {
|
||||
t.Fatalf("date(2020-02-28,2020-02-29) = %q, out of range", got)
|
||||
}
|
||||
lo, hi = lo || got == "2020-02-28", hi || got == "2020-02-29"
|
||||
}
|
||||
if !lo || !hi {
|
||||
t.Fatalf("date never hit a bound: lo=%v hi=%v (bounds must be inclusive)", lo, hi)
|
||||
}
|
||||
if got := mustRender(t, f, `"{date(2020-07-04,2020-07-05,'January 2, 2006')}"`); got != "July 4, 2020" && got != "July 5, 2020" {
|
||||
t.Fatalf("date with a comma in its layout = %q", got)
|
||||
}
|
||||
rfc := regexp.MustCompile(`^2021-\d\d-\d\dT\d\d:\d\d:\d\dZ$`)
|
||||
clock := regexp.MustCompile(`^([01]\d|2[0-3]):[0-5]\d$`)
|
||||
ampm := regexp.MustCompile(`^(1[0-2]|[1-9]):[0-5]\d (AM|PM)$`)
|
||||
seconds := map[string]bool{}
|
||||
for i := 0; i < 300; i++ {
|
||||
got := mustRender(t, f, `"{date(2021-01-01,2021-12-31,'2006-01-02T15:04:05Z07:00')}"`)
|
||||
if !rfc.MatchString(got) {
|
||||
t.Fatalf("date in an RFC 3339 layout = %q, want %s", got, rfc)
|
||||
}
|
||||
seconds[got[17:19]] = true
|
||||
if got := mustRender(t, f, `"{time('15:04')}"`); !clock.MatchString(got) {
|
||||
t.Fatalf("time('15:04') = %q, want %s", got, clock)
|
||||
}
|
||||
if got := mustRender(t, f, `"{time('3:04 PM')}"`); !ampm.MatchString(got) {
|
||||
t.Fatalf("time('3:04 PM') = %q, want %s", got, ampm)
|
||||
}
|
||||
got = mustRender(t, f, `"{date(1950-01-01,2000-12-31,'060102')}-238{luhn()}"`)
|
||||
if d := digitsOnly(got); len(d) != 10 || !luhnValid(d) {
|
||||
t.Fatalf("a personnummer over date() = %q, want ten Luhn-valid digits", got)
|
||||
}
|
||||
}
|
||||
if len(seconds) < 30 {
|
||||
t.Fatalf("date drew %d distinct seconds in 300, want the whole day, not midnight", len(seconds))
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuiltinDateArgs pins the New-time checks: bounds are calendar dates in order,
|
||||
// the layout is quoted, names a field, and for time names no date field.
|
||||
func TestBuiltinDateArgs(t *testing.T) {
|
||||
for tmpl, want := range map[string]string{
|
||||
`"{date(1990-13-01,1990-12-31,'2006-01-02')}"`: "1990-13-01",
|
||||
`"{date(1990-12-31,1990-01-01,'2006-01-02')}"`: "is after",
|
||||
`"{date(1990-01-01,1990-01-01,'2006-01-02')}"`: "write it as text",
|
||||
`"{date(1990-01-01,1990-12-31,2006-01-02)}"`: "'2006-01-02'",
|
||||
`"{date(1990-01-01,1990-12-31,'January 2, 2006)}"`: "'",
|
||||
`"{date(1990-01-01,1990-12-31,\"January 2, 2006\")}"`: "quoted: 'January 2, 2006'",
|
||||
`"{date(1990-01-01,1990-12-31,'x')}"`: "text",
|
||||
`"{date(1990-01-01,1990-12-31,'')}"`: "text",
|
||||
`"{date(1990-01-01,1990-12-31)}"`: "3 arguments",
|
||||
`"{date(1990-01-01,1990-12-31,January 2, 2006)}"`: "'January 2, 2006'",
|
||||
`"{time(3:04 PM, Mon)}"`: "'3:04 PM, Mon'",
|
||||
`"{time(15:04)}"`: "'15:04'",
|
||||
`"{time('2006-01-02 15:04')}"`: "date(",
|
||||
`"{time('x')}"`: "text",
|
||||
`"{date(1990-01-01,1990-12-31,'15:04')}"`: "time('15:04')",
|
||||
} {
|
||||
_, err := compile(parse(t, tmpl))
|
||||
if err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("compile(%s) = %v, want an error mentioning %q", tmpl, err, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuiltinLayoutErrorsNameARunnableSpelling pins the two layout errors a user
|
||||
// can follow: a double-quoted layout is named single-quoted, without its own
|
||||
// quotes carried into the suggestion, whether it split on a comma or not.
|
||||
func TestBuiltinLayoutErrorsNameARunnableSpelling(t *testing.T) {
|
||||
_, err := compile(parse(t, `"{date(1990-01-01,1990-12-31,\"2006-01-02\")}"`))
|
||||
if err == nil || !strings.Contains(err.Error(), "write '2006-01-02'") {
|
||||
t.Fatalf("a double-quoted layout = %v, want it named single-quoted", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), `'"`) {
|
||||
t.Errorf("%v names a layout that renders its own quotes", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), "is double-quoted") {
|
||||
t.Errorf("%v does not say which quotes were wrong", err)
|
||||
}
|
||||
_, err = compile(parse(t, `"{time(15:04)}"`))
|
||||
if err == nil || !strings.Contains(err.Error(), "write '15:04'") {
|
||||
t.Fatalf("a bare layout = %v, want it named quoted", err)
|
||||
}
|
||||
// The comma hint belongs to a layout that split, not to a call given extra args.
|
||||
_, err = compile(parse(t, `"{time(0,12,'15:04')}"`))
|
||||
if err == nil || !strings.Contains(err.Error(), "takes 1 argument, got 3") {
|
||||
t.Fatalf("time with three args = %v, want the count named in the singular", err)
|
||||
}
|
||||
if strings.Contains(err.Error(), "holding a comma") {
|
||||
t.Errorf("%v offers the comma hint though the layout is already quoted", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestBuiltinDateSpansOneDay pins that from == to is a day: with a clock layout it
|
||||
// draws every second of it, and without one it could only emit one value.
|
||||
func TestBuiltinDateSpansOneDay(t *testing.T) {
|
||||
f := engine(1)
|
||||
seen := map[string]bool{}
|
||||
for i := 0; i < 500; i++ {
|
||||
got := mustRender(t, f, `"{date(2026-01-01,2026-01-01,'2006-01-02 15:04:05')}"`)
|
||||
if !strings.HasPrefix(got, "2026-01-01 ") {
|
||||
t.Fatalf("date over one day = %q, out of range", got)
|
||||
}
|
||||
seen[got] = true
|
||||
}
|
||||
if len(seen) < 400 {
|
||||
t.Fatalf("date over one day drew %d distinct seconds in 500, want the whole day", len(seen))
|
||||
}
|
||||
_, err := compile(parse(t, `"{date(2026-01-01,2026-01-01,'2006-01-02')}"`))
|
||||
if err == nil || !strings.Contains(err.Error(), "write it as text") {
|
||||
t.Fatalf("one day in a date-only layout = %v, want it named a constant", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,88 +78,18 @@ func checkCalc(fields map[string]node, args []string) error {
|
||||
return fmt.Errorf("calc(%q): operand %q is never a number: it renders %q", args[0], name, text)
|
||||
}
|
||||
}
|
||||
if divisor, zero := constantZeroDivisor(expr, fields); zero {
|
||||
return fmt.Errorf("calc(%q) divides by %s, which is always zero", args[0], divisor)
|
||||
}
|
||||
if len(args) == 2 {
|
||||
dp, err := plainInt(args[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("calc decimals %w", err)
|
||||
}
|
||||
if dp < 0 || dp > maxDecimals {
|
||||
return fmt.Errorf("calc decimals %d must be in 0..%d", dp, maxDecimals)
|
||||
if dp, err := strconv.Atoi(args[1]); err != nil || dp < 0 || dp > maxDecimals {
|
||||
return fmt.Errorf("calc decimals %q must be an integer in 0..%d", args[1], maxDecimals)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// constantZeroDivisor finds a division whose right side is a constant zero: number
|
||||
// literals and fixed operands folded, anything that varies left unknown.
|
||||
func constantZeroDivisor(n calcNode, fields map[string]node) (string, bool) {
|
||||
switch n := n.(type) {
|
||||
case calcNeg:
|
||||
return constantZeroDivisor(n.x, fields)
|
||||
case calcBin:
|
||||
if n.op == '/' {
|
||||
if v, known := constantValue(n.r, fields); known && v == 0 {
|
||||
return calcText(n.r), true
|
||||
}
|
||||
}
|
||||
if d, zero := constantZeroDivisor(n.l, fields); zero {
|
||||
return d, true
|
||||
}
|
||||
return constantZeroDivisor(n.r, fields)
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// constantValue evaluates an expression whose every operand is fixed.
|
||||
func constantValue(n calcNode, fields map[string]node) (float64, bool) {
|
||||
switch n := n.(type) {
|
||||
case calcNum:
|
||||
return float64(n), true
|
||||
case calcVar:
|
||||
t, ok := fields[string(n)].(*template)
|
||||
if !ok || !t.fixed || t.repeat > 1 {
|
||||
return 0, false
|
||||
}
|
||||
v, err := strconv.ParseFloat(strings.TrimSpace(t.lit), 64)
|
||||
return v, err == nil
|
||||
case calcNeg:
|
||||
v, ok := constantValue(n.x, fields)
|
||||
return -v, ok
|
||||
case calcBin:
|
||||
l, lok := constantValue(n.l, fields)
|
||||
r, rok := constantValue(n.r, fields)
|
||||
if !lok || !rok {
|
||||
return 0, false
|
||||
}
|
||||
return calcBin{n.op, calcNum(l), calcNum(r)}.eval(nil), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// calcText spells an expression node the way an author would read it.
|
||||
func calcText(n calcNode) string {
|
||||
switch n := n.(type) {
|
||||
case calcNum:
|
||||
return strconv.FormatFloat(float64(n), 'f', -1, 64)
|
||||
case calcVar:
|
||||
return string(n)
|
||||
case calcNeg:
|
||||
return "-" + calcText(n.x)
|
||||
case calcBin:
|
||||
return "(" + calcText(n.l) + " " + string(n.op) + " " + calcText(n.r) + ")"
|
||||
}
|
||||
return "?"
|
||||
}
|
||||
|
||||
// neverNumeric reports a node no render of which is a number: a null, fixed text that
|
||||
// does not parse, or a choice of only such items. text is one such render.
|
||||
// neverNumeric reports a node no render of which is a number: fixed text that does
|
||||
// not parse, or a choice of only such items. text is one such render.
|
||||
func neverNumeric(n node) (text string, never bool) {
|
||||
switch n := n.(type) {
|
||||
case *null:
|
||||
return "", true
|
||||
case *template:
|
||||
if !n.fixed || n.repeat > 1 {
|
||||
return "", false
|
||||
@@ -193,18 +123,13 @@ func calcPrep(args []string) callFn {
|
||||
at[name] = i
|
||||
}
|
||||
placed := indexVars(expr, at)
|
||||
dp := calcDecimals(args)
|
||||
return func(_ *session, _ string, operands []string) string {
|
||||
return formatFloat(placed.eval(operands), dp)
|
||||
}
|
||||
}
|
||||
|
||||
// calcDecimals is a calc's decimals count, or -1 for the shortest form.
|
||||
func calcDecimals(args []string) int {
|
||||
dp := -1
|
||||
if len(args) == 2 {
|
||||
return atoi(args[1])
|
||||
dp = atoi(args[1])
|
||||
}
|
||||
return func(_ *session, _ string, operands []string) string {
|
||||
return strconv.FormatFloat(placed.eval(operands), 'f', dp, 64)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// indexVars replaces each operand name with its position in the values expand reads.
|
||||
|
||||
@@ -36,7 +36,6 @@ func TestCalcAuto(t *testing.T) {
|
||||
`"{calc(10 / 3)}"`: "3.3333333333333335",
|
||||
`"{calc(6 / 2)}"`: "3",
|
||||
`"{calc(1 / 4)}"`: "0.25",
|
||||
`"{calc(0 * -1)}"`: "0",
|
||||
}
|
||||
for tmpl, want := range cases {
|
||||
if got := mustRender(t, f, tmpl); got != want {
|
||||
@@ -53,7 +52,6 @@ func TestCalcDecimals(t *testing.T) {
|
||||
`"{calc(10 / 3, 2)}"`: "3.33",
|
||||
`"{calc(10 / 3, 0)}"`: "3",
|
||||
`"{calc(2 * 3, 2)}"`: "6.00",
|
||||
`"{calc(-0.001, 2)}"`: "0.00",
|
||||
}
|
||||
for tmpl, want := range cases {
|
||||
if got := mustRender(t, f, tmpl); got != want {
|
||||
@@ -247,28 +245,3 @@ func TestCalcOverANeverNumericOperandIsRejected(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalcConstantZeroDivisorIsRejected(t *testing.T) {
|
||||
for src, want := range map[string]string{
|
||||
`"{calc(1/0)}"`: "divides by 0",
|
||||
`"{calc(2/(1-1))}"`: "divides by (1 - 1)",
|
||||
`{"format":"{calc(x/y)}","x":"1","y":"0"}`: "divides by y",
|
||||
`{"format":"{calc(x/(y*2))}","x":"1","y":" 0 "}`: "divides by (y * 2)",
|
||||
`{"format":"{calc((a/0)+b)}","a":"1","b":"2"}`: "divides by 0",
|
||||
`{"format":"{calc(a/-0)}","a":"1"}`: "divides by -0",
|
||||
} {
|
||||
if _, err := compile(parse(t, src)); err == nil || !strings.Contains(err.Error(), want) {
|
||||
t.Errorf("compile(%s) = %v, want the constant zero divisor rejected naming %q", src, err, want)
|
||||
}
|
||||
}
|
||||
if _, err := compile(parse(t, `{"format":"{calc(a/(b*c))}","a":"1","b":"0","c":["1","2"]}`)); err != nil {
|
||||
t.Errorf("compile(a/(b*c)) = %v, want a divisor that varies accepted", err)
|
||||
}
|
||||
f := engine(1)
|
||||
for i := 0; i < 50; i++ {
|
||||
if got := mustRender(t, f, `{"format":"{calc(x/y)}","x":"1","y":["0","1"]}`); got == "+Inf" {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Fatal("a sometimes-zero divisor never printed +Inf in 50 draws")
|
||||
}
|
||||
|
||||
-96
@@ -1,96 +0,0 @@
|
||||
package fejkdata
|
||||
|
||||
import "fmt"
|
||||
|
||||
// luhnCheck returns the Luhn check digit (0-9) over the digits of s; non-digit
|
||||
// runes are skipped. Doubling runs from the rightmost digit, so the result is
|
||||
// correct whatever the payload length.
|
||||
func luhnCheck(s string) int {
|
||||
sum, double := 0, true
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
continue
|
||||
}
|
||||
d := int(c - '0')
|
||||
if double {
|
||||
if d *= 2; d > 9 {
|
||||
d -= 9
|
||||
}
|
||||
}
|
||||
double = !double
|
||||
sum += d
|
||||
}
|
||||
return (10 - sum%10) % 10
|
||||
}
|
||||
|
||||
// mod11Check returns the weighted mod-11 check character over the digits of s
|
||||
// (weights 2..7 cycling from the right). A would-be value of 10 emits 'X', as in
|
||||
// ISBN-10 / ISO 7064; non-digits are skipped.
|
||||
func mod11Check(s string) string {
|
||||
sum, w := 0, 2
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
continue
|
||||
}
|
||||
sum += int(c-'0') * w
|
||||
if w++; w > 7 {
|
||||
w = 2
|
||||
}
|
||||
}
|
||||
if chk := (11 - sum%11) % 11; chk != 10 {
|
||||
return string(rune('0' + chk))
|
||||
}
|
||||
return "X"
|
||||
}
|
||||
|
||||
// eanCheck returns the EAN-13 / UPC-A / ISBN-13 / GTIN check digit over the
|
||||
// digits of s: weights 3 and 1 alternating from the rightmost digit, mod 10.
|
||||
func eanCheck(s string) string {
|
||||
sum, w := 0, 3
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
c := s[i]
|
||||
if c < '0' || c > '9' {
|
||||
continue
|
||||
}
|
||||
sum += int(c-'0') * w
|
||||
w = 4 - w // 3 <-> 1
|
||||
}
|
||||
return string(rune('0' + (10-sum%10)%10))
|
||||
}
|
||||
|
||||
// ibanLen maps a supported country code to the full IBAN length. The check digits
|
||||
// sit between the country code and the BBAN, so — unlike luhn/ean — iban can't be
|
||||
// a left-to-right derivation; it generates the whole value instead.
|
||||
var ibanLen = map[string]int{"BE": 16, "DE": 22, "DK": 18, "ES": 24, "FI": 18, "NO": 15, "SE": 24}
|
||||
|
||||
func ibanArg(_ map[string]node, a []string) error {
|
||||
if _, ok := ibanLen[a[0]]; !ok {
|
||||
return fmt.Errorf("iban(%q): unsupported country code", a[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// iban generates a structurally valid IBAN for cc: a numeric BBAN of the right
|
||||
// length, then mod-97 check digits. Real bank/branch structure isn't modelled —
|
||||
// the result passes length and checksum validation, which is what fake data needs.
|
||||
func iban(r rng, cc string) string {
|
||||
bban := make([]byte, ibanLen[cc]-4)
|
||||
for i := range bban {
|
||||
bban[i] = byte('0' + r.IntN(10))
|
||||
}
|
||||
rem := 0
|
||||
feed := func(d int) { rem = (rem*10 + d) % 97 }
|
||||
for _, c := range bban {
|
||||
feed(int(c - '0'))
|
||||
}
|
||||
for i := 0; i < len(cc); i++ { // letters A-Z -> 10..35, fed as two digits
|
||||
v := int(cc[i]-'A') + 10
|
||||
feed(v / 10)
|
||||
feed(v % 10)
|
||||
}
|
||||
feed(0)
|
||||
feed(0)
|
||||
return fmt.Sprintf("%s%02d%s", cc, 98-rem, bban)
|
||||
}
|
||||
+42
-277
@@ -8,72 +8,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.larvit.se/larvit/fejkdata"
|
||||
)
|
||||
|
||||
const usage = `Usage: fejkdata [flags] <path|template>
|
||||
const usage = `Usage: fejkdata [flags] <path>
|
||||
|
||||
<path> a category, or a dotted path into one (person, person.last);
|
||||
a table's row by key or name: 'misc.territory[SE]', 'misc.territory[Sweden].tld'
|
||||
<template> a format string or JSON value to render inline, e.g.
|
||||
'name: {/sv_SE.person.last}' or '{"format":"{x}","x":["bosse","lina"]}'
|
||||
|
||||
A layout inside a template is single-quoted, so quote the whole argument with " to
|
||||
keep it: "{date(1990-01-01,2010-12-31,'2006-01-02')}".
|
||||
|
||||
An argument containing a { token, or a JSON object, array or string, is a
|
||||
template; any other argument is a path (a path never contains a brace or a quote,
|
||||
and a bracket only as a [selector] after a table's name). Templates reach the
|
||||
data by reference from the root — {/sv_SE.person.last} — whether the data is
|
||||
shipped or layered with --data-path. An argument carrying a closing brace or a
|
||||
quote but no valid JSON names neither.
|
||||
|
||||
With --format json, ndjson, csv or sql the argument must name a record — a
|
||||
template whose fields are its columns — and the rows are written as one JSON
|
||||
array, one JSON object per line, one CSV row (after a header), or one INSERT.
|
||||
<path> a category, or a dotted path into one (person, person.last)
|
||||
|
||||
-d, --data-path D a data directory to layer over the shipped data (repeatable; last wins on a clash)
|
||||
--format F output form: text (default), json, ndjson, csv or sql
|
||||
-h, --help print this help, then exit
|
||||
--list list the paths the data offers, then exit
|
||||
--no-shipped-data load only the --data-path directories
|
||||
-n, --repeat N render the value N times, 1..1048576 (default 1)
|
||||
-n, --repeat N render the path N times (default 1)
|
||||
-s, --seed N seed for reproducible output
|
||||
--separator S string between repeated values (default newline)
|
||||
--table T the INSERT target for --format sql (default: the path's last segment, or records for an inline template)
|
||||
--version print the version, then exit
|
||||
|
||||
Flags may come before or after <path|template>; -- ends the flags. A short flag's
|
||||
value attaches or follows (-n3, -n 3); short flags bundle (-hn 3).
|
||||
Flags may come before or after <path>; -- ends the flags. A short flag's value
|
||||
attaches or follows (-n3, -n 3); short flags bundle (-hn 3).
|
||||
`
|
||||
|
||||
type invocation struct {
|
||||
dirs []string
|
||||
format string
|
||||
formatSet bool
|
||||
help bool
|
||||
list bool
|
||||
noShipped bool
|
||||
paths []string
|
||||
repeat int
|
||||
repeatSet bool
|
||||
seed uint64
|
||||
seeded bool
|
||||
separator string
|
||||
separatorSet bool
|
||||
table string
|
||||
tableSet bool
|
||||
version bool
|
||||
dirs []string
|
||||
help bool
|
||||
list bool
|
||||
noShipped bool
|
||||
paths []string
|
||||
repeat int
|
||||
seed uint64
|
||||
seeded bool
|
||||
separator string
|
||||
version bool
|
||||
}
|
||||
|
||||
type flagDef struct {
|
||||
@@ -85,16 +58,15 @@ type flagDef struct {
|
||||
|
||||
var flagDefs = []flagDef{
|
||||
{"data-path", "d", true, func(in *invocation, v string) error { in.dirs = append(in.dirs, v); return nil }},
|
||||
{"format", "", true, func(in *invocation, v string) error { in.format, in.formatSet = v, true; return nil }},
|
||||
{"help", "h", false, func(in *invocation, _ string) error { in.help = true; return nil }},
|
||||
{"list", "", false, func(in *invocation, _ string) error { in.list = true; return nil }},
|
||||
{"no-shipped-data", "", false, func(in *invocation, _ string) error { in.noShipped = true; return nil }},
|
||||
{"repeat", "n", true, func(in *invocation, v string) error {
|
||||
n, err := strconv.Atoi(v)
|
||||
if err != nil || n < 1 || n > fejkdata.MaxRepeat {
|
||||
return fmt.Errorf("--repeat needs an integer in 1..%d, got %q", fejkdata.MaxRepeat, v)
|
||||
if err != nil || n < 1 {
|
||||
return fmt.Errorf("--repeat needs a positive integer, got %q", v)
|
||||
}
|
||||
in.repeat, in.repeatSet = n, true
|
||||
in.repeat = n
|
||||
return nil
|
||||
}},
|
||||
{"seed", "s", true, func(in *invocation, v string) error {
|
||||
@@ -105,8 +77,7 @@ var flagDefs = []flagDef{
|
||||
in.seed, in.seeded = n, true
|
||||
return nil
|
||||
}},
|
||||
{"separator", "", true, func(in *invocation, v string) error { in.separator, in.separatorSet = v, true; return nil }},
|
||||
{"table", "", true, func(in *invocation, v string) error { in.table, in.tableSet = v, true; return nil }},
|
||||
{"separator", "", true, func(in *invocation, v string) error { in.separator = v; return nil }},
|
||||
{"version", "", false, func(in *invocation, _ string) error { in.version = true; return nil }},
|
||||
}
|
||||
|
||||
@@ -153,8 +124,8 @@ func splitFlags(arg string) ([]flagArg, error) {
|
||||
}
|
||||
var flags []flagArg
|
||||
letters := arg[1:]
|
||||
for i, r := range letters {
|
||||
letter := string(r)
|
||||
for i := 0; i < len(letters); i++ {
|
||||
letter := letters[i : i+1]
|
||||
def := flagByShort(letter)
|
||||
if def == nil {
|
||||
return nil, fmt.Errorf("unknown flag -%s", letter)
|
||||
@@ -163,7 +134,7 @@ func splitFlags(arg string) ([]flagArg, error) {
|
||||
flags = append(flags, flagArg{def: def})
|
||||
continue
|
||||
}
|
||||
rest := letters[i+len(letter):]
|
||||
rest := letters[i+1:]
|
||||
if strings.HasPrefix(rest, "=") {
|
||||
return nil, fmt.Errorf("-%s takes its value attached (-%s%s) or next (-%s %s); = belongs to --%s=%s",
|
||||
letter, letter, rest[1:], letter, rest[1:], def.long, rest[1:])
|
||||
@@ -174,7 +145,7 @@ func splitFlags(arg string) ([]flagArg, error) {
|
||||
}
|
||||
|
||||
func parseArgs(argv []string) (invocation, error) {
|
||||
in := invocation{repeat: 1, separator: "\n", format: "text"}
|
||||
in := invocation{repeat: 1, separator: "\n"}
|
||||
for i := 0; i < len(argv); i++ {
|
||||
arg := argv[i]
|
||||
if arg == "--" {
|
||||
@@ -212,81 +183,17 @@ func parseArgs(argv []string) (invocation, error) {
|
||||
return in, nil
|
||||
}
|
||||
|
||||
// recordFormat is one way to write a record out: the line each record renders,
|
||||
// the header that precedes the first one, and the open/close frame plus the
|
||||
// between-record separator a document form needs.
|
||||
type recordFormat struct {
|
||||
header func(*fejkdata.Record) string
|
||||
line func(r *fejkdata.Record, table string) string
|
||||
open string
|
||||
close string
|
||||
sep string
|
||||
}
|
||||
|
||||
// recordFormats is every --format that writes records. json frames the records
|
||||
// as one array document; ndjson is the same column, one object per line.
|
||||
var recordFormats = map[string]recordFormat{
|
||||
"csv": {header: (*fejkdata.Record).CSVHeader, line: func(r *fejkdata.Record, _ string) string { return r.CSVLine() }, sep: "\n"},
|
||||
"json": {line: jsonLine, open: "[", close: "]", sep: ",\n"},
|
||||
"ndjson": {line: jsonLine, sep: "\n"},
|
||||
"sql": {line: func(r *fejkdata.Record, table string) string { return r.SQLInsert(table) }, sep: "\n"},
|
||||
}
|
||||
|
||||
func jsonLine(r *fejkdata.Record, _ string) string { return r.JSON() }
|
||||
|
||||
// writesRecords reports whether the format writes records rather than plain text.
|
||||
func (in invocation) writesRecords() bool {
|
||||
return in.format != "text"
|
||||
}
|
||||
|
||||
// formatNames lists the --format values, text included, for the misuse error.
|
||||
func formatNames() string {
|
||||
names := []string{"text"}
|
||||
for name := range recordFormats {
|
||||
names = append(names, name)
|
||||
// check rejects a flag combination that cannot run.
|
||||
func (in invocation) check() error {
|
||||
if in.list && len(in.paths) > 0 {
|
||||
return errors.New("--list takes no path")
|
||||
}
|
||||
sort.Strings(names)
|
||||
return strings.Join(names[:len(names)-1], ", ") + " or " + names[len(names)-1]
|
||||
}
|
||||
|
||||
// checkFlags rejects a flag value or combination that cannot run.
|
||||
func (in invocation) checkFlags() error {
|
||||
if _, ok := recordFormats[in.format]; !ok && in.format != "text" {
|
||||
return fmt.Errorf("--format takes %s, got %q", formatNames(), in.format)
|
||||
}
|
||||
if in.tableSet && in.table == "" {
|
||||
return errors.New("--table names the INSERT target, so it cannot be empty")
|
||||
}
|
||||
if in.tableSet && in.format != "sql" {
|
||||
return errors.New("--table names the INSERT target, so it needs --format sql")
|
||||
}
|
||||
if in.writesRecords() && in.separatorSet {
|
||||
return errors.New("--separator joins text values, so it has no effect with --format " + in.format)
|
||||
if !in.list && len(in.paths) != 1 {
|
||||
return fmt.Errorf("expected one path, got %d", len(in.paths))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// check rejects a flag combination or an argument that cannot run, and reports
|
||||
// what the argument names, so its shape is settled before any data is read.
|
||||
func (in invocation) check() (argKind, error) {
|
||||
if err := in.checkFlags(); err != nil {
|
||||
return argPath, err
|
||||
}
|
||||
if in.list && len(in.paths) > 0 {
|
||||
return argPath, errors.New("--list takes no path")
|
||||
}
|
||||
if in.list && (in.repeatSet || in.separatorSet || in.formatSet || in.tableSet) {
|
||||
return argPath, errors.New("--list takes no --repeat, --separator, --format or --table")
|
||||
}
|
||||
if in.list {
|
||||
return argPath, nil
|
||||
}
|
||||
if len(in.paths) != 1 {
|
||||
return argPath, fmt.Errorf("expected one path or template, got %d%s", len(in.paths), templateSplit(in.paths))
|
||||
}
|
||||
return classify(in.paths[0])
|
||||
}
|
||||
|
||||
func (in invocation) options() []fejkdata.Option {
|
||||
var opts []fejkdata.Option
|
||||
if in.noShipped {
|
||||
@@ -301,141 +208,17 @@ func (in invocation) options() []fejkdata.Option {
|
||||
return opts
|
||||
}
|
||||
|
||||
// write streams the argument's renders to w, repeat of them joined by the
|
||||
// separator and ended by a newline. A value that renders once renders every time,
|
||||
// so a render failure comes before anything is written; a write failure surfaces
|
||||
// from Flush, bufio keeping the first one.
|
||||
func (in invocation) write(f *fejkdata.Generator, kind argKind, w io.Writer) error {
|
||||
if in.writesRecords() {
|
||||
return in.writeRecords(f, kind, w)
|
||||
}
|
||||
draw, err := in.textDraw(f, kind, in.paths[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out := bufio.NewWriter(w)
|
||||
for i := 0; i < in.repeat; i++ {
|
||||
v, err := draw()
|
||||
// values renders the path repeat times, joined by the separator.
|
||||
func (in invocation) values(f *fejkdata.Generator) (string, error) {
|
||||
vals := make([]string, in.repeat)
|
||||
for i := range vals {
|
||||
v, err := f.Fake(in.paths[0])
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
if i > 0 {
|
||||
out.WriteString(in.separator)
|
||||
}
|
||||
out.WriteString(v)
|
||||
vals[i] = v
|
||||
}
|
||||
out.WriteString("\n")
|
||||
return out.Flush()
|
||||
}
|
||||
|
||||
// textDraw builds what one text render yields: the value, from a path or an
|
||||
// inline template.
|
||||
func (in invocation) textDraw(f *fejkdata.Generator, kind argKind, arg string) (func() (string, error), error) {
|
||||
if kind != argTemplate {
|
||||
return func() (string, error) { return f.Fake(arg) }, nil
|
||||
}
|
||||
t, err := f.NewTemplate(arg)
|
||||
if err != nil {
|
||||
return nil, templateError{err}
|
||||
}
|
||||
return func() (string, error) { return t.Fake(), nil }, nil
|
||||
}
|
||||
|
||||
// recordStream builds the record drawer for the argument, plus the INSERT table
|
||||
// a sql format names.
|
||||
func (in invocation) recordStream(f *fejkdata.Generator, kind argKind, arg string) (func() (*fejkdata.Record, error), string, error) {
|
||||
record := func() (*fejkdata.Record, error) { return f.FakeRecord(arg) }
|
||||
if kind == argTemplate {
|
||||
t, err := f.NewRecordTemplate(arg)
|
||||
if err != nil {
|
||||
return nil, "", templateError{err}
|
||||
}
|
||||
record = func() (*fejkdata.Record, error) { return t.Fake(), nil }
|
||||
}
|
||||
table := in.table
|
||||
if table == "" {
|
||||
table = defaultTable(arg, kind)
|
||||
}
|
||||
return record, table, nil
|
||||
}
|
||||
|
||||
// writeRecords streams a record per line in the chosen format, framing a document
|
||||
// form with its open/close brackets and a header preceding the first record.
|
||||
func (in invocation) writeRecords(f *fejkdata.Generator, kind argKind, w io.Writer) error {
|
||||
record, table, err := in.recordStream(f, kind, in.paths[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
format := recordFormats[in.format]
|
||||
out := bufio.NewWriter(w)
|
||||
if format.open != "" {
|
||||
out.WriteString(format.open)
|
||||
out.WriteByte('\n')
|
||||
}
|
||||
for i := 0; i < in.repeat; i++ {
|
||||
r, err := record()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i == 0 && format.header != nil {
|
||||
out.WriteString(format.header(r))
|
||||
out.WriteByte('\n')
|
||||
}
|
||||
if i > 0 {
|
||||
out.WriteString(format.sep)
|
||||
}
|
||||
out.WriteString(format.line(r, table))
|
||||
}
|
||||
if format.close != "" {
|
||||
out.WriteByte('\n')
|
||||
out.WriteString(format.close)
|
||||
}
|
||||
out.WriteByte('\n')
|
||||
return out.Flush()
|
||||
}
|
||||
|
||||
// defaultTable names the INSERT target when --table is absent: the path's last
|
||||
// segment, or "records" for an inline template that sits in no folder.
|
||||
func defaultTable(arg string, kind argKind) string {
|
||||
if kind == argTemplate {
|
||||
return "records"
|
||||
}
|
||||
var names strings.Builder // the path with its [selectors] cut out
|
||||
for depth, i := 0, 0; i < len(arg); i++ {
|
||||
switch {
|
||||
case arg[i] == '[':
|
||||
depth++
|
||||
case arg[i] == ']':
|
||||
depth--
|
||||
case depth == 0:
|
||||
names.WriteByte(arg[i])
|
||||
}
|
||||
}
|
||||
segments := strings.Split(names.String(), ".")
|
||||
return segments[len(segments)-1]
|
||||
}
|
||||
|
||||
// templateError marks a render failure that is the argument's own fault — an
|
||||
// inline template that does not compile. run reports it as misuse (exit 2, with a
|
||||
// pointer to --help), unlike an unknown path, which is a runtime error (exit 1).
|
||||
type templateError struct{ error }
|
||||
|
||||
func (e templateError) Unwrap() error { return e.error }
|
||||
|
||||
type argKind int
|
||||
|
||||
const (
|
||||
argPath argKind = iota
|
||||
argTemplate
|
||||
)
|
||||
|
||||
// classify reads what a positional argument names by its shape (see fejkdata.IsTemplate).
|
||||
func classify(arg string) (argKind, error) {
|
||||
inline, err := fejkdata.IsTemplate(arg)
|
||||
if inline {
|
||||
return argTemplate, nil
|
||||
}
|
||||
return argPath, err
|
||||
return strings.Join(vals, in.separator), nil
|
||||
}
|
||||
|
||||
func main() { os.Exit(run(os.Args[1:], os.Stdout, os.Stderr)) }
|
||||
@@ -454,8 +237,7 @@ func run(args []string, stdout, stderr io.Writer) int {
|
||||
fmt.Fprintln(stdout, "fejkdata "+buildVersion())
|
||||
return 0
|
||||
}
|
||||
kind, err := in.check()
|
||||
if err != nil {
|
||||
if err := in.check(); err != nil {
|
||||
return misuse(stderr, err)
|
||||
}
|
||||
f, err := fejkdata.New(in.options()...)
|
||||
@@ -472,34 +254,17 @@ func run(args []string, stdout, stderr io.Writer) int {
|
||||
}
|
||||
return 0
|
||||
}
|
||||
if err := in.write(f, kind, stdout); err != nil {
|
||||
var te templateError
|
||||
if errors.As(err, &te) {
|
||||
return misuse(stderr, te.error)
|
||||
}
|
||||
out, err := in.values(f)
|
||||
if err != nil {
|
||||
fmt.Fprintln(stderr, err)
|
||||
if errors.Is(err, fejkdata.ErrNoColumns) {
|
||||
fmt.Fprintln(stderr, "wrap that JSON in single quotes, which keep a shell from expanding its braces")
|
||||
}
|
||||
return 1
|
||||
}
|
||||
fmt.Fprintln(stdout, out)
|
||||
return 0
|
||||
}
|
||||
|
||||
// templateSplit names the cure for a template a shell split on its spaces, which is
|
||||
// what several arguments carrying a token mean.
|
||||
func templateSplit(paths []string) string {
|
||||
for _, p := range paths {
|
||||
if strings.Contains(p, "{") {
|
||||
return `; a template's spaces split the argument, so wrap the whole argument in "…", or in '…' where it carries double quotes`
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func misuse(stderr io.Writer, err error) int {
|
||||
// A library error already names the program, so the prefix is not doubled.
|
||||
fmt.Fprintf(stderr, "fejkdata: %s\ntry 'fejkdata --help'\n", strings.TrimPrefix(err.Error(), "fejkdata: "))
|
||||
fmt.Fprintf(stderr, "fejkdata: %v\ntry 'fejkdata --help'\n", err)
|
||||
return 2
|
||||
}
|
||||
|
||||
|
||||
+4
-288
@@ -2,11 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
@@ -14,7 +11,6 @@ import (
|
||||
const (
|
||||
svSE = "../../data/sv_SE"
|
||||
enUS = "../../data/en_US"
|
||||
misc = "../../data/misc"
|
||||
)
|
||||
|
||||
func runOut(args ...string) (int, string, string) {
|
||||
@@ -96,7 +92,7 @@ func TestRunShortFlagValues(t *testing.T) {
|
||||
{[]string{"-s=42", "-d", svSE, "address"}, "-s42"},
|
||||
{[]string{"-s=42", "-d", svSE, "address"}, "--seed=42"},
|
||||
{[]string{"-d=" + svSE, "address"}, "--data-path="},
|
||||
{[]string{"-nd", "3", "-d", svSE, "word"}, `--repeat needs an integer in 1..1048576, got "d"`},
|
||||
{[]string{"-nd", "3", "-d", svSE, "word"}, `--repeat needs a positive integer, got "d"`},
|
||||
{[]string{"--seed=", "-d", svSE, "word"}, `--seed needs an unsigned integer, got ""`},
|
||||
} {
|
||||
code, out, errb := runOut(c.args...)
|
||||
@@ -223,14 +219,6 @@ func TestRunMisuse(t *testing.T) {
|
||||
t.Errorf("run(%v) stdout = %q, want nothing", args, out)
|
||||
}
|
||||
}
|
||||
// A template a shell split names both quotings, since which one cures it turns
|
||||
// on whether the argument carries a double-quoted JSON.
|
||||
_, _, errb := runOut("{date(1990-01-01,2010-12-31,", "January", "2,", "2006)}")
|
||||
for _, want := range []string{`wrap the whole argument in "…"`, `or in '…'`} {
|
||||
if !strings.Contains(errb, want) {
|
||||
t.Errorf("a split template stderr = %q, want it to name %s", errb, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunMultipleDataPaths(t *testing.T) {
|
||||
@@ -313,115 +301,15 @@ func TestRunShippedDataByDefault(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassify(t *testing.T) {
|
||||
for arg, want := range map[string]argKind{"sv_SE.person": argPath, "name: {x}": argTemplate} {
|
||||
if got, err := classify(arg); err != nil || got != want {
|
||||
t.Errorf("classify(%q) = %v, %v; want %v", arg, got, err, want)
|
||||
}
|
||||
}
|
||||
if _, err := classify("[abc]"); err == nil || !strings.Contains(err.Error(), `"["`) || !strings.Contains(err.Error(), "JSON") {
|
||||
t.Errorf("classify([abc]) = %v; want it rejected naming the leading bracket", err)
|
||||
}
|
||||
if got, err := classify("geo.SE.municipality[St. Louis].name"); err != nil || got != argPath {
|
||||
t.Errorf("classify(a selecting path) = %v, %v; want a path", got, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunSelectsATableRow(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
for name, content := range map[string]string{
|
||||
"region.json": `{"format":"{name}","rows":"region.tsv","key":"code","name":"name"}`,
|
||||
"region.tsv": "code\tname\n01\tStockholms län\n12\tSkåne län\n",
|
||||
} {
|
||||
if err := os.WriteFile(filepath.Join(dir, name), []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if code, out, errb := runOut("--no-shipped-data", "-d", dir, "region[12]"); code != 0 || out != "Skåne län\n" {
|
||||
t.Fatalf("region[12] = %d, %q, stderr=%q", code, out, errb)
|
||||
}
|
||||
if code, out, _ := runOut("--no-shipped-data", "-d", dir, "region[Skåne län].code"); code != 0 || out != "12\n" {
|
||||
t.Fatalf("region[Skåne län].code = %d, %q", code, out)
|
||||
}
|
||||
if code, out, _ := runOut("--no-shipped-data", "-d", dir, "--format", "sql", "region[12]"); code != 0 || out != `INSERT INTO "region" ("code", "name") VALUES ('12', 'Skåne län');`+"\n" {
|
||||
t.Fatalf("--format sql region[12] = %d, %q, want the table named without its selector", code, out)
|
||||
}
|
||||
if code, _, errb := runOut("--no-shipped-data", "-d", dir, "region[99]"); code != 1 || !strings.Contains(errb, `"99"`) {
|
||||
t.Fatalf("region[99] = %d, stderr=%q, want a runtime error naming the row", code, errb)
|
||||
}
|
||||
if code, _, errb := runOut("--no-shipped-data", "-d", dir, "[Skåne län]"); code != 2 || !strings.Contains(errb, "JSON") {
|
||||
t.Fatalf("[Skåne län] = %d, stderr=%q, want misuse: a leading bracket that is no JSON names nothing", code, errb)
|
||||
}
|
||||
if code, out, _ := runOut("--seed", "1", "--format", "csv", "misc.territory[SE]"); code != 0 || !strings.HasPrefix(out, "alpha2,") || !strings.Contains(out, "\nSE,SWE,") {
|
||||
t.Fatalf("--format csv misc.territory[SE] = %d, %q", code, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUsageReferencesResolve(t *testing.T) {
|
||||
for _, token := range regexp.MustCompile(`\{/[^}]+\}`).FindAllString(usage, -1) {
|
||||
path := token[2 : len(token)-1]
|
||||
code, out, errb := runOut("--seed", "1", path)
|
||||
if code != 0 || strings.TrimSpace(out) == "" {
|
||||
t.Errorf("usage advertises %s: run %s = %d, %q, stderr %q", token, path, code, out, errb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunShapeMisuseBeforeLoad(t *testing.T) {
|
||||
code, _, errb := runOut("--no-shipped-data", "[abc]")
|
||||
if code != 2 || !strings.Contains(errb, "[abc]") || strings.Contains(errb, "--data-path") {
|
||||
t.Fatalf("shape misuse with no data = %d, %q; want the shape error before any load", code, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunInlineTemplate(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "name: {/sv_SE.person.last}")
|
||||
if code != 0 || !strings.HasPrefix(out, "name: ") || strings.Contains(out, "{") {
|
||||
t.Fatalf("inline format string = %d, %q, stderr %q", code, out, errb)
|
||||
}
|
||||
code, out, errb = runOut("--seed", "1", `{"format":"name: {x}","x":["bosse","lina"]}`)
|
||||
if code != 0 || (out != "name: bosse\n" && out != "name: lina\n") {
|
||||
t.Fatalf("inline JSON template = %d, %q, want one name, stderr %q", code, out, errb)
|
||||
}
|
||||
code, out, errb = runOut("--seed", "1", `"name: {/sv_SE.person.last}"`)
|
||||
if code != 0 || !strings.HasPrefix(out, "name: ") || strings.Contains(out, "{") {
|
||||
t.Fatalf("inline JSON string = %d, %q, stderr %q", code, out, errb)
|
||||
}
|
||||
code, out, errb = runOut("--seed", "1", "-n", "2", `{digits(1)}`)
|
||||
if code != 0 || len(strings.Split(strings.TrimRight(out, "\n"), "\n")) != 2 {
|
||||
t.Fatalf("inline template with --repeat = %d, %q, stderr %q", code, out, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunTemplateMisuse(t *testing.T) {
|
||||
for arg, want := range map[string]string{
|
||||
"{bad": "unterminated",
|
||||
"[red,green]": `starts with "["`,
|
||||
`{"format":"x"}`: "is a string",
|
||||
"name: {/no.such.path}": "no entry",
|
||||
"{/sv_SE.person}": "write sv_SE.person",
|
||||
"x[1]y": `"]"`,
|
||||
` ["a","b"] `: "may not be padded",
|
||||
} {
|
||||
code, out, errb := runOut("--seed", "1", arg)
|
||||
if code != 2 || out != "" || !strings.Contains(errb, "try 'fejkdata --help'") || !strings.Contains(errb, want) {
|
||||
t.Errorf("run(%q) = %d, %q, %q; want misuse naming %q and --help", arg, code, out, errb, want)
|
||||
}
|
||||
if strings.Contains(errb, "fejkdata: fejkdata:") {
|
||||
t.Errorf("run(%q) doubled the program prefix: %q", arg, errb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunNoShippedData(t *testing.T) {
|
||||
code, list, errb := runOut("--no-shipped-data", "-d", misc, "--list")
|
||||
code, list, errb := runOut("--no-shipped-data", "-d", svSE, "--list")
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
if strings.Contains(list, "sv_SE") || !strings.Contains(list, "uuid\n") {
|
||||
if strings.Contains(list, "en_US") || !strings.Contains(list, "person\n") {
|
||||
t.Errorf("--no-shipped-data --list = %q, want only the given dir", list)
|
||||
}
|
||||
code, out, _ := runOut("--no-shipped-data", "-d", misc, "-s", "3", "uuid")
|
||||
code, out, _ := runOut("--no-shipped-data", "-d", svSE, "-s", "3", "person")
|
||||
if code != 0 || strings.TrimSpace(out) == "" {
|
||||
t.Errorf("run = %d, out=%q", code, out)
|
||||
}
|
||||
@@ -467,175 +355,3 @@ func TestRunUnknownFlagIsNamedByRune(t *testing.T) {
|
||||
t.Errorf("run(-ä) = %d, %q, want the flag named whole", code, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunEmptyDataPathIsNamed(t *testing.T) {
|
||||
code, _, errb := runOut("--data-path=", "sv_SE.word")
|
||||
if code != 1 || !strings.Contains(errb, "empty") {
|
||||
t.Errorf("run(--data-path=) = %d, %q, want the empty path named", code, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func recordDir(t *testing.T) string {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
content := `{"format":"{first} {last}","first":["Ada","Bo"],"last":["Lovelace","Ek"]}`
|
||||
if err := os.WriteFile(filepath.Join(dir, "users.json"), []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return dir
|
||||
}
|
||||
|
||||
func TestRunRecordJSON(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "json", "--repeat", "2", "--data-path", recordDir(t), "users")
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
var arr []map[string]string
|
||||
if err := json.Unmarshal([]byte(out), &arr); err != nil {
|
||||
t.Fatalf("json output is not one JSON array: %v\n%q", err, out)
|
||||
}
|
||||
if len(arr) != 2 || len(arr[0]) != 2 || arr[0]["first"] == "" || arr[0]["last"] == "" {
|
||||
t.Fatalf("json output = %q, want an array of two records with first and last columns", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRecordNDJSON(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "ndjson", "--repeat", "2", "--data-path", recordDir(t), "users")
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
lines := strings.Split(strings.TrimRight(out, "\n"), "\n")
|
||||
if len(lines) != 2 {
|
||||
t.Fatalf("ndjson output = %d lines %q, want one object per line", len(lines), out)
|
||||
}
|
||||
for _, line := range lines {
|
||||
var m map[string]string
|
||||
if err := json.Unmarshal([]byte(line), &m); err != nil || len(m) != 2 {
|
||||
t.Fatalf("ndjson line %q is not one object: %v", line, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRecordCSVRoundTrips(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "csv", "--repeat", "3", "--data-path", recordDir(t), "users")
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
lines := strings.Split(strings.TrimRight(out, "\n"), "\n")
|
||||
if len(lines) != 4 {
|
||||
t.Fatalf("csv output = %d lines %q, want a header and 3 rows", len(lines), out)
|
||||
}
|
||||
if lines[0] != "first,last" {
|
||||
t.Fatalf("csv header = %q, want first,last", lines[0])
|
||||
}
|
||||
r, err := csv.NewReader(strings.NewReader(out)).ReadAll()
|
||||
if err != nil {
|
||||
t.Fatalf("csv output is not valid CSV: %v", err)
|
||||
}
|
||||
if len(r) != 4 || len(r[0]) != 2 {
|
||||
t.Fatalf("csv parsed to %v, want 4 records of 2 fields", r)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRecordSQL(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "sql", "--repeat", "2", "--table", "people", "--data-path", recordDir(t), "users")
|
||||
if code != 0 {
|
||||
t.Fatalf("run = %d, stderr=%q", code, errb)
|
||||
}
|
||||
lines := strings.Split(strings.TrimRight(out, "\n"), "\n")
|
||||
if len(lines) != 2 || !strings.HasPrefix(lines[0], `INSERT INTO "people" ("first", "last") VALUES (`) {
|
||||
t.Fatalf("sql output = %q, want two INSERT INTO people statements", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRecordDefaultTable(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "sql", "--data-path", recordDir(t), "users")
|
||||
if code != 0 || !strings.HasPrefix(out, `INSERT INTO "users" (`) {
|
||||
t.Fatalf("default table = %d, %q, want the path's last segment; stderr %q", code, out, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRecordInlineTemplate(t *testing.T) {
|
||||
code, out, errb := runOut("--seed", "1", "--format", "json", `{"format":"{x}","x":["a","b"]}`)
|
||||
if code != 0 {
|
||||
t.Fatalf("inline record = %d, stderr=%q", code, errb)
|
||||
}
|
||||
var arr []map[string]string
|
||||
if err := json.Unmarshal([]byte(out), &arr); err != nil || len(arr) != 1 || (arr[0]["x"] != "a" && arr[0]["x"] != "b") {
|
||||
t.Fatalf("inline record json = %q, want one record with a column x (err %v)", out, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRecordMisuse(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{[]string{"--format", "yaml", "users"}, "--format takes csv, json, ndjson, sql or text"},
|
||||
{[]string{"--format", "json", "--separator", ",", "users"}, "--separator joins text values"},
|
||||
{[]string{"--table", "t", "users"}, "--table names the INSERT target"},
|
||||
{[]string{"--format", "json", "--table", "t", "users"}, "--table names the INSERT target"},
|
||||
{[]string{"--format", "sql", "--table", "", "users"}, "--table names the INSERT target"},
|
||||
} {
|
||||
code, out, errb := runOut(c.args...)
|
||||
if code != 2 || out != "" || !strings.Contains(errb, c.want) {
|
||||
t.Errorf("run(%v) = %d, %q, %q; want misuse naming %q", c.args, code, out, errb, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRecordOnABareValueIsRuntimeError(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(dir, "word.json"), []byte(`["a","b"]`), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, _, errb := runOut("--format", "json", "--data-path", dir, "word")
|
||||
if code != 1 {
|
||||
t.Fatalf("record on a choice = %d, want exit 1 (runtime error), stderr %q", code, errb)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunRecordRejectsATopLevelRepeat(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
content := `{"format":"{a}-","repeat":3,"separator":"|","a":["x","y"]}`
|
||||
if err := os.WriteFile(filepath.Join(dir, "rep.json"), []byte(content), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
code, _, errb := runOut("--format", "json", "--data-path", dir, "rep")
|
||||
if code != 1 || !strings.Contains(errb, "carries repeat 3") {
|
||||
t.Errorf("record on a repeating path = %d, %q; want exit 1 naming the repeat", code, errb)
|
||||
}
|
||||
code, _, errb = runOut("--format", "json", content)
|
||||
if code != 2 || !strings.Contains(errb, "carries repeat 3") {
|
||||
t.Errorf("record on a repeating inline template = %d, %q; want exit 2 naming the repeat", code, errb)
|
||||
}
|
||||
if code, out, _ := runOut("--seed", "1", "--data-path", dir, "rep"); code != 0 || strings.TrimSpace(out) != "x-|x-|x-" {
|
||||
t.Errorf("text view = %d, %q; want the repeat still composed", code, out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunNumbersFollowTheShell(t *testing.T) {
|
||||
_, want, _ := runOut("--seed", "7", "--repeat", "3", "sv_SE.word")
|
||||
code, got, errb := runOut("--seed", "007", "--repeat", "+3", "sv_SE.word")
|
||||
if code != 0 || got != want {
|
||||
t.Errorf("run(--seed 007 --repeat +3) = %d, %q, stderr %q; want the same as --seed 7 --repeat 3 %q", code, got, errb, want)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRunRecordOfAFieldlessPath pins the way out of asking a category of one value
|
||||
// for columns: the record to write, and the quoting a shell needs to take it. The
|
||||
// argument is well-formed and only the data cannot serve it, so the code is 1.
|
||||
func TestRunRecordOfAFieldlessPath(t *testing.T) {
|
||||
code, out, errb := runOut("--format", "csv", "sv_SE.personnummer")
|
||||
if code != 1 {
|
||||
t.Fatalf("run(--format csv sv_SE.personnummer) = %d, want 1", code)
|
||||
}
|
||||
for _, want := range []string{`{"format":"","personnummer":"{/sv_SE.personnummer}"}`, "single quotes"} {
|
||||
if !strings.Contains(errb, want) {
|
||||
t.Errorf("stderr = %q, want it to name %s", errb, want)
|
||||
}
|
||||
}
|
||||
if out != "" {
|
||||
t.Errorf("stdout = %q, want nothing", out)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ x-go: &go
|
||||
HOME: /cache
|
||||
GOCACHE: /cache/build
|
||||
GOMODCACHE: /cache/mod
|
||||
REPIN: ${REPIN:-}
|
||||
|
||||
services:
|
||||
test:
|
||||
@@ -64,12 +63,5 @@ services:
|
||||
<<: *go
|
||||
command: sh
|
||||
|
||||
data-import:
|
||||
image: python:3.14.7-slim
|
||||
working_dir: /app
|
||||
volumes:
|
||||
- .:/app
|
||||
entrypoint: python3
|
||||
|
||||
volumes:
|
||||
gocache:
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/currency.tsv from datasets/currency-codes (PDDL) and CLDR's symbols (Unicode).
|
||||
|
||||
data-import/currency.py [--source URL_OR_FILE] [--symbols URL_OR_FILE ...] [--cache DIR] [--out FILE]
|
||||
|
||||
The symbols come from the first locale file that has one, narrow symbols before wide.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
import xml.etree.ElementTree as ET
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://raw.githubusercontent.com/datasets/currency-codes/main/data/codes-all.csv"
|
||||
SYMBOLS = [
|
||||
"https://raw.githubusercontent.com/unicode-org/cldr/main/common/main/en.xml",
|
||||
"https://raw.githubusercontent.com/unicode-org/cldr/main/common/main/root.xml",
|
||||
]
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "currency.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["code", "decimals", "name", "numeric", "symbol"]
|
||||
|
||||
|
||||
def symbols(xml_texts):
|
||||
"""CLDR's symbol per code: the first locale's narrow symbol, else the first locale's wide one."""
|
||||
narrow, wide = {}, {}
|
||||
for text in xml_texts:
|
||||
for c in ET.fromstring(text).iter("currency"):
|
||||
for s in c.findall("symbol"):
|
||||
into = narrow if s.get("alt") == "narrow" else wide if s.get("alt") is None else None
|
||||
if into is not None:
|
||||
into.setdefault(c.get("type"), s.text)
|
||||
return {**wide, **narrow}
|
||||
|
||||
|
||||
def rows(text, symbol):
|
||||
seen = set()
|
||||
for r in csv.DictReader(io.StringIO(text)):
|
||||
code = r["AlphabeticCode"]
|
||||
if not code or code in seen or r["WithdrawalDate"] or r["Entity"].startswith("ZZ") or not r["MinorUnit"].isdigit():
|
||||
continue
|
||||
seen.add(code)
|
||||
yield {
|
||||
"code": code,
|
||||
"decimals": r["MinorUnit"],
|
||||
"name": r["Currency"],
|
||||
"numeric": r["NumericCode"].zfill(3),
|
||||
"symbol": symbol.get(code, code),
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--symbols", nargs="+", default=SYMBOLS)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
symbol = symbols(source.fetch(s, a.cache, Path(s).name).decode("utf-8") for s in a.symbols)
|
||||
table = rows(source.fetch(a.source, a.cache, "codes-all.csv").decode("utf-8"), symbol)
|
||||
tsv.write(a.out, COLUMNS, sorted(table, key=lambda r: r["code"]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,234 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/geo/SE/*.tsv from SCB (CC0), GeoNames (CC BY 4.0) and Trafikverket NVDB (CC0).
|
||||
|
||||
TRAFIKVERKET_API_KEY=… data-import/geo-se.py [--key-file FILE] [--cache DIR] [--streets-per-locality N] [--out DIR]
|
||||
"""
|
||||
import argparse
|
||||
import collections
|
||||
import csv
|
||||
import io
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import urllib.request
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
import xlsx
|
||||
|
||||
CODES = "https://www.scb.se/contentassets/7a89e48960f741e08918e489ea36354a/kommunlankod-2026.xlsx"
|
||||
POPULATION = "https://api.scb.se/OV0104/v1/doris/sv/ssd/START/BE/BE0101/BE0101A/BefolkningNy"
|
||||
POPULATION_QUERY = {
|
||||
"query": [
|
||||
{"code": "Region", "selection": {"filter": "all", "values": ["*"]}},
|
||||
{"code": "ContentsCode", "selection": {"filter": "item", "values": ["BE0101N1"]}},
|
||||
{"code": "Tid", "selection": {"filter": "top", "values": ["1"]}},
|
||||
],
|
||||
"response": {"format": "json"},
|
||||
}
|
||||
TATORTER = "https://geodata.scb.se/geoserver/stat/wfs?service=WFS&version=2.0.0&request=GetFeature&typeNames=stat:Tatorter_2023&outputFormat=csv&propertyName=tatort,kommun,bef"
|
||||
POSTAL_CODES = "https://download.geonames.org/export/zip/SE.zip"
|
||||
NVDB = "https://api.trafikinfo.trafikverket.se/v2/data.json"
|
||||
NVDB_PAGE = 50000
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "geo" / "SE"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
TIMEZONE = "Europe/Stockholm"
|
||||
ONE_POSITION = {"Stockholm", "Göteborg", "Malmö"}
|
||||
UNMATCHED_POPULATION = 200
|
||||
|
||||
|
||||
def scb_codes(cache):
|
||||
regions, municipalities = {}, {}
|
||||
for cells in xlsx.rows(source.fetch(CODES, cache, "kommunlankod.xlsx", magic=b"PK")):
|
||||
if len(cells) < 2 or not re.fullmatch(r"\d{2}|\d{4}", cells[0]):
|
||||
continue
|
||||
(regions if len(cells[0]) == 2 else municipalities)[cells[0]] = cells[1].strip()
|
||||
return regions, municipalities
|
||||
|
||||
|
||||
def scb_population(cache):
|
||||
body = json.dumps(POPULATION_QUERY).encode()
|
||||
data = source.fetch(POPULATION, cache, "befolkning.json", data=body, headers={"Content-Type": "application/json"})
|
||||
return {row["key"][0]: row["values"][0] for row in json.loads(data.decode("utf-8-sig"))["data"]}
|
||||
|
||||
|
||||
def scb_tatorter(cache):
|
||||
text = source.fetch(TATORTER, cache, "tatorter.csv").decode("utf-8")
|
||||
by_name = collections.defaultdict(list)
|
||||
for r in csv.DictReader(io.StringIO(text)):
|
||||
by_name[r["tatort"]].append((r["kommun"], int(r["bef"])))
|
||||
return by_name
|
||||
|
||||
|
||||
def geonames(cache):
|
||||
z = zipfile.ZipFile(io.BytesIO(source.fetch(POSTAL_CODES, cache, "SE.zip", magic=b"PK")))
|
||||
rows = []
|
||||
for line in z.read("SE.txt").decode("utf-8").splitlines():
|
||||
f = line.split("\t")
|
||||
lat, lon = (float(f[9]), float(f[10])) if f[9] and f[10] else (None, None)
|
||||
rows.append({"code": f[1], "locality": f[2], "municipality": f[6], "lat": lat, "lon": lon})
|
||||
return rows
|
||||
|
||||
|
||||
def nvdb_segments(cache, key):
|
||||
path = cache / "nvdb-gatunamn.tsv"
|
||||
if not path.exists():
|
||||
with open(path.with_suffix(".part"), "w", encoding="utf-8") as out:
|
||||
change = "0"
|
||||
while True:
|
||||
query = (
|
||||
f'<REQUEST><LOGIN authenticationkey="{key}"/>'
|
||||
f'<QUERY objecttype="Gatunamn" namespace="vägdata.nvdb_dk_o" schemaversion="1.0" limit="{NVDB_PAGE}" changeid="{change}">'
|
||||
"<FILTER><EQ name=\"Deleted\" value=\"false\"/></FILTER>"
|
||||
"<INCLUDE>Namn</INCLUDE><INCLUDE>Geometry.WKT-WGS84-3D</INCLUDE></QUERY></REQUEST>"
|
||||
)
|
||||
req = urllib.request.Request(NVDB, data=query.encode(), headers={"Content-Type": "text/xml"})
|
||||
with urllib.request.urlopen(req, timeout=600) as r:
|
||||
result = json.load(r)["RESPONSE"]["RESULT"][0]
|
||||
rows = result.get("Gatunamn", [])
|
||||
for row in rows:
|
||||
m = re.match(r"LINESTRING Z \(([-\d.]+) ([-\d.]+) ", row.get("Geometry", {}).get("WKT-WGS84-3D", ""))
|
||||
name = " ".join(row.get("Namn", "").split())
|
||||
if m and name:
|
||||
out.write(f"{name}\t{m.group(1)}\t{m.group(2)}\n")
|
||||
change = result["INFO"]["LASTCHANGEID"]
|
||||
if len(rows) < NVDB_PAGE:
|
||||
break
|
||||
path.with_suffix(".part").rename(path)
|
||||
for line in path.read_text(encoding="utf-8").splitlines():
|
||||
name, lon, lat = line.split("\t")
|
||||
yield name, float(lat), float(lon)
|
||||
|
||||
|
||||
class Nearest:
|
||||
"""Nearest point by an equirectangular distance, over a degree grid."""
|
||||
|
||||
def __init__(self, points, cell=0.05):
|
||||
self.cell = cell
|
||||
self.grid = collections.defaultdict(list)
|
||||
for lat, lon, value in points:
|
||||
self.grid[(int(lat // cell), int(lon // cell))].append((lat, lon, value))
|
||||
|
||||
def find(self, lat, lon):
|
||||
ci, cj = int(lat // self.cell), int(lon // self.cell)
|
||||
best, best_d = None, math.inf
|
||||
ring = 0
|
||||
while ring < 400:
|
||||
for i in range(ci - ring, ci + ring + 1):
|
||||
for j in range(cj - ring, cj + ring + 1):
|
||||
if max(abs(i - ci), abs(j - cj)) != ring:
|
||||
continue
|
||||
for plat, plon, value in self.grid.get((i, j), ()):
|
||||
d = (plat - lat) ** 2 + ((plon - lon) * math.cos(math.radians(lat))) ** 2
|
||||
if d < best_d:
|
||||
best, best_d = value, d
|
||||
if best is not None and math.sqrt(best_d) < ring * self.cell * math.cos(math.radians(lat)):
|
||||
return best
|
||||
ring += 1
|
||||
return best
|
||||
|
||||
|
||||
def street_delivery(name, codes):
|
||||
"""The codes delivered to a street: the digit after the postort's own prefix says box, company or reply."""
|
||||
if name in ONE_POSITION:
|
||||
return [c for c in codes if c[1] != "0"]
|
||||
largest = collections.Counter(c[:3] for c in codes).most_common(1)[0][1]
|
||||
length = 3 if largest * 2 >= len(codes) else 2
|
||||
return [c for c in codes if c[length] not in "018"]
|
||||
|
||||
|
||||
def municipality_of(name, rows, tatorter, municipalities):
|
||||
named = [code for code, n in municipalities.items() if n == name]
|
||||
if named:
|
||||
return named[0], "kommun"
|
||||
voted = collections.Counter(r["municipality"] for r in rows if r["municipality"] in municipalities)
|
||||
matches = tatorter.get(name, [])
|
||||
if matches:
|
||||
in_vote = [m for m in matches if voted and m[0] == voted.most_common(1)[0][0]]
|
||||
return max(in_vote or matches, key=lambda m: m[1])[0], "tatort"
|
||||
if voted:
|
||||
return voted.most_common(1)[0][0], "codes"
|
||||
return None, "unplaced"
|
||||
|
||||
|
||||
def population_of(name, municipality, tatorter, municipalities, population):
|
||||
matches = [m for m in tatorter.get(name, []) if m[0] == municipality]
|
||||
if matches:
|
||||
return str(max(m[1] for m in matches))
|
||||
return population[municipality] if municipalities[municipality] == name else str(UNMATCHED_POPULATION)
|
||||
|
||||
|
||||
def well_cased(name):
|
||||
return all(part[:1].isupper() and (len(part) == 1 or not part.isupper()) for part in re.split(r"[ -]", name))
|
||||
|
||||
|
||||
def localities(codes, tatorter, municipalities, population):
|
||||
"""Each postort with its municipality, weight, centroid and street-delivery codes."""
|
||||
by_locality = collections.defaultdict(list)
|
||||
for r in codes:
|
||||
by_locality[r["locality"]].append(r)
|
||||
out, how = {}, collections.Counter()
|
||||
for name, rows in by_locality.items():
|
||||
municipality, method = municipality_of(name, rows, tatorter, municipalities)
|
||||
how[method] += 1
|
||||
kept = street_delivery(name, [r["code"].replace(" ", "") for r in rows])
|
||||
with_point = [r for r in rows if r["lat"] is not None]
|
||||
if municipality is None or not kept or not with_point or not well_cased(name):
|
||||
continue
|
||||
lat = sum(r["lat"] for r in with_point) / len(with_point)
|
||||
lon = sum(r["lon"] for r in with_point) / len(with_point)
|
||||
out[name] = {"name": name, "municipality": municipality, "population": population_of(name, municipality, tatorter, municipalities, population), "lat": f"{lat:.4f}", "lon": f"{lon:.4f}", "codes": kept}
|
||||
print(f"municipality by {dict(how)}; {len(by_locality) - len(out)} postorter dropped", file=sys.stderr)
|
||||
return out
|
||||
|
||||
|
||||
def streets(segments, codes, localities, per_locality):
|
||||
"""The names with most segments per locality, each segment at its nearest code centroid."""
|
||||
nearest = Nearest((r["lat"], r["lon"], r["locality"]) for r in codes if r["lat"] is not None and r["locality"] in localities)
|
||||
count = collections.Counter()
|
||||
for name, lat, lon in segments:
|
||||
if name[0].isalpha():
|
||||
count[(nearest.find(lat, lon), name)] += 1
|
||||
of = collections.defaultdict(list)
|
||||
for (locality, name), n in count.items():
|
||||
of[locality].append((n, name))
|
||||
return {locality: [{"name": name, "locality": locality, "segments": n} for n, name in sorted(named, key=lambda s: (-s[0], s[1]))[:per_locality]] for locality, named in of.items()}
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--key-file", help="file holding the Trafikverket API key; TRAFIKVERKET_API_KEY otherwise")
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
p.add_argument("--streets-per-locality", type=int, default=10)
|
||||
a = p.parse_args()
|
||||
key = Path(a.key_file).read_text().strip() if a.key_file else os.environ.get("TRAFIKVERKET_API_KEY")
|
||||
if not key:
|
||||
sys.exit("set TRAFIKVERKET_API_KEY or pass --key-file")
|
||||
cache, out = Path(a.cache), Path(a.out)
|
||||
cache.mkdir(parents=True, exist_ok=True)
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
regions, municipalities = scb_codes(cache)
|
||||
population = scb_population(cache)
|
||||
codes = geonames(cache)
|
||||
places = localities(codes, scb_tatorter(cache), municipalities, population)
|
||||
named = streets(nvdb_segments(cache, key), codes, places, a.streets_per_locality)
|
||||
places = {name: l for name, l in places.items() if name in named}
|
||||
empty = sorted(m for m in municipalities if not any(l["municipality"] == m for l in places.values()))
|
||||
if empty:
|
||||
sys.exit(f"municipalities without a locality: {empty}")
|
||||
|
||||
tsv.write(out / "region.tsv", ["code", "name", "population", "timezone"], [{"code": c, "name": n, "population": population[c], "timezone": TIMEZONE} for c, n in sorted(regions.items())])
|
||||
tsv.write(out / "municipality.tsv", ["code", "name", "region", "population"], [{"code": c, "name": n, "region": c[:2], "population": population[c]} for c, n in sorted(municipalities.items())])
|
||||
tsv.write(out / "locality.tsv", ["name", "municipality", "population", "lat", "lon"], [l for _, l in sorted(places.items())])
|
||||
tsv.write(out / "postal-code.tsv", ["code", "locality"], sorted(({"code": f"{c[:3]} {c[3:]}", "locality": l["name"]} for l in places.values() for c in l["codes"]), key=lambda r: r["code"]))
|
||||
tsv.write(out / "street.tsv", ["name", "locality", "segments"], [s for locality in sorted(named) for s in named[locality]])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,202 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/geo/US/*.tsv from the Census Bureau's Gazetteer, population estimates, ZCTA relationships and TIGER/Line files (public domain).
|
||||
|
||||
data-import/geo-us.py [--cache DIR] [--min-population N] [--streets-per-locality N] [--out DIR]
|
||||
"""
|
||||
import argparse
|
||||
import collections
|
||||
import concurrent.futures
|
||||
import csv
|
||||
import io
|
||||
import re
|
||||
import struct
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
GAZETTEER = "https://www2.census.gov/geo/docs/maps-data/data/gazetteer/2026_Gazetteer/2026_Gaz_{}_national.zip"
|
||||
POPULATION = "https://www2.census.gov/programs-surveys/popest/datasets/2020-2025/{}"
|
||||
STATES = POPULATION.format("state/totals/NST-EST2025-ALLDATA.csv")
|
||||
COUNTIES = POPULATION.format("counties/totals/co-est2025-alldata.csv")
|
||||
PLACES = POPULATION.format("cities/totals/sub-est2025.csv")
|
||||
ZCTA_PLACE = "https://www2.census.gov/geo/docs/maps-data/data/rel2020/zcta520/tab20_zcta520_place20_natl.txt"
|
||||
TIGER = "https://www2.census.gov/geo/tiger/TIGER2025/{0}/tl_2025_{1}_{2}.zip"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "geo" / "US"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
ESTIMATE = "POPESTIMATE2025"
|
||||
CDP = "57"
|
||||
HIGHWAY = re.compile(r"\b(I- |Hwy |Highway |Loop |Rte |Route |Rd )\d")
|
||||
SUFFIX = re.compile(r" (city and borough|city|town|village|borough|municipality|comunidad|zona urbana|metropolitan government|metro government|consolidated government|unified government|urban county|corporation|plantation)( \(balance\))?$")
|
||||
# Places whose Census name is a merged government's; the postal city is what an address carries.
|
||||
NAMES = {"1303440": "Athens", "1304204": "Augusta", "1349008": "Macon", "2148006": "Louisville", "3011397": "Butte", "4732742": "Hartsville", "4752006": "Nashville"}
|
||||
TIMEZONES = {
|
||||
"AK": "America/Anchorage", "AL": "America/Chicago", "AR": "America/Chicago", "AZ": "America/Phoenix",
|
||||
"CA": "America/Los_Angeles", "CO": "America/Denver", "CT": "America/New_York", "DC": "America/New_York",
|
||||
"DE": "America/New_York", "FL": "America/New_York", "GA": "America/New_York", "HI": "Pacific/Honolulu",
|
||||
"IA": "America/Chicago", "ID": "America/Boise", "IL": "America/Chicago", "IN": "America/Indiana/Indianapolis",
|
||||
"KS": "America/Chicago", "KY": "America/New_York", "LA": "America/Chicago", "MA": "America/New_York",
|
||||
"MD": "America/New_York", "ME": "America/New_York", "MI": "America/Detroit", "MN": "America/Chicago",
|
||||
"MO": "America/Chicago", "MS": "America/Chicago", "MT": "America/Denver", "NC": "America/New_York",
|
||||
"ND": "America/Chicago", "NE": "America/Chicago", "NH": "America/New_York", "NJ": "America/New_York",
|
||||
"NM": "America/Denver", "NV": "America/Los_Angeles", "NY": "America/New_York", "OH": "America/New_York",
|
||||
"OK": "America/Chicago", "OR": "America/Los_Angeles", "PA": "America/New_York", "PR": "America/Puerto_Rico",
|
||||
"RI": "America/New_York", "SC": "America/New_York", "SD": "America/Chicago", "TN": "America/Chicago",
|
||||
"TX": "America/Chicago", "UT": "America/Denver", "VA": "America/New_York", "VT": "America/New_York",
|
||||
"WA": "America/Los_Angeles", "WI": "America/Chicago", "WV": "America/New_York", "WY": "America/Denver",
|
||||
}
|
||||
|
||||
|
||||
def text(data):
|
||||
try:
|
||||
return data.decode("utf-8-sig")
|
||||
except UnicodeDecodeError:
|
||||
return data.decode("latin-1")
|
||||
|
||||
|
||||
def gazetteer(cache, kind):
|
||||
z = zipfile.ZipFile(io.BytesIO(source.fetch(GAZETTEER.format(kind), cache, f"gaz_{kind}.zip", magic=b"PK")))
|
||||
rows = text(z.read(z.namelist()[0])).splitlines()
|
||||
header = [h.strip() for h in rows[0].split("|")]
|
||||
return [dict(zip(header, (c.strip() for c in row.split("|")))) for row in rows[1:]]
|
||||
|
||||
|
||||
def csv_rows(cache, url, name):
|
||||
return list(csv.DictReader(io.StringIO(text(source.fetch(url, cache, name)))))
|
||||
|
||||
|
||||
def dbf_rows(data, wanted):
|
||||
"""The records of a dBASE file, the wanted fields only."""
|
||||
count, header_len, record_len = struct.unpack("<xxxxIHH", data[:12])
|
||||
fields, pos = [], 32
|
||||
while data[pos] != 0x0D:
|
||||
name = data[pos:pos + 11].split(b"\0")[0].decode()
|
||||
fields.append((name, data[pos + 16]))
|
||||
pos += 32
|
||||
pos = header_len
|
||||
for _ in range(count):
|
||||
record = data[pos:pos + record_len]
|
||||
pos += record_len
|
||||
if record[:1] == b"*":
|
||||
continue
|
||||
row, at = {}, 1
|
||||
for name, length in fields:
|
||||
if name in wanted:
|
||||
row[name] = record[at:at + length].decode("utf-8", "replace").strip()
|
||||
at += length
|
||||
yield row
|
||||
|
||||
|
||||
def tiger_zip(cache, kind, county):
|
||||
return source.fetch(TIGER.format(kind.upper(), county, kind), cache, f"tl_{county}_{kind}.zip", magic=b"PK")
|
||||
|
||||
|
||||
def tiger(cache, kind, county, wanted):
|
||||
z = zipfile.ZipFile(io.BytesIO(tiger_zip(cache, kind, county)))
|
||||
return dbf_rows(z.read(f"tl_2025_{county}_{kind}.dbf"), wanted)
|
||||
|
||||
|
||||
def place_name(geoid, name):
|
||||
if geoid in NAMES:
|
||||
return NAMES[geoid]
|
||||
stripped = SUFFIX.sub("", name)
|
||||
if stripped == name:
|
||||
print(f"{geoid}: no suffix stripped from {name!r}", file=sys.stderr)
|
||||
return stripped
|
||||
|
||||
|
||||
def localities(cache, min_population, counties):
|
||||
"""Each shipped place with its county, population and centroid."""
|
||||
place_population, county_part = {}, collections.defaultdict(list)
|
||||
for r in csv_rows(cache, PLACES, "sub-est2025.csv"):
|
||||
if r["SUMLEV"] == "162":
|
||||
place_population[r["STATE"] + r["PLACE"]] = int(r[ESTIMATE])
|
||||
elif r["SUMLEV"] == "157":
|
||||
county_part[r["STATE"] + r["PLACE"]].append((int(r[ESTIMATE]), r["STATE"] + r["COUNTY"]))
|
||||
out = {}
|
||||
for r in gazetteer(cache, "place"):
|
||||
geoid, population = r["GEOID"], place_population.get(r["GEOID"], 0)
|
||||
if r["FUNCSTAT"] not in ("A", "F", "N") or r["LSAD"] == CDP or population < min_population or not county_part.get(geoid):
|
||||
continue
|
||||
county = max(county_part[geoid])[1]
|
||||
if county not in counties:
|
||||
print(f"{geoid} {r['NAME']}: county {county} unknown, dropped", file=sys.stderr)
|
||||
continue
|
||||
out[geoid] = {"code": geoid, "name": place_name(geoid, r["NAME"]), "municipality": county, "population": population, "lat": r["INTPTLAT"], "lon": r["INTPTLONG"]}
|
||||
return out
|
||||
|
||||
|
||||
def postal_codes(cache, localities):
|
||||
"""Each ZCTA whose largest part inside an incorporated place lies in a shipped place."""
|
||||
parts = {}
|
||||
for r in csv.DictReader(io.StringIO(text(source.fetch(ZCTA_PLACE, cache, "zcta-place.txt"))), delimiter="|"):
|
||||
if r["GEOID_ZCTA5_20"] and r["GEOID_PLACE_20"] and not r["NAMELSAD_PLACE_20"].endswith(" CDP"):
|
||||
parts.setdefault(r["GEOID_ZCTA5_20"], []).append((int(r["AREALAND_PART"]), r["GEOID_PLACE_20"]))
|
||||
largest = {zcta: max(p)[1] for zcta, p in parts.items()}
|
||||
return {zcta: place for zcta, place in largest.items() if place in localities}
|
||||
|
||||
|
||||
def streets(cache, counties, locality_of_zcta, per_locality):
|
||||
"""The names with most TIGER address ranges per place, and the address ranges per ZCTA."""
|
||||
with concurrent.futures.ThreadPoolExecutor(3) as pool:
|
||||
list(pool.map(lambda c: (tiger_zip(cache, "addr", c), tiger_zip(cache, "featnames", c)), counties))
|
||||
addresses, count = collections.Counter(), collections.Counter()
|
||||
for county in counties:
|
||||
zips = collections.defaultdict(set)
|
||||
for r in tiger(cache, "addr", county, {"TLID", "ZIP"}):
|
||||
if r["ZIP"] in locality_of_zcta:
|
||||
zips[r["TLID"]].add(r["ZIP"])
|
||||
addresses[r["ZIP"]] += 1
|
||||
for r in tiger(cache, "featnames", county, {"TLID", "FULLNAME", "PAFLAG"}):
|
||||
if r["PAFLAG"] == "P" and r["FULLNAME"] and not HIGHWAY.search(r["FULLNAME"]):
|
||||
for z in zips.get(r["TLID"], ()):
|
||||
count[(locality_of_zcta[z], r["FULLNAME"])] += 1
|
||||
of = collections.defaultdict(list)
|
||||
for (locality, name), n in count.items():
|
||||
of[locality].append((n, name))
|
||||
named = {locality: [{"name": name, "locality": locality, "addresses": n} for n, name in sorted(ranked, key=lambda s: (-s[0], s[1]))[:per_locality]] for locality, ranked in of.items()}
|
||||
return named, addresses
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--min-population", type=int, default=25000)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
p.add_argument("--streets-per-locality", type=int, default=10)
|
||||
a = p.parse_args()
|
||||
cache, out = Path(a.cache), Path(a.out)
|
||||
cache.mkdir(parents=True, exist_ok=True)
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
state_population = {r["STATE"]: r[ESTIMATE] for r in csv_rows(cache, STATES, "nst-est2025.csv") if r["SUMLEV"] == "040"}
|
||||
regions = {r["USPS"]: {"abbr": r["USPS"], "code": r["GEOID"], "name": r["NAME"], "population": state_population[r["GEOID"]], "timezone": TIMEZONES[r["USPS"]]} for r in gazetteer(cache, "state")}
|
||||
county_population = {r["STATE"] + r["COUNTY"]: r[ESTIMATE] for r in csv_rows(cache, COUNTIES, "co-est2025.csv") if r["SUMLEV"] == "050"}
|
||||
counties, unestimated = {}, collections.Counter()
|
||||
for r in gazetteer(cache, "counties"):
|
||||
if r["GEOID"] not in county_population:
|
||||
unestimated[r["USPS"]] += 1
|
||||
continue
|
||||
counties[r["GEOID"]] = {"code": r["GEOID"], "name": r["NAME"], "region": r["USPS"], "population": county_population[r["GEOID"]]}
|
||||
print(f"counties without a population estimate, dropped: {dict(unestimated)}", file=sys.stderr)
|
||||
|
||||
places = localities(cache, a.min_population, counties)
|
||||
locality_of_zcta = postal_codes(cache, places)
|
||||
named, addresses = streets(cache, sorted({l["municipality"] for l in places.values()}), locality_of_zcta, a.streets_per_locality)
|
||||
for geoid in [l for l in places if l not in named]:
|
||||
print(f"{geoid} {places[geoid]['name']}: no streets, dropped", file=sys.stderr)
|
||||
del places[geoid]
|
||||
kept_counties = {l["municipality"] for l in places.values()}
|
||||
kept_regions = {counties[c]["region"] for c in kept_counties}
|
||||
|
||||
tsv.write(out / "region.tsv", ["abbr", "code", "name", "population", "timezone"], [r for _, r in sorted(regions.items()) if r["abbr"] in kept_regions])
|
||||
tsv.write(out / "municipality.tsv", ["code", "name", "region", "population"], [c for _, c in sorted(counties.items()) if c["code"] in kept_counties])
|
||||
tsv.write(out / "locality.tsv", ["code", "name", "municipality", "population", "lat", "lon"], [l for _, l in sorted(places.items())])
|
||||
tsv.write(out / "postal-code.tsv", ["code", "locality", "addresses"], [{"code": z, "locality": l, "addresses": addresses[z]} for z, l in sorted(locality_of_zcta.items()) if addresses[z] and l in places])
|
||||
tsv.write(out / "street.tsv", ["name", "locality", "addresses"], [s for locality in sorted(named) for s in named[locality]])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/httpmethod.tsv from the IANA HTTP Method Registry.
|
||||
|
||||
data-import/httpmethod.py [--source URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
|
||||
A method ships when the register cites RFC 9110 section 9.3 or RFC 5789 for it, which
|
||||
is the eight HTTP defines and PATCH; the register's other entries do not. The
|
||||
register's `yes` and `no` ship as `true` and `false`, which a consumer's boolean reads.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://www.iana.org/assignments/http-methods/methods.csv"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "httpmethod.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["idempotent", "method", "safe"]
|
||||
CORE = re.compile(r"\[RFC9110, Section 9\.3\.|\[RFC5789, Section 2\]")
|
||||
BOOLEAN = {"yes": "true", "no": "false"}
|
||||
SELECTOR = '[]{}"|' # mirrors inSelector in table.go
|
||||
EXPECTED = 9
|
||||
|
||||
|
||||
def rows(text):
|
||||
for r in csv.DictReader(io.StringIO(text)):
|
||||
method = (r["Method Name"] or "").strip()
|
||||
if not CORE.search(r["Reference"] or ""):
|
||||
continue
|
||||
if set(method) & set(SELECTOR):
|
||||
sys.exit(f"{method}: a method holding one of {SELECTOR} is one a selector cannot spell")
|
||||
answers = {}
|
||||
for column in ("Safe", "Idempotent"):
|
||||
answer = (r[column] or "").strip()
|
||||
if answer not in BOOLEAN:
|
||||
sys.exit(f"{method}: {column} {answer!r} is neither yes nor no")
|
||||
answers[column] = BOOLEAN[answer]
|
||||
yield {"idempotent": answers["Idempotent"], "method": method, "safe": answers["Safe"]}
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
table = sorted(rows(source.fetch(a.source, a.cache, "http-methods.csv").decode("utf-8")), key=lambda r: r["method"])
|
||||
if len(table) != EXPECTED:
|
||||
sys.exit(f"{len(table)} methods cite RFC 9110 section 9.3 or RFC 5789, not {EXPECTED}; the register's Reference column has moved")
|
||||
tsv.write(a.out, COLUMNS, table)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,40 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/httpstatus.tsv from the IANA HTTP Status Code Registry.
|
||||
|
||||
data-import/httpstatus.py [--source URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
|
||||
What ships is the codes in use: a range, an unassigned code and a qualified reason are all skipped.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "httpstatus.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["code", "reason"]
|
||||
|
||||
|
||||
def rows(text):
|
||||
for r in csv.DictReader(io.StringIO(text)):
|
||||
code, reason = r["Value"].strip(), r["Description"].strip()
|
||||
if code.isdigit() and reason and "(" not in reason and reason != "Unassigned":
|
||||
yield {"code": code, "reason": reason}
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
table = rows(source.fetch(a.source, a.cache, "http-status-codes-1.csv").decode("utf-8"))
|
||||
tsv.write(a.out, COLUMNS, sorted(table, key=lambda r: int(r["code"])))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/language.tsv from datasets/language-codes (PDDL), the LoC ISO 639-2 register.
|
||||
|
||||
data-import/language.py [--source URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
|
||||
Only the 639-2 entries carrying a 639-1 code ship, since the alpha-2 code is the key.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://raw.githubusercontent.com/datasets/language-codes/main/data/language-codes-full.csv"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "language.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["code", "code3", "name"]
|
||||
|
||||
|
||||
def name(english):
|
||||
"""The register lists every synonym; the first, without its qualifier, is the one a selector spells."""
|
||||
return re.sub(r"\s*\([^)]*\)", "", english.split(";")[0]).strip()
|
||||
|
||||
|
||||
def rows(text):
|
||||
for r in csv.DictReader(io.StringIO(text)):
|
||||
if r["alpha2"]:
|
||||
yield {"code": r["alpha2"], "code3": r["alpha3-t"] or r["alpha3-b"], "name": name(r["English"])}
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
table = rows(source.fetch(a.source, a.cache, "language-codes-full.csv").decode("utf-8"))
|
||||
tsv.write(a.out, COLUMNS, sorted(table, key=lambda r: r["code"]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/loglevel.tsv from RFC 5424 and POSIX <syslog.h>.
|
||||
|
||||
data-import/loglevel.py [--source URL_OR_FILE] [--posix URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
|
||||
Every severity in Table 2 ships, paired to POSIX's keywords by the order each source
|
||||
lists them, which holds only because a keyword prefixes the severity RFC 5424 numbers
|
||||
alike.
|
||||
"""
|
||||
import argparse
|
||||
import html
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://www.rfc-editor.org/rfc/rfc5424.txt"
|
||||
POSIX = "https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/syslog.h.html"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "loglevel.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["code", "keyword", "severity"]
|
||||
TABLE = re.compile(r"Table 1\.\s+Syslog Message Facilities(.*?)Table 2\.\s+Syslog Message Severities", re.S)
|
||||
ROW = re.compile(r"^ +(\d) +([A-Z][a-z]+): ", re.M)
|
||||
SECTION = re.compile(r"severity level portion of the.*?macros:(.*?)The following shall be declared as functions", re.S)
|
||||
MACRO = re.compile(r"\bLOG_([A-Z]+)\b")
|
||||
EXPECTED = 8
|
||||
|
||||
|
||||
def severities(text):
|
||||
table = TABLE.search(text)
|
||||
if not table:
|
||||
sys.exit("the RFC holds no text between the facility and the severity caption; its tables have moved")
|
||||
return ROW.findall(table.group(1))
|
||||
|
||||
|
||||
def keywords(page):
|
||||
section = SECTION.search(html.unescape(re.sub(r"<[^>]+>", "", page)))
|
||||
if not section:
|
||||
sys.exit("POSIX spells no severity list between its own caption and the function declarations; the page has moved")
|
||||
return [m.lower() for m in MACRO.findall(section.group(1))]
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--posix", default=POSIX)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
table = sorted(severities(source.fetch(a.source, a.cache, "rfc5424.txt").decode("utf-8")))
|
||||
codes = [code for code, _ in table]
|
||||
if codes != [str(i) for i in range(EXPECTED)]:
|
||||
sys.exit(f"the severity table numbers {codes}, not 0 through {EXPECTED - 1}")
|
||||
words = keywords(source.fetch(a.posix, a.cache, "posix-syslog.h.html").decode("utf-8"))
|
||||
if len(words) != EXPECTED:
|
||||
sys.exit(f"POSIX lists {len(words)} severity macros, not {EXPECTED}: {words}")
|
||||
rows = []
|
||||
for (code, severity), keyword in zip(table, words):
|
||||
if not severity.lower().startswith(keyword):
|
||||
sys.exit(f"severity {code} is {severity} in the RFC and {keyword} in POSIX, which is no prefix of it")
|
||||
rows.append({"code": code, "keyword": keyword, "severity": severity})
|
||||
tsv.write(a.out, COLUMNS, rows)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/mimetype.tsv from mime-db (MIT), the IANA media type registry with extensions.
|
||||
|
||||
data-import/mimetype.py [--source URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
|
||||
Only types mime-db records as IANA-registered, with a filename extension, ship.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://raw.githubusercontent.com/jshttp/mime-db/master/db.json"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "mimetype.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["ext", "type"]
|
||||
FIXUPS = {"application/mp4": "mp4s", "audio/mpeg": "mp3", "audio/ogg": "ogg", "video/quicktime": "mov"}
|
||||
|
||||
|
||||
def rows(db):
|
||||
for mimetype, entry in db.items():
|
||||
if entry.get("source") == "iana" and entry.get("extensions"):
|
||||
yield {"ext": "." + FIXUPS.get(mimetype, entry["extensions"][0]), "type": mimetype}
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
table = rows(json.loads(source.fetch(a.source, a.cache, "mime-db.json").decode("utf-8")))
|
||||
tsv.write(a.out, COLUMNS, sorted(table, key=lambda r: r["type"]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/sv_SE/first-name.tsv and last-name.tsv from SCB's whole-population name counts (CC0, "Källa: SCB").
|
||||
|
||||
data-import/names-se.py [--source URL_OR_FILE] [--first N] [--last N] [--cache DIR] [--out DIR]
|
||||
|
||||
A tilltalsnamn goes under each sex that carries it, so a name both sexes carry has two rows.
|
||||
"""
|
||||
import argparse
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
import xlsx
|
||||
|
||||
SOURCE = "https://www.scb.se/contentassets/9fe7dbb460994c72b835163dbc491ef9/namn-med-minst-tva-barare-31-december-2022.xlsx"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "sv_SE"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
SHEETS = {"f": "Tilltalsnamn kvinnor", "m": "Tilltalsnamn män"}
|
||||
SURNAMES = "Efternamn"
|
||||
NAME = re.compile(r"^[^\W\d_]{2,}([ '-][^\W\d_]{2,})*$")
|
||||
PARTICLES = {"af", "av", "da", "de", "del", "den", "der", "di", "dos", "du", "la", "le", "van", "von"}
|
||||
|
||||
|
||||
def cased(name):
|
||||
"""SCB's uppercase name as it is written: each part capitalised, a particle before another part lowercased."""
|
||||
parts = name.lower().split(" ")
|
||||
return " ".join(w if w in PARTICLES and len(parts) > 1 else w.title() for w in parts)
|
||||
|
||||
|
||||
def counted(rows, top):
|
||||
"""The top names of a sheet by bearers, initials and single letters dropped."""
|
||||
names = [(cells[0], int(cells[1])) for cells in rows if len(cells) >= 2 and cells[1].isdigit() and NAME.match(cells[0])]
|
||||
return sorted(names, key=lambda n: (-n[1], n[0]))[:top]
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--first", type=int, default=2000, help="names per sex")
|
||||
p.add_argument("--last", type=int, default=5000)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
a = p.parse_args()
|
||||
data = source.fetch(a.source, a.cache, "scb-namn-2022.xlsx", magic=b"PK")
|
||||
first = [{"name": cased(n), "sex": sex, "count": c} for sex, sheet in SHEETS.items() for n, c in counted(xlsx.rows(data, sheet), a.first)]
|
||||
last = [{"name": cased(n), "count": c} for n, c in counted(xlsx.rows(data, SURNAMES), a.last)]
|
||||
out = Path(a.out)
|
||||
tsv.write(out / "first-name.tsv", ["name", "sex", "count"], sorted(first, key=lambda r: (r["name"], r["sex"])))
|
||||
tsv.write(out / "last-name.tsv", ["name", "count"], sorted(last, key=lambda r: r["name"]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/en_US/first-name.tsv and last-name.tsv from SSA baby names and the Census 2010 surnames (public domain).
|
||||
|
||||
data-import/names-us.py [--names URL_OR_FILE] [--surnames URL_OR_FILE] [--from-year YEAR] [--first N] [--last N] [--cache DIR] [--out DIR]
|
||||
|
||||
A given name is counted over the births of --from-year and later, under each sex it was given to, so a
|
||||
name both sexes carry has two rows. --names takes SSA's names.zip or a merged name,sex,count,year file;
|
||||
--surnames takes the Census names.zip or its Names_2010Census.csv.
|
||||
"""
|
||||
import argparse
|
||||
import collections
|
||||
import csv
|
||||
import io
|
||||
import re
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
NAMES = "https://raw.githubusercontent.com/hackerb9/ssa-baby-names/master/alldata.txt"
|
||||
SURNAMES = "https://www2.census.gov/topics/genealogy/2010surnames/names.zip"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "en_US"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
MC = re.compile(r"^Mc([a-z])")
|
||||
PLACEHOLDERS = {"Baby", "Female", "Infant", "Male", "Notnamed", "Unknown", "Unnamed"}
|
||||
|
||||
|
||||
def csv_or_zip_rows(data, member):
|
||||
"""The rows of a CSV, or of every member of a zip named like member, name,sex,count[,year]."""
|
||||
if data.startswith(b"PK"):
|
||||
z = zipfile.ZipFile(io.BytesIO(data))
|
||||
for n in sorted(z.namelist()):
|
||||
if re.fullmatch(member, n):
|
||||
year = re.sub(r"\D", "", n)
|
||||
for r in csv.reader(io.StringIO(z.read(n).decode("utf-8-sig"))):
|
||||
yield r + [year] if year else r
|
||||
return
|
||||
yield from csv.reader(io.StringIO(data.decode("utf-8-sig")))
|
||||
|
||||
|
||||
def given(data, from_year):
|
||||
counts = collections.Counter()
|
||||
for r in csv_or_zip_rows(data, r"yob\d{4}\.txt"):
|
||||
if len(r) >= 4 and r[3].isdigit() and int(r[3]) >= from_year and r[0] not in PLACEHOLDERS:
|
||||
counts[(r[0], r[1].lower())] += int(r[2])
|
||||
return counts
|
||||
|
||||
|
||||
def surname(name):
|
||||
"""A Census uppercase surname as it is written: capitalised, and Mc before a capital."""
|
||||
return MC.sub(lambda m: "Mc" + m.group(1).upper(), name.title())
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--first", type=int, default=2000, help="names per sex")
|
||||
p.add_argument("--from-year", type=int, default=1930)
|
||||
p.add_argument("--last", type=int, default=5000)
|
||||
p.add_argument("--names", default=NAMES)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
p.add_argument("--surnames", default=SURNAMES)
|
||||
a = p.parse_args()
|
||||
counts = given(source.fetch(a.names, a.cache, "ssa-names.txt"), a.from_year)
|
||||
first = []
|
||||
for sex in ("f", "m"):
|
||||
top = sorted(((n, c) for (n, s), c in counts.items() if s == sex), key=lambda n: (-n[1], n[0]))[:a.first]
|
||||
first += [{"name": n, "sex": sex, "count": c} for n, c in top]
|
||||
rows = csv_or_zip_rows(source.fetch(a.surnames, a.cache, "census-surnames-2010.zip"), r"Names_2010Census\.csv")
|
||||
last = [{"name": surname(r[0]), "count": int(r[2])} for r in rows if len(r) >= 3 and r[2].isdigit() and r[0].isalpha()]
|
||||
last = sorted(last, key=lambda r: (-r["count"], r["name"]))[:a.last]
|
||||
out = Path(a.out)
|
||||
tsv.write(out / "first-name.tsv", ["name", "sex", "count"], sorted(first, key=lambda r: (r["name"], r["sex"])))
|
||||
tsv.write(out / "last-name.tsv", ["name", "count"], sorted(last, key=lambda r: r["name"]))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,55 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/port.tsv from the IANA Service Name and Transport Protocol Port Number Registry.
|
||||
|
||||
data-import/port.py [--source URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
|
||||
A row needs a service name and a numeric TCP port. A port the registry lists more than
|
||||
once keeps the first service it describes, or the first of them where it describes none,
|
||||
so a number selects one row.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.csv"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "port.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["number", "service"]
|
||||
FLOOR = 4000
|
||||
|
||||
|
||||
def rows(text):
|
||||
best, described = {}, set()
|
||||
for r in csv.DictReader(io.StringIO(text)):
|
||||
service, number = (r["Service Name"] or "").strip(), (r["Port Number"] or "").strip()
|
||||
transport, description = (r["Transport Protocol"] or "").strip(), (r["Description"] or "").strip()
|
||||
if transport != "tcp" or not service or not number.isdigit():
|
||||
continue
|
||||
if not 1 <= int(number) <= 65535:
|
||||
sys.exit(f"{service}: port {number} is outside 1-65535")
|
||||
if number not in best or (description and number not in described):
|
||||
best[number] = {"number": number, "service": service}
|
||||
if description:
|
||||
described.add(number)
|
||||
return sorted(best.values(), key=lambda r: int(r["number"]))
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
table = rows(source.fetch(a.source, a.cache, "service-names-port-numbers.csv").decode("utf-8"))
|
||||
if len(table) < FLOOR:
|
||||
sys.exit(f"only {len(table)} ports named a TCP service; the registry's columns have moved")
|
||||
tsv.write(a.out, COLUMNS, table)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,67 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/protocol.tsv from the IANA Protocol Numbers registry.
|
||||
|
||||
data-import/protocol.py [--source URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
|
||||
A row needs a number and a keyword, which drops the seven numbers assigned to a class of
|
||||
protocols rather than to one; a keyword the registry reserves or marks deprecated goes
|
||||
too. Where the registry names no protocol beside the keyword, the keyword is the name.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://www.iana.org/assignments/protocol-numbers/protocol-numbers-1.csv"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "protocol.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["keyword", "name", "number"]
|
||||
SKIP = ("Reserved", "deprecated")
|
||||
SELECTOR = '[]{}"|' # mirrors inSelector in table.go
|
||||
|
||||
|
||||
def rows(text):
|
||||
for r in csv.DictReader(io.StringIO(text)):
|
||||
number, keyword = (r["Decimal"] or "").strip(), (r["Keyword"] or "").strip()
|
||||
if not number.isdigit() or not keyword or any(s in keyword for s in SKIP):
|
||||
continue
|
||||
if not 0 <= int(number) <= 255:
|
||||
sys.exit(f"{keyword}: protocol number {number} is outside 0-255")
|
||||
yield {"keyword": keyword, "name": " ".join((r["Protocol"] or "").split()) or keyword, "number": number}
|
||||
|
||||
|
||||
def refuse_an_unselectable_row(table):
|
||||
"""What the loader refuses at New, and a name that names two rows rather than one."""
|
||||
keywords, names = {}, {}
|
||||
for r in table:
|
||||
for column in ("keyword", "name"):
|
||||
if set(r[column]) & set(SELECTOR):
|
||||
sys.exit(f"{r['keyword']}: {column} {r[column]!r} holds one of {SELECTOR}, which a selector cannot spell")
|
||||
if r["keyword"] in keywords:
|
||||
sys.exit(f"{r['keyword']}: repeats the keyword of protocol {keywords[r['keyword']]}")
|
||||
keywords[r["keyword"]] = r["number"]
|
||||
for r in table:
|
||||
if r["name"] != r["keyword"] and r["name"] in keywords:
|
||||
sys.exit(f"{r['keyword']}: name {r['name']!r} is another protocol's keyword")
|
||||
if r["name"] in names:
|
||||
sys.exit(f"{r['keyword']}: name {r['name']!r} repeats {names[r['name']]}'s, so it selects neither")
|
||||
names[r["name"]] = r["keyword"]
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
table = sorted(rows(source.fetch(a.source, a.cache, "protocol-numbers-1.csv").decode("utf-8")), key=lambda r: int(r["number"]))
|
||||
refuse_an_unselectable_row(table)
|
||||
tsv.write(a.out, COLUMNS, table)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,29 +0,0 @@
|
||||
"""A source fetched once into the cache."""
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def fetch(source, cache, name, magic=b"", data=None, headers=None):
|
||||
"""The bytes of a URL, downloaded into cache/name once, or of a local file."""
|
||||
if not re.match(r"^https?://", source):
|
||||
return Path(source).read_bytes()
|
||||
path = Path(cache) / name
|
||||
if path.exists():
|
||||
return path.read_bytes()
|
||||
for attempt in range(1, 6):
|
||||
req = urllib.request.Request(source, data=data, headers={"User-Agent": "fejkdata data-import", **(headers or {})})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=600) as r:
|
||||
body = r.read()
|
||||
except OSError:
|
||||
body = b""
|
||||
if body and body.startswith(magic) and b"Request Rejected" not in body[:512]:
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(body)
|
||||
return body
|
||||
if attempt < 5:
|
||||
time.sleep(10 * attempt)
|
||||
sys.exit(f"{source}: no valid download in 5 attempts")
|
||||
@@ -1,85 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/territory.tsv from datasets/country-codes (PDDL).
|
||||
|
||||
data-import/territory.py [--source URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://raw.githubusercontent.com/datasets/country-codes/main/data/country-codes.csv"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "territory.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["alpha2", "alpha3", "calling-code", "capital", "country", "currency", "flag", "languages", "name", "numeric", "tld"]
|
||||
SOVEREIGN = re.compile(r"^(?:Part of|Territor(?:y|ies) of|Crown dependency of|Commonwealth of|Associated with) ([A-Z]{2})$")
|
||||
STANDALONE = {"In contention", "International"}
|
||||
# Gaps in the source, keyed by alpha2.
|
||||
FIXUPS = {"TR": {"currency": "TRY"}}
|
||||
|
||||
|
||||
def flag(alpha2):
|
||||
return "".join(chr(0x1F1E6 + ord(c) - ord("A")) for c in alpha2)
|
||||
|
||||
|
||||
def calling_code(dial):
|
||||
first = re.sub(r"[^0-9-]", "", dial.split(",")[0])
|
||||
return first if re.match(r"^\d+-\d{3}$", first) else first.split("-")[0]
|
||||
|
||||
|
||||
def languages(field):
|
||||
return ",".join(p for p in field.split(",") if p)
|
||||
|
||||
|
||||
def country(alpha2, independent):
|
||||
"""The sovereign state the register records; a territory it records none for stands alone."""
|
||||
m = SOVEREIGN.match(independent)
|
||||
if m:
|
||||
return m.group(1)
|
||||
if independent == "Yes" or independent in STANDALONE:
|
||||
return alpha2
|
||||
sys.exit(f"{alpha2}: is_independent {independent!r} names no sovereign this script can read")
|
||||
|
||||
|
||||
def rows(text):
|
||||
for r in csv.DictReader(io.StringIO(text)):
|
||||
alpha2 = r["ISO3166-1-Alpha-2"]
|
||||
row = {
|
||||
"alpha2": alpha2,
|
||||
"alpha3": r["ISO3166-1-Alpha-3"],
|
||||
"calling-code": calling_code(r["Dial"]),
|
||||
"capital": r["Capital"],
|
||||
"country": country(alpha2, r["is_independent"]),
|
||||
"currency": r["ISO4217-currency_alphabetic_code"].split(",")[0],
|
||||
"flag": flag(alpha2),
|
||||
"languages": languages(r["Languages"]),
|
||||
"name": r["CLDR display name"] or r["official_name_en"],
|
||||
"numeric": r["ISO3166-1-numeric"].zfill(3),
|
||||
"tld": r["TLD"],
|
||||
}
|
||||
row.update(FIXUPS.get(alpha2, {}))
|
||||
if all(row[c] for c in ("alpha2", "alpha3", "calling-code", "capital", "currency", "name", "tld")):
|
||||
yield row
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
table = sorted(rows(source.fetch(a.source, a.cache, "country-codes.csv").decode("utf-8")), key=lambda r: r["alpha2"])
|
||||
shipped = {r["alpha2"] for r in table}
|
||||
for r in table:
|
||||
if r["country"] not in shipped:
|
||||
sys.exit(f"{r['alpha2']}: country {r['country']} is no shipped territory; a sovereign the set drops leaves its territories unreadable")
|
||||
tsv.write(a.out, COLUMNS, table)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,138 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/timezone.tsv from the IANA tzdb tarball (public domain) and GeoNames (CC BY 4.0).
|
||||
|
||||
data-import/timezone.py [--source URL_OR_FILE] [--cities URL_OR_FILE] [--cache DIR] [--out FILE] [--territories FILE]
|
||||
|
||||
The offset is the zone's standard offset, the first field of its Zone rule's last
|
||||
continuation line, with a Link resolved to its target. The weight is the population
|
||||
GeoNames records in the zone's cities of 15,000 or more, never below that threshold,
|
||||
so a drawn zone is one people live in.
|
||||
"""
|
||||
import argparse
|
||||
import csv
|
||||
import io
|
||||
import re
|
||||
import sys
|
||||
import tarfile
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://data.iana.org/time-zones/tzdata-latest.tar.gz"
|
||||
CITIES = "https://download.geonames.org/export/dump/cities15000.zip"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "timezone.tsv"
|
||||
TERRITORIES = Path(__file__).resolve().parent.parent / "data" / "misc" / "territory.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["offset", "territory", "weight", "zone"]
|
||||
REGIONS = ["africa", "antarctica", "asia", "australasia", "backward", "etcetera", "europe", "northamerica", "southamerica"]
|
||||
# GeoNames' own cutoff, so a zone it records no city in weighs less than one would.
|
||||
FLOOR = 15000
|
||||
|
||||
|
||||
def member(tar, name):
|
||||
try:
|
||||
f = tar.extractfile(name)
|
||||
except KeyError:
|
||||
f = None
|
||||
if f is None:
|
||||
sys.exit(f"{name}: the tarball no longer holds it as a file; the tzdb layout has moved")
|
||||
return f.read().decode("utf-8")
|
||||
|
||||
|
||||
def offsets(tar):
|
||||
"""Every zone's standard offset, and every link's target."""
|
||||
std, links = {}, {}
|
||||
for name in REGIONS:
|
||||
zone = None
|
||||
for raw in member(tar, name).splitlines():
|
||||
line = raw.split("#")[0].rstrip()
|
||||
if not line.strip():
|
||||
continue
|
||||
if line.startswith("Zone"):
|
||||
f = line.split()
|
||||
zone, std[f[1]] = f[1], f[2]
|
||||
elif line.startswith("Link"):
|
||||
f = line.split()
|
||||
links[f[2]], zone = f[1], None
|
||||
elif line[0] in " \t" and zone:
|
||||
std[zone] = line.split()[0]
|
||||
else:
|
||||
zone = None
|
||||
return std, links
|
||||
|
||||
|
||||
def resolve(zone, std, links):
|
||||
for _ in range(10):
|
||||
if zone in std:
|
||||
return std[zone]
|
||||
if zone not in links:
|
||||
return None
|
||||
zone = links[zone]
|
||||
return None
|
||||
|
||||
|
||||
def utc_offset(raw):
|
||||
"""±HH:MM from a tzdb STDOFF field, which writes the minutes and seconds only when it has them."""
|
||||
m = re.match(r"^(-)?(\d{1,2})(?::(\d{2})(?::(\d{2}))?)?$", raw)
|
||||
if not m or (m.group(4) or "00") != "00":
|
||||
return None
|
||||
return f"{'-' if m.group(1) else '+'}{int(m.group(2)):02d}:{m.group(3) or '00'}"
|
||||
|
||||
|
||||
def population(body):
|
||||
"""The population GeoNames records per timezone, summed over its cities."""
|
||||
with zipfile.ZipFile(io.BytesIO(body)) as z:
|
||||
text = z.read("cities15000.txt").decode("utf-8")
|
||||
per = {}
|
||||
for city in csv.reader(io.StringIO(text), delimiter="\t", quoting=csv.QUOTE_NONE):
|
||||
if len(city) > 17 and city[17] and city[14].isdigit():
|
||||
per[city[17]] = per.get(city[17], 0) + int(city[14])
|
||||
return per
|
||||
|
||||
|
||||
def rows(tar, territories, pop):
|
||||
std, links = offsets(tar)
|
||||
for line in member(tar, "zone.tab").splitlines():
|
||||
if line.startswith("#") or not line.strip():
|
||||
continue
|
||||
fields = line.split("\t")
|
||||
if len(fields) < 3:
|
||||
sys.exit(f"zone.tab: {line!r} has {len(fields)} fields; a row names a territory, a location and a zone")
|
||||
territory, zone = fields[0], fields[2]
|
||||
if territory not in territories:
|
||||
continue
|
||||
raw = resolve(zone, std, links)
|
||||
if raw is None:
|
||||
sys.exit(f"{zone}: the tarball gives it no Zone rule and no Link to one")
|
||||
offset = utc_offset(raw)
|
||||
if offset is None:
|
||||
sys.exit(f"{zone}: standard offset {raw!r} is not a ±HH:MM the table can spell")
|
||||
yield {"offset": offset, "territory": territory, "weight": max(pop.get(zone, 0), FLOOR), "zone": zone}
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
p.add_argument("--cities", default=CITIES)
|
||||
p.add_argument("--territories", default=str(TERRITORIES))
|
||||
a = p.parse_args()
|
||||
shipped = {r["alpha2"] for r in csv.DictReader(io.StringIO(Path(a.territories).read_text(encoding="utf-8")), delimiter="\t")}
|
||||
pop = population(source.fetch(a.cities, a.cache, "cities15000.zip", magic=b"PK"))
|
||||
body = source.fetch(a.source, a.cache, "tzdata-latest.tar.gz", magic=b"\x1f\x8b")
|
||||
with tarfile.open(fileobj=io.BytesIO(body)) as tar:
|
||||
table = sorted(rows(tar, shipped, pop), key=lambda r: r["zone"])
|
||||
print(f"tzdb {tar.extractfile('version').read().decode('utf-8').strip()}", file=sys.stderr)
|
||||
linked = {r["territory"] for r in table}
|
||||
if missing := shipped - linked:
|
||||
sys.exit(f"no zone for {sorted(missing)}; a parent row without a child is a load error")
|
||||
if (floored := sum(1 for r in table if r["weight"] == FLOOR)) > len(table) // 2:
|
||||
sys.exit(f"{floored} of {len(table)} zones fell to the floor; GeoNames' population or timezone column has moved")
|
||||
tsv.write(a.out, COLUMNS, table)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,68 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/tld.tsv from the IANA Root Zone Database.
|
||||
|
||||
data-import/tld.py [--source URL_OR_FILE] [--zone URL_OR_FILE] [--cache DIR] [--out FILE]
|
||||
|
||||
A TLD ships when the database names a manager for it; one it marks "Not assigned" is a
|
||||
delegation the root zone no longer holds, and the zone file is fetched to prove the two
|
||||
agree. The key is the A-label the root zone holds; `unicode` is the form the database
|
||||
displays, less the bidi marks it wraps a right-to-left label in, and proved to punycode
|
||||
back to the key.
|
||||
"""
|
||||
import argparse
|
||||
import html
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://www.iana.org/domains/root/db"
|
||||
ZONE = "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "tld.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["tld", "type", "unicode"]
|
||||
TYPES = ("country-code", "generic", "generic-restricted", "infrastructure", "sponsored")
|
||||
BIDI = {0x200E: None, 0x200F: None}
|
||||
ROW = re.compile(r'<a href="/domains/root/db/([^"]+)\.html">([^<]+)</a>.*?</td>\s*<td>([^<]*)</td>\s*<td>(.*?)</td>', re.S)
|
||||
|
||||
|
||||
def rows(page):
|
||||
for a_label, shown, kind, manager in ROW.findall(page):
|
||||
if "Not assigned" in manager:
|
||||
continue
|
||||
kind = kind.strip()
|
||||
if kind not in TYPES:
|
||||
sys.exit(f"{a_label}: the database types it {kind!r}, which is none of {TYPES}")
|
||||
yield {"tld": "." + a_label, "type": kind, "unicode": html.unescape(shown).strip().translate(BIDI)}
|
||||
|
||||
|
||||
def prove_each_unicode_form_is_its_key(table):
|
||||
"""The displayed form is the key's own spelling, which punycode encodes back to."""
|
||||
for r in table:
|
||||
label = r["unicode"][1:]
|
||||
encoded = label if label.isascii() else "xn--" + label.encode("punycode").decode("ascii")
|
||||
if "." + encoded != r["tld"]:
|
||||
sys.exit(f"{r['tld']}: the database displays {r['unicode']!r}, which is no spelling of it")
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--zone", default=ZONE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
table = sorted(rows(source.fetch(a.source, a.cache, "root-db.html").decode("utf-8")), key=lambda r: r["tld"])
|
||||
prove_each_unicode_form_is_its_key(table)
|
||||
listing = source.fetch(a.zone, a.cache, "tlds-alpha-by-domain.txt").decode("utf-8")
|
||||
delegated = {"." + line.strip().lower() for line in listing.splitlines() if line.strip() and not line.startswith("#")}
|
||||
shipped = {r["tld"] for r in table}
|
||||
if len(table) != len(delegated) or shipped != delegated:
|
||||
sys.exit(f"the database assigns {len(table)} rows where the root zone holds {len(delegated)} TLDs: {sorted(shipped ^ delegated)[:10]}")
|
||||
tsv.write(a.out, COLUMNS, table)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,18 +0,0 @@
|
||||
"""A table written as the loader admits it."""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def write(path, columns, rows):
|
||||
"""Write the rows as a TSV; the table needs two rows, and every cell must be non-empty and free of tabs, newlines and braces."""
|
||||
lines = ["\t".join(columns)]
|
||||
for row in rows:
|
||||
cells = [str(row[c]) for c in columns]
|
||||
if not all(cells) or any(re.search(r"[\t\n{}]", c) for c in cells):
|
||||
raise ValueError(f"{path}: a cell is empty or holds a tab, newline or brace: {row}")
|
||||
lines.append("\t".join(cells))
|
||||
if len(lines) < 3:
|
||||
raise ValueError(f"{path}: has {len(lines) - 1} rows below its header; a table is at least two rows")
|
||||
Path(path).write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||
print(f"{path}: {len(lines) - 1} rows", file=sys.stderr)
|
||||
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Rebuild data/misc/useragent.tsv from microlinkhq/top-user-agents (MIT).
|
||||
|
||||
data-import/useragent.py [--source URL] [--cache DIR] [--out FILE]
|
||||
|
||||
The desktop and mobile lists partition the whole, so `device` is read rather than
|
||||
parsed. A row ships when the string names a browser and an operating system both.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import source
|
||||
import tsv
|
||||
|
||||
SOURCE = "https://raw.githubusercontent.com/microlinkhq/top-user-agents/master/src"
|
||||
OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "useragent.tsv"
|
||||
CACHE = Path(__file__).resolve().parent / "cache"
|
||||
COLUMNS = ["browser", "device", "os", "ua"]
|
||||
# First match wins: a Chromium fork this list does not name ships as Chrome.
|
||||
BROWSERS = [("Edge", r"Edg(A|iOS)?/"), ("Opera", r"OPR/"), ("Samsung Internet", r"SamsungBrowser/"),
|
||||
("Chrome", r"(Chrome|CriOS)/"), ("Firefox", r"(Firefox|FxiOS)/"), ("Safari", r"Version/[\d.]+ .*Safari")]
|
||||
SYSTEMS = [("iOS", r"iPhone|iPad|CPU OS "), ("Android", r"Android"), ("ChromeOS", r"CrOS"),
|
||||
("Windows", r"Windows NT"), ("macOS", r"Macintosh|Mac OS X"), ("Linux", r"X11.*Linux|Ubuntu")]
|
||||
|
||||
|
||||
def named(table, ua):
|
||||
for name, pattern in table:
|
||||
if re.search(pattern, ua):
|
||||
return name
|
||||
return ""
|
||||
|
||||
|
||||
def rows(desktop, mobile):
|
||||
seen, kept = set(), []
|
||||
for device, uas in (("desktop", desktop), ("mobile", mobile)):
|
||||
for ua in uas:
|
||||
browser, os = named(BROWSERS, ua), named(SYSTEMS, ua)
|
||||
if not browser or not os:
|
||||
print(f"dropped, naming no {'browser' if not browser else 'operating system'}: {ua}", file=sys.stderr)
|
||||
continue
|
||||
if ua in seen:
|
||||
sys.exit(f"{ua}: listed twice, which would draw it twice as often")
|
||||
seen.add(ua)
|
||||
kept.append({"browser": browser, "device": device, "os": os, "ua": ua})
|
||||
if len(kept) < 0.8 * (len(desktop) + len(mobile)):
|
||||
sys.exit(f"only {len(kept)} of {len(desktop) + len(mobile)} strings named both; the tokens have moved")
|
||||
return kept
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
||||
p.add_argument("--cache", default=str(CACHE))
|
||||
p.add_argument("--source", default=SOURCE)
|
||||
p.add_argument("--out", default=str(OUT))
|
||||
a = p.parse_args()
|
||||
lists = [json.loads(source.fetch(f"{a.source}/{n}.json", a.cache, f"top-user-agents-{n}.json")) for n in ("desktop", "mobile")]
|
||||
tsv.write(a.out, COLUMNS, sorted(rows(*lists), key=lambda r: (r["browser"], r["os"], r["ua"])))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,21 +0,0 @@
|
||||
"""The rows of an xlsx sheet."""
|
||||
import io
|
||||
import xml.etree.ElementTree as ET
|
||||
import zipfile
|
||||
|
||||
NS = {"m": "http://schemas.openxmlformats.org/spreadsheetml/2006/main", "r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships"}
|
||||
|
||||
|
||||
def rows(data, sheet=None):
|
||||
"""The rows of an xlsx sheet named sheet, the first sheet by default, each a list of cell texts."""
|
||||
z = zipfile.ZipFile(io.BytesIO(data))
|
||||
strings = ["".join(t.text or "" for t in si.iter("{%s}t" % NS["m"])) for si in ET.fromstring(z.read("xl/sharedStrings.xml")).findall("m:si", NS)]
|
||||
rels = {r.get("Id"): r.get("Target") for r in ET.fromstring(z.read("xl/_rels/workbook.xml.rels"))}
|
||||
sheets = {s.get("name"): rels[s.get("{%s}id" % NS["r"])] for s in ET.fromstring(z.read("xl/workbook.xml")).iter("{%s}sheet" % NS["m"])}
|
||||
target = sheets[sheet] if sheet else next(iter(sheets.values()))
|
||||
for row in ET.fromstring(z.read("xl/" + target)).findall(".//m:row", NS):
|
||||
cells = []
|
||||
for c in row.findall("m:c", NS):
|
||||
v = c.find("m:v", NS)
|
||||
cells.append("" if v is None else strings[int(v.text)] if c.get("t") == "s" else v.text)
|
||||
yield cells
|
||||
@@ -10,14 +10,13 @@ import (
|
||||
)
|
||||
|
||||
// dataSource is one tree to load: an fs.FS and the directory in it to start from.
|
||||
// label prefixes file names in errors; onDisk marks path as a directory that must
|
||||
// exist.
|
||||
// label prefixes file names in errors; path, when set, is a directory on disk that
|
||||
// must exist.
|
||||
type dataSource struct {
|
||||
fsys fs.FS
|
||||
label string
|
||||
onDisk bool
|
||||
path string
|
||||
root string
|
||||
fsys fs.FS
|
||||
label string
|
||||
path string
|
||||
root string
|
||||
}
|
||||
|
||||
func (s dataSource) name(p string) string {
|
||||
@@ -28,22 +27,19 @@ func (s dataSource) name(p string) string {
|
||||
}
|
||||
|
||||
// loadData loads every source into one namespace tree and returns its root
|
||||
// children. A directory becomes a folder; each *.json file in it compiles to a node
|
||||
// children. A directory becomes a group; each *.json file in it compiles to a node
|
||||
// keyed by its base name (address.json -> "address"); each subdirectory becomes a
|
||||
// nested folder, so folders turn into dot-path segments. Sources merge left to right:
|
||||
// matching folders merge by their children, and any other clash is won by the last
|
||||
// nested group, so folders turn into dot-path segments. Sources merge left to right:
|
||||
// matching groups merge by their children, and any other clash is won by the last
|
||||
// source loaded. Once merged, linkRefs binds every reference against the
|
||||
// final tree.
|
||||
func loadData(sources []dataSource) (map[string]node, error) {
|
||||
root := map[string]node{}
|
||||
for _, src := range sources {
|
||||
if src.onDisk {
|
||||
if src.path == "" {
|
||||
return nil, fmt.Errorf("a data path is empty")
|
||||
}
|
||||
if src.path != "" {
|
||||
info, err := os.Stat(src.path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("data path %s: %w", src.path, err)
|
||||
return nil, fmt.Errorf("%s: no such directory", src.path)
|
||||
}
|
||||
if !info.IsDir() {
|
||||
return nil, fmt.Errorf("%s is not a directory", src.path)
|
||||
@@ -62,83 +58,48 @@ func loadData(sources []dataSource) (map[string]node, error) {
|
||||
if len(root) == 0 {
|
||||
return nil, fmt.Errorf("no .json data found")
|
||||
}
|
||||
setTablePaths(root)
|
||||
if err := linkTables(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := linkRefs(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := checkNoCycles(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := checkScope(treeScope(root)); err != nil {
|
||||
if err := checkRepeatReach(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := checkBoundLevelsHeld(root); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return root, nil
|
||||
}
|
||||
|
||||
// loadDir compiles one directory into a folder. fs.ReadDir yields entries sorted
|
||||
// loadDir compiles one directory into a group. fs.ReadDir yields entries sorted
|
||||
// by name, so the tree is built deterministically. Empty subdirectories (no JSON
|
||||
// anywhere under them) are skipped rather than added as empty namespaces.
|
||||
func loadDir(src dataSource, dir string) (*folder, error) {
|
||||
func loadDir(src dataSource, dir string) (*group, error) {
|
||||
entries, err := fs.ReadDir(src.fsys, dir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", src.name(dir), err)
|
||||
}
|
||||
g := &folder{children: map[string]node{}}
|
||||
files := &categoryFiles{src: src, dir: dir, tsv: map[string]bool{}}
|
||||
for _, e := range entries {
|
||||
if strings.HasSuffix(e.Name(), ".tsv") && !e.IsDir() {
|
||||
files.tsv[e.Name()] = false
|
||||
}
|
||||
}
|
||||
g := &group{children: map[string]node{}}
|
||||
for _, e := range entries {
|
||||
if strings.HasPrefix(e.Name(), ".") { // hidden: a checkout or an editor's file, never data
|
||||
continue
|
||||
}
|
||||
full := path.Join(dir, e.Name())
|
||||
load := loadFile
|
||||
if e.IsDir() {
|
||||
if err := loadFolder(src, g, full, e.Name()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
load = loadFolder
|
||||
}
|
||||
if err := loadFile(src, g, full, e.Name(), files); err != nil {
|
||||
if err := load(src, g, full, e.Name()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for name, named := range files.tsv {
|
||||
if !named && !strings.HasPrefix(name, ".") {
|
||||
return nil, fmt.Errorf("%s: no category names it in its rows; a table's rows file sits beside a category file naming it", src.name(path.Join(dir, name)))
|
||||
}
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
// categoryFiles is what a category may name beside itself: the rows files of its
|
||||
// directory, each marked once a category names it.
|
||||
type categoryFiles struct {
|
||||
src dataSource
|
||||
dir string
|
||||
tsv map[string]bool
|
||||
}
|
||||
|
||||
// rows reads the rows file a category names beside it.
|
||||
func (c *categoryFiles) rows(name string) (string, error) {
|
||||
if _, present := c.tsv[name]; !present {
|
||||
return "", fmt.Errorf("rows names %s, which is not beside it in %s", name, c.src.name(c.dir))
|
||||
}
|
||||
c.tsv[name] = true
|
||||
b, err := fs.ReadFile(c.src.fsys, path.Join(c.dir, name))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%s: %w", c.src.name(path.Join(c.dir, name)), err)
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// loadFolder adds a subdirectory as a nested folder, unless nothing under it is data.
|
||||
func loadFolder(src dataSource, g *folder, full, name string) error {
|
||||
// loadFolder adds a subdirectory as a nested group, unless nothing under it is data.
|
||||
func loadFolder(src dataSource, g *group, full, name string) error {
|
||||
child, err := loadDir(src, full)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -155,7 +116,7 @@ func loadFolder(src dataSource, g *folder, full, name string) error {
|
||||
|
||||
// loadFile compiles a *.json file into a category named after it; any other file
|
||||
// is skipped.
|
||||
func loadFile(src dataSource, g *folder, full, file string, files *categoryFiles) error {
|
||||
func loadFile(src dataSource, g *group, full, file string) error {
|
||||
if !strings.HasSuffix(file, ".json") {
|
||||
return nil
|
||||
}
|
||||
@@ -171,7 +132,7 @@ func loadFile(src dataSource, g *folder, full, file string, files *categoryFiles
|
||||
if err := json.Unmarshal(b, &raw); err != nil {
|
||||
return fmt.Errorf("%s: %w", src.name(full), err)
|
||||
}
|
||||
n, err := compileCategory(raw, name, files)
|
||||
n, err := compile(raw)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s: %w", src.name(full), err)
|
||||
}
|
||||
@@ -179,13 +140,13 @@ func loadFile(src dataSource, g *folder, full, file string, files *categoryFiles
|
||||
return nil
|
||||
}
|
||||
|
||||
// mergeChildren overlays src onto dst. Two folders under the same key merge
|
||||
// mergeChildren overlays src onto dst. Two groups under the same key merge
|
||||
// recursively (so locales/categories from several paths combine); every other
|
||||
// key is replaced, making the last-loaded directory win on a conflict.
|
||||
func mergeChildren(dst, src map[string]node) {
|
||||
for k, v := range src {
|
||||
if dg, ok := dst[k].(*folder); ok {
|
||||
if sg, ok := v.(*folder); ok {
|
||||
if dg, ok := dst[k].(*group); ok {
|
||||
if sg, ok := v.(*group); ok {
|
||||
mergeChildren(dg.children, sg.children)
|
||||
continue
|
||||
}
|
||||
|
||||
+14
-5
@@ -1,8 +1,17 @@
|
||||
{
|
||||
"format": "{street-number} {street}\n{locality}, {region} {postal-code}",
|
||||
"street": "{/geo.US.address.street}",
|
||||
"street-number": "{/geo.US.address.street-number}",
|
||||
"locality": "{/geo.US.address.locality}",
|
||||
"region": "{/geo.US.address.region}",
|
||||
"postal-code": "{/geo.US.address.postal-code}"
|
||||
"street": {
|
||||
"format": "{name} {suffix}",
|
||||
"name": ["Adams", "Ashby", "Aspen", "Bay", "Birch", "Bridge", "Cedar", "Chestnut", "Church", "Clark", "Cypress", "Dogwood", "Elm", "Forest", "Franklin", "Garden", "Grove", "Hawthorn", "Hickory", "Highland", "Jackson", "Jefferson", "Juniper", "Lake", "Laurel", "Liberty", "Lincoln", "Madison", "Magnolia", "Maple", "Market", "Meadow", "Mill", "Oak", "Park", "Pine", "Poplar", "Prospect", "Ridge", "River", "Spruce", "Sunset", "Sycamore", "Union", "Walnut", "Washington", "Willow", "Wilson"],
|
||||
"suffix": ["Avenue", "Boulevard", "Circle", "Court", "Drive", "Lane", "Place", "Road", "Street", "Terrace", "Trail", "Way"]
|
||||
},
|
||||
"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": ["Albany", "Atlanta", "Austin", "Baltimore", "Boston", "Charlotte", "Chicago", "Cincinnati", "Cleveland", "Columbus", "Dallas", "Denver", "Detroit", "El Paso", "Fort Worth", "Fresno", "Houston", "Indianapolis", "Jacksonville", "Kansas City", "Las Vegas", "Long Beach", "Los Angeles", "Memphis", "Mesa", "Miami", "Milwaukee", "Minneapolis", "Nashville", "New Orleans", "Oakland", "Oklahoma City", "Omaha", "Orlando", "Philadelphia", "Phoenix", "Pittsburgh", "Portland", "Raleigh", "Sacramento", "San Antonio", "San Diego", "San Jose", "Seattle", "St. Louis", "Tampa", "Tucson", "Tulsa"],
|
||||
"region": ["AL", "AZ", "CA", "CO", "CT", "FL", "GA", "IL", "IN", "KY", "LA", "MA", "MD", "MI", "MN", "MO", "NC", "NJ", "NV", "NY", "OH", "OK", "OR", "PA", "TN", "TX", "VA", "WA", "WI"],
|
||||
"postal-code": ["{int(10000,99999)}", { "format": "{int(10000,99999)}-{digits(4)}", "weight": 0.3 }]
|
||||
}
|
||||
|
||||
+13
-1
@@ -1 +1,13 @@
|
||||
"{date(1970-01-01,2029-12-31,'01/02/2006')}"
|
||||
{
|
||||
"format": "{month}/{day}/{year}",
|
||||
"month": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"],
|
||||
"day": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28"],
|
||||
"year": [
|
||||
"197{digits(1)}",
|
||||
"198{digits(1)}",
|
||||
{ "format": "199{digits(1)}", "weight": 2 },
|
||||
{ "format": "200{digits(1)}", "weight": 3 },
|
||||
{ "format": "201{digits(1)}", "weight": 3 },
|
||||
{ "format": "202{digits(1)}", "weight": 2 }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "first-name.tsv", "name": "name", "parent": "sex", "weight": "count" }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"format": "{area}-{group}-{serial}",
|
||||
"area": "9{digits(2)}",
|
||||
"group": [
|
||||
{ "format": "{int(50,65)}", "weight": 16 },
|
||||
{ "format": "{int(70,88)}", "weight": 19 },
|
||||
{ "format": "{int(90,92)}", "weight": 3 },
|
||||
{ "format": "{int(94,99)}", "weight": 6 }
|
||||
],
|
||||
"serial": [
|
||||
{ "format": "000{int(1,9)}", "weight": 9 },
|
||||
{ "format": "00{int(10,99)}", "weight": 90 },
|
||||
{ "format": "0{int(100,999)}", "weight": 900 },
|
||||
{ "format": "{int(1000,9999)}", "weight": 9000 }
|
||||
]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "last-name.tsv", "key": "name", "weight": "count" }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"format": "{prefix}{first} {last}",
|
||||
"first": "{.first-name.name}",
|
||||
"last": "{.last-name.name}",
|
||||
"prefix": [null, { "format": "{.title.name} ", "weight": 0.1 }],
|
||||
"sex": "{.sex.name}"
|
||||
"format": "{prefix}{femalefirst|malefirst} {last}",
|
||||
"femalefirst": ["Abigail", "Addison", "Amelia", "Aria", "Aubrey", "Audrey", "Aurora", "Ava", "Bella", "Brooklyn", "Camila", "Caroline", "Charlotte", "Chloe", "Claire", "Eleanor", "Elizabeth", "Ella", "Ellie", "Emily", "Emma", "Evelyn", "Gianna", "Grace", "Hannah", "Harper", "Hazel", "Isabella", "Layla", "Leah", "Lillian", "Lily", "Lucy", "Luna", "Madison", "Maya", "Mia", "Mila", "Naomi", "Natalie", "Nora", "Olivia", "Paisley", "Penelope", "Riley", "Savannah", "Scarlett", "Sofia", "Sophia", "Stella", "Victoria", "Violet", "Zoe"],
|
||||
"malefirst": ["Aiden", "Alexander", "Andrew", "Anthony", "Asher", "Benjamin", "Caleb", "Carter", "Charles", "Christopher", "Daniel", "David", "Dylan", "Elijah", "Ethan", "Ezra", "Gabriel", "Grayson", "Henry", "Isaac", "Jack", "Jackson", "Jacob", "James", "Jayden", "John", "Joseph", "Joshua", "Julian", "Levi", "Liam", "Lincoln", "Logan", "Lucas", "Luke", "Mason", "Mateo", "Matthew", "Michael", "Nathan", "Noah", "Oliver", "Owen", "Samuel", "Sebastian", "Theodore", "Thomas", "William", "Wyatt"],
|
||||
"last": ["Adams", "Allen", "Anderson", "Bailey", "Baker", "Bell", "Bennett", "Brooks", "Brown", "Campbell", "Carter", "Clark", "Collins", "Cook", "Cooper", "Cox", "Davis", "Edwards", "Evans", "Flores", "Foster", "Garcia", "Gonzalez", "Gray", "Green", "Hall", "Harris", "Hayes", "Hernandez", "Hill", "Howard", "Hughes", "Jackson", "James", "Jenkins", "Johnson", "Jones", "Kelly", "King", "Lee", "Lewis", "Long", "Lopez", "Martin", "Martinez", "Miller", "Mitchell", "Moore", "Morgan", "Morris", "Murphy", "Nelson", "Parker", "Perez", "Perry", "Peterson", "Phillips", "Powell", "Price", "Ramirez", "Reed", "Richardson", "Rivera", "Roberts", "Robinson", "Rodriguez", "Rogers", "Ross", "Russell", "Sanchez", "Sanders", "Scott", "Smith", "Stewart", "Sullivan", "Taylor", "Thomas", "Thompson", "Torres", "Turner", "Walker", "Ward", "Watson", "White", "Williams", "Wilson", "Wood", "Wright", "Young"],
|
||||
"prefix": ["", { "format": "{title} ", "title": ["Dr", "Miss", "Mr", "Mrs", "Ms", "Mx", "Prof"], "weight": 0.1 }]
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "sex.tsv", "key": "code", "name": "name" }
|
||||
@@ -1,3 +0,0 @@
|
||||
code name
|
||||
f female
|
||||
m male
|
||||
|
+1
-19
@@ -1,19 +1 @@
|
||||
{
|
||||
"format": "{area}-{group}-{serial}",
|
||||
"area": [
|
||||
{ "format": "00{int(1,9)}", "weight": 9 },
|
||||
{ "format": "0{int(10,99)}", "weight": 90 },
|
||||
{ "format": "{int(100,665)}", "weight": 566 },
|
||||
{ "format": "{int(667,899)}", "weight": 233 }
|
||||
],
|
||||
"group": [
|
||||
{ "format": "0{int(1,9)}", "weight": 9 },
|
||||
{ "format": "{int(10,99)}", "weight": 90 }
|
||||
],
|
||||
"serial": [
|
||||
{ "format": "000{int(1,9)}", "weight": 9 },
|
||||
{ "format": "00{int(10,99)}", "weight": 90 },
|
||||
{ "format": "0{int(100,999)}", "weight": 900 },
|
||||
{ "format": "{int(1000,9999)}", "weight": 9000 }
|
||||
]
|
||||
}
|
||||
"{int(100,999)}-{digits(2)}-{digits(4)}"
|
||||
|
||||
@@ -1 +1,6 @@
|
||||
"{time('3:04 PM')}"
|
||||
{
|
||||
"format": "{hour}:{minute} {ampm}",
|
||||
"hour": ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"],
|
||||
"minute": { "format": "{t}{digits(1)}", "t": ["0", "1", "2", "3", "4", "5"] },
|
||||
"ampm": ["AM", "PM"]
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "title.tsv", "name": "name", "parent": "sex", "weight": "share" }
|
||||
@@ -1,11 +0,0 @@
|
||||
name sex share
|
||||
Dr f 6
|
||||
Dr m 6
|
||||
Miss f 4
|
||||
Mr m 60
|
||||
Mrs f 22
|
||||
Ms f 30
|
||||
Mx f 1
|
||||
Mx m 1
|
||||
Prof f 3
|
||||
Prof m 3
|
||||
|
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"format": "{street} {street-number}\n{postal-code} {locality}",
|
||||
"street": "{.street.name}",
|
||||
"street-number": [
|
||||
"{int(1,9)}",
|
||||
"{int(10,99)}",
|
||||
{ "format": "{int(100,999)}", "weight": 0.2 },
|
||||
{ "format": "{int(1,9)}{upper(1)}", "weight": 0.2 },
|
||||
{ "format": "{int(10,99)}{upper(1)}", "weight": 0.1 },
|
||||
{ "format": "{int(100,999)}{upper(1)}", "weight": 0.05 }
|
||||
],
|
||||
"postal-code": "{.postal-code.code}",
|
||||
"locality": "{.locality.name}"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "locality.tsv", "key": "name", "parent": "municipality", "weight": "population" }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "municipality.tsv", "key": "code", "name": "name", "parent": "region", "weight": "population" }
|
||||
@@ -1,291 +0,0 @@
|
||||
code name region population
|
||||
0114 Upplands Väsby 01 50323
|
||||
0115 Vallentuna 01 35119
|
||||
0117 Österåker 01 49787
|
||||
0120 Värmdö 01 46635
|
||||
0123 Järfälla 01 88950
|
||||
0125 Ekerö 01 28910
|
||||
0126 Huddinge 01 114304
|
||||
0127 Botkyrka 01 95905
|
||||
0128 Salem 01 17507
|
||||
0136 Haninge 01 100895
|
||||
0138 Tyresö 01 49179
|
||||
0139 Upplands-Bro 01 32868
|
||||
0140 Nykvarn 01 12342
|
||||
0160 Täby 01 77744
|
||||
0162 Danderyd 01 32425
|
||||
0163 Sollentuna 01 77624
|
||||
0180 Stockholm 01 995574
|
||||
0181 Södertälje 01 102911
|
||||
0182 Nacka 01 112112
|
||||
0183 Sundbyberg 01 56274
|
||||
0184 Solna 01 85789
|
||||
0186 Lidingö 01 48377
|
||||
0187 Vaxholm 01 11822
|
||||
0188 Norrtälje 01 66585
|
||||
0191 Sigtuna 01 52767
|
||||
0192 Nynäshamn 01 30579
|
||||
0305 Håbo 03 22973
|
||||
0319 Älvkarleby 03 9552
|
||||
0330 Knivsta 03 21193
|
||||
0331 Heby 03 14345
|
||||
0360 Tierp 03 21104
|
||||
0380 Uppsala 03 248016
|
||||
0381 Enköping 03 48591
|
||||
0382 Östhammar 03 22138
|
||||
0428 Vingåker 04 8750
|
||||
0461 Gnesta 04 11458
|
||||
0480 Nyköping 04 58344
|
||||
0481 Oxelösund 04 12031
|
||||
0482 Flen 04 15362
|
||||
0483 Katrineholm 04 34154
|
||||
0484 Eskilstuna 04 107203
|
||||
0486 Strängnäs 04 39313
|
||||
0488 Trosa 04 14927
|
||||
0509 Ödeshög 05 5237
|
||||
0512 Ydre 05 3626
|
||||
0513 Kinda 05 9957
|
||||
0560 Boxholm 05 5517
|
||||
0561 Åtvidaberg 05 11467
|
||||
0562 Finspång 05 21623
|
||||
0563 Valdemarsvik 05 7525
|
||||
0580 Linköping 05 168035
|
||||
0581 Norrköping 05 144980
|
||||
0582 Söderköping 05 14789
|
||||
0583 Motala 05 43505
|
||||
0584 Vadstena 05 7490
|
||||
0586 Mjölby 05 28695
|
||||
0604 Aneby 06 6797
|
||||
0617 Gnosjö 06 9131
|
||||
0642 Mullsjö 06 7594
|
||||
0643 Habo 06 13456
|
||||
0662 Gislaved 06 28936
|
||||
0665 Vaggeryd 06 14825
|
||||
0680 Jönköping 06 147654
|
||||
0682 Nässjö 06 31587
|
||||
0683 Värnamo 06 34542
|
||||
0684 Sävsjö 06 11563
|
||||
0685 Vetlanda 06 27528
|
||||
0686 Eksjö 06 17792
|
||||
0687 Tranås 06 18604
|
||||
0760 Uppvidinge 07 9061
|
||||
0761 Lessebo 07 8289
|
||||
0763 Tingsryd 07 11966
|
||||
0764 Alvesta 07 19830
|
||||
0765 Älmhult 07 17653
|
||||
0767 Markaryd 07 9938
|
||||
0780 Växjö 07 98334
|
||||
0781 Ljungby 07 28280
|
||||
0821 Högsby 08 5321
|
||||
0834 Torsås 08 6984
|
||||
0840 Mörbylånga 08 16224
|
||||
0860 Hultsfred 08 13673
|
||||
0861 Mönsterås 08 13069
|
||||
0862 Emmaboda 08 9006
|
||||
0880 Kalmar 08 72704
|
||||
0881 Nybro 08 19951
|
||||
0882 Oskarshamn 08 26923
|
||||
0883 Västervik 08 36447
|
||||
0884 Vimmerby 08 15384
|
||||
0885 Borgholm 08 10666
|
||||
0980 Gotland 09 60971
|
||||
1060 Olofström 10 13000
|
||||
1080 Karlskrona 10 66301
|
||||
1081 Ronneby 10 28741
|
||||
1082 Karlshamn 10 31751
|
||||
1083 Sölvesborg 10 17430
|
||||
1214 Svalöv 12 14543
|
||||
1230 Staffanstorp 12 27303
|
||||
1231 Burlöv 12 20101
|
||||
1233 Vellinge 12 37816
|
||||
1256 Östra Göinge 12 13978
|
||||
1257 Örkelljunga 12 10277
|
||||
1260 Bjuv 12 15985
|
||||
1261 Kävlinge 12 32477
|
||||
1262 Lomma 12 24715
|
||||
1263 Svedala 12 23581
|
||||
1264 Skurup 12 17099
|
||||
1265 Sjöbo 12 19337
|
||||
1266 Hörby 12 15562
|
||||
1267 Höör 12 17518
|
||||
1270 Tomelilla 12 13639
|
||||
1272 Bromölla 12 12470
|
||||
1273 Osby 12 12947
|
||||
1275 Perstorp 12 7235
|
||||
1276 Klippan 12 17714
|
||||
1277 Åstorp 12 16449
|
||||
1278 Båstad 12 16026
|
||||
1280 Malmö 12 365644
|
||||
1281 Lund 12 131590
|
||||
1282 Landskrona 12 47309
|
||||
1283 Helsingborg 12 152091
|
||||
1284 Höganäs 12 28430
|
||||
1285 Eslöv 12 34922
|
||||
1286 Ystad 12 32106
|
||||
1287 Trelleborg 12 47269
|
||||
1290 Kristianstad 12 86379
|
||||
1291 Simrishamn 12 18890
|
||||
1292 Ängelholm 12 45110
|
||||
1293 Hässleholm 12 52114
|
||||
1315 Hylte 13 10196
|
||||
1380 Halmstad 13 106084
|
||||
1381 Laholm 13 26595
|
||||
1382 Falkenberg 13 47337
|
||||
1383 Varberg 13 69070
|
||||
1384 Kungsbacka 13 85792
|
||||
1401 Härryda 14 40003
|
||||
1402 Partille 14 41060
|
||||
1407 Öckerö 14 12771
|
||||
1415 Stenungsund 14 27851
|
||||
1419 Tjörn 14 16092
|
||||
1421 Orust 14 15352
|
||||
1427 Sotenäs 14 9104
|
||||
1430 Munkedal 14 10354
|
||||
1435 Tanum 14 12773
|
||||
1438 Dals-Ed 14 4606
|
||||
1439 Färgelanda 14 6376
|
||||
1440 Ale 14 32576
|
||||
1441 Lerum 14 43570
|
||||
1442 Vårgårda 14 12474
|
||||
1443 Bollebygd 14 9802
|
||||
1444 Grästorp 14 5555
|
||||
1445 Essunga 14 5560
|
||||
1446 Karlsborg 14 7023
|
||||
1447 Gullspång 14 5031
|
||||
1452 Tranemo 14 11839
|
||||
1460 Bengtsfors 14 9076
|
||||
1461 Mellerud 14 9052
|
||||
1462 Lilla Edet 14 14442
|
||||
1463 Mark 14 35155
|
||||
1465 Svenljunga 14 10747
|
||||
1466 Herrljunga 14 9497
|
||||
1470 Vara 14 16088
|
||||
1471 Götene 14 13286
|
||||
1472 Tibro 14 11338
|
||||
1473 Töreboda 14 9043
|
||||
1480 Göteborg 14 608993
|
||||
1481 Mölndal 14 71420
|
||||
1482 Kungälv 14 50313
|
||||
1484 Lysekil 14 13907
|
||||
1485 Uddevalla 14 57010
|
||||
1486 Strömstad 14 13482
|
||||
1487 Vänersborg 14 40041
|
||||
1488 Trollhättan 14 59003
|
||||
1489 Alingsås 14 42722
|
||||
1490 Borås 14 114872
|
||||
1491 Ulricehamn 14 24985
|
||||
1492 Åmål 14 11906
|
||||
1493 Mariestad 14 24583
|
||||
1494 Lidköping 14 40425
|
||||
1495 Skara 14 18707
|
||||
1496 Skövde 14 57995
|
||||
1497 Hjo 14 9350
|
||||
1498 Tidaholm 14 12805
|
||||
1499 Falköping 14 32806
|
||||
1715 Kil 17 12061
|
||||
1730 Eda 17 8412
|
||||
1737 Torsby 17 11321
|
||||
1760 Storfors 17 3788
|
||||
1761 Hammarö 17 16992
|
||||
1762 Munkfors 17 3625
|
||||
1763 Forshaga 17 11520
|
||||
1764 Grums 17 9004
|
||||
1765 Årjäng 17 9825
|
||||
1766 Sunne 17 13356
|
||||
1780 Karlstad 17 98084
|
||||
1781 Kristinehamn 17 23756
|
||||
1782 Filipstad 17 9776
|
||||
1783 Hagfors 17 11418
|
||||
1784 Arvika 17 25547
|
||||
1785 Säffle 17 14899
|
||||
1814 Lekeberg 18 8606
|
||||
1860 Laxå 18 5423
|
||||
1861 Hallsberg 18 16120
|
||||
1862 Degerfors 18 9278
|
||||
1863 Hällefors 18 6321
|
||||
1864 Ljusnarsberg 18 4369
|
||||
1880 Örebro 18 160140
|
||||
1881 Kumla 18 22681
|
||||
1882 Askersund 18 11477
|
||||
1883 Karlskoga 18 30180
|
||||
1884 Nora 18 10639
|
||||
1885 Lindesberg 18 23141
|
||||
1904 Skinnskatteberg 19 4256
|
||||
1907 Surahammar 19 9845
|
||||
1960 Kungsör 19 8694
|
||||
1961 Hallstahammar 19 16653
|
||||
1962 Norberg 19 5452
|
||||
1980 Västerås 19 160634
|
||||
1981 Sala 19 22843
|
||||
1982 Fagersta 19 13072
|
||||
1983 Köping 19 25729
|
||||
1984 Arboga 19 13980
|
||||
2021 Vansbro 20 6752
|
||||
2023 Malung-Sälen 20 10254
|
||||
2026 Gagnef 20 10384
|
||||
2029 Leksand 20 16137
|
||||
2031 Rättvik 20 10998
|
||||
2034 Orsa 20 6851
|
||||
2039 Älvdalen 20 6882
|
||||
2061 Smedjebacken 20 10823
|
||||
2062 Mora 20 20540
|
||||
2080 Falun 20 59945
|
||||
2081 Borlänge 20 51425
|
||||
2082 Säter 20 11223
|
||||
2083 Hedemora 20 15281
|
||||
2084 Avesta 20 22417
|
||||
2085 Ludvika 20 26634
|
||||
2101 Ockelbo 21 5715
|
||||
2104 Hofors 21 9281
|
||||
2121 Ovanåker 21 11341
|
||||
2132 Nordanstig 21 9262
|
||||
2161 Ljusdal 21 18445
|
||||
2180 Gävle 21 103838
|
||||
2181 Sandviken 21 38360
|
||||
2182 Söderhamn 21 24545
|
||||
2183 Bollnäs 21 26243
|
||||
2184 Hudiksvall 21 37528
|
||||
2260 Ånge 22 9044
|
||||
2262 Timrå 22 17521
|
||||
2280 Härnösand 22 24515
|
||||
2281 Sundsvall 22 99048
|
||||
2282 Kramfors 22 17491
|
||||
2283 Sollefteå 22 18396
|
||||
2284 Örnsköldsvik 22 55443
|
||||
2303 Ragunda 23 5146
|
||||
2305 Bräcke 23 6035
|
||||
2309 Krokom 23 15680
|
||||
2313 Strömsund 23 11023
|
||||
2321 Åre 23 12693
|
||||
2326 Berg 23 7108
|
||||
2361 Härjedalen 23 10175
|
||||
2380 Östersund 23 64979
|
||||
2401 Nordmaling 24 6942
|
||||
2403 Bjurholm 24 2359
|
||||
2404 Vindeln 24 5417
|
||||
2409 Robertsfors 24 6690
|
||||
2417 Norsjö 24 3968
|
||||
2418 Malå 24 2962
|
||||
2421 Storuman 24 5577
|
||||
2422 Sorsele 24 2357
|
||||
2425 Dorotea 24 2294
|
||||
2460 Vännäs 24 9132
|
||||
2462 Vilhelmina 24 6229
|
||||
2463 Åsele 24 2694
|
||||
2480 Umeå 24 134249
|
||||
2481 Lycksele 24 12118
|
||||
2482 Skellefteå 24 78150
|
||||
2505 Arvidsjaur 25 6089
|
||||
2506 Arjeplog 25 2599
|
||||
2510 Jokkmokk 25 4701
|
||||
2513 Överkalix 25 3201
|
||||
2514 Kalix 25 15391
|
||||
2518 Övertorneå 25 4057
|
||||
2521 Pajala 25 5857
|
||||
2523 Gällivare 25 17233
|
||||
2560 Älvsbyn 25 7774
|
||||
2580 Luleå 25 79645
|
||||
2581 Piteå 25 42447
|
||||
2582 Boden 25 28049
|
||||
2583 Haparanda 25 9151
|
||||
2584 Kiruna 25 22426
|
||||
|
@@ -1 +0,0 @@
|
||||
{ "format": "{code}", "rows": "postal-code.tsv", "key": "code", "parent": "locality" }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "region.tsv", "key": "code", "name": "name", "weight": "population" }
|
||||
@@ -1,22 +0,0 @@
|
||||
code name population timezone
|
||||
01 Stockholms län 2473307 Europe/Stockholm
|
||||
03 Uppsala län 407912 Europe/Stockholm
|
||||
04 Södermanlands län 301542 Europe/Stockholm
|
||||
05 Östergötlands län 472446 Europe/Stockholm
|
||||
06 Jönköpings län 370009 Europe/Stockholm
|
||||
07 Kronobergs län 203351 Europe/Stockholm
|
||||
08 Kalmar län 246352 Europe/Stockholm
|
||||
09 Gotlands län 60971 Europe/Stockholm
|
||||
10 Blekinge län 157223 Europe/Stockholm
|
||||
12 Skåne län 1428626 Europe/Stockholm
|
||||
13 Hallands län 345074 Europe/Stockholm
|
||||
14 Västra Götalands län 1772821 Europe/Stockholm
|
||||
17 Värmlands län 283384 Europe/Stockholm
|
||||
18 Örebro län 308375 Europe/Stockholm
|
||||
19 Västmanlands län 281158 Europe/Stockholm
|
||||
20 Dalarnas län 286546 Europe/Stockholm
|
||||
21 Gävleborgs län 284558 Europe/Stockholm
|
||||
22 Västernorrlands län 241458 Europe/Stockholm
|
||||
23 Jämtlands län 132839 Europe/Stockholm
|
||||
24 Västerbottens län 281138 Europe/Stockholm
|
||||
25 Norrbottens län 248620 Europe/Stockholm
|
||||
|
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "street.tsv", "parent": "locality", "weight": "segments" }
|
||||
-14765
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"format": "{street-number} {street}\n{locality}, {region} {postal-code}",
|
||||
"street": "{.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": "{.locality.name}",
|
||||
"region": "{.region.abbr}",
|
||||
"postal-code": "{.postal-code.code}"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "locality.tsv", "key": "code", "name": "name", "parent": "municipality", "weight": "population" }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "municipality.tsv", "key": "code", "name": "name", "parent": "region", "weight": "population" }
|
||||
@@ -1,666 +0,0 @@
|
||||
code name region population
|
||||
01001 Autauga County AL 61920
|
||||
01003 Baldwin County AL 267761
|
||||
01031 Coffee County AL 56953
|
||||
01055 Etowah County AL 103886
|
||||
01069 Houston County AL 110318
|
||||
01073 Jefferson County AL 665742
|
||||
01077 Lauderdale County AL 97135
|
||||
01081 Lee County AL 189881
|
||||
01083 Limestone County AL 122928
|
||||
01089 Madison County AL 433516
|
||||
01097 Mobile County AL 411658
|
||||
01101 Montgomery County AL 225891
|
||||
01103 Morgan County AL 126483
|
||||
01113 Russell County AL 58898
|
||||
01117 Shelby County AL 238552
|
||||
01125 Tuscaloosa County AL 241368
|
||||
02020 Anchorage Municipality AK 287155
|
||||
02090 Fairbanks North Star Borough AK 93972
|
||||
02110 Juneau City and Borough AK 31609
|
||||
04003 Cochise County AZ 126332
|
||||
04005 Coconino County AZ 144368
|
||||
04013 Maricopa County AZ 4689558
|
||||
04015 Mohave County AZ 228102
|
||||
04019 Pima County AZ 1074685
|
||||
04021 Pinal County AZ 539380
|
||||
04025 Yavapai County AZ 252552
|
||||
04027 Yuma County AZ 224449
|
||||
05007 Benton County AR 332554
|
||||
05031 Craighead County AR 116957
|
||||
05045 Faulkner County AR 133979
|
||||
05051 Garland County AR 99695
|
||||
05055 Greene County AR 47411
|
||||
05069 Jefferson County AR 62987
|
||||
05085 Lonoke County AR 76664
|
||||
05091 Miller County AR 42357
|
||||
05115 Pope County AR 64976
|
||||
05119 Pulaski County AR 404611
|
||||
05125 Saline County AR 133288
|
||||
05131 Sebastian County AR 130641
|
||||
05143 Washington County AR 271213
|
||||
06001 Alameda County CA 1636630
|
||||
06007 Butte County CA 209211
|
||||
06013 Contra Costa County CA 1170070
|
||||
06019 Fresno County CA 1035456
|
||||
06023 Humboldt County CA 131647
|
||||
06025 Imperial County CA 181411
|
||||
06029 Kern County CA 927068
|
||||
06031 Kings County CA 154327
|
||||
06037 Los Angeles County CA 9694934
|
||||
06039 Madera County CA 167927
|
||||
06041 Marin County CA 253694
|
||||
06047 Merced County CA 297260
|
||||
06053 Monterey County CA 433729
|
||||
06055 Napa County CA 132949
|
||||
06059 Orange County CA 3149507
|
||||
06061 Placer County CA 442081
|
||||
06065 Riverside County CA 2544916
|
||||
06067 Sacramento County CA 1618460
|
||||
06069 San Benito County CA 70082
|
||||
06071 San Bernardino County CA 2224091
|
||||
06073 San Diego County CA 3282248
|
||||
06075 San Francisco County CA 826079
|
||||
06077 San Joaquin County CA 823815
|
||||
06079 San Luis Obispo County CA 282367
|
||||
06081 San Mateo County CA 743568
|
||||
06083 Santa Barbara County CA 442065
|
||||
06085 Santa Clara County CA 1914391
|
||||
06087 Santa Cruz County CA 258852
|
||||
06089 Shasta County CA 181648
|
||||
06095 Solano County CA 455376
|
||||
06097 Sonoma County CA 486444
|
||||
06099 Stanislaus County CA 557719
|
||||
06101 Sutter County CA 98787
|
||||
06107 Tulare County CA 485146
|
||||
06111 Ventura County CA 830851
|
||||
06113 Yolo County CA 224410
|
||||
08001 Adams County CO 554668
|
||||
08005 Arapahoe County CO 673820
|
||||
08013 Boulder County CO 328560
|
||||
08014 Broomfield County CO 79174
|
||||
08031 Denver County CO 740613
|
||||
08035 Douglas County CO 399396
|
||||
08041 El Paso County CO 757040
|
||||
08059 Jefferson County CO 580451
|
||||
08069 Larimer County CO 377292
|
||||
08077 Mesa County CO 162845
|
||||
08101 Pueblo County CO 169277
|
||||
08123 Weld County CO 378426
|
||||
09110 Capitol Planning Region CT 994115
|
||||
09120 Greater Bridgeport Planning Region CT 337697
|
||||
09130 Lower Connecticut River Valley Planning Region CT 177311
|
||||
09140 Naugatuck Valley Planning Region CT 463349
|
||||
09160 Northwest Hills Planning Region CT 114690
|
||||
09170 South Central Connecticut Planning Region CT 578741
|
||||
09180 Southeastern Connecticut Planning Region CT 284015
|
||||
09190 Western Connecticut Planning Region CT 640482
|
||||
10001 Kent County DE 194786
|
||||
10003 New Castle County DE 588026
|
||||
11001 District of Columbia DC 693645
|
||||
12001 Alachua County FL 290028
|
||||
12005 Bay County FL 204479
|
||||
12009 Brevard County FL 663982
|
||||
12011 Broward County FL 2013317
|
||||
12031 Duval County FL 1062963
|
||||
12033 Escambia County FL 333834
|
||||
12035 Flagler County FL 140360
|
||||
12057 Hillsborough County FL 1574115
|
||||
12061 Indian River County FL 172799
|
||||
12069 Lake County FL 456068
|
||||
12071 Lee County FL 875607
|
||||
12073 Leon County FL 299048
|
||||
12081 Manatee County FL 468200
|
||||
12083 Marion County FL 442660
|
||||
12086 Miami-Dade County FL 2802029
|
||||
12091 Okaloosa County FL 221810
|
||||
12095 Orange County FL 1528002
|
||||
12097 Osceola County FL 481718
|
||||
12099 Palm Beach County FL 1575726
|
||||
12103 Pinellas County FL 948563
|
||||
12105 Polk County FL 874790
|
||||
12111 St. Lucie County FL 402449
|
||||
12115 Sarasota County FL 479958
|
||||
12117 Seminole County FL 491884
|
||||
12127 Volusia County FL 606573
|
||||
13015 Bartow County GA 120800
|
||||
13021 Bibb County GA 157556
|
||||
13031 Bulloch County GA 86949
|
||||
13045 Carroll County GA 131036
|
||||
13051 Chatham County GA 311855
|
||||
13057 Cherokee County GA 299273
|
||||
13059 Clarke County GA 129921
|
||||
13067 Cobb County GA 793345
|
||||
13077 Coweta County GA 160240
|
||||
13089 DeKalb County GA 774394
|
||||
13095 Dougherty County GA 82616
|
||||
13097 Douglas County GA 154293
|
||||
13113 Fayette County GA 125156
|
||||
13115 Floyd County GA 101378
|
||||
13121 Fulton County GA 1098791
|
||||
13135 Gwinnett County GA 1018099
|
||||
13139 Hall County GA 226568
|
||||
13151 Henry County GA 264922
|
||||
13153 Houston County GA 178214
|
||||
13179 Liberty County GA 70313
|
||||
13185 Lowndes County GA 122867
|
||||
13215 Muscogee County GA 202171
|
||||
13245 Richmond County GA 206559
|
||||
13285 Troup County GA 72844
|
||||
13313 Whitfield County GA 106212
|
||||
16001 Ada County ID 546141
|
||||
16005 Bannock County ID 91591
|
||||
16019 Bonneville County ID 135771
|
||||
16027 Canyon County ID 275123
|
||||
16055 Kootenai County ID 191864
|
||||
16057 Latah County ID 41842
|
||||
16065 Madison County ID 55172
|
||||
16069 Nez Perce County ID 42905
|
||||
16083 Twin Falls County ID 97539
|
||||
17001 Adams County IL 64267
|
||||
17007 Boone County IL 53568
|
||||
17019 Champaign County IL 209972
|
||||
17031 Cook County IL 5194625
|
||||
17037 DeKalb County IL 101835
|
||||
17043 DuPage County IL 934298
|
||||
17089 Kane County IL 525757
|
||||
17093 Kendall County IL 145470
|
||||
17095 Knox County IL 47767
|
||||
17097 Lake County IL 719339
|
||||
17111 McHenry County IL 317751
|
||||
17113 McLean County IL 171419
|
||||
17115 Macon County IL 99300
|
||||
17119 Madison County IL 263110
|
||||
17143 Peoria County IL 178553
|
||||
17161 Rock Island County IL 141869
|
||||
17163 St. Clair County IL 250708
|
||||
17167 Sangamon County IL 194170
|
||||
17179 Tazewell County IL 130049
|
||||
17183 Vermilion County IL 71259
|
||||
17197 Will County IL 712253
|
||||
17201 Winnebago County IL 283674
|
||||
18003 Allen County IN 402329
|
||||
18005 Bartholomew County IN 85729
|
||||
18011 Boone County IN 80689
|
||||
18019 Clark County IN 130451
|
||||
18035 Delaware County IN 113106
|
||||
18039 Elkhart County IN 208774
|
||||
18043 Floyd County IN 82153
|
||||
18053 Grant County IN 66524
|
||||
18057 Hamilton County IN 387036
|
||||
18059 Hancock County IN 90969
|
||||
18063 Hendricks County IN 193510
|
||||
18067 Howard County IN 83904
|
||||
18081 Johnson County IN 174262
|
||||
18089 Lake County IN 504612
|
||||
18091 LaPorte County IN 111294
|
||||
18095 Madison County IN 135088
|
||||
18097 Marion County IN 992196
|
||||
18105 Monroe County IN 143345
|
||||
18127 Porter County IN 176049
|
||||
18141 St. Joseph County IN 272861
|
||||
18157 Tippecanoe County IN 190456
|
||||
18163 Vanderburgh County IN 181995
|
||||
18167 Vigo County IN 106512
|
||||
18177 Wayne County IN 66169
|
||||
19013 Black Hawk County IA 131532
|
||||
19033 Cerro Gordo County IA 42372
|
||||
19049 Dallas County IA 118457
|
||||
19061 Dubuque County IA 99381
|
||||
19103 Johnson County IA 160044
|
||||
19113 Linn County IA 232028
|
||||
19127 Marshall County IA 39890
|
||||
19153 Polk County IA 516546
|
||||
19155 Pottawattamie County IA 92996
|
||||
19163 Scott County IA 175259
|
||||
19169 Story County IA 101291
|
||||
19179 Wapello County IA 35210
|
||||
19193 Woodbury County IA 106649
|
||||
20045 Douglas County KS 120920
|
||||
20055 Finney County KS 37505
|
||||
20057 Ford County KS 33993
|
||||
20091 Johnson County KS 636906
|
||||
20103 Leavenworth County KS 84590
|
||||
20155 Reno County KS 61539
|
||||
20161 Riley County KS 72598
|
||||
20169 Saline County KS 53377
|
||||
20173 Sedgwick County KS 538433
|
||||
20177 Shawnee County KS 178607
|
||||
20209 Wyandotte County KS 170597
|
||||
21015 Boone County KY 145316
|
||||
21047 Christian County KY 70115
|
||||
21059 Daviess County KY 104898
|
||||
21067 Fayette County KY 329751
|
||||
21073 Franklin County KY 52649
|
||||
21093 Hardin County KY 113482
|
||||
21101 Henderson County KY 44255
|
||||
21111 Jefferson County KY 795222
|
||||
21113 Jessamine County KY 57147
|
||||
21117 Kenton County KY 175779
|
||||
21145 McCracken County KY 67553
|
||||
21151 Madison County KY 101696
|
||||
21209 Scott County KY 62262
|
||||
21227 Warren County KY 149375
|
||||
22015 Bossier Parish LA 131867
|
||||
22017 Caddo Parish LA 224226
|
||||
22019 Calcasieu Parish LA 208466
|
||||
22033 East Baton Rouge Parish LA 456180
|
||||
22045 Iberia Parish LA 66846
|
||||
22051 Jefferson Parish LA 431398
|
||||
22071 Orleans Parish LA 362154
|
||||
22073 Ouachita Parish LA 158542
|
||||
22079 Rapides Parish LA 125877
|
||||
22103 St. Tammany Parish LA 279108
|
||||
22109 Terrebonne Parish LA 104163
|
||||
23001 Androscoggin County ME 116487
|
||||
23005 Cumberland County ME 317222
|
||||
23019 Penobscot County ME 157967
|
||||
24003 Anne Arundel County MD 603380
|
||||
24021 Frederick County MD 302883
|
||||
24031 Montgomery County MD 1074582
|
||||
24033 Prince George's County MD 970374
|
||||
24043 Washington County MD 157731
|
||||
24045 Wicomico County MD 106899
|
||||
24510 Baltimore city MD 569997
|
||||
25001 Barnstable County MA 233539
|
||||
25003 Berkshire County MA 128224
|
||||
25005 Bristol County MA 593640
|
||||
25009 Essex County MA 826653
|
||||
25013 Hampden County MA 464338
|
||||
25015 Hampshire County MA 164065
|
||||
25017 Middlesex County MA 1669979
|
||||
25021 Norfolk County MA 739749
|
||||
25023 Plymouth County MA 546829
|
||||
25025 Suffolk County MA 791891
|
||||
25027 Worcester County MA 888502
|
||||
26017 Bay County MI 102123
|
||||
26025 Calhoun County MI 133408
|
||||
26049 Genesee County MI 401093
|
||||
26065 Ingham County MI 289709
|
||||
26075 Jackson County MI 159552
|
||||
26077 Kalamazoo County MI 263795
|
||||
26081 Kent County MI 675232
|
||||
26099 Macomb County MI 886221
|
||||
26111 Midland County MI 83754
|
||||
26121 Muskegon County MI 177901
|
||||
26125 Oakland County MI 1288337
|
||||
26139 Ottawa County MI 308459
|
||||
26145 Saginaw County MI 187688
|
||||
26147 St. Clair County MI 160486
|
||||
26161 Washtenaw County MI 370214
|
||||
26163 Wayne County MI 1769038
|
||||
27003 Anoka County MN 381605
|
||||
27013 Blue Earth County MN 70634
|
||||
27019 Carver County MN 114379
|
||||
27027 Clay County MN 67734
|
||||
27037 Dakota County MN 457710
|
||||
27053 Hennepin County MN 1284784
|
||||
27099 Mower County MN 40971
|
||||
27109 Olmsted County MN 166731
|
||||
27123 Ramsey County MN 541623
|
||||
27131 Rice County MN 69939
|
||||
27137 St. Louis County MN 200518
|
||||
27139 Scott County MN 159017
|
||||
27141 Sherburne County MN 104194
|
||||
27145 Stearns County MN 164110
|
||||
27147 Steele County MN 37464
|
||||
27163 Washington County MN 286895
|
||||
27169 Winona County MN 50523
|
||||
28033 DeSoto County MS 197918
|
||||
28035 Forrest County MS 79034
|
||||
28047 Harrison County MS 217136
|
||||
28049 Hinds County MS 211888
|
||||
28071 Lafayette County MS 59597
|
||||
28075 Lauderdale County MS 70317
|
||||
28081 Lee County MS 83731
|
||||
28089 Madison County MS 116298
|
||||
28105 Oktibbeha County MS 51896
|
||||
28121 Rankin County MS 162181
|
||||
28151 Washington County MS 40446
|
||||
29019 Boone County MO 191746
|
||||
29021 Buchanan County MO 83540
|
||||
29031 Cape Girardeau County MO 83999
|
||||
29037 Cass County MO 115859
|
||||
29043 Christian County MO 96725
|
||||
29047 Clay County MO 265032
|
||||
29051 Cole County MO 77908
|
||||
29077 Greene County MO 309286
|
||||
29095 Jackson County MO 732994
|
||||
29097 Jasper County MO 127428
|
||||
29183 St. Charles County MO 426499
|
||||
29189 St. Louis County MO 990911
|
||||
29510 St. Louis city MO 278144
|
||||
30013 Cascade County MT 85029
|
||||
30029 Flathead County MT 115429
|
||||
30031 Gallatin County MT 128740
|
||||
30049 Lewis and Clark County MT 75331
|
||||
30063 Missoula County MT 123513
|
||||
30093 Silver Bow County MT 36118
|
||||
30111 Yellowstone County MT 172692
|
||||
31001 Adams County NE 31071
|
||||
31019 Buffalo County NE 51172
|
||||
31053 Dodge County NE 38057
|
||||
31055 Douglas County NE 606460
|
||||
31079 Hall County NE 63633
|
||||
31109 Lancaster County NE 334049
|
||||
31119 Madison County NE 36106
|
||||
31141 Platte County NE 35649
|
||||
31153 Sarpy County NE 208303
|
||||
32003 Clark County NV 2407226
|
||||
32019 Lyon County NV 65088
|
||||
32031 Washoe County NV 509386
|
||||
32510 Carson City NV 58571
|
||||
33011 Hillsborough County NH 433415
|
||||
33013 Merrimack County NH 158078
|
||||
33017 Strafford County NH 135043
|
||||
34001 Atlantic County NJ 278657
|
||||
34003 Bergen County NJ 977026
|
||||
34007 Camden County NJ 535799
|
||||
34011 Cumberland County NJ 157148
|
||||
34013 Essex County NJ 896379
|
||||
34017 Hudson County NJ 735033
|
||||
34021 Mercer County NJ 399289
|
||||
34023 Middlesex County NJ 883335
|
||||
34025 Monmouth County NJ 651035
|
||||
34031 Passaic County NJ 531624
|
||||
34039 Union County NJ 601863
|
||||
35001 Bernalillo County NM 667601
|
||||
35005 Chaves County NM 63364
|
||||
35009 Curry County NM 46655
|
||||
35013 Doña Ana County NM 229091
|
||||
35015 Eddy County NM 62509
|
||||
35025 Lea County NM 74749
|
||||
35035 Otero County NM 70368
|
||||
35043 Sandoval County NM 159565
|
||||
35045 San Juan County NM 120340
|
||||
35049 Santa Fe County NM 156907
|
||||
36001 Albany County NY 321225
|
||||
36007 Broome County NY 195736
|
||||
36011 Cayuga County NY 74365
|
||||
36013 Chautauqua County NY 124126
|
||||
36015 Chemung County NY 80415
|
||||
36027 Dutchess County NY 300708
|
||||
36029 Erie County NY 946741
|
||||
36047 Kings County NY 2653963
|
||||
36055 Monroe County NY 750506
|
||||
36059 Nassau County NY 1398939
|
||||
36063 Niagara County NY 208912
|
||||
36065 Oneida County NY 226392
|
||||
36067 Onondaga County NY 466584
|
||||
36071 Orange County NY 417669
|
||||
36083 Rensselaer County NY 160510
|
||||
36091 Saratoga County NY 241343
|
||||
36093 Schenectady County NY 162581
|
||||
36103 Suffolk County NY 1546090
|
||||
36109 Tompkins County NY 104047
|
||||
36119 Westchester County NY 1015743
|
||||
37001 Alamance County NC 186177
|
||||
37019 Brunswick County NC 174702
|
||||
37021 Buncombe County NC 277417
|
||||
37025 Cabarrus County NC 249725
|
||||
37035 Catawba County NC 170172
|
||||
37049 Craven County NC 105025
|
||||
37051 Cumberland County NC 338473
|
||||
37057 Davidson County NC 180182
|
||||
37063 Durham County NC 347240
|
||||
37067 Forsyth County NC 401718
|
||||
37071 Gaston County NC 246558
|
||||
37081 Guilford County NC 562234
|
||||
37097 Iredell County NC 211798
|
||||
37101 Johnston County NC 256448
|
||||
37105 Lee County NC 70258
|
||||
37119 Mecklenburg County NC 1233383
|
||||
37127 Nash County NC 99365
|
||||
37129 New Hanover County NC 245959
|
||||
37133 Onslow County NC 217175
|
||||
37135 Orange County NC 152498
|
||||
37147 Pitt County NC 182936
|
||||
37151 Randolph County NC 149516
|
||||
37159 Rowan County NC 155096
|
||||
37179 Union County NC 267674
|
||||
37183 Wake County NC 1257235
|
||||
37191 Wayne County NC 122278
|
||||
37195 Wilson County NC 81150
|
||||
38015 Burleigh County ND 103251
|
||||
38017 Cass County ND 201794
|
||||
38035 Grand Forks County ND 74501
|
||||
38059 Morton County ND 34601
|
||||
38089 Stark County ND 34013
|
||||
38101 Ward County ND 68233
|
||||
38105 Williams County ND 41767
|
||||
39003 Allen County OH 100881
|
||||
39009 Athens County OH 63197
|
||||
39017 Butler County OH 400128
|
||||
39023 Clark County OH 135340
|
||||
39035 Cuyahoga County OH 1232925
|
||||
39041 Delaware County OH 242032
|
||||
39045 Fairfield County OH 169752
|
||||
39049 Franklin County OH 1361536
|
||||
39057 Greene County OH 174322
|
||||
39061 Hamilton County OH 838418
|
||||
39063 Hancock County OH 75034
|
||||
39085 Lake County OH 232217
|
||||
39089 Licking County OH 185564
|
||||
39093 Lorain County OH 323219
|
||||
39095 Lucas County OH 423347
|
||||
39099 Mahoning County OH 224706
|
||||
39101 Marion County OH 65115
|
||||
39103 Medina County OH 185025
|
||||
39109 Miami County OH 112634
|
||||
39113 Montgomery County OH 539598
|
||||
39119 Muskingum County OH 87014
|
||||
39133 Portage County OH 163404
|
||||
39139 Richland County OH 124893
|
||||
39151 Stark County OH 373771
|
||||
39153 Summit County OH 538376
|
||||
39155 Trumbull County OH 198972
|
||||
39159 Union County OH 73446
|
||||
39165 Warren County OH 257181
|
||||
39169 Wayne County OH 116758
|
||||
39173 Wood County OH 134176
|
||||
40027 Cleveland County OK 303973
|
||||
40031 Comanche County OK 122158
|
||||
40047 Garfield County OK 61779
|
||||
40101 Muskogee County OK 66708
|
||||
40109 Oklahoma County OK 822125
|
||||
40119 Payne County OK 83889
|
||||
40125 Pottawatomie County OK 75102
|
||||
40143 Tulsa County OK 698782
|
||||
40147 Washington County OK 54037
|
||||
41003 Benton County OR 97728
|
||||
41005 Clackamas County OR 426280
|
||||
41017 Deschutes County OR 213072
|
||||
41029 Jackson County OR 221795
|
||||
41033 Josephine County OR 87867
|
||||
41039 Lane County OR 381584
|
||||
41043 Linn County OR 132843
|
||||
41047 Marion County OR 355777
|
||||
41051 Multnomah County OR 795391
|
||||
41067 Washington County OR 611708
|
||||
41071 Yamhill County OR 110024
|
||||
42003 Allegheny County PA 1225035
|
||||
42011 Berks County PA 440072
|
||||
42013 Blair County PA 119541
|
||||
42027 Centre County PA 157393
|
||||
42043 Dauphin County PA 293351
|
||||
42045 Delaware County PA 580937
|
||||
42049 Erie County PA 265832
|
||||
42069 Lackawanna County PA 216502
|
||||
42071 Lancaster County PA 563159
|
||||
42075 Lebanon County PA 146380
|
||||
42077 Lehigh County PA 384383
|
||||
42079 Luzerne County PA 332126
|
||||
42081 Lycoming County PA 112587
|
||||
42091 Montgomery County PA 877643
|
||||
42095 Northampton County PA 324411
|
||||
42101 Philadelphia County PA 1574281
|
||||
42133 York County PA 473197
|
||||
44003 Kent County RI 173495
|
||||
44007 Providence County RI 678179
|
||||
45003 Aiken County SC 181515
|
||||
45007 Anderson County SC 219930
|
||||
45013 Beaufort County SC 204433
|
||||
45015 Berkeley County SC 274666
|
||||
45019 Charleston County SC 436200
|
||||
45035 Dorchester County SC 178397
|
||||
45041 Florence County SC 138504
|
||||
45045 Greenville County SC 583125
|
||||
45051 Horry County SC 427551
|
||||
45063 Lexington County SC 317588
|
||||
45077 Pickens County SC 139198
|
||||
45079 Richland County SC 434956
|
||||
45083 Spartanburg County SC 380857
|
||||
45085 Sumter County SC 105067
|
||||
45091 York County SC 306887
|
||||
46011 Brookings County SD 37635
|
||||
46013 Brown County SD 37561
|
||||
46099 Minnehaha County SD 212691
|
||||
46103 Pennington County SD 116792
|
||||
47001 Anderson County TN 82066
|
||||
47003 Bedford County TN 55273
|
||||
47009 Blount County TN 143820
|
||||
47011 Bradley County TN 115465
|
||||
47037 Davidson County TN 745904
|
||||
47063 Hamblen County TN 68843
|
||||
47065 Hamilton County TN 390833
|
||||
47093 Knox County TN 511453
|
||||
47113 Madison County TN 100790
|
||||
47119 Maury County TN 118131
|
||||
47125 Montgomery County TN 249935
|
||||
47141 Putnam County TN 86612
|
||||
47149 Rutherford County TN 386352
|
||||
47157 Shelby County TN 910226
|
||||
47163 Sullivan County TN 163759
|
||||
47165 Sumner County TN 215538
|
||||
47179 Washington County TN 141199
|
||||
47187 Williamson County TN 272061
|
||||
47189 Wilson County TN 175033
|
||||
48005 Angelina County TX 88154
|
||||
48027 Bell County TX 402248
|
||||
48029 Bexar County TX 2160088
|
||||
48037 Bowie County TX 92696
|
||||
48039 Brazoria County TX 419080
|
||||
48041 Brazos County TX 249088
|
||||
48061 Cameron County TX 433946
|
||||
48085 Collin County TX 1297179
|
||||
48091 Comal County TX 209166
|
||||
48099 Coryell County TX 85592
|
||||
48113 Dallas County TX 2661397
|
||||
48121 Denton County TX 1069346
|
||||
48135 Ector County TX 173801
|
||||
48139 Ellis County TX 240867
|
||||
48141 El Paso County TX 877858
|
||||
48157 Fort Bend County TX 975191
|
||||
48167 Galveston County TX 372207
|
||||
48181 Grayson County TX 153613
|
||||
48183 Gregg County TX 126095
|
||||
48187 Guadalupe County TX 201111
|
||||
48201 Harris County TX 5045026
|
||||
48209 Hays County TX 304390
|
||||
48215 Hidalgo County TX 921549
|
||||
48231 Hunt County TX 123336
|
||||
48245 Jefferson County TX 254321
|
||||
48251 Johnson County TX 218048
|
||||
48257 Kaufman County TX 209235
|
||||
48265 Kerr County TX 54037
|
||||
48277 Lamar County TX 51503
|
||||
48303 Lubbock County TX 328906
|
||||
48309 McLennan County TX 272020
|
||||
48323 Maverick County TX 58823
|
||||
48329 Midland County TX 187855
|
||||
48339 Montgomery County TX 781194
|
||||
48347 Nacogdoches County TX 66035
|
||||
48349 Navarro County TX 57181
|
||||
48355 Nueces County TX 352992
|
||||
48367 Parker County TX 184767
|
||||
48381 Randall County TX 152351
|
||||
48397 Rockwall County TX 140738
|
||||
48423 Smith County TX 252549
|
||||
48439 Tarrant County TX 2248466
|
||||
48441 Taylor County TX 150077
|
||||
48451 Tom Green County TX 120602
|
||||
48453 Travis County TX 1389670
|
||||
48465 Val Verde County TX 47835
|
||||
48469 Victoria County TX 92656
|
||||
48471 Walker County TX 83842
|
||||
48479 Webb County TX 281224
|
||||
48485 Wichita County TX 129555
|
||||
48491 Williamson County TX 752827
|
||||
49005 Cache County UT 145000
|
||||
49011 Davis County UT 381227
|
||||
49021 Iron County UT 67141
|
||||
49035 Salt Lake County UT 1220916
|
||||
49045 Tooele County UT 87461
|
||||
49049 Utah County UT 759859
|
||||
49053 Washington County UT 213670
|
||||
49057 Weber County UT 278174
|
||||
50007 Chittenden County VT 169115
|
||||
51059 Fairfax County VA 1167873
|
||||
51107 Loudoun County VA 449749
|
||||
51121 Montgomery County VA 98434
|
||||
51510 Alexandria city VA 160662
|
||||
51540 Charlottesville city VA 44388
|
||||
51550 Chesapeake city VA 255332
|
||||
51590 Danville city VA 41647
|
||||
51600 Fairfax city VA 26772
|
||||
51630 Fredericksburg city VA 30393
|
||||
51650 Hampton city VA 137315
|
||||
51660 Harrisonburg city VA 50839
|
||||
51680 Lynchburg city VA 81347
|
||||
51683 Manassas city VA 44332
|
||||
51700 Newport News city VA 183230
|
||||
51710 Norfolk city VA 231013
|
||||
51730 Petersburg city VA 33734
|
||||
51740 Portsmouth city VA 96777
|
||||
51760 Richmond city VA 237257
|
||||
51770 Roanoke city VA 99111
|
||||
51775 Salem city VA 25816
|
||||
51790 Staunton city VA 26801
|
||||
51800 Suffolk city VA 104699
|
||||
51810 Virginia Beach city VA 453737
|
||||
51840 Winchester city VA 28272
|
||||
53005 Benton County WA 221722
|
||||
53007 Chelan County WA 81941
|
||||
53011 Clark County WA 532119
|
||||
53015 Cowlitz County WA 114885
|
||||
53021 Franklin County WA 102612
|
||||
53025 Grant County WA 105727
|
||||
53033 King County WA 2344939
|
||||
53035 Kitsap County WA 283374
|
||||
53053 Pierce County WA 946288
|
||||
53057 Skagit County WA 132975
|
||||
53061 Snohomish County WA 870656
|
||||
53063 Spokane County WA 558344
|
||||
53067 Thurston County WA 304261
|
||||
53071 Walla Walla County WA 62361
|
||||
53073 Whatcom County WA 236392
|
||||
53075 Whitman County WA 48512
|
||||
53077 Yakima County WA 259185
|
||||
54011 Cabell County WV 91183
|
||||
54039 Kanawha County WV 172381
|
||||
54061 Monongalia County WV 107991
|
||||
54069 Ohio County WV 40496
|
||||
54107 Wood County WV 82385
|
||||
55009 Brown County WI 275803
|
||||
55025 Dane County WI 590375
|
||||
55031 Douglas County WI 43990
|
||||
55035 Eau Claire County WI 109033
|
||||
55039 Fond du Lac County WI 104669
|
||||
55059 Kenosha County WI 168448
|
||||
55063 La Crosse County WI 121339
|
||||
55071 Manitowoc County WI 81710
|
||||
55073 Marathon County WI 139432
|
||||
55079 Milwaukee County WI 924216
|
||||
55087 Outagamie County WI 195894
|
||||
55089 Ozaukee County WI 94346
|
||||
55097 Portage County WI 71943
|
||||
55101 Racine County WI 198919
|
||||
55105 Rock County WI 166472
|
||||
55117 Sheboygan County WI 118047
|
||||
55131 Washington County WI 139238
|
||||
55133 Waukesha County WI 417210
|
||||
55139 Winnebago County WI 174218
|
||||
56001 Albany County WY 38558
|
||||
56005 Campbell County WY 48145
|
||||
56021 Laramie County WY 102938
|
||||
56025 Natrona County WY 80526
|
||||
|
@@ -1 +0,0 @@
|
||||
{ "format": "{code}", "rows": "postal-code.tsv", "key": "code", "parent": "locality", "weight": "addresses" }
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "region.tsv", "key": "abbr", "name": "name", "weight": "population" }
|
||||
@@ -1,51 +0,0 @@
|
||||
abbr code name population timezone
|
||||
AK 02 Alaska 737270 America/Anchorage
|
||||
AL 01 Alabama 5193088 America/Chicago
|
||||
AR 05 Arkansas 3114791 America/Chicago
|
||||
AZ 04 Arizona 7623818 America/Phoenix
|
||||
CA 06 California 39355309 America/Los_Angeles
|
||||
CO 08 Colorado 6012561 America/Denver
|
||||
CT 09 Connecticut 3688496 America/New_York
|
||||
DC 11 District of Columbia 693645 America/New_York
|
||||
DE 10 Delaware 1059952 America/New_York
|
||||
FL 12 Florida 23462518 America/New_York
|
||||
GA 13 Georgia 11302748 America/New_York
|
||||
IA 19 Iowa 3238387 America/Chicago
|
||||
ID 16 Idaho 2029733 America/Boise
|
||||
IL 17 Illinois 12719141 America/Chicago
|
||||
IN 18 Indiana 6973333 America/Indiana/Indianapolis
|
||||
KS 20 Kansas 2977220 America/Chicago
|
||||
KY 21 Kentucky 4606864 America/New_York
|
||||
LA 22 Louisiana 4618189 America/Chicago
|
||||
MA 25 Massachusetts 7154084 America/New_York
|
||||
MD 24 Maryland 6265347 America/New_York
|
||||
ME 23 Maine 1414874 America/New_York
|
||||
MI 26 Michigan 10127884 America/Detroit
|
||||
MN 27 Minnesota 5830405 America/Chicago
|
||||
MO 29 Missouri 6270541 America/Chicago
|
||||
MS 28 Mississippi 2954160 America/Chicago
|
||||
MT 30 Montana 1144694 America/Denver
|
||||
NC 37 North Carolina 11197968 America/New_York
|
||||
ND 38 North Dakota 799358 America/Chicago
|
||||
NE 31 Nebraska 2018006 America/Chicago
|
||||
NH 33 New Hampshire 1415342 America/New_York
|
||||
NJ 34 New Jersey 9548215 America/New_York
|
||||
NM 35 New Mexico 2125498 America/Denver
|
||||
NV 32 Nevada 3282188 America/Los_Angeles
|
||||
NY 36 New York 20002427 America/New_York
|
||||
OH 39 Ohio 11900510 America/New_York
|
||||
OK 40 Oklahoma 4123288 America/Chicago
|
||||
OR 41 Oregon 4273586 America/Los_Angeles
|
||||
PA 42 Pennsylvania 13059432 America/New_York
|
||||
RI 44 Rhode Island 1114521 America/New_York
|
||||
SC 45 South Carolina 5570274 America/New_York
|
||||
SD 46 South Dakota 935094 America/Chicago
|
||||
TN 47 Tennessee 7315076 America/Chicago
|
||||
TX 48 Texas 31709821 America/Chicago
|
||||
UT 49 Utah 3538904 America/Denver
|
||||
VA 51 Virginia 8880107 America/New_York
|
||||
VT 50 Vermont 644663 America/New_York
|
||||
WA 53 Washington 8001020 America/Los_Angeles
|
||||
WI 55 Wisconsin 5972787 America/Chicago
|
||||
WV 54 West Virginia 1766147 America/New_York
|
||||
WY 56 Wyoming 588753 America/Denver
|
||||
|
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "street.tsv", "parent": "locality", "weight": "addresses" }
|
||||
-15841
File diff suppressed because it is too large
Load Diff
+10
-1
@@ -1 +1,10 @@
|
||||
{ "format": "{make} {model}", "rows": "car.tsv" }
|
||||
[
|
||||
{ "format": "{maker} {model}", "maker": "BMW", "model": ["3 Series", "5 Series", "X3", "X5"] },
|
||||
{ "format": "{maker} {model}", "maker": "Ford", "model": ["Fiesta", "Focus", "Mustang", "Explorer"] },
|
||||
{ "format": "{maker} {model}", "maker": "Honda", "model": ["Civic", "Accord", "CR-V", "Jazz"] },
|
||||
{ "format": "{maker} {model}", "maker": "Mercedes-Benz", "model": ["A-Class", "C-Class", "E-Class", "GLC"] },
|
||||
{ "format": "{maker} {model}", "maker": "Tesla", "model": ["Model 3", "Model S", "Model X", "Model Y"] },
|
||||
{ "format": "{maker} {model}", "maker": "Toyota", "model": ["Corolla", "Camry", "RAV4", "Yaris"] },
|
||||
{ "format": "{maker} {model}", "maker": "Volkswagen", "model": ["Golf", "Passat", "Polo", "Tiguan"] },
|
||||
{ "format": "{maker} {model}", "maker": "Volvo", "model": ["XC40", "XC60", "XC90", "V60"] }
|
||||
]
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
make model
|
||||
BMW 3 Series
|
||||
BMW 5 Series
|
||||
BMW X3
|
||||
BMW X5
|
||||
Ford Explorer
|
||||
Ford Fiesta
|
||||
Ford Focus
|
||||
Ford Mustang
|
||||
Honda Accord
|
||||
Honda CR-V
|
||||
Honda Civic
|
||||
Honda Jazz
|
||||
Mercedes-Benz A-Class
|
||||
Mercedes-Benz C-Class
|
||||
Mercedes-Benz E-Class
|
||||
Mercedes-Benz GLC
|
||||
Tesla Model 3
|
||||
Tesla Model S
|
||||
Tesla Model X
|
||||
Tesla Model Y
|
||||
Toyota Camry
|
||||
Toyota Corolla
|
||||
Toyota RAV4
|
||||
Toyota Yaris
|
||||
Volkswagen Golf
|
||||
Volkswagen Passat
|
||||
Volkswagen Polo
|
||||
Volkswagen Tiguan
|
||||
Volvo V60
|
||||
Volvo XC40
|
||||
Volvo XC60
|
||||
Volvo XC90
|
||||
|
@@ -0,0 +1,22 @@
|
||||
[
|
||||
{ "format": "{name}", "name": "Australia", "alpha2": "AU", "alpha3": "AUS" },
|
||||
{ "format": "{name}", "name": "Brazil", "alpha2": "BR", "alpha3": "BRA" },
|
||||
{ "format": "{name}", "name": "Canada", "alpha2": "CA", "alpha3": "CAN" },
|
||||
{ "format": "{name}", "name": "China", "alpha2": "CN", "alpha3": "CHN" },
|
||||
{ "format": "{name}", "name": "Denmark", "alpha2": "DK", "alpha3": "DNK" },
|
||||
{ "format": "{name}", "name": "Finland", "alpha2": "FI", "alpha3": "FIN" },
|
||||
{ "format": "{name}", "name": "France", "alpha2": "FR", "alpha3": "FRA" },
|
||||
{ "format": "{name}", "name": "Germany", "alpha2": "DE", "alpha3": "DEU" },
|
||||
{ "format": "{name}", "name": "India", "alpha2": "IN", "alpha3": "IND" },
|
||||
{ "format": "{name}", "name": "Italy", "alpha2": "IT", "alpha3": "ITA" },
|
||||
{ "format": "{name}", "name": "Japan", "alpha2": "JP", "alpha3": "JPN" },
|
||||
{ "format": "{name}", "name": "Mexico", "alpha2": "MX", "alpha3": "MEX" },
|
||||
{ "format": "{name}", "name": "Netherlands", "alpha2": "NL", "alpha3": "NLD" },
|
||||
{ "format": "{name}", "name": "Norway", "alpha2": "NO", "alpha3": "NOR" },
|
||||
{ "format": "{name}", "name": "Poland", "alpha2": "PL", "alpha3": "POL" },
|
||||
{ "format": "{name}", "name": "Spain", "alpha2": "ES", "alpha3": "ESP" },
|
||||
{ "format": "{name}", "name": "Sweden", "alpha2": "SE", "alpha3": "SWE" },
|
||||
{ "format": "{name}", "name": "Switzerland", "alpha2": "CH", "alpha3": "CHE" },
|
||||
{ "format": "{name}", "name": "United Kingdom", "alpha2": "GB", "alpha3": "GBR" },
|
||||
{ "format": "{name}", "name": "United States", "alpha2": "US", "alpha3": "USA" }
|
||||
]
|
||||
+18
-1
@@ -1 +1,18 @@
|
||||
{ "format": "{code}", "rows": "currency.tsv", "key": "code", "name": "name" }
|
||||
[
|
||||
{ "format": "{code}", "code": "AUD", "name": "Australian Dollar", "symbol": "$" },
|
||||
{ "format": "{code}", "code": "BRL", "name": "Brazilian Real", "symbol": "R$" },
|
||||
{ "format": "{code}", "code": "CAD", "name": "Canadian Dollar", "symbol": "$" },
|
||||
{ "format": "{code}", "code": "CHF", "name": "Swiss Franc", "symbol": "CHF" },
|
||||
{ "format": "{code}", "code": "CNY", "name": "Chinese Yuan", "symbol": "¥" },
|
||||
{ "format": "{code}", "code": "DKK", "name": "Danish Krone", "symbol": "kr" },
|
||||
{ "format": "{code}", "code": "EUR", "name": "Euro", "symbol": "€" },
|
||||
{ "format": "{code}", "code": "GBP", "name": "Pound Sterling", "symbol": "£" },
|
||||
{ "format": "{code}", "code": "INR", "name": "Indian Rupee", "symbol": "₹" },
|
||||
{ "format": "{code}", "code": "JPY", "name": "Japanese Yen", "symbol": "¥" },
|
||||
{ "format": "{code}", "code": "MXN", "name": "Mexican Peso", "symbol": "$" },
|
||||
{ "format": "{code}", "code": "NOK", "name": "Norwegian Krone", "symbol": "kr" },
|
||||
{ "format": "{code}", "code": "PLN", "name": "Polish Zloty", "symbol": "zł" },
|
||||
{ "format": "{code}", "code": "SEK", "name": "Swedish Krona", "symbol": "kr" },
|
||||
{ "format": "{code}", "code": "USD", "name": "US Dollar", "symbol": "$" },
|
||||
{ "format": "{code}", "code": "ZAR", "name": "South African Rand", "symbol": "R" }
|
||||
]
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
code decimals name numeric symbol
|
||||
AED 2 UAE Dirham 784 AED
|
||||
AFN 2 Afghani 971 ؋
|
||||
ALL 2 Lek 008 ALL
|
||||
AMD 2 Armenian Dram 051 ֏
|
||||
AOA 2 Kwanza 973 Kz
|
||||
ARS 2 Argentine Peso 032 $
|
||||
AUD 2 Australian Dollar 036 $
|
||||
AWG 2 Aruban Florin 533 AWG
|
||||
AZN 2 Azerbaijan Manat 944 ₼
|
||||
BAM 2 Convertible Mark 977 KM
|
||||
BBD 2 Barbados Dollar 052 $
|
||||
BDT 2 Taka 050 ৳
|
||||
BHD 3 Bahraini Dinar 048 BHD
|
||||
BIF 0 Burundi Franc 108 BIF
|
||||
BMD 2 Bermudian Dollar 060 $
|
||||
BND 2 Brunei Dollar 096 $
|
||||
BOB 2 Boliviano 068 Bs
|
||||
BOV 2 Mvdol 984 BOV
|
||||
BRL 2 Brazilian Real 986 R$
|
||||
BSD 2 Bahamian Dollar 044 $
|
||||
BTN 2 Ngultrum 064 BTN
|
||||
BWP 2 Pula 072 P
|
||||
BYN 2 Belarusian Ruble 933 BYN
|
||||
BZD 2 Belize Dollar 084 $
|
||||
CAD 2 Canadian Dollar 124 $
|
||||
CDF 2 Congolese Franc 976 CDF
|
||||
CHE 2 WIR Euro 947 CHE
|
||||
CHF 2 Swiss Franc 756 Fr.
|
||||
CHW 2 WIR Franc 948 CHW
|
||||
CLF 4 Unidad de Fomento 990 CLF
|
||||
CLP 0 Chilean Peso 152 $
|
||||
CNY 2 Yuan Renminbi 156 ¥
|
||||
COP 2 Colombian Peso 170 $
|
||||
COU 2 Unidad de Valor Real 970 COU
|
||||
CRC 2 Costa Rican Colon 188 ₡
|
||||
CUP 2 Cuban Peso 192 $
|
||||
CVE 2 Cabo Verde Escudo 132 CVE
|
||||
CZK 2 Czech Koruna 203 Kč
|
||||
DJF 0 Djibouti Franc 262 DJF
|
||||
DKK 2 Danish Krone 208 kr
|
||||
DOP 2 Dominican Peso 214 $
|
||||
DZD 2 Algerian Dinar 012 DZD
|
||||
EGP 2 Egyptian Pound 818 E£
|
||||
ERN 2 Nakfa 232 ERN
|
||||
ETB 2 Ethiopian Birr 230 ETB
|
||||
EUR 2 Euro 978 €
|
||||
FJD 2 Fiji Dollar 242 $
|
||||
FKP 2 Falkland Islands Pound 238 £
|
||||
GBP 2 Pound Sterling 826 £
|
||||
GEL 2 Lari 981 ₾
|
||||
GHS 2 Ghana Cedi 936 GH₵
|
||||
GIP 2 Gibraltar Pound 292 £
|
||||
GMD 2 Dalasi 270 GMD
|
||||
GNF 0 Guinean Franc 324 FG
|
||||
GTQ 2 Quetzal 320 Q
|
||||
GYD 2 Guyana Dollar 328 $
|
||||
HKD 2 Hong Kong Dollar 344 $
|
||||
HNL 2 Lempira 340 L
|
||||
HTG 2 Gourde 332 HTG
|
||||
HUF 2 Forint 348 Ft
|
||||
IDR 2 Rupiah 360 Rp
|
||||
ILS 2 New Israeli Sheqel 376 ₪
|
||||
INR 2 Indian Rupee 356 ₹
|
||||
IQD 3 Iraqi Dinar 368 IQD
|
||||
IRR 2 Iranian Rial 364 IRR
|
||||
ISK 0 Iceland Krona 352 kr
|
||||
JMD 2 Jamaican Dollar 388 $
|
||||
JOD 3 Jordanian Dinar 400 JOD
|
||||
JPY 0 Yen 392 ¥
|
||||
KES 2 Kenyan Shilling 404 KES
|
||||
KGS 2 Som 417 ⃀
|
||||
KHR 2 Riel 116 ៛
|
||||
KMF 0 Comorian Franc 174 CF
|
||||
KPW 2 North Korean Won 408 ₩
|
||||
KRW 0 Won 410 ₩
|
||||
KWD 3 Kuwaiti Dinar 414 KWD
|
||||
KYD 2 Cayman Islands Dollar 136 $
|
||||
KZT 2 Tenge 398 ₸
|
||||
LAK 2 Lao Kip 418 ₭
|
||||
LBP 2 Lebanese Pound 422 L£
|
||||
LKR 2 Sri Lanka Rupee 144 Rs
|
||||
LRD 2 Liberian Dollar 430 $
|
||||
LSL 2 Loti 426 LSL
|
||||
LYD 3 Libyan Dinar 434 LYD
|
||||
MAD 2 Moroccan Dirham 504 MAD
|
||||
MDL 2 Moldovan Leu 498 MDL
|
||||
MGA 2 Malagasy Ariary 969 Ar
|
||||
MKD 2 Denar 807 MKD
|
||||
MMK 2 Kyat 104 K
|
||||
MNT 2 Tugrik 496 ₮
|
||||
MOP 2 Pataca 446 MOP
|
||||
MRU 2 Ouguiya 929 MRU
|
||||
MUR 2 Mauritius Rupee 480 Rs
|
||||
MVR 2 Rufiyaa 462 MVR
|
||||
MWK 2 Malawi Kwacha 454 MWK
|
||||
MXN 2 Mexican Peso 484 $
|
||||
MXV 2 Mexican Unidad de Inversion (UDI) 979 MXV
|
||||
MYR 2 Malaysian Ringgit 458 RM
|
||||
MZN 2 Mozambique Metical 943 MZN
|
||||
NAD 2 Namibia Dollar 516 $
|
||||
NGN 2 Naira 566 ₦
|
||||
NIO 2 Cordoba Oro 558 C$
|
||||
NOK 2 Norwegian Krone 578 kr
|
||||
NPR 2 Nepalese Rupee 524 Rs
|
||||
NZD 2 New Zealand Dollar 554 $
|
||||
OMR 3 Rial Omani 512 OMR
|
||||
PAB 2 Balboa 590 PAB
|
||||
PEN 2 Sol 604 PEN
|
||||
PGK 2 Kina 598 PGK
|
||||
PHP 2 Philippine Peso 608 ₱
|
||||
PKR 2 Pakistan Rupee 586 Rs
|
||||
PLN 2 Zloty 985 zł
|
||||
PYG 0 Guarani 600 ₲
|
||||
QAR 2 Qatari Rial 634 QAR
|
||||
RON 2 Romanian Leu 946 lei
|
||||
RSD 2 Serbian Dinar 941 RSD
|
||||
RUB 2 Russian Ruble 643 ₽
|
||||
RWF 0 Rwanda Franc 646 RF
|
||||
SAR 2 Saudi Riyal 682 SAR
|
||||
SBD 2 Solomon Islands Dollar 090 $
|
||||
SCR 2 Seychelles Rupee 690 SCR
|
||||
SDG 2 Sudanese Pound 938 SDG
|
||||
SEK 2 Swedish Krona 752 kr
|
||||
SGD 2 Singapore Dollar 702 $
|
||||
SHP 2 Saint Helena Pound 654 £
|
||||
SLE 2 Leone 925 SLE
|
||||
SOS 2 Somali Shilling 706 SOS
|
||||
SRD 2 Surinam Dollar 968 $
|
||||
SSP 2 South Sudanese Pound 728 £
|
||||
STN 2 Dobra 930 Db
|
||||
SVC 2 El Salvador Colon 222 SVC
|
||||
SYP 2 Syrian Pound 760 £
|
||||
SZL 2 Lilangeni 748 SZL
|
||||
THB 2 Baht 764 ฿
|
||||
TJS 2 Somoni 972 TJS
|
||||
TMT 2 Turkmenistan New Manat 934 TMT
|
||||
TND 3 Tunisian Dinar 788 TND
|
||||
TOP 2 Pa’anga 776 T$
|
||||
TRY 2 Turkish Lira 949 ₺
|
||||
TTD 2 Trinidad and Tobago Dollar 780 $
|
||||
TWD 2 New Taiwan Dollar 901 $
|
||||
TZS 2 Tanzanian Shilling 834 TZS
|
||||
UAH 2 Hryvnia 980 ₴
|
||||
UGX 0 Uganda Shilling 800 UGX
|
||||
USD 2 US Dollar 840 $
|
||||
USN 2 US Dollar (Next day) 997 USN
|
||||
UYI 0 Uruguay Peso en Unidades Indexadas (UI) 940 UYI
|
||||
UYU 2 Peso Uruguayo 858 $
|
||||
UYW 4 Unidad Previsional 927 UYW
|
||||
UZS 2 Uzbekistan Sum 860 UZS
|
||||
VED 2 Bolívar Soberano 926 VED
|
||||
VES 2 Bolívar Soberano 928 VES
|
||||
VND 0 Dong 704 ₫
|
||||
VUV 0 Vatu 548 VUV
|
||||
WST 2 Tala 882 WST
|
||||
XAD 2 Arab Accounting Dinar 396 XAD
|
||||
XAF 0 CFA Franc BEAC 950 FCFA
|
||||
XCD 2 East Caribbean Dollar 951 $
|
||||
XCG 2 Caribbean Guilder 532 Cg.
|
||||
XOF 0 CFA Franc BCEAO 952 F CFA
|
||||
XPF 0 CFP Franc 953 CFPF
|
||||
YER 2 Yemeni Rial 886 YER
|
||||
ZAR 2 Rand 710 R
|
||||
ZMW 2 Zambian Kwacha 967 ZK
|
||||
ZWG 2 Zimbabwe Gold 924 ZWG
|
||||
|
@@ -1 +0,0 @@
|
||||
"{date(2000-01-01,2029-12-31,'2006-01-02T15:04:05Z')}"
|
||||
@@ -1 +0,0 @@
|
||||
{ "format": "{method}", "rows": "httpmethod.tsv", "key": "method" }
|
||||
@@ -1,10 +0,0 @@
|
||||
idempotent method safe
|
||||
false CONNECT false
|
||||
true DELETE false
|
||||
true GET true
|
||||
true HEAD true
|
||||
true OPTIONS true
|
||||
false PATCH false
|
||||
false POST false
|
||||
true PUT false
|
||||
true TRACE true
|
||||
|
@@ -1 +1,18 @@
|
||||
{ "format": "{code} {reason}", "rows": "httpstatus.tsv", "key": "code", "name": "reason" }
|
||||
[
|
||||
{ "format": "{code} {reason}", "code": "200", "reason": "OK" },
|
||||
{ "format": "{code} {reason}", "code": "201", "reason": "Created" },
|
||||
{ "format": "{code} {reason}", "code": "204", "reason": "No Content" },
|
||||
{ "format": "{code} {reason}", "code": "301", "reason": "Moved Permanently" },
|
||||
{ "format": "{code} {reason}", "code": "302", "reason": "Found" },
|
||||
{ "format": "{code} {reason}", "code": "304", "reason": "Not Modified" },
|
||||
{ "format": "{code} {reason}", "code": "400", "reason": "Bad Request" },
|
||||
{ "format": "{code} {reason}", "code": "401", "reason": "Unauthorized" },
|
||||
{ "format": "{code} {reason}", "code": "403", "reason": "Forbidden" },
|
||||
{ "format": "{code} {reason}", "code": "404", "reason": "Not Found" },
|
||||
{ "format": "{code} {reason}", "code": "409", "reason": "Conflict" },
|
||||
{ "format": "{code} {reason}", "code": "422", "reason": "Unprocessable Entity" },
|
||||
{ "format": "{code} {reason}", "code": "429", "reason": "Too Many Requests" },
|
||||
{ "format": "{code} {reason}", "code": "500", "reason": "Internal Server Error" },
|
||||
{ "format": "{code} {reason}", "code": "502", "reason": "Bad Gateway" },
|
||||
{ "format": "{code} {reason}", "code": "503", "reason": "Service Unavailable" }
|
||||
]
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
code reason
|
||||
100 Continue
|
||||
101 Switching Protocols
|
||||
102 Processing
|
||||
103 Early Hints
|
||||
200 OK
|
||||
201 Created
|
||||
202 Accepted
|
||||
203 Non-Authoritative Information
|
||||
204 No Content
|
||||
205 Reset Content
|
||||
206 Partial Content
|
||||
207 Multi-Status
|
||||
208 Already Reported
|
||||
226 IM Used
|
||||
300 Multiple Choices
|
||||
301 Moved Permanently
|
||||
302 Found
|
||||
303 See Other
|
||||
304 Not Modified
|
||||
305 Use Proxy
|
||||
307 Temporary Redirect
|
||||
308 Permanent Redirect
|
||||
400 Bad Request
|
||||
401 Unauthorized
|
||||
402 Payment Required
|
||||
403 Forbidden
|
||||
404 Not Found
|
||||
405 Method Not Allowed
|
||||
406 Not Acceptable
|
||||
407 Proxy Authentication Required
|
||||
408 Request Timeout
|
||||
409 Conflict
|
||||
410 Gone
|
||||
411 Length Required
|
||||
412 Precondition Failed
|
||||
413 Content Too Large
|
||||
414 URI Too Long
|
||||
415 Unsupported Media Type
|
||||
416 Range Not Satisfiable
|
||||
417 Expectation Failed
|
||||
421 Misdirected Request
|
||||
422 Unprocessable Content
|
||||
423 Locked
|
||||
424 Failed Dependency
|
||||
425 Too Early
|
||||
426 Upgrade Required
|
||||
428 Precondition Required
|
||||
429 Too Many Requests
|
||||
431 Request Header Fields Too Large
|
||||
451 Unavailable For Legal Reasons
|
||||
500 Internal Server Error
|
||||
501 Not Implemented
|
||||
502 Bad Gateway
|
||||
503 Service Unavailable
|
||||
504 Gateway Timeout
|
||||
505 HTTP Version Not Supported
|
||||
506 Variant Also Negotiates
|
||||
507 Insufficient Storage
|
||||
508 Loop Detected
|
||||
511 Network Authentication Required
|
||||
|
+18
-1
@@ -1 +1,18 @@
|
||||
{ "format": "{name}", "rows": "language.tsv", "key": "code", "name": "name" }
|
||||
[
|
||||
{ "format": "{name}", "name": "Arabic", "code": "ar" },
|
||||
{ "format": "{name}", "name": "Chinese", "code": "zh" },
|
||||
{ "format": "{name}", "name": "Danish", "code": "da" },
|
||||
{ "format": "{name}", "name": "Dutch", "code": "nl" },
|
||||
{ "format": "{name}", "name": "English", "code": "en" },
|
||||
{ "format": "{name}", "name": "Finnish", "code": "fi" },
|
||||
{ "format": "{name}", "name": "French", "code": "fr" },
|
||||
{ "format": "{name}", "name": "German", "code": "de" },
|
||||
{ "format": "{name}", "name": "Italian", "code": "it" },
|
||||
{ "format": "{name}", "name": "Japanese", "code": "ja" },
|
||||
{ "format": "{name}", "name": "Norwegian", "code": "no" },
|
||||
{ "format": "{name}", "name": "Polish", "code": "pl" },
|
||||
{ "format": "{name}", "name": "Portuguese", "code": "pt" },
|
||||
{ "format": "{name}", "name": "Russian", "code": "ru" },
|
||||
{ "format": "{name}", "name": "Spanish", "code": "es" },
|
||||
{ "format": "{name}", "name": "Swedish", "code": "sv" }
|
||||
]
|
||||
|
||||
@@ -1,184 +0,0 @@
|
||||
code code3 name
|
||||
aa aar Afar
|
||||
ab abk Abkhazian
|
||||
ae ave Avestan
|
||||
af afr Afrikaans
|
||||
ak aka Akan
|
||||
am amh Amharic
|
||||
an arg Aragonese
|
||||
ar ara Arabic
|
||||
as asm Assamese
|
||||
av ava Avaric
|
||||
ay aym Aymara
|
||||
az aze Azerbaijani
|
||||
ba bak Bashkir
|
||||
be bel Belarusian
|
||||
bg bul Bulgarian
|
||||
bi bis Bislama
|
||||
bm bam Bambara
|
||||
bn ben Bengali
|
||||
bo bod Tibetan
|
||||
br bre Breton
|
||||
bs bos Bosnian
|
||||
ca cat Catalan
|
||||
ce che Chechen
|
||||
ch cha Chamorro
|
||||
co cos Corsican
|
||||
cr cre Cree
|
||||
cs ces Czech
|
||||
cu chu Church Slavic
|
||||
cv chv Chuvash
|
||||
cy cym Welsh
|
||||
da dan Danish
|
||||
de deu German
|
||||
dv div Divehi
|
||||
dz dzo Dzongkha
|
||||
ee ewe Ewe
|
||||
el ell Modern Greek
|
||||
en eng English
|
||||
eo epo Esperanto
|
||||
es spa Spanish
|
||||
et est Estonian
|
||||
eu eus Basque
|
||||
fa fas Persian
|
||||
ff ful Fulah
|
||||
fi fin Finnish
|
||||
fj fij Fijian
|
||||
fo fao Faroese
|
||||
fr fra French
|
||||
fy fry Western Frisian
|
||||
ga gle Irish
|
||||
gd gla Gaelic
|
||||
gl glg Galician
|
||||
gn grn Guarani
|
||||
gu guj Gujarati
|
||||
gv glv Manx
|
||||
ha hau Hausa
|
||||
he heb Hebrew
|
||||
hi hin Hindi
|
||||
ho hmo Hiri Motu
|
||||
hr hrv Croatian
|
||||
ht hat Haitian
|
||||
hu hun Hungarian
|
||||
hy hye Armenian
|
||||
hz her Herero
|
||||
ia ina Interlingua
|
||||
id ind Indonesian
|
||||
ie ile Interlingue
|
||||
ig ibo Igbo
|
||||
ii iii Sichuan Yi
|
||||
ik ipk Inupiaq
|
||||
io ido Ido
|
||||
is isl Icelandic
|
||||
it ita Italian
|
||||
iu iku Inuktitut
|
||||
ja jpn Japanese
|
||||
jv jav Javanese
|
||||
ka kat Georgian
|
||||
kg kon Kongo
|
||||
ki kik Kikuyu
|
||||
kj kua Kuanyama
|
||||
kk kaz Kazakh
|
||||
kl kal Kalaallisut
|
||||
km khm Central Khmer
|
||||
kn kan Kannada
|
||||
ko kor Korean
|
||||
kr kau Kanuri
|
||||
ks kas Kashmiri
|
||||
ku kur Kurdish
|
||||
kv kom Komi
|
||||
kw cor Cornish
|
||||
ky kir Kirghiz
|
||||
la lat Latin
|
||||
lb ltz Luxembourgish
|
||||
lg lug Ganda
|
||||
li lim Limburgan
|
||||
ln lin Lingala
|
||||
lo lao Lao
|
||||
lt lit Lithuanian
|
||||
lu lub Luba-Katanga
|
||||
lv lav Latvian
|
||||
mg mlg Malagasy
|
||||
mh mah Marshallese
|
||||
mi mri Maori
|
||||
mk mkd Macedonian
|
||||
ml mal Malayalam
|
||||
mn mon Mongolian
|
||||
mr mar Marathi
|
||||
ms msa Malay
|
||||
mt mlt Maltese
|
||||
my mya Burmese
|
||||
na nau Nauru
|
||||
nb nob Norwegian Bokmål
|
||||
nd nde North Ndebele
|
||||
ne nep Nepali
|
||||
ng ndo Ndonga
|
||||
nl nld Dutch
|
||||
nn nno Norwegian Nynorsk
|
||||
no nor Norwegian
|
||||
nr nbl South Ndebele
|
||||
nv nav Navajo
|
||||
ny nya Chichewa
|
||||
oc oci Occitan
|
||||
oj oji Ojibwa
|
||||
om orm Oromo
|
||||
or ori Oriya
|
||||
os oss Ossetian
|
||||
pa pan Panjabi
|
||||
pi pli Pali
|
||||
pl pol Polish
|
||||
ps pus Pushto
|
||||
pt por Portuguese
|
||||
qu que Quechua
|
||||
rm roh Romansh
|
||||
rn run Rundi
|
||||
ro ron Romanian
|
||||
ru rus Russian
|
||||
rw kin Kinyarwanda
|
||||
sa san Sanskrit
|
||||
sc srd Sardinian
|
||||
sd snd Sindhi
|
||||
se sme Northern Sami
|
||||
sg sag Sango
|
||||
si sin Sinhala
|
||||
sk slk Slovak
|
||||
sl slv Slovenian
|
||||
sm smo Samoan
|
||||
sn sna Shona
|
||||
so som Somali
|
||||
sq sqi Albanian
|
||||
sr srp Serbian
|
||||
ss ssw Swati
|
||||
st sot Sotho, Southern
|
||||
su sun Sundanese
|
||||
sv swe Swedish
|
||||
sw swa Swahili
|
||||
ta tam Tamil
|
||||
te tel Telugu
|
||||
tg tgk Tajik
|
||||
th tha Thai
|
||||
ti tir Tigrinya
|
||||
tk tuk Turkmen
|
||||
tl tgl Tagalog
|
||||
tn tsn Tswana
|
||||
to ton Tonga
|
||||
tr tur Turkish
|
||||
ts tso Tsonga
|
||||
tt tat Tatar
|
||||
tw twi Twi
|
||||
ty tah Tahitian
|
||||
ug uig Uighur
|
||||
uk ukr Ukrainian
|
||||
ur urd Urdu
|
||||
uz uzb Uzbek
|
||||
ve ven Venda
|
||||
vi vie Vietnamese
|
||||
vo vol Volapük
|
||||
wa wln Walloon
|
||||
wo wol Wolof
|
||||
xh xho Xhosa
|
||||
yi yid Yiddish
|
||||
yo yor Yoruba
|
||||
za zha Zhuang
|
||||
zh zho Chinese
|
||||
zu zul Zulu
|
||||
|
@@ -1 +0,0 @@
|
||||
{ "format": "{keyword}", "rows": "loglevel.tsv", "key": "code", "name": "keyword" }
|
||||
@@ -1,9 +0,0 @@
|
||||
code keyword severity
|
||||
0 emerg Emergency
|
||||
1 alert Alert
|
||||
2 crit Critical
|
||||
3 err Error
|
||||
4 warning Warning
|
||||
5 notice Notice
|
||||
6 info Informational
|
||||
7 debug Debug
|
||||
|
+17
-1
@@ -1 +1,17 @@
|
||||
{ "format": "{type}", "rows": "mimetype.tsv", "key": "type", "name": "ext" }
|
||||
[
|
||||
{ "format": "{type}", "type": "application/gzip", "ext": ".gz" },
|
||||
{ "format": "{type}", "type": "application/json", "ext": ".json" },
|
||||
{ "format": "{type}", "type": "application/pdf", "ext": ".pdf" },
|
||||
{ "format": "{type}", "type": "application/xml", "ext": ".xml" },
|
||||
{ "format": "{type}", "type": "application/zip", "ext": ".zip" },
|
||||
{ "format": "{type}", "type": "audio/mpeg", "ext": ".mp3" },
|
||||
{ "format": "{type}", "type": "image/gif", "ext": ".gif" },
|
||||
{ "format": "{type}", "type": "image/jpeg", "ext": ".jpg" },
|
||||
{ "format": "{type}", "type": "image/png", "ext": ".png" },
|
||||
{ "format": "{type}", "type": "image/svg+xml", "ext": ".svg" },
|
||||
{ "format": "{type}", "type": "image/webp", "ext": ".webp" },
|
||||
{ "format": "{type}", "type": "text/csv", "ext": ".csv" },
|
||||
{ "format": "{type}", "type": "text/html", "ext": ".html" },
|
||||
{ "format": "{type}", "type": "text/plain", "ext": ".txt" },
|
||||
{ "format": "{type}", "type": "video/mp4", "ext": ".mp4" }
|
||||
]
|
||||
|
||||
@@ -1,702 +0,0 @@
|
||||
ext type
|
||||
.ez application/andrew-inset
|
||||
.atom application/atom+xml
|
||||
.atomcat application/atomcat+xml
|
||||
.atomdeleted application/atomdeleted+xml
|
||||
.atomsvc application/atomsvc+xml
|
||||
.dwd application/atsc-dwd+xml
|
||||
.held application/atsc-held+xml
|
||||
.rsat application/atsc-rsat+xml
|
||||
.aml application/automationml-aml+xml
|
||||
.amlx application/automationml-amlx+zip
|
||||
.xcs application/calendar+xml
|
||||
.ccxml application/ccxml+xml
|
||||
.cdfx application/cdfx+xml
|
||||
.cdmia application/cdmi-capability
|
||||
.cdmic application/cdmi-container
|
||||
.cdmid application/cdmi-domain
|
||||
.cdmio application/cdmi-object
|
||||
.cdmiq application/cdmi-queue
|
||||
.cpl application/cpl+xml
|
||||
.cwl application/cwl
|
||||
.mpd application/dash+xml
|
||||
.mpp application/dash-patch+xml
|
||||
.davmount application/davmount+xml
|
||||
.dcm application/dicom
|
||||
.dssc application/dssc+der
|
||||
.xdssc application/dssc+xml
|
||||
.emma application/emma+xml
|
||||
.emotionml application/emotionml+xml
|
||||
.epub application/epub+zip
|
||||
.exi application/exi
|
||||
.exp application/express
|
||||
.fdf application/fdf
|
||||
.fdt application/fdt+xml
|
||||
.pfr application/font-tdpfr
|
||||
.geojson application/geo+json
|
||||
.gml application/gml+xml
|
||||
.gz application/gzip
|
||||
.stk application/hyperstudio
|
||||
.ink application/inkml+xml
|
||||
.ipfix application/ipfix
|
||||
.its application/its+xml
|
||||
.jar application/java-archive
|
||||
.json application/json
|
||||
.kbl application/kbl+xml
|
||||
.jsonld application/ld+json
|
||||
.lgr application/lgr+xml
|
||||
.lostxml application/lost+xml
|
||||
.hqx application/mac-binhex40
|
||||
.mads application/mads+xml
|
||||
.webmanifest application/manifest+json
|
||||
.mrc application/marc
|
||||
.mrcx application/marcxml+xml
|
||||
.ma application/mathematica
|
||||
.mathml application/mathml+xml
|
||||
.mbox application/mbox
|
||||
.mpf application/media-policy-dataset+xml
|
||||
.mscml application/mediaservercontrol+xml
|
||||
.meta4 application/metalink4+xml
|
||||
.mets application/mets+xml
|
||||
.maei application/mmt-aei+xml
|
||||
.musd application/mmt-usd+xml
|
||||
.mods application/mods+xml
|
||||
.m21 application/mp21
|
||||
.mp4s application/mp4
|
||||
.doc application/msword
|
||||
.mxf application/mxf
|
||||
.nq application/n-quads
|
||||
.nt application/n-triples
|
||||
.cjs application/node
|
||||
.bin application/octet-stream
|
||||
.oda application/oda
|
||||
.opf application/oebps-package+xml
|
||||
.ogx application/ogg
|
||||
.oxps application/oxps
|
||||
.relo application/p2p-overlay+xml
|
||||
.xer application/patch-ops-error+xml
|
||||
.pdf application/pdf
|
||||
.pgp application/pgp-encrypted
|
||||
.asc application/pgp-keys
|
||||
.sig application/pgp-signature
|
||||
.p10 application/pkcs10
|
||||
.p7m application/pkcs7-mime
|
||||
.p7s application/pkcs7-signature
|
||||
.p8 application/pkcs8
|
||||
.ac application/pkix-attr-cert
|
||||
.cer application/pkix-cert
|
||||
.crl application/pkix-crl
|
||||
.pkipath application/pkix-pkipath
|
||||
.pki application/pkixcmp
|
||||
.pls application/pls+xml
|
||||
.ai application/postscript
|
||||
.provx application/provenance+xml
|
||||
.cww application/prs.cww
|
||||
.xsf application/prs.xsf+xml
|
||||
.pskcxml application/pskc+xml
|
||||
.rdf application/rdf+xml
|
||||
.rif application/reginfo+xml
|
||||
.rnc application/relax-ng-compact-syntax
|
||||
.rl application/resource-lists+xml
|
||||
.rld application/resource-lists-diff+xml
|
||||
.rs application/rls-services+xml
|
||||
.rapd application/route-apd+xml
|
||||
.sls application/route-s-tsid+xml
|
||||
.rusd application/route-usd+xml
|
||||
.gbr application/rpki-ghostbusters
|
||||
.mft application/rpki-manifest
|
||||
.roa application/rpki-roa
|
||||
.rtf application/rtf
|
||||
.sbml application/sbml+xml
|
||||
.scq application/scvp-cv-request
|
||||
.scs application/scvp-cv-response
|
||||
.spq application/scvp-vp-request
|
||||
.spp application/scvp-vp-response
|
||||
.sdp application/sdp
|
||||
.senmlx application/senml+xml
|
||||
.sensmlx application/sensml+xml
|
||||
.setpay application/set-payment-initiation
|
||||
.setreg application/set-registration-initiation
|
||||
.shf application/shf+xml
|
||||
.siv application/sieve
|
||||
.smi application/smil+xml
|
||||
.rq application/sparql-query
|
||||
.srx application/sparql-results+xml
|
||||
.sql application/sql
|
||||
.gram application/srgs
|
||||
.grxml application/srgs+xml
|
||||
.sru application/sru+xml
|
||||
.ssml application/ssml+xml
|
||||
.swidtag application/swid+xml
|
||||
.tei application/tei+xml
|
||||
.tfi application/thraud+xml
|
||||
.tsd application/timestamped-data
|
||||
.toml application/toml
|
||||
.trig application/trig
|
||||
.ttml application/ttml+xml
|
||||
.rsheet application/urc-ressheet+xml
|
||||
.td application/urc-targetdesc+xml
|
||||
.vec application/vec+xml
|
||||
.1km application/vnd.1000minds.decision-model+xml
|
||||
.plb application/vnd.3gpp.pic-bw-large
|
||||
.psb application/vnd.3gpp.pic-bw-small
|
||||
.pvb application/vnd.3gpp.pic-bw-var
|
||||
.tcap application/vnd.3gpp2.tcap
|
||||
.pwn application/vnd.3m.post-it-notes
|
||||
.aso application/vnd.accpac.simply.aso
|
||||
.imp application/vnd.accpac.simply.imp
|
||||
.acu application/vnd.acucobol
|
||||
.atc application/vnd.acucorp
|
||||
.fcdt application/vnd.adobe.formscentral.fcdt
|
||||
.fxp application/vnd.adobe.fxp
|
||||
.xdp application/vnd.adobe.xdp+xml
|
||||
.age application/vnd.age
|
||||
.ahead application/vnd.ahead.space
|
||||
.azf application/vnd.airzip.filesecure.azf
|
||||
.azs application/vnd.airzip.filesecure.azs
|
||||
.acc application/vnd.americandynamics.acc
|
||||
.ami application/vnd.amiga.ami
|
||||
.cii application/vnd.anser-web-certificate-issue-initiation
|
||||
.atx application/vnd.antix.game-component
|
||||
.parquet application/vnd.apache.parquet
|
||||
.mpkg application/vnd.apple.installer+xml
|
||||
.key application/vnd.apple.keynote
|
||||
.m3u8 application/vnd.apple.mpegurl
|
||||
.numbers application/vnd.apple.numbers
|
||||
.pages application/vnd.apple.pages
|
||||
.swi application/vnd.aristanetworks.swi
|
||||
.iota application/vnd.astraea-software.iota
|
||||
.aep application/vnd.audiograph
|
||||
.bmml application/vnd.balsamiq.bmml+xml
|
||||
.mpm application/vnd.blueice.multipass
|
||||
.bmi application/vnd.bmi
|
||||
.rep application/vnd.businessobjects
|
||||
.cdxml application/vnd.chemdraw+xml
|
||||
.mmd application/vnd.chipnuts.karaoke-mmd
|
||||
.cdy application/vnd.cinderella
|
||||
.csl application/vnd.citationstyles.style+xml
|
||||
.cla application/vnd.claymore
|
||||
.rp9 application/vnd.cloanto.rp9
|
||||
.c4g application/vnd.clonk.c4group
|
||||
.c11amc application/vnd.cluetrust.cartomobile-config
|
||||
.c11amz application/vnd.cluetrust.cartomobile-config-pkg
|
||||
.csp application/vnd.commonspace
|
||||
.cdbcmsg application/vnd.contact.cmsg
|
||||
.cmc application/vnd.cosmocaller
|
||||
.clkx application/vnd.crick.clicker
|
||||
.clkk application/vnd.crick.clicker.keyboard
|
||||
.clkp application/vnd.crick.clicker.palette
|
||||
.clkt application/vnd.crick.clicker.template
|
||||
.clkw application/vnd.crick.clicker.wordbank
|
||||
.wbs application/vnd.criticaltools.wbs+xml
|
||||
.pml application/vnd.ctc-posml
|
||||
.ppd application/vnd.cups-ppd
|
||||
.dart application/vnd.dart
|
||||
.rdz application/vnd.data-vision.rdz
|
||||
.dbf application/vnd.dbf
|
||||
.dcmp application/vnd.dcmp+xml
|
||||
.uvf application/vnd.dece.data
|
||||
.uvt application/vnd.dece.ttml+xml
|
||||
.uvx application/vnd.dece.unspecified
|
||||
.uvz application/vnd.dece.zip
|
||||
.fe_launch application/vnd.denovo.fcselayout-link
|
||||
.dna application/vnd.dna
|
||||
.dpg application/vnd.dpgraph
|
||||
.dfac application/vnd.dreamfactory
|
||||
.ait application/vnd.dvb.ait
|
||||
.svc application/vnd.dvb.service
|
||||
.geo application/vnd.dynageo
|
||||
.mag application/vnd.ecowin.chart
|
||||
.nml application/vnd.enliven
|
||||
.esf application/vnd.epson.esf
|
||||
.msf application/vnd.epson.msf
|
||||
.qam application/vnd.epson.quickanime
|
||||
.slt application/vnd.epson.salt
|
||||
.ssf application/vnd.epson.ssf
|
||||
.es3 application/vnd.eszigno3+xml
|
||||
.ez2 application/vnd.ezpix-album
|
||||
.ez3 application/vnd.ezpix-package
|
||||
.mseed application/vnd.fdsn.mseed
|
||||
.seed application/vnd.fdsn.seed
|
||||
.gph application/vnd.flographit
|
||||
.ftc application/vnd.fluxtime.clip
|
||||
.fm application/vnd.framemaker
|
||||
.fsc application/vnd.fsc.weblaunch
|
||||
.oas application/vnd.fujitsu.oasys
|
||||
.oa2 application/vnd.fujitsu.oasys2
|
||||
.oa3 application/vnd.fujitsu.oasys3
|
||||
.fg5 application/vnd.fujitsu.oasysgp
|
||||
.bh2 application/vnd.fujitsu.oasysprs
|
||||
.ddd application/vnd.fujixerox.ddd
|
||||
.xdw application/vnd.fujixerox.docuworks
|
||||
.xbd application/vnd.fujixerox.docuworks.binder
|
||||
.fzs application/vnd.fuzzysheet
|
||||
.txd application/vnd.genomatix.tuxedo
|
||||
.ggb application/vnd.geogebra.file
|
||||
.ggs application/vnd.geogebra.slides
|
||||
.ggt application/vnd.geogebra.tool
|
||||
.gex application/vnd.geometry-explorer
|
||||
.gxt application/vnd.geonext
|
||||
.g2w application/vnd.geoplan
|
||||
.g3w application/vnd.geospace
|
||||
.gmx application/vnd.gmx
|
||||
.kml application/vnd.google-earth.kml+xml
|
||||
.kmz application/vnd.google-earth.kmz
|
||||
.xdcf application/vnd.gov.sk.xmldatacontainer+xml
|
||||
.gqf application/vnd.grafeq
|
||||
.gac application/vnd.groove-account
|
||||
.ghf application/vnd.groove-help
|
||||
.gim application/vnd.groove-identity-message
|
||||
.grv application/vnd.groove-injector
|
||||
.gtm application/vnd.groove-tool-message
|
||||
.tpl application/vnd.groove-tool-template
|
||||
.vcg application/vnd.groove-vcard
|
||||
.hal application/vnd.hal+xml
|
||||
.zmm application/vnd.handheld-entertainment+xml
|
||||
.hbci application/vnd.hbci
|
||||
.les application/vnd.hhe.lesson-player
|
||||
.hpgl application/vnd.hp-hpgl
|
||||
.hpid application/vnd.hp-hpid
|
||||
.hps application/vnd.hp-hps
|
||||
.jlt application/vnd.hp-jlyt
|
||||
.pcl application/vnd.hp-pcl
|
||||
.pclxl application/vnd.hp-pclxl
|
||||
.sfd-hdstx application/vnd.hydrostatix.sof-data
|
||||
.mpy application/vnd.ibm.minipay
|
||||
.irm application/vnd.ibm.rights-management
|
||||
.sc application/vnd.ibm.secure-container
|
||||
.icc application/vnd.iccprofile
|
||||
.igl application/vnd.igloader
|
||||
.ivp application/vnd.immervision-ivp
|
||||
.ivu application/vnd.immervision-ivu
|
||||
.igm application/vnd.insors.igm
|
||||
.xpw application/vnd.intercon.formnet
|
||||
.i2g application/vnd.intergeo
|
||||
.qbo application/vnd.intu.qbo
|
||||
.qfx application/vnd.intu.qfx
|
||||
.rcprofile application/vnd.ipunplugged.rcprofile
|
||||
.irp application/vnd.irepository.package+xml
|
||||
.xpr application/vnd.is-xpr
|
||||
.fcs application/vnd.isac.fcs
|
||||
.jam application/vnd.jam
|
||||
.rms application/vnd.jcp.javame.midlet-rms
|
||||
.jisp application/vnd.jisp
|
||||
.joda application/vnd.joost.joda-archive
|
||||
.ktz application/vnd.kahootz
|
||||
.karbon application/vnd.kde.karbon
|
||||
.chrt application/vnd.kde.kchart
|
||||
.kfo application/vnd.kde.kformula
|
||||
.flw application/vnd.kde.kivio
|
||||
.kon application/vnd.kde.kontour
|
||||
.kpr application/vnd.kde.kpresenter
|
||||
.ksp application/vnd.kde.kspread
|
||||
.kwd application/vnd.kde.kword
|
||||
.htke application/vnd.kenameaapp
|
||||
.kia application/vnd.kidspiration
|
||||
.kne application/vnd.kinar
|
||||
.skp application/vnd.koan
|
||||
.sse application/vnd.kodak-descriptor
|
||||
.lasxml application/vnd.las.las+xml
|
||||
.lbd application/vnd.llamagraphics.life-balance.desktop
|
||||
.lbe application/vnd.llamagraphics.life-balance.exchange+xml
|
||||
.123 application/vnd.lotus-1-2-3
|
||||
.apr application/vnd.lotus-approach
|
||||
.pre application/vnd.lotus-freelance
|
||||
.nsf application/vnd.lotus-notes
|
||||
.org application/vnd.lotus-organizer
|
||||
.scm application/vnd.lotus-screencam
|
||||
.lwp application/vnd.lotus-wordpro
|
||||
.portpkg application/vnd.macports.portpkg
|
||||
.mvt application/vnd.mapbox-vector-tile
|
||||
.mcd application/vnd.mcd
|
||||
.mc1 application/vnd.medcalcdata
|
||||
.cdkey application/vnd.mediastation.cdkey
|
||||
.mwf application/vnd.mfer
|
||||
.mfm application/vnd.mfmp
|
||||
.flo application/vnd.micrografx.flo
|
||||
.igx application/vnd.micrografx.igx
|
||||
.mif application/vnd.mif
|
||||
.daf application/vnd.mobius.daf
|
||||
.dis application/vnd.mobius.dis
|
||||
.mbk application/vnd.mobius.mbk
|
||||
.mqy application/vnd.mobius.mqy
|
||||
.msl application/vnd.mobius.msl
|
||||
.plc application/vnd.mobius.plc
|
||||
.txf application/vnd.mobius.txf
|
||||
.mpn application/vnd.mophun.application
|
||||
.mpc application/vnd.mophun.certificate
|
||||
.xul application/vnd.mozilla.xul+xml
|
||||
.cil application/vnd.ms-artgalry
|
||||
.cab application/vnd.ms-cab-compressed
|
||||
.xls application/vnd.ms-excel
|
||||
.xlam application/vnd.ms-excel.addin.macroenabled.12
|
||||
.xlsb application/vnd.ms-excel.sheet.binary.macroenabled.12
|
||||
.xlsm application/vnd.ms-excel.sheet.macroenabled.12
|
||||
.xltm application/vnd.ms-excel.template.macroenabled.12
|
||||
.eot application/vnd.ms-fontobject
|
||||
.chm application/vnd.ms-htmlhelp
|
||||
.ims application/vnd.ms-ims
|
||||
.lrm application/vnd.ms-lrm
|
||||
.thmx application/vnd.ms-officetheme
|
||||
.ppt application/vnd.ms-powerpoint
|
||||
.ppam application/vnd.ms-powerpoint.addin.macroenabled.12
|
||||
.pptm application/vnd.ms-powerpoint.presentation.macroenabled.12
|
||||
.sldm application/vnd.ms-powerpoint.slide.macroenabled.12
|
||||
.ppsm application/vnd.ms-powerpoint.slideshow.macroenabled.12
|
||||
.potm application/vnd.ms-powerpoint.template.macroenabled.12
|
||||
.mpp application/vnd.ms-project
|
||||
.docm application/vnd.ms-word.document.macroenabled.12
|
||||
.dotm application/vnd.ms-word.template.macroenabled.12
|
||||
.wps application/vnd.ms-works
|
||||
.wpl application/vnd.ms-wpl
|
||||
.xps application/vnd.ms-xpsdocument
|
||||
.mseq application/vnd.mseq
|
||||
.mus application/vnd.musician
|
||||
.msty application/vnd.muvee.style
|
||||
.taglet application/vnd.mynfc
|
||||
.bdo application/vnd.nato.bindingdataobject+xml
|
||||
.nlu application/vnd.neurolanguage.nlu
|
||||
.ntf application/vnd.nitf
|
||||
.nnd application/vnd.noblenet-directory
|
||||
.nns application/vnd.noblenet-sealer
|
||||
.nnw application/vnd.noblenet-web
|
||||
.ac application/vnd.nokia.n-gage.ac+xml
|
||||
.ngdat application/vnd.nokia.n-gage.data
|
||||
.rpst application/vnd.nokia.radio-preset
|
||||
.rpss application/vnd.nokia.radio-presets
|
||||
.edm application/vnd.novadigm.edm
|
||||
.edx application/vnd.novadigm.edx
|
||||
.ext application/vnd.novadigm.ext
|
||||
.odc application/vnd.oasis.opendocument.chart
|
||||
.otc application/vnd.oasis.opendocument.chart-template
|
||||
.odf application/vnd.oasis.opendocument.formula
|
||||
.odft application/vnd.oasis.opendocument.formula-template
|
||||
.odg application/vnd.oasis.opendocument.graphics
|
||||
.otg application/vnd.oasis.opendocument.graphics-template
|
||||
.odi application/vnd.oasis.opendocument.image
|
||||
.oti application/vnd.oasis.opendocument.image-template
|
||||
.odp application/vnd.oasis.opendocument.presentation
|
||||
.otp application/vnd.oasis.opendocument.presentation-template
|
||||
.ods application/vnd.oasis.opendocument.spreadsheet
|
||||
.ots application/vnd.oasis.opendocument.spreadsheet-template
|
||||
.odt application/vnd.oasis.opendocument.text
|
||||
.odm application/vnd.oasis.opendocument.text-master
|
||||
.ott application/vnd.oasis.opendocument.text-template
|
||||
.oth application/vnd.oasis.opendocument.text-web
|
||||
.xo application/vnd.olpc-sugar
|
||||
.dd2 application/vnd.oma.dd2+xml
|
||||
.obgx application/vnd.openblox.game+xml
|
||||
.osm application/vnd.openstreetmap.data+xml
|
||||
.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
.sldx application/vnd.openxmlformats-officedocument.presentationml.slide
|
||||
.ppsx application/vnd.openxmlformats-officedocument.presentationml.slideshow
|
||||
.potx application/vnd.openxmlformats-officedocument.presentationml.template
|
||||
.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
.xltx application/vnd.openxmlformats-officedocument.spreadsheetml.template
|
||||
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
.dotx application/vnd.openxmlformats-officedocument.wordprocessingml.template
|
||||
.mgp application/vnd.osgeo.mapguide.package
|
||||
.dp application/vnd.osgi.dp
|
||||
.esa application/vnd.osgi.subsystem
|
||||
.pdb application/vnd.palm
|
||||
.paw application/vnd.pawaafile
|
||||
.str application/vnd.pg.format
|
||||
.ei6 application/vnd.pg.osasli
|
||||
.efif application/vnd.picsel
|
||||
.wg application/vnd.pmi.widget
|
||||
.plf application/vnd.pocketlearn
|
||||
.pbd application/vnd.powerbuilder6
|
||||
.systemverify application/vnd.pp.systemverify+xml
|
||||
.box application/vnd.previewsystems.box
|
||||
.mgz application/vnd.proteus.magazine
|
||||
.qps application/vnd.publishare-delta-tree
|
||||
.ptid application/vnd.pvi.ptid1
|
||||
.xhtm application/vnd.pwg-xhtml-print+xml
|
||||
.qxd application/vnd.quark.quarkxpress
|
||||
.rar application/vnd.rar
|
||||
.bed application/vnd.realvnc.bed
|
||||
.mxl application/vnd.recordare.musicxml
|
||||
.musicxml application/vnd.recordare.musicxml+xml
|
||||
.cryptonote application/vnd.rig.cryptonote
|
||||
.link66 application/vnd.route66.link66+xml
|
||||
.st application/vnd.sailingtracker.track
|
||||
.see application/vnd.seemail
|
||||
.sema application/vnd.sema
|
||||
.semd application/vnd.semd
|
||||
.semf application/vnd.semf
|
||||
.ifm application/vnd.shana.informed.formdata
|
||||
.itp application/vnd.shana.informed.formtemplate
|
||||
.iif application/vnd.shana.informed.interchange
|
||||
.ipk application/vnd.shana.informed.package
|
||||
.twd application/vnd.simtech-mindmapper
|
||||
.mmf application/vnd.smaf
|
||||
.teacher application/vnd.smart.teacher
|
||||
.fo application/vnd.software602.filler.form+xml
|
||||
.sdkm application/vnd.solent.sdkm+xml
|
||||
.dxp application/vnd.spotfire.dxp
|
||||
.sfs application/vnd.spotfire.sfs
|
||||
.sqlite application/vnd.sqlite3
|
||||
.smzip application/vnd.stepmania.package
|
||||
.sm application/vnd.stepmania.stepchart
|
||||
.wadl application/vnd.sun.wadl+xml
|
||||
.sus application/vnd.sus-calendar
|
||||
.svd application/vnd.svd
|
||||
.xsm application/vnd.syncml+xml
|
||||
.bdm application/vnd.syncml.dm+wbxml
|
||||
.xdm application/vnd.syncml.dm+xml
|
||||
.ddf application/vnd.syncml.dmddf+xml
|
||||
.tao application/vnd.tao.intent-module-archive
|
||||
.pcap application/vnd.tcpdump.pcap
|
||||
.tmo application/vnd.tmobile-livetv
|
||||
.tpt application/vnd.trid.tpt
|
||||
.mxs application/vnd.triscape.mxs
|
||||
.tra application/vnd.trueapp
|
||||
.ufd application/vnd.ufdl
|
||||
.utz application/vnd.uiq.theme
|
||||
.umj application/vnd.umajin
|
||||
.unityweb application/vnd.unity
|
||||
.uoml application/vnd.uoml+xml
|
||||
.vcx application/vnd.vcx
|
||||
.vsd application/vnd.visio
|
||||
.vis application/vnd.visionary
|
||||
.vsf application/vnd.vsf
|
||||
.wbxml application/vnd.wap.wbxml
|
||||
.wmlc application/vnd.wap.wmlc
|
||||
.wmlsc application/vnd.wap.wmlscriptc
|
||||
.wtb application/vnd.webturbo
|
||||
.nbp application/vnd.wolfram.player
|
||||
.wpd application/vnd.wordperfect
|
||||
.wqd application/vnd.wqd
|
||||
.stf application/vnd.wt.stf
|
||||
.xar application/vnd.xara
|
||||
.xfdl application/vnd.xfdl
|
||||
.hvd application/vnd.yamaha.hv-dic
|
||||
.hvs application/vnd.yamaha.hv-script
|
||||
.hvp application/vnd.yamaha.hv-voice
|
||||
.osf application/vnd.yamaha.openscoreformat
|
||||
.osfpvg application/vnd.yamaha.openscoreformat.osfpvg+xml
|
||||
.saf application/vnd.yamaha.smaf-audio
|
||||
.spf application/vnd.yamaha.smaf-phrase
|
||||
.cmp application/vnd.yellowriver-custom-menu
|
||||
.zir application/vnd.zul
|
||||
.zaz application/vnd.zzazz.deck+xml
|
||||
.vxml application/voicexml+xml
|
||||
.wasm application/wasm
|
||||
.wif application/watcherinfo+xml
|
||||
.wgt application/widget
|
||||
.wsdl application/wsdl+xml
|
||||
.wspolicy application/wspolicy+xml
|
||||
.der application/x-x509-ca-cert
|
||||
.xav application/xcap-att+xml
|
||||
.xca application/xcap-caps+xml
|
||||
.xdf application/xcap-diff+xml
|
||||
.xel application/xcap-el+xml
|
||||
.xns application/xcap-ns+xml
|
||||
.xenc application/xenc+xml
|
||||
.xfdf application/xfdf
|
||||
.xhtml application/xhtml+xml
|
||||
.xlf application/xliff+xml
|
||||
.xml application/xml
|
||||
.dtd application/xml-dtd
|
||||
.xop application/xop+xml
|
||||
.xsl application/xslt+xml
|
||||
.mxml application/xv+xml
|
||||
.yang application/yang
|
||||
.yin application/yin+xml
|
||||
.zip application/zip
|
||||
.3gpp audio/3gpp
|
||||
.adts audio/aac
|
||||
.amr audio/amr
|
||||
.au audio/basic
|
||||
.mka audio/matroska
|
||||
.mxmf audio/mobile-xmf
|
||||
.m4a audio/mp4
|
||||
.mp3 audio/mpeg
|
||||
.ogg audio/ogg
|
||||
.uva audio/vnd.dece.audio
|
||||
.eol audio/vnd.digital-winds
|
||||
.dra audio/vnd.dra
|
||||
.dts audio/vnd.dts
|
||||
.dtshd audio/vnd.dts.hd
|
||||
.lvp audio/vnd.lucent.voice
|
||||
.pya audio/vnd.ms-playready.media.pya
|
||||
.ecelp4800 audio/vnd.nuera.ecelp4800
|
||||
.ecelp7470 audio/vnd.nuera.ecelp7470
|
||||
.ecelp9600 audio/vnd.nuera.ecelp9600
|
||||
.rip audio/vnd.rip
|
||||
.ttc font/collection
|
||||
.otf font/otf
|
||||
.ttf font/ttf
|
||||
.woff font/woff
|
||||
.woff2 font/woff2
|
||||
.exr image/aces
|
||||
.apng image/apng
|
||||
.avci image/avci
|
||||
.avcs image/avcs
|
||||
.avif image/avif
|
||||
.bmp image/bmp
|
||||
.cgm image/cgm
|
||||
.drle image/dicom-rle
|
||||
.dpx image/dpx
|
||||
.emf image/emf
|
||||
.fits image/fits
|
||||
.g3 image/g3fax
|
||||
.gif image/gif
|
||||
.heic image/heic
|
||||
.heics image/heic-sequence
|
||||
.heif image/heif
|
||||
.heifs image/heif-sequence
|
||||
.hej2 image/hej2k
|
||||
.ief image/ief
|
||||
.jaii image/jaii
|
||||
.jais image/jais
|
||||
.jls image/jls
|
||||
.jp2 image/jp2
|
||||
.jpg image/jpeg
|
||||
.jph image/jph
|
||||
.jhc image/jphc
|
||||
.jpm image/jpm
|
||||
.jpx image/jpx
|
||||
.jxl image/jxl
|
||||
.jxr image/jxr
|
||||
.jxra image/jxra
|
||||
.jxrs image/jxrs
|
||||
.jxs image/jxs
|
||||
.jxsc image/jxsc
|
||||
.jxsi image/jxsi
|
||||
.jxss image/jxss
|
||||
.ktx image/ktx
|
||||
.ktx2 image/ktx2
|
||||
.png image/png
|
||||
.btif image/prs.btif
|
||||
.pti image/prs.pti
|
||||
.svg image/svg+xml
|
||||
.t38 image/t38
|
||||
.tif image/tiff
|
||||
.tfx image/tiff-fx
|
||||
.psd image/vnd.adobe.photoshop
|
||||
.azv image/vnd.airzip.accelerator.azv
|
||||
.facti image/vnd.blockfact.facti
|
||||
.uvi image/vnd.dece.graphic
|
||||
.djvu image/vnd.djvu
|
||||
.sub image/vnd.dvb.subtitle
|
||||
.dwg image/vnd.dwg
|
||||
.dxf image/vnd.dxf
|
||||
.fbs image/vnd.fastbidsheet
|
||||
.fpx image/vnd.fpx
|
||||
.fst image/vnd.fst
|
||||
.mmr image/vnd.fujixerox.edmics-mmr
|
||||
.rlc image/vnd.fujixerox.edmics-rlc
|
||||
.ico image/vnd.microsoft.icon
|
||||
.mdi image/vnd.ms-modi
|
||||
.npx image/vnd.net-fpx
|
||||
.b16 image/vnd.pco.b16
|
||||
.tap image/vnd.tencent.tap
|
||||
.vtf image/vnd.valve.source.texture
|
||||
.wbmp image/vnd.wap.wbmp
|
||||
.xif image/vnd.xiff
|
||||
.pcx image/vnd.zbrush.pcx
|
||||
.webp image/webp
|
||||
.wmf image/wmf
|
||||
.disposition-notification message/disposition-notification
|
||||
.u8msg message/global
|
||||
.u8dsn message/global-delivery-status
|
||||
.u8mdn message/global-disposition-notification
|
||||
.u8hdr message/global-headers
|
||||
.eml message/rfc822
|
||||
.wsc message/vnd.wfa.wsc
|
||||
.3mf model/3mf
|
||||
.gltf model/gltf+json
|
||||
.glb model/gltf-binary
|
||||
.igs model/iges
|
||||
.jt model/jt
|
||||
.msh model/mesh
|
||||
.mtl model/mtl
|
||||
.obj model/obj
|
||||
.prc model/prc
|
||||
.step model/step
|
||||
.stpx model/step+xml
|
||||
.stpz model/step+zip
|
||||
.stpxz model/step-xml+zip
|
||||
.stl model/stl
|
||||
.u3d model/u3d
|
||||
.bary model/vnd.bary
|
||||
.cld model/vnd.cld
|
||||
.dae model/vnd.collada+xml
|
||||
.dwf model/vnd.dwf
|
||||
.gdl model/vnd.gdl
|
||||
.gtw model/vnd.gtw
|
||||
.mts model/vnd.mts
|
||||
.ogex model/vnd.opengex
|
||||
.x_b model/vnd.parasolid.transmit.binary
|
||||
.x_t model/vnd.parasolid.transmit.text
|
||||
.pyo model/vnd.pytha.pyox
|
||||
.vds model/vnd.sap.vds
|
||||
.usda model/vnd.usda
|
||||
.usdz model/vnd.usdz+zip
|
||||
.bsp model/vnd.valve.source.compiled-map
|
||||
.vtu model/vnd.vtu
|
||||
.wrl model/vrml
|
||||
.x3db model/x3d+fastinfoset
|
||||
.x3d model/x3d+xml
|
||||
.x3dv model/x3d-vrml
|
||||
.appcache text/cache-manifest
|
||||
.ics text/calendar
|
||||
.css text/css
|
||||
.csv text/csv
|
||||
.html text/html
|
||||
.js text/javascript
|
||||
.md text/markdown
|
||||
.n3 text/n3
|
||||
.txt text/plain
|
||||
.dsc text/prs.lines.tag
|
||||
.rtx text/richtext
|
||||
.rtf text/rtf
|
||||
.sgml text/sgml
|
||||
.shex text/shex
|
||||
.spdx text/spdx
|
||||
.tsv text/tab-separated-values
|
||||
.t text/troff
|
||||
.ttl text/turtle
|
||||
.uri text/uri-list
|
||||
.vcard text/vcard
|
||||
.curl text/vnd.curl
|
||||
.sub text/vnd.dvb.subtitle
|
||||
.ged text/vnd.familysearch.gedcom
|
||||
.fly text/vnd.fly
|
||||
.flx text/vnd.fmi.flexstor
|
||||
.gv text/vnd.graphviz
|
||||
.3dml text/vnd.in3d.3dml
|
||||
.spot text/vnd.in3d.spot
|
||||
.jad text/vnd.sun.j2me.app-descriptor
|
||||
.wml text/vnd.wap.wml
|
||||
.wmls text/vnd.wap.wmlscript
|
||||
.vtt text/vtt
|
||||
.wgsl text/wgsl
|
||||
.xml text/xml
|
||||
.3gp video/3gpp
|
||||
.3g2 video/3gpp2
|
||||
.h261 video/h261
|
||||
.h263 video/h263
|
||||
.h264 video/h264
|
||||
.m4s video/iso.segment
|
||||
.jpgv video/jpeg
|
||||
.mkv video/matroska
|
||||
.mk3d video/matroska-3d
|
||||
.mj2 video/mj2
|
||||
.ts video/mp2t
|
||||
.mp4 video/mp4
|
||||
.mpeg video/mpeg
|
||||
.ogv video/ogg
|
||||
.mov video/quicktime
|
||||
.uvh video/vnd.dece.hd
|
||||
.uvm video/vnd.dece.mobile
|
||||
.uvp video/vnd.dece.pd
|
||||
.uvs video/vnd.dece.sd
|
||||
.uvv video/vnd.dece.video
|
||||
.dvb video/vnd.dvb.file
|
||||
.fvt video/vnd.fvt
|
||||
.mxu video/vnd.mpegurl
|
||||
.pyv video/vnd.ms-playready.media.pyv
|
||||
.uvu video/vnd.uvvu.mp4
|
||||
.viv video/vnd.vivo
|
||||
|
@@ -1 +0,0 @@
|
||||
{ "format": "{number}", "rows": "port.tsv", "key": "number" }
|
||||
-5890
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
{ "format": "{keyword}", "rows": "protocol.tsv", "key": "keyword", "name": "name" }
|
||||
@@ -1,140 +0,0 @@
|
||||
keyword name number
|
||||
HOPOPT IPv6 Hop-by-Hop Option 0
|
||||
ICMP Internet Control Message 1
|
||||
IGMP Internet Group Management 2
|
||||
GGP Gateway-to-Gateway 3
|
||||
IPv4 IPv4 encapsulation 4
|
||||
ST Stream 5
|
||||
TCP Transmission Control 6
|
||||
CBT CBT 7
|
||||
EGP Exterior Gateway Protocol 8
|
||||
IGP any private interior gateway (used by Cisco for their IGRP) 9
|
||||
BBN-RCC-MON BBN RCC Monitoring 10
|
||||
NVP-II Network Voice Protocol 11
|
||||
PUP PUP 12
|
||||
EMCON EMCON 14
|
||||
XNET Cross Net Debugger 15
|
||||
CHAOS Chaos 16
|
||||
UDP User Datagram 17
|
||||
MUX Multiplexing 18
|
||||
DCN-MEAS DCN Measurement Subsystems 19
|
||||
HMP Host Monitoring 20
|
||||
PRM Packet Radio Measurement 21
|
||||
XNS-IDP XEROX NS IDP 22
|
||||
TRUNK-1 Trunk-1 23
|
||||
TRUNK-2 Trunk-2 24
|
||||
LEAF-1 Leaf-1 25
|
||||
LEAF-2 Leaf-2 26
|
||||
RDP Reliable Data Protocol 27
|
||||
IRTP Internet Reliable Transaction 28
|
||||
ISO-TP4 ISO Transport Protocol Class 4 29
|
||||
NETBLT Bulk Data Transfer Protocol 30
|
||||
MFE-NSP MFE Network Services Protocol 31
|
||||
MERIT-INP MERIT Internodal Protocol 32
|
||||
DCCP Datagram Congestion Control Protocol 33
|
||||
3PC Third Party Connect Protocol 34
|
||||
IDPR Inter-Domain Policy Routing Protocol 35
|
||||
XTP XTP 36
|
||||
DDP Datagram Delivery Protocol 37
|
||||
IDPR-CMTP IDPR Control Message Transport Proto 38
|
||||
TP++ TP++ Transport Protocol 39
|
||||
IL IL Transport Protocol 40
|
||||
IPv6 IPv6 encapsulation 41
|
||||
SDRP Source Demand Routing Protocol 42
|
||||
IPv6-Route Routing Header for IPv6 43
|
||||
IPv6-Frag Fragment Header for IPv6 44
|
||||
IDRP Inter-Domain Routing Protocol 45
|
||||
RSVP Reservation Protocol 46
|
||||
GRE Generic Routing Encapsulation 47
|
||||
DSR Dynamic Source Routing Protocol 48
|
||||
BNA BNA 49
|
||||
ESP Encap Security Payload 50
|
||||
AH Authentication Header 51
|
||||
I-NLSP Integrated Net Layer Security TUBA 52
|
||||
NARP NBMA Address Resolution Protocol 54
|
||||
Min-IPv4 Minimal IPv4 Encapsulation 55
|
||||
TLSP Transport Layer Security Protocol using Kryptonet key management 56
|
||||
SKIP SKIP 57
|
||||
IPv6-ICMP ICMP for IPv6 58
|
||||
IPv6-NoNxt No Next Header for IPv6 59
|
||||
IPv6-Opts Destination Options for IPv6 60
|
||||
CFTP CFTP 62
|
||||
SAT-EXPAK SATNET and Backroom EXPAK 64
|
||||
KRYPTOLAN Kryptolan 65
|
||||
RVD MIT Remote Virtual Disk Protocol 66
|
||||
IPPC Internet Pluribus Packet Core 67
|
||||
SAT-MON SATNET Monitoring 69
|
||||
VISA VISA Protocol 70
|
||||
IPCV Internet Packet Core Utility 71
|
||||
CPNX Computer Protocol Network Executive 72
|
||||
CPHB Computer Protocol Heart Beat 73
|
||||
WSN Wang Span Network 74
|
||||
PVP Packet Video Protocol 75
|
||||
BR-SAT-MON Backroom SATNET Monitoring 76
|
||||
SUN-ND SUN ND PROTOCOL-Temporary 77
|
||||
WB-MON WIDEBAND Monitoring 78
|
||||
WB-EXPAK WIDEBAND EXPAK 79
|
||||
ISO-IP ISO Internet Protocol 80
|
||||
VMTP VMTP 81
|
||||
SECURE-VMTP SECURE-VMTP 82
|
||||
VINES VINES 83
|
||||
IPTM Internet Protocol Traffic Manager 84
|
||||
NSFNET-IGP NSFNET-IGP 85
|
||||
DGP Dissimilar Gateway Protocol 86
|
||||
TCF TCF 87
|
||||
EIGRP EIGRP 88
|
||||
OSPFIGP OSPFIGP 89
|
||||
Sprite-RPC Sprite RPC Protocol 90
|
||||
LARP Locus Address Resolution Protocol 91
|
||||
MTP Multicast Transport Protocol 92
|
||||
AX.25 AX.25 Frames 93
|
||||
IPIP IP-within-IP Encapsulation Protocol 94
|
||||
SCC-SP Semaphore Communications Sec. Pro. 96
|
||||
ETHERIP Ethernet-within-IP Encapsulation 97
|
||||
ENCAP Encapsulation Header 98
|
||||
GMTP GMTP 100
|
||||
IFMP Ipsilon Flow Management Protocol 101
|
||||
PNNI PNNI over IP 102
|
||||
PIM Protocol Independent Multicast 103
|
||||
ARIS ARIS 104
|
||||
SCPS SCPS 105
|
||||
QNX QNX 106
|
||||
A/N Active Networks 107
|
||||
IPComp IP Payload Compression Protocol 108
|
||||
SNP Sitara Networks Protocol 109
|
||||
Compaq-Peer Compaq Peer Protocol 110
|
||||
IPX-in-IP IPX in IP 111
|
||||
VRRP Virtual Router Redundancy Protocol 112
|
||||
PGM PGM Reliable Transport Protocol 113
|
||||
L2TP Layer Two Tunneling Protocol 115
|
||||
DDX D-II Data Exchange (DDX) 116
|
||||
IATP Interactive Agent Transfer Protocol 117
|
||||
STP Schedule Transfer Protocol 118
|
||||
SRP SpectraLink Radio Protocol 119
|
||||
UTI UTI 120
|
||||
SMP Simple Message Protocol 121
|
||||
PTP Performance Transparency Protocol 123
|
||||
ISIS over IPv4 ISIS over IPv4 124
|
||||
FIRE FIRE 125
|
||||
CRTP Combat Radio Transport Protocol 126
|
||||
CRUDP Combat Radio User Datagram 127
|
||||
SSCOPMCE SSCOPMCE 128
|
||||
IPLT IPLT 129
|
||||
SPS Secure Packet Shield 130
|
||||
PIPE Private IP Encapsulation within IP 131
|
||||
SCTP Stream Control Transmission Protocol 132
|
||||
FC Fibre Channel 133
|
||||
RSVP-E2E-IGNORE RSVP-E2E-IGNORE 134
|
||||
Mobility Header Mobility Header 135
|
||||
UDPLite UDPLite 136
|
||||
MPLS-in-IP MPLS-in-IP 137
|
||||
manet MANET Protocols 138
|
||||
HIP Host Identity Protocol 139
|
||||
Shim6 Shim6 Protocol 140
|
||||
WESP Wrapped Encapsulating Security Payload 141
|
||||
ROHC Robust Header Compression 142
|
||||
Ethernet Ethernet 143
|
||||
AGGFRAG AGGFRAG encapsulation payload for ESP 144
|
||||
NSH Network Service Header 145
|
||||
Homa Homa 146
|
||||
BIT-EMU Bit-stream Emulation 147
|
||||
|
@@ -1 +0,0 @@
|
||||
{ "format": "{name}", "rows": "territory.tsv", "key": "alpha2", "name": "name" }
|
||||
@@ -1,242 +0,0 @@
|
||||
alpha2 alpha3 calling-code capital country currency flag languages name numeric tld
|
||||
AD AND 376 Andorra la Vella AD EUR 🇦🇩 ca Andorra 020 .ad
|
||||
AE ARE 971 Abu Dhabi AE AED 🇦🇪 ar-AE,fa,en,hi,ur United Arab Emirates 784 .ae
|
||||
AF AFG 93 Kabul AF AFN 🇦🇫 fa-AF,ps,uz-AF,tk Afghanistan 004 .af
|
||||
AG ATG 1-268 St. John's AG XCD 🇦🇬 en-AG Antigua & Barbuda 028 .ag
|
||||
AI AIA 1-264 The Valley GB XCD 🇦🇮 en-AI Anguilla 660 .ai
|
||||
AL ALB 355 Tirana AL ALL 🇦🇱 sq,el Albania 008 .al
|
||||
AM ARM 374 Yerevan AM AMD 🇦🇲 hy Armenia 051 .am
|
||||
AO AGO 244 Luanda AO AOA 🇦🇴 pt-AO Angola 024 .ao
|
||||
AR ARG 54 Buenos Aires AR ARS 🇦🇷 es-AR,en,it,de,fr,gn Argentina 032 .ar
|
||||
AS ASM 1-684 Pago Pago US USD 🇦🇸 en-AS,sm,to American Samoa 016 .as
|
||||
AT AUT 43 Vienna AT EUR 🇦🇹 de-AT,hr,hu,sl Austria 040 .at
|
||||
AU AUS 61 Canberra AU AUD 🇦🇺 en-AU Australia 036 .au
|
||||
AW ABW 297 Oranjestad NL AWG 🇦🇼 nl-AW,pap,es,en Aruba 533 .aw
|
||||
AX ALA 358 Mariehamn FI EUR 🇦🇽 sv-AX Åland Islands 248 .ax
|
||||
AZ AZE 994 Baku AZ AZN 🇦🇿 az,ru,hy Azerbaijan 031 .az
|
||||
BA BIH 387 Sarajevo BA BAM 🇧🇦 bs,hr-BA,sr-BA Bosnia 070 .ba
|
||||
BB BRB 1-246 Bridgetown BB BBD 🇧🇧 en-BB Barbados 052 .bb
|
||||
BD BGD 880 Dhaka BD BDT 🇧🇩 bn-BD,en Bangladesh 050 .bd
|
||||
BE BEL 32 Brussels BE EUR 🇧🇪 nl-BE,fr-BE,de-BE Belgium 056 .be
|
||||
BF BFA 226 Ouagadougou BF XOF 🇧🇫 fr-BF,mos Burkina Faso 854 .bf
|
||||
BG BGR 359 Sofia BG EUR 🇧🇬 bg,tr-BG,rom Bulgaria 100 .bg
|
||||
BH BHR 973 Manama BH BHD 🇧🇭 ar-BH,en,fa,ur Bahrain 048 .bh
|
||||
BI BDI 257 Gitega BI BIF 🇧🇮 fr-BI,rn Burundi 108 .bi
|
||||
BJ BEN 229 Porto-Novo BJ XOF 🇧🇯 fr-BJ Benin 204 .bj
|
||||
BL BLM 590 Gustavia FR EUR 🇧🇱 fr St. Barthélemy 652 .gp
|
||||
BM BMU 1-441 Hamilton GB BMD 🇧🇲 en-BM,pt Bermuda 060 .bm
|
||||
BN BRN 673 Bandar Seri Begawan BN BND 🇧🇳 ms-BN,en-BN Brunei 096 .bn
|
||||
BO BOL 591 Sucre BO BOB 🇧🇴 es-BO,qu,ay Bolivia 068 .bo
|
||||
BR BRA 55 Brasilia BR BRL 🇧🇷 pt-BR,es,en,fr Brazil 076 .br
|
||||
BS BHS 1-242 Nassau BS BSD 🇧🇸 en-BS Bahamas 044 .bs
|
||||
BT BTN 975 Thimphu BT INR 🇧🇹 dz Bhutan 064 .bt
|
||||
BW BWA 267 Gaborone BW BWP 🇧🇼 en-BW,tn-BW Botswana 072 .bw
|
||||
BY BLR 375 Minsk BY BYN 🇧🇾 be,ru Belarus 112 .by
|
||||
BZ BLZ 501 Belmopan BZ BZD 🇧🇿 en-BZ,es Belize 084 .bz
|
||||
CA CAN 1 Ottawa CA CAD 🇨🇦 en-CA,fr-CA,iu Canada 124 .ca
|
||||
CC CCK 61 West Island AU AUD 🇨🇨 ms-CC,en Cocos Islands 166 .cc
|
||||
CD COD 243 Kinshasa CD CDF 🇨🇩 fr-CD,ln,ktu,kg,sw,lua Congo - Kinshasa 180 .cd
|
||||
CF CAF 236 Bangui CF XAF 🇨🇫 fr-CF,sg,ln,kg Central African Republic 140 .cf
|
||||
CG COG 242 Brazzaville CG XAF 🇨🇬 fr-CG,kg,ln-CG Congo - Brazzaville 178 .cg
|
||||
CH CHE 41 Bern CH CHF 🇨🇭 de-CH,fr-CH,it-CH,rm Switzerland 756 .ch
|
||||
CI CIV 225 Yamoussoukro CI XOF 🇨🇮 fr-CI Côte d’Ivoire 384 .ci
|
||||
CK COK 682 Avarua NZ NZD 🇨🇰 en-CK,mi Cook Islands 184 .ck
|
||||
CL CHL 56 Santiago CL CLP 🇨🇱 es-CL Chile 152 .cl
|
||||
CM CMR 237 Yaounde CM XAF 🇨🇲 en-CM,fr-CM Cameroon 120 .cm
|
||||
CN CHN 86 Beijing CN CNY 🇨🇳 zh-CN,yue,wuu,dta,ug,za China 156 .cn
|
||||
CO COL 57 Bogota CO COP 🇨🇴 es-CO Colombia 170 .co
|
||||
CR CRI 506 San Jose CR CRC 🇨🇷 es-CR,en Costa Rica 188 .cr
|
||||
CU CUB 53 Havana CU CUP 🇨🇺 es-CU,pap Cuba 192 .cu
|
||||
CV CPV 238 Praia CV CVE 🇨🇻 pt-CV Cape Verde 132 .cv
|
||||
CW CUW 599 Willemstad NL XCG 🇨🇼 nl,pap Curaçao 531 .cw
|
||||
CX CXR 61 Flying Fish Cove AU AUD 🇨🇽 en,zh,ms-CX Christmas Island 162 .cx
|
||||
CY CYP 357 Nicosia CY EUR 🇨🇾 el-CY,tr-CY,en Cyprus 196 .cy
|
||||
CZ CZE 420 Prague CZ CZK 🇨🇿 cs,sk Czechia 203 .cz
|
||||
DE DEU 49 Berlin DE EUR 🇩🇪 de Germany 276 .de
|
||||
DJ DJI 253 Djibouti DJ DJF 🇩🇯 fr-DJ,ar,so-DJ,aa Djibouti 262 .dj
|
||||
DK DNK 45 Copenhagen DK DKK 🇩🇰 da-DK,en,fo,de-DK Denmark 208 .dk
|
||||
DM DMA 1-767 Roseau DM XCD 🇩🇲 en-DM Dominica 212 .dm
|
||||
DO DOM 1-809 Santo Domingo DO DOP 🇩🇴 es-DO Dominican Republic 214 .do
|
||||
DZ DZA 213 Algiers DZ DZD 🇩🇿 ar-DZ Algeria 012 .dz
|
||||
EC ECU 593 Quito EC USD 🇪🇨 es-EC Ecuador 218 .ec
|
||||
EE EST 372 Tallinn EE EUR 🇪🇪 et,ru Estonia 233 .ee
|
||||
EG EGY 20 Cairo EG EGP 🇪🇬 ar-EG,en,fr Egypt 818 .eg
|
||||
EH ESH 212 El-Aaiun EH MAD 🇪🇭 ar,mey Western Sahara 732 .eh
|
||||
ER ERI 291 Asmara ER ERN 🇪🇷 aa-ER,ar,tig,kun,ti-ER Eritrea 232 .er
|
||||
ES ESP 34 Madrid ES EUR 🇪🇸 es-ES,ca,gl,eu,oc Spain 724 .es
|
||||
ET ETH 251 Addis Ababa ET ETB 🇪🇹 am,en-ET,om-ET,ti-ET,so-ET,sid Ethiopia 231 .et
|
||||
FI FIN 358 Helsinki FI EUR 🇫🇮 fi-FI,sv-FI,smn Finland 246 .fi
|
||||
FJ FJI 679 Suva FJ FJD 🇫🇯 en-FJ,fj Fiji 242 .fj
|
||||
FK FLK 500 Stanley GB FKP 🇫🇰 en-FK Falkland Islands 238 .fk
|
||||
FM FSM 691 Palikir FM USD 🇫🇲 en-FM,chk,pon,yap,kos,uli,woe,nkr,kpg Micronesia 583 .fm
|
||||
FO FRO 298 Torshavn DK DKK 🇫🇴 fo,da-FO Faroe Islands 234 .fo
|
||||
FR FRA 33 Paris FR EUR 🇫🇷 fr-FR,frp,br,co,ca,eu,oc France 250 .fr
|
||||
GA GAB 241 Libreville GA XAF 🇬🇦 fr-GA Gabon 266 .ga
|
||||
GB GBR 44 London GB GBP 🇬🇧 en-GB,cy-GB,gd UK 826 .uk
|
||||
GD GRD 1-473 St. George's GD XCD 🇬🇩 en-GD Grenada 308 .gd
|
||||
GE GEO 995 Tbilisi GE GEL 🇬🇪 ka,ru,hy,az Georgia 268 .ge
|
||||
GF GUF 594 Cayenne FR EUR 🇬🇫 fr-GF French Guiana 254 .gf
|
||||
GG GGY 44 St Peter Port GB GBP 🇬🇬 en,nrf Guernsey 831 .gg
|
||||
GH GHA 233 Accra GH GHS 🇬🇭 en-GH,ak,ee,tw Ghana 288 .gh
|
||||
GI GIB 350 Gibraltar GB GIP 🇬🇮 en-GI,es,it,pt Gibraltar 292 .gi
|
||||
GL GRL 299 Nuuk DK DKK 🇬🇱 kl,da-GL,en Greenland 304 .gl
|
||||
GM GMB 220 Banjul GM GMD 🇬🇲 en-GM,mnk,wof,wo,ff Gambia 270 .gm
|
||||
GN GIN 224 Conakry GN GNF 🇬🇳 fr-GN Guinea 324 .gn
|
||||
GP GLP 590 Basse-Terre FR EUR 🇬🇵 fr-GP Guadeloupe 312 .gp
|
||||
GQ GNQ 240 Ciudad de la Paz GQ XAF 🇬🇶 es-GQ,fr,pt Equatorial Guinea 226 .gq
|
||||
GR GRC 30 Athens GR EUR 🇬🇷 el-GR,en,fr Greece 300 .gr
|
||||
GT GTM 502 Guatemala City GT GTQ 🇬🇹 es-GT Guatemala 320 .gt
|
||||
GU GUM 1-671 Hagatna US USD 🇬🇺 en-GU,ch-GU Guam 316 .gu
|
||||
GW GNB 245 Bissau GW XOF 🇬🇼 pt-GW,pov Guinea-Bissau 624 .gw
|
||||
GY GUY 592 Georgetown GY GYD 🇬🇾 en-GY Guyana 328 .gy
|
||||
HK HKG 852 Hong Kong CN HKD 🇭🇰 zh-HK,yue,zh,en Hong Kong 344 .hk
|
||||
HN HND 504 Tegucigalpa HN HNL 🇭🇳 es-HN,cab,miq Honduras 340 .hn
|
||||
HR HRV 385 Zagreb HR EUR 🇭🇷 hr-HR,sr Croatia 191 .hr
|
||||
HT HTI 509 Port-au-Prince HT HTG 🇭🇹 ht,fr-HT Haiti 332 .ht
|
||||
HU HUN 36 Budapest HU HUF 🇭🇺 hu-HU Hungary 348 .hu
|
||||
ID IDN 62 Jakarta ID IDR 🇮🇩 id,en,nl,jv Indonesia 360 .id
|
||||
IE IRL 353 Dublin IE EUR 🇮🇪 en-IE,ga-IE Ireland 372 .ie
|
||||
IL ISR 972 Jerusalem IL ILS 🇮🇱 he,ar-IL,en-IL Israel 376 .il
|
||||
IM IMN 44 Douglas GB GBP 🇮🇲 en,gv Isle of Man 833 .im
|
||||
IN IND 91 New Delhi IN INR 🇮🇳 en-IN,hi,bn,te,mr,ta,ur,gu,kn,ml,or,pa,as,bh,sat,ks,ne,sd,kok,doi,mni,sit,sa,fr,lus,inc India 356 .in
|
||||
IO IOT 246 Diego Garcia GB USD 🇮🇴 en-IO British Indian Ocean Territory 086 .io
|
||||
IQ IRQ 964 Baghdad IQ IQD 🇮🇶 ar-IQ,ku,hy Iraq 368 .iq
|
||||
IR IRN 98 Tehran IR IRR 🇮🇷 fa-IR,ku Iran 364 .ir
|
||||
IS ISL 354 Reykjavik IS ISK 🇮🇸 is,en,de,da,sv,no Iceland 352 .is
|
||||
IT ITA 39 Rome IT EUR 🇮🇹 it-IT,de-IT,fr-IT,sc,ca,co,sl Italy 380 .it
|
||||
JE JEY 44 Saint Helier GB GBP 🇯🇪 en,fr,nrf Jersey 832 .je
|
||||
JM JAM 1-876 Kingston JM JMD 🇯🇲 en-JM Jamaica 388 .jm
|
||||
JO JOR 962 Amman JO JOD 🇯🇴 ar-JO,en Jordan 400 .jo
|
||||
JP JPN 81 Tokyo JP JPY 🇯🇵 ja Japan 392 .jp
|
||||
KE KEN 254 Nairobi KE KES 🇰🇪 en-KE,sw-KE Kenya 404 .ke
|
||||
KG KGZ 996 Bishkek KG KGS 🇰🇬 ky,uz,ru Kyrgyzstan 417 .kg
|
||||
KH KHM 855 Phnom Penh KH KHR 🇰🇭 km,fr,en Cambodia 116 .kh
|
||||
KI KIR 686 Tarawa KI AUD 🇰🇮 en-KI,gil Kiribati 296 .ki
|
||||
KM COM 269 Moroni KM KMF 🇰🇲 ar,fr-KM Comoros 174 .km
|
||||
KN KNA 1-869 Basseterre KN XCD 🇰🇳 en-KN St. Kitts & Nevis 659 .kn
|
||||
KP PRK 850 Pyongyang KP KPW 🇰🇵 ko-KP North Korea 408 .kp
|
||||
KR KOR 82 Seoul KR KRW 🇰🇷 ko-KR,en South Korea 410 .kr
|
||||
KW KWT 965 Kuwait City KW KWD 🇰🇼 ar-KW,en Kuwait 414 .kw
|
||||
KY CYM 1-345 George Town GB KYD 🇰🇾 en-KY Cayman Islands 136 .ky
|
||||
KZ KAZ 7 Nur-Sultan KZ KZT 🇰🇿 kk,ru Kazakhstan 398 .kz
|
||||
LA LAO 856 Vientiane LA LAK 🇱🇦 lo,fr,en Laos 418 .la
|
||||
LB LBN 961 Beirut LB LBP 🇱🇧 ar-LB,fr-LB,en,hy Lebanon 422 .lb
|
||||
LC LCA 1-758 Castries LC XCD 🇱🇨 en-LC St. Lucia 662 .lc
|
||||
LI LIE 423 Vaduz LI CHF 🇱🇮 de-LI Liechtenstein 438 .li
|
||||
LK LKA 94 Colombo LK LKR 🇱🇰 si,ta,en Sri Lanka 144 .lk
|
||||
LR LBR 231 Monrovia LR LRD 🇱🇷 en-LR Liberia 430 .lr
|
||||
LS LSO 266 Maseru LS LSL 🇱🇸 en-LS,st,zu,xh Lesotho 426 .ls
|
||||
LT LTU 370 Vilnius LT EUR 🇱🇹 lt,ru,pl Lithuania 440 .lt
|
||||
LU LUX 352 Luxembourg LU EUR 🇱🇺 lb,de-LU,fr-LU Luxembourg 442 .lu
|
||||
LV LVA 371 Riga LV EUR 🇱🇻 lv,ru,lt Latvia 428 .lv
|
||||
LY LBY 218 Tripoli LY LYD 🇱🇾 ar-LY,it,en Libya 434 .ly
|
||||
MA MAR 212 Rabat MA MAD 🇲🇦 ar-MA,ber,fr Morocco 504 .ma
|
||||
MC MCO 377 Monaco MC EUR 🇲🇨 fr-MC,en,it Monaco 492 .mc
|
||||
MD MDA 373 Chisinau MD MDL 🇲🇩 ro,ru,gag,tr Moldova 498 .md
|
||||
ME MNE 382 Podgorica ME EUR 🇲🇪 sr,hu,bs,sq,hr,rom Montenegro 499 .me
|
||||
MF MAF 590 Marigot FR EUR 🇲🇫 fr St. Martin 663 .gp
|
||||
MG MDG 261 Antananarivo MG MGA 🇲🇬 fr-MG,mg Madagascar 450 .mg
|
||||
MH MHL 692 Majuro MH USD 🇲🇭 mh,en-MH Marshall Islands 584 .mh
|
||||
MK MKD 389 Skopje MK MKD 🇲🇰 mk,sq,tr,rmm,sr North Macedonia 807 .mk
|
||||
ML MLI 223 Bamako ML XOF 🇲🇱 fr-ML,bm Mali 466 .ml
|
||||
MM MMR 95 Nay Pyi Taw MM MMK 🇲🇲 my Myanmar 104 .mm
|
||||
MN MNG 976 Ulaanbaatar MN MNT 🇲🇳 mn,ru Mongolia 496 .mn
|
||||
MO MAC 853 Macao CN MOP 🇲🇴 zh,zh-MO,pt Macao 446 .mo
|
||||
MP MNP 1-670 Saipan US USD 🇲🇵 fil,tl,zh,ch-MP,en-MP Northern Mariana Islands 580 .mp
|
||||
MQ MTQ 596 Fort-de-France FR EUR 🇲🇶 fr-MQ Martinique 474 .mq
|
||||
MR MRT 222 Nouakchott MR MRU 🇲🇷 ar-MR,fuc,snk,fr,mey,wo Mauritania 478 .mr
|
||||
MS MSR 1-664 Plymouth GB XCD 🇲🇸 en-MS Montserrat 500 .ms
|
||||
MT MLT 356 Valletta MT EUR 🇲🇹 mt,en-MT Malta 470 .mt
|
||||
MU MUS 230 Port Louis MU MUR 🇲🇺 en-MU,bho,fr Mauritius 480 .mu
|
||||
MV MDV 960 Male MV MVR 🇲🇻 dv,en Maldives 462 .mv
|
||||
MW MWI 265 Lilongwe MW MWK 🇲🇼 ny,yao,tum,swk Malawi 454 .mw
|
||||
MX MEX 52 Mexico City MX MXN 🇲🇽 es-MX Mexico 484 .mx
|
||||
MY MYS 60 Kuala Lumpur MY MYR 🇲🇾 ms-MY,en,zh,ta,te,ml,pa,th Malaysia 458 .my
|
||||
MZ MOZ 258 Maputo MZ MZN 🇲🇿 pt-MZ,vmw Mozambique 508 .mz
|
||||
NA NAM 264 Windhoek NA NAD 🇳🇦 en-NA,af,de,hz,naq Namibia 516 .na
|
||||
NC NCL 687 Noumea FR XPF 🇳🇨 fr-NC New Caledonia 540 .nc
|
||||
NE NER 227 Niamey NE XOF 🇳🇪 fr-NE,ha,kr,dje Niger 562 .ne
|
||||
NF NFK 672 Kingston AU AUD 🇳🇫 en-NF Norfolk Island 574 .nf
|
||||
NG NGA 234 Abuja NG NGN 🇳🇬 en-NG,ha,yo,ig,ff Nigeria 566 .ng
|
||||
NI NIC 505 Managua NI NIO 🇳🇮 es-NI,en Nicaragua 558 .ni
|
||||
NL NLD 31 Amsterdam NL EUR 🇳🇱 nl-NL,fy-NL Netherlands 528 .nl
|
||||
NO NOR 47 Oslo NO NOK 🇳🇴 no,nb,nn,se,fi Norway 578 .no
|
||||
NP NPL 977 Kathmandu NP NPR 🇳🇵 ne,en Nepal 524 .np
|
||||
NR NRU 674 Yaren NR AUD 🇳🇷 na,en-NR Nauru 520 .nr
|
||||
NU NIU 683 Alofi NZ NZD 🇳🇺 niu,en-NU Niue 570 .nu
|
||||
NZ NZL 64 Wellington NZ NZD 🇳🇿 en-NZ,mi New Zealand 554 .nz
|
||||
OM OMN 968 Muscat OM OMR 🇴🇲 ar-OM,en,bal,ur Oman 512 .om
|
||||
PA PAN 507 Panama City PA PAB 🇵🇦 es-PA,en Panama 591 .pa
|
||||
PE PER 51 Lima PE PEN 🇵🇪 es-PE,qu,ay Peru 604 .pe
|
||||
PF PYF 689 Papeete FR XPF 🇵🇫 fr-PF,ty French Polynesia 258 .pf
|
||||
PG PNG 675 Port Moresby PG PGK 🇵🇬 en-PG,ho,meu,tpi Papua New Guinea 598 .pg
|
||||
PH PHL 63 Manila PH PHP 🇵🇭 tl,en-PH,fil,ceb,ilo,hil,war,pam,bik,bcl,pag,mrw,tsg,mdh,cbk,krj,sgd,msb,akl,ibg,yka,mta,abx Philippines 608 .ph
|
||||
PK PAK 92 Islamabad PK PKR 🇵🇰 ur-PK,en-PK,pa,sd,ps,brh Pakistan 586 .pk
|
||||
PL POL 48 Warsaw PL PLN 🇵🇱 pl Poland 616 .pl
|
||||
PM SPM 508 Saint-Pierre FR EUR 🇵🇲 fr-PM St. Pierre & Miquelon 666 .pm
|
||||
PN PCN 870 Adamstown GB NZD 🇵🇳 en-PN Pitcairn 612 .pn
|
||||
PR PRI 1 San Juan US USD 🇵🇷 en-PR,es-PR Puerto Rico 630 .pr
|
||||
PT PRT 351 Lisbon PT EUR 🇵🇹 pt-PT,mwl Portugal 620 .pt
|
||||
PW PLW 680 Melekeok PW USD 🇵🇼 pau,sov,en-PW,tox,ja,fil,zh Palau 585 .pw
|
||||
PY PRY 595 Asuncion PY PYG 🇵🇾 es-PY,gn Paraguay 600 .py
|
||||
QA QAT 974 Doha QA QAR 🇶🇦 ar-QA,es Qatar 634 .qa
|
||||
RE REU 262 Saint-Denis FR EUR 🇷🇪 fr-RE Réunion 638 .re
|
||||
RO ROU 40 Bucharest RO RON 🇷🇴 ro,hu,rom Romania 642 .ro
|
||||
RS SRB 381 Belgrade RS RSD 🇷🇸 sr,hu,bs,rom Serbia 688 .rs
|
||||
RU RUS 7 Moscow RU RUB 🇷🇺 ru,tt,xal,cau,ady,kv,ce,tyv,cv,udm,tut,mns,bua,myv,mdf,chm,ba,inh,kbd,krc,av,sah,nog Russia 643 .ru
|
||||
RW RWA 250 Kigali RW RWF 🇷🇼 rw,en-RW,fr-RW,sw Rwanda 646 .rw
|
||||
SA SAU 966 Riyadh SA SAR 🇸🇦 ar-SA Saudi Arabia 682 .sa
|
||||
SB SLB 677 Honiara SB SBD 🇸🇧 en-SB,tpi Solomon Islands 090 .sb
|
||||
SC SYC 248 Victoria SC SCR 🇸🇨 en-SC,fr-SC Seychelles 690 .sc
|
||||
SD SDN 249 Khartoum SD SDG 🇸🇩 ar-SD,en,fia Sudan 729 .sd
|
||||
SE SWE 46 Stockholm SE SEK 🇸🇪 sv-SE,se,sma,fi-SE Sweden 752 .se
|
||||
SG SGP 65 Singapore SG SGD 🇸🇬 cmn,en-SG,ms-SG,ta-SG,zh-SG Singapore 702 .sg
|
||||
SH SHN 290 Jamestown GB SHP 🇸🇭 en-SH St. Helena 654 .sh
|
||||
SI SVN 386 Ljubljana SI EUR 🇸🇮 sl,sh Slovenia 705 .si
|
||||
SJ SJM 47 Longyearbyen NO NOK 🇸🇯 no,ru Svalbard & Jan Mayen 744 .sj
|
||||
SK SVK 421 Bratislava SK EUR 🇸🇰 sk,hu Slovakia 703 .sk
|
||||
SL SLE 232 Freetown SL SLE 🇸🇱 en-SL,men,tem Sierra Leone 694 .sl
|
||||
SM SMR 378 San Marino SM EUR 🇸🇲 it-SM San Marino 674 .sm
|
||||
SN SEN 221 Dakar SN XOF 🇸🇳 fr-SN,wo,fuc,mnk Senegal 686 .sn
|
||||
SO SOM 252 Mogadishu SO SOS 🇸🇴 so-SO,ar-SO,it,en-SO Somalia 706 .so
|
||||
SR SUR 597 Paramaribo SR SRD 🇸🇷 nl-SR,en,srn,hns,jv Suriname 740 .sr
|
||||
SS SSD 211 Juba SS SSP 🇸🇸 en South Sudan 728 .ss
|
||||
ST STP 239 Sao Tome ST STN 🇸🇹 pt-ST São Tomé & Príncipe 678 .st
|
||||
SV SLV 503 San Salvador SV SVC 🇸🇻 es-SV El Salvador 222 .sv
|
||||
SX SXM 1-721 Philipsburg NL XCG 🇸🇽 nl,en Sint Maarten 534 .sx
|
||||
SY SYR 963 Damascus SY SYP 🇸🇾 ar-SY,ku,hy,arc,fr,en Syria 760 .sy
|
||||
SZ SWZ 268 Mbabane SZ SZL 🇸🇿 en-SZ,ss-SZ Eswatini 748 .sz
|
||||
TC TCA 1-649 Cockburn Town GB USD 🇹🇨 en-TC Turks & Caicos Islands 796 .tc
|
||||
TD TCD 235 N'Djamena TD XAF 🇹🇩 fr-TD,ar-TD,sre Chad 148 .td
|
||||
TF ATF 262 Port-aux-Francais FR EUR 🇹🇫 fr French Southern Territories 260 .tf
|
||||
TG TGO 228 Lome TG XOF 🇹🇬 fr-TG,ee,hna,kbp,dag,ha Togo 768 .tg
|
||||
TH THA 66 Bangkok TH THB 🇹🇭 th,en Thailand 764 .th
|
||||
TJ TJK 992 Dushanbe TJ TJS 🇹🇯 tg,ru Tajikistan 762 .tj
|
||||
TL TLS 670 Dili TL USD 🇹🇱 tet,pt-TL,id,en Timor-Leste 626 .tl
|
||||
TM TKM 993 Ashgabat TM TMT 🇹🇲 tk,ru,uz Turkmenistan 795 .tm
|
||||
TN TUN 216 Tunis TN TND 🇹🇳 ar-TN,fr Tunisia 788 .tn
|
||||
TO TON 676 Nuku'alofa TO TOP 🇹🇴 to,en-TO Tonga 776 .to
|
||||
TR TUR 90 Ankara TR TRY 🇹🇷 tr-TR,ku,diq,az,av Türkiye 792 .tr
|
||||
TT TTO 1-868 Port of Spain TT TTD 🇹🇹 en-TT,hns,fr,es,zh Trinidad & Tobago 780 .tt
|
||||
TV TUV 688 Funafuti TV AUD 🇹🇻 tvl,en,sm,gil Tuvalu 798 .tv
|
||||
TW TWN 886 Taipei TW TWD 🇹🇼 zh-TW,zh,nan,hak Taiwan 158 .tw
|
||||
TZ TZA 255 Dodoma TZ TZS 🇹🇿 sw-TZ,en,ar Tanzania 834 .tz
|
||||
UA UKR 380 Kyiv UA UAH 🇺🇦 uk,ru-UA,rom,pl,hu Ukraine 804 .ua
|
||||
UG UGA 256 Kampala UG UGX 🇺🇬 en-UG,lg,sw,ar Uganda 800 .ug
|
||||
US USA 1 Washington US USD 🇺🇸 en-US,es-US,haw,fr US 840 .us
|
||||
UY URY 598 Montevideo UY UYU 🇺🇾 es-UY Uruguay 858 .uy
|
||||
UZ UZB 998 Tashkent UZ UZS 🇺🇿 uz,ru,tg Uzbekistan 860 .uz
|
||||
VA VAT 39 Vatican City VA EUR 🇻🇦 la,it,fr Vatican City 336 .va
|
||||
VC VCT 1-784 Kingstown VC XCD 🇻🇨 en-VC,fr St. Vincent & Grenadines 670 .vc
|
||||
VE VEN 58 Caracas VE VES 🇻🇪 es-VE Venezuela 862 .ve
|
||||
VG VGB 1-284 Road Town GB USD 🇻🇬 en-VG British Virgin Islands 092 .vg
|
||||
VI VIR 1-340 Charlotte Amalie US USD 🇻🇮 en-VI U.S. Virgin Islands 850 .vi
|
||||
VN VNM 84 Hanoi VN VND 🇻🇳 vi,en,fr,zh,km Vietnam 704 .vn
|
||||
VU VUT 678 Port Vila VU VUV 🇻🇺 bi,en-VU,fr-VU Vanuatu 548 .vu
|
||||
WF WLF 681 Mata Utu FR XPF 🇼🇫 wls,fud,fr-WF Wallis & Futuna 876 .wf
|
||||
WS WSM 685 Apia WS WST 🇼🇸 sm,en-WS Samoa 882 .ws
|
||||
YE YEM 967 Sanaa YE YER 🇾🇪 ar-YE Yemen 887 .ye
|
||||
YT MYT 262 Mamoudzou FR EUR 🇾🇹 fr-YT Mayotte 175 .yt
|
||||
ZA ZAF 27 Pretoria ZA ZAR 🇿🇦 zu,xh,af,nso,en-ZA,tn,st,ts,ss,ve,nr South Africa 710 .za
|
||||
ZM ZMB 260 Lusaka ZM ZMW 🇿🇲 en-ZM,bem,loz,lun,lue,ny,toi Zambia 894 .zm
|
||||
ZW ZWE 263 Harare ZW ZWG 🇿🇼 en-ZW,sn,nr,nd Zimbabwe 716 .zw
|
||||
|
@@ -1 +1 @@
|
||||
{ "format": "{zone}", "rows": "timezone.tsv", "key": "zone", "parent": "territory", "weight": "weight" }
|
||||
["UTC", "Africa/Cairo", "Africa/Johannesburg", "America/Chicago", "America/Denver", "America/Los_Angeles", "America/Mexico_City", "America/New_York", "America/Sao_Paulo", "America/Toronto", "Asia/Dubai", "Asia/Hong_Kong", "Asia/Kolkata", "Asia/Shanghai", "Asia/Singapore", "Asia/Tokyo", "Australia/Sydney", "Europe/Amsterdam", "Europe/Berlin", "Europe/London", "Europe/Madrid", "Europe/Moscow", "Europe/Oslo", "Europe/Paris", "Europe/Stockholm", "Europe/Warsaw", "Pacific/Auckland"]
|
||||
|
||||
@@ -1,402 +0,0 @@
|
||||
offset territory weight zone
|
||||
+00:00 CI 17496811 Africa/Abidjan
|
||||
+00:00 GH 11277813 Africa/Accra
|
||||
+03:00 ET 18690487 Africa/Addis_Ababa
|
||||
+01:00 DZ 20111539 Africa/Algiers
|
||||
+03:00 ER 798575 Africa/Asmara
|
||||
+00:00 ML 7673493 Africa/Bamako
|
||||
+01:00 CF 2582076 Africa/Bangui
|
||||
+00:00 GM 1305981 Africa/Banjul
|
||||
+00:00 GW 904776 Africa/Bissau
|
||||
+02:00 MW 3030905 Africa/Blantyre
|
||||
+01:00 CG 3748808 Africa/Brazzaville
|
||||
+02:00 BI 1251675 Africa/Bujumbura
|
||||
+02:00 EG 47242427 Africa/Cairo
|
||||
+00:00 MA 22808531 Africa/Casablanca
|
||||
+01:00 ES 85985 Africa/Ceuta
|
||||
+00:00 GN 6679241 Africa/Conakry
|
||||
+00:00 SN 10055356 Africa/Dakar
|
||||
+03:00 TZ 21350525 Africa/Dar_es_Salaam
|
||||
+03:00 DJ 1316108 Africa/Djibouti
|
||||
+01:00 CM 8330801 Africa/Douala
|
||||
+00:00 EH 374107 Africa/El_Aaiun
|
||||
+00:00 SL 1752243 Africa/Freetown
|
||||
+02:00 BW 1262880 Africa/Gaborone
|
||||
+02:00 ZW 4508424 Africa/Harare
|
||||
+02:00 ZA 47939803 Africa/Johannesburg
|
||||
+02:00 SS 1679674 Africa/Juba
|
||||
+03:00 UG 9664318 Africa/Kampala
|
||||
+02:00 SD 11781926 Africa/Khartoum
|
||||
+02:00 RW 2910190 Africa/Kigali
|
||||
+01:00 CD 20651243 Africa/Kinshasa
|
||||
+01:00 NG 75725576 Africa/Lagos
|
||||
+01:00 GA 1731692 Africa/Libreville
|
||||
+00:00 TG 3089219 Africa/Lome
|
||||
+01:00 AO 23567341 Africa/Luanda
|
||||
+02:00 CD 16660579 Africa/Lubumbashi
|
||||
+02:00 ZM 8422414 Africa/Lusaka
|
||||
+01:00 GQ 355840 Africa/Malabo
|
||||
+02:00 MZ 8598836 Africa/Maputo
|
||||
+02:00 LS 739682 Africa/Maseru
|
||||
+02:00 SZ 191312 Africa/Mbabane
|
||||
+03:00 SO 6165052 Africa/Mogadishu
|
||||
+00:00 LR 2127307 Africa/Monrovia
|
||||
+03:00 KE 16920989 Africa/Nairobi
|
||||
+01:00 TD 3363418 Africa/Ndjamena
|
||||
+01:00 NE 3689982 Africa/Niamey
|
||||
+00:00 MR 2157288 Africa/Nouakchott
|
||||
+00:00 BF 6125427 Africa/Ouagadougou
|
||||
+01:00 BJ 4001576 Africa/Porto-Novo
|
||||
+00:00 ST 53300 Africa/Sao_Tome
|
||||
+02:00 LY 6105792 Africa/Tripoli
|
||||
+01:00 TN 7314557 Africa/Tunis
|
||||
+02:00 NA 983097 Africa/Windhoek
|
||||
-10:00 US 15000 America/Adak
|
||||
-09:00 US 366200 America/Anchorage
|
||||
-04:00 AI 15000 America/Anguilla
|
||||
-04:00 AG 51737 America/Antigua
|
||||
-03:00 BR 858988 America/Araguaina
|
||||
-03:00 AR 11817784 America/Argentina/Buenos_Aires
|
||||
-03:00 AR 580969 America/Argentina/Catamarca
|
||||
-03:00 AR 8998034 America/Argentina/Cordoba
|
||||
-03:00 AR 413866 America/Argentina/Jujuy
|
||||
-03:00 AR 237745 America/Argentina/La_Rioja
|
||||
-03:00 AR 695484 America/Argentina/Mendoza
|
||||
-03:00 AR 241619 America/Argentina/Rio_Gallegos
|
||||
-03:00 AR 2120781 America/Argentina/Salta
|
||||
-03:00 AR 518624 America/Argentina/San_Juan
|
||||
-03:00 AR 266728 America/Argentina/San_Luis
|
||||
-03:00 AR 788916 America/Argentina/Tucuman
|
||||
-03:00 AR 109506 America/Argentina/Ushuaia
|
||||
-04:00 AW 90927 America/Aruba
|
||||
-03:00 PY 3653883 America/Asuncion
|
||||
-05:00 CA 15000 America/Atikokan
|
||||
-03:00 BR 13131907 America/Bahia
|
||||
-06:00 MX 36253 America/Bahia_Banderas
|
||||
-04:00 BB 98511 America/Barbados
|
||||
-03:00 BR 7977349 America/Belem
|
||||
-06:00 BZ 132559 America/Belize
|
||||
-04:00 CA 15000 America/Blanc-Sablon
|
||||
-04:00 BR 550434 America/Boa_Vista
|
||||
-05:00 CO 37243567 America/Bogota
|
||||
-07:00 US 748802 America/Boise
|
||||
-07:00 CA 15000 America/Cambridge_Bay
|
||||
-04:00 BR 2429053 America/Campo_Grande
|
||||
-05:00 MX 1328961 America/Cancun
|
||||
-04:00 VE 28324666 America/Caracas
|
||||
-03:00 GF 155245 America/Cayenne
|
||||
-05:00 KY 44705 America/Cayman
|
||||
-06:00 US 55510713 America/Chicago
|
||||
-06:00 MX 1533843 America/Chihuahua
|
||||
-07:00 MX 1512450 America/Ciudad_Juarez
|
||||
-06:00 CR 1458629 America/Costa_Rica
|
||||
-03:00 CL 62723 America/Coyhaique
|
||||
-07:00 CA 15000 America/Creston
|
||||
-04:00 BR 2960376 America/Cuiaba
|
||||
-04:00 CW 145838 America/Curacao
|
||||
+00:00 GL 15000 America/Danmarkshavn
|
||||
-07:00 CA 15000 America/Dawson
|
||||
-07:00 CA 21465 America/Dawson_Creek
|
||||
-07:00 US 9069711 America/Denver
|
||||
-05:00 US 4344725 America/Detroit
|
||||
-04:00 DM 16571 America/Dominica
|
||||
-06:00 CA 3518059 America/Edmonton
|
||||
-05:00 BR 142900 America/Eirunepe
|
||||
-06:00 SV 2571611 America/El_Salvador
|
||||
-07:00 CA 15000 America/Fort_Nelson
|
||||
-03:00 BR 21824546 America/Fortaleza
|
||||
-04:00 CA 122883 America/Glace_Bay
|
||||
-04:00 CA 15000 America/Goose_Bay
|
||||
-05:00 TC 27489 America/Grand_Turk
|
||||
-04:00 GD 15000 America/Grenada
|
||||
-04:00 GP 250260 America/Guadeloupe
|
||||
-06:00 GT 7302205 America/Guatemala
|
||||
-05:00 EC 11564603 America/Guayaquil
|
||||
-04:00 GY 314746 America/Guyana
|
||||
-04:00 CA 843888 America/Halifax
|
||||
-05:00 CU 11972822 America/Havana
|
||||
-07:00 MX 2215048 America/Hermosillo
|
||||
-05:00 US 2672398 America/Indiana/Indianapolis
|
||||
-06:00 US 15000 America/Indiana/Knox
|
||||
-05:00 US 15000 America/Indiana/Marengo
|
||||
-05:00 US 15000 America/Indiana/Petersburg
|
||||
-06:00 US 15000 America/Indiana/Tell_City
|
||||
-05:00 US 15000 America/Indiana/Vevay
|
||||
-05:00 US 15451 America/Indiana/Vincennes
|
||||
-05:00 US 15000 America/Indiana/Winamac
|
||||
-06:00 CA 15000 America/Inuvik
|
||||
-05:00 CA 15000 America/Iqaluit
|
||||
-05:00 JM 2069735 America/Jamaica
|
||||
-09:00 US 31555 America/Juneau
|
||||
-05:00 US 909379 America/Kentucky/Louisville
|
||||
-05:00 US 15000 America/Kentucky/Monticello
|
||||
-04:00 BO 6958595 America/La_Paz
|
||||
-05:00 PE 21406340 America/Lima
|
||||
-08:00 US 46301242 America/Los_Angeles
|
||||
-04:00 SX 15000 America/Lower_Princes
|
||||
-03:00 BR 4805317 America/Maceio
|
||||
-06:00 NI 2621115 America/Managua
|
||||
-04:00 BR 3918958 America/Manaus
|
||||
-04:00 MF 15000 America/Marigot
|
||||
-04:00 MQ 243860 America/Martinique
|
||||
-06:00 MX 1899165 America/Matamoros
|
||||
-07:00 MX 2951216 America/Mazatlan
|
||||
-06:00 US 15000 America/Menominee
|
||||
-06:00 MX 4154924 America/Merida
|
||||
-09:00 US 15000 America/Metlakatla
|
||||
-06:00 MX 61603325 America/Mexico_City
|
||||
-03:00 PM 15000 America/Miquelon
|
||||
-04:00 CA 381536 America/Moncton
|
||||
-06:00 MX 9714766 America/Monterrey
|
||||
-03:00 UY 2457462 America/Montevideo
|
||||
-04:00 MS 15000 America/Montserrat
|
||||
-05:00 BS 319054 America/Nassau
|
||||
-05:00 US 88988896 America/New_York
|
||||
-09:00 US 15000 America/Nome
|
||||
-02:00 BR 15000 America/Noronha
|
||||
-06:00 US 15000 America/North_Dakota/Beulah
|
||||
-06:00 US 15000 America/North_Dakota/Center
|
||||
-06:00 US 21382 America/North_Dakota/New_Salem
|
||||
-02:00 GL 15000 America/Nuuk
|
||||
-06:00 MX 50784 America/Ojinaga
|
||||
-05:00 PA 1692726 America/Panama
|
||||
-03:00 SR 454064 America/Paramaribo
|
||||
-07:00 US 6541994 America/Phoenix
|
||||
-05:00 HT 5328623 America/Port-au-Prince
|
||||
-04:00 TT 456832 America/Port_of_Spain
|
||||
-04:00 BR 1551818 America/Porto_Velho
|
||||
-04:00 PR 1605968 America/Puerto_Rico
|
||||
-03:00 CL 135935 America/Punta_Arenas
|
||||
-06:00 CA 15000 America/Rankin_Inlet
|
||||
-03:00 BR 8832691 America/Recife
|
||||
-06:00 CA 982810 America/Regina
|
||||
-06:00 CA 15000 America/Resolute
|
||||
-05:00 BR 794527 America/Rio_Branco
|
||||
-03:00 BR 1403419 America/Santarem
|
||||
-04:00 CL 15912038 America/Santiago
|
||||
-04:00 DO 7521391 America/Santo_Domingo
|
||||
-03:00 BR 129186469 America/Sao_Paulo
|
||||
-02:00 GL 15000 America/Scoresbysund
|
||||
-09:00 US 15000 America/Sitka
|
||||
-04:00 BL 15000 America/St_Barthelemy
|
||||
-03:30 CA 202443 America/St_Johns
|
||||
-04:00 KN 15000 America/St_Kitts
|
||||
-04:00 LC 45210 America/St_Lucia
|
||||
-04:00 VI 70601 America/St_Thomas
|
||||
-04:00 VC 48723 America/St_Vincent
|
||||
-06:00 CA 16604 America/Swift_Current
|
||||
-06:00 HN 4808928 America/Tegucigalpa
|
||||
-04:00 GL 15000 America/Thule
|
||||
-08:00 MX 3797218 America/Tijuana
|
||||
-05:00 CA 24763654 America/Toronto
|
||||
-04:00 VG 15000 America/Tortola
|
||||
-07:00 CA 6340886 America/Vancouver
|
||||
-07:00 CA 28201 America/Whitehorse
|
||||
-06:00 CA 922566 America/Winnipeg
|
||||
-09:00 US 15000 America/Yakutat
|
||||
+10:00 AU 15000 Antarctica/Macquarie
|
||||
+01:00 SJ 15000 Arctic/Longyearbyen
|
||||
+03:00 YE 6904010 Asia/Aden
|
||||
+05:00 KZ 9055499 Asia/Almaty
|
||||
+03:00 JO 4950859 Asia/Amman
|
||||
+12:00 RU 15604 Asia/Anadyr
|
||||
+05:00 KZ 282409 Asia/Aqtau
|
||||
+05:00 KZ 598827 Asia/Aqtobe
|
||||
+05:00 TM 2706037 Asia/Ashgabat
|
||||
+05:00 KZ 388248 Asia/Atyrau
|
||||
+03:00 IQ 29681856 Asia/Baghdad
|
||||
+03:00 BH 815447 Asia/Bahrain
|
||||
+04:00 AZ 5491302 Asia/Baku
|
||||
+07:00 TH 49896297 Asia/Bangkok
|
||||
+07:00 RU 1413356 Asia/Barnaul
|
||||
+02:00 LB 4467748 Asia/Beirut
|
||||
+06:00 KG 2233412 Asia/Bishkek
|
||||
+08:00 BN 230416 Asia/Brunei
|
||||
+09:00 RU 486311 Asia/Chita
|
||||
+05:30 LK 4297175 Asia/Colombo
|
||||
+03:00 SY 10939175 Asia/Damascus
|
||||
+06:00 BD 35955225 Asia/Dhaka
|
||||
+09:00 TL 311685 Asia/Dili
|
||||
+04:00 AE 12931222 Asia/Dubai
|
||||
+05:00 TJ 2578933 Asia/Dushanbe
|
||||
+02:00 CY 109959 Asia/Famagusta
|
||||
+07:00 VN 42129036 Asia/Ho_Chi_Minh
|
||||
+08:00 HK 24678261 Asia/Hong_Kong
|
||||
+07:00 MN 122174 Asia/Hovd
|
||||
+08:00 RU 2231790 Asia/Irkutsk
|
||||
+07:00 ID 68794489 Asia/Jakarta
|
||||
+09:00 ID 2512221 Asia/Jayapura
|
||||
+02:00 IL 7901151 Asia/Jerusalem
|
||||
+04:30 AF 8764027 Asia/Kabul
|
||||
+12:00 RU 247112 Asia/Kamchatka
|
||||
+05:00 PK 88744953 Asia/Karachi
|
||||
+05:45 NP 5913313 Asia/Kathmandu
|
||||
+09:00 RU 15000 Asia/Khandyga
|
||||
+05:30 IN 365373326 Asia/Kolkata
|
||||
+07:00 RU 2615869 Asia/Krasnoyarsk
|
||||
+08:00 MY 33322164 Asia/Kuala_Lumpur
|
||||
+08:00 MY 4013140 Asia/Kuching
|
||||
+03:00 KW 1662180 Asia/Kuwait
|
||||
+08:00 MO 1236695 Asia/Macau
|
||||
+11:00 RU 92782 Asia/Magadan
|
||||
+08:00 ID 12458359 Asia/Makassar
|
||||
+08:00 PH 64227523 Asia/Manila
|
||||
+04:00 OM 3290021 Asia/Muscat
|
||||
+02:00 CY 679420 Asia/Nicosia
|
||||
+07:00 RU 2355475 Asia/Novokuznetsk
|
||||
+07:00 RU 2119272 Asia/Novosibirsk
|
||||
+06:00 RU 1243165 Asia/Omsk
|
||||
+05:00 KZ 364799 Asia/Oral
|
||||
+07:00 KH 4111780 Asia/Phnom_Penh
|
||||
+07:00 ID 1819584 Asia/Pontianak
|
||||
+09:00 KP 12876883 Asia/Pyongyang
|
||||
+03:00 QA 1566088 Asia/Qatar
|
||||
+05:00 KZ 529104 Asia/Qostanay
|
||||
+05:00 KZ 511391 Asia/Qyzylorda
|
||||
+03:00 SA 23913997 Asia/Riyadh
|
||||
+11:00 RU 326982 Asia/Sakhalin
|
||||
+05:00 UZ 3864475 Asia/Samarkand
|
||||
+09:00 KR 47400555 Asia/Seoul
|
||||
+08:00 CN 861293141 Asia/Shanghai
|
||||
+08:00 SG 11621053 Asia/Singapore
|
||||
+11:00 RU 15000 Asia/Srednekolymsk
|
||||
+08:00 TW 26394699 Asia/Taipei
|
||||
+05:00 UZ 7452125 Asia/Tashkent
|
||||
+04:00 GE 1827911 Asia/Tbilisi
|
||||
+03:30 IR 50648371 Asia/Tehran
|
||||
+06:00 BT 170210 Asia/Thimphu
|
||||
+09:00 JP 141330654 Asia/Tokyo
|
||||
+07:00 RU 783576 Asia/Tomsk
|
||||
+08:00 MN 1438738 Asia/Ulaanbaatar
|
||||
+06:00 CN 12667986 Asia/Urumqi
|
||||
+10:00 RU 15000 Asia/Ust-Nera
|
||||
+07:00 LA 1561716 Asia/Vientiane
|
||||
+10:00 RU 2534711 Asia/Vladivostok
|
||||
+09:00 RU 902983 Asia/Yakutsk
|
||||
+06:30 MM 18836338 Asia/Yangon
|
||||
+05:00 RU 15407217 Asia/Yekaterinburg
|
||||
+04:00 AM 2880620 Asia/Yerevan
|
||||
-01:00 PT 20056 Atlantic/Azores
|
||||
-04:00 BM 15000 Atlantic/Bermuda
|
||||
+00:00 ES 2054431 Atlantic/Canary
|
||||
-01:00 CV 252678 Atlantic/Cape_Verde
|
||||
+00:00 FO 15000 Atlantic/Faroe
|
||||
+00:00 PT 213794 Atlantic/Madeira
|
||||
+00:00 IS 245356 Atlantic/Reykjavik
|
||||
+00:00 SH 15000 Atlantic/St_Helena
|
||||
-03:00 FK 15000 Atlantic/Stanley
|
||||
+09:30 AU 1706678 Australia/Adelaide
|
||||
+10:00 AU 5807246 Australia/Brisbane
|
||||
+09:30 AU 17456 Australia/Broken_Hill
|
||||
+09:30 AU 199509 Australia/Darwin
|
||||
+08:45 AU 15000 Australia/Eucla
|
||||
+10:00 AU 392450 Australia/Hobart
|
||||
+10:00 AU 15000 Australia/Lindeman
|
||||
+10:30 AU 15000 Australia/Lord_Howe
|
||||
+10:00 AU 9304158 Australia/Melbourne
|
||||
+08:00 AU 3086441 Australia/Perth
|
||||
+10:00 AU 9352313 Australia/Sydney
|
||||
+01:00 NL 13892513 Europe/Amsterdam
|
||||
+01:00 AD 36283 Europe/Andorra
|
||||
+04:00 RU 644812 Europe/Astrakhan
|
||||
+02:00 GR 6286690 Europe/Athens
|
||||
+01:00 RS 5756549 Europe/Belgrade
|
||||
+01:00 DE 62740682 Europe/Berlin
|
||||
+01:00 SK 2548401 Europe/Bratislava
|
||||
+01:00 BE 9038306 Europe/Brussels
|
||||
+02:00 RO 11070040 Europe/Bucharest
|
||||
+01:00 HU 9366333 Europe/Budapest
|
||||
+01:00 DE 15000 Europe/Busingen
|
||||
+02:00 MD 1460347 Europe/Chisinau
|
||||
+01:00 DK 3885672 Europe/Copenhagen
|
||||
+01:00 IE 2720758 Europe/Dublin
|
||||
+01:00 GI 26544 Europe/Gibraltar
|
||||
+00:00 GG 16488 Europe/Guernsey
|
||||
+02:00 FI 5265184 Europe/Helsinki
|
||||
+00:00 IM 26218 Europe/Isle_of_Man
|
||||
+03:00 TR 77554557 Europe/Istanbul
|
||||
+00:00 JE 28000 Europe/Jersey
|
||||
+02:00 RU 664545 Europe/Kaliningrad
|
||||
+03:00 RU 782604 Europe/Kirov
|
||||
+02:00 UA 35633582 Europe/Kyiv
|
||||
+00:00 PT 6372176 Europe/Lisbon
|
||||
+01:00 SI 552127 Europe/Ljubljana
|
||||
+00:00 GB 57817786 Europe/London
|
||||
+01:00 LU 131322 Europe/Luxembourg
|
||||
+01:00 ES 42918247 Europe/Madrid
|
||||
+01:00 MT 141714 Europe/Malta
|
||||
+02:00 AX 15000 Europe/Mariehamn
|
||||
+03:00 BY 6105410 Europe/Minsk
|
||||
+01:00 MC 48977 Europe/Monaco
|
||||
+03:00 RU 69342062 Europe/Moscow
|
||||
+01:00 NO 3296711 Europe/Oslo
|
||||
+01:00 FR 33093827 Europe/Paris
|
||||
+01:00 ME 398494 Europe/Podgorica
|
||||
+01:00 CZ 5924565 Europe/Prague
|
||||
+02:00 LV 1206422 Europe/Riga
|
||||
+01:00 IT 34033666 Europe/Rome
|
||||
+04:00 RU 3546813 Europe/Samara
|
||||
+01:00 SM 15000 Europe/San_Marino
|
||||
+01:00 BA 1905798 Europe/Sarajevo
|
||||
+04:00 RU 1705529 Europe/Saratov
|
||||
+03:00 UA 1620906 Europe/Simferopol
|
||||
+01:00 MK 1754303 Europe/Skopje
|
||||
+02:00 BG 4111057 Europe/Sofia
|
||||
+01:00 SE 7149541 Europe/Stockholm
|
||||
+02:00 EE 1106943 Europe/Tallinn
|
||||
+01:00 AL 1629643 Europe/Tirane
|
||||
+04:00 RU 814189 Europe/Ulyanovsk
|
||||
+01:00 LI 15000 Europe/Vaduz
|
||||
+01:00 VA 15000 Europe/Vatican
|
||||
+01:00 AT 5274040 Europe/Vienna
|
||||
+02:00 LT 2103133 Europe/Vilnius
|
||||
+03:00 RU 1837053 Europe/Volgograd
|
||||
+01:00 PL 22720110 Europe/Warsaw
|
||||
+01:00 HR 1650334 Europe/Zagreb
|
||||
+01:00 CH 3461664 Europe/Zurich
|
||||
+03:00 MG 6115943 Indian/Antananarivo
|
||||
+06:00 IO 15000 Indian/Chagos
|
||||
+07:00 CX 15000 Indian/Christmas
|
||||
+06:30 CC 15000 Indian/Cocos
|
||||
+03:00 KM 118353 Indian/Comoro
|
||||
+05:00 TF 15000 Indian/Kerguelen
|
||||
+04:00 SC 22881 Indian/Mahe
|
||||
+05:00 MV 103693 Indian/Maldives
|
||||
+04:00 MU 701493 Indian/Mauritius
|
||||
+03:00 YT 138967 Indian/Mayotte
|
||||
+04:00 RE 804329 Indian/Reunion
|
||||
+13:00 WS 40407 Pacific/Apia
|
||||
+12:00 NZ 5203989 Pacific/Auckland
|
||||
+11:00 PG 40266 Pacific/Bougainville
|
||||
+12:45 NZ 15000 Pacific/Chatham
|
||||
+10:00 FM 15000 Pacific/Chuuk
|
||||
-06:00 CL 15000 Pacific/Easter
|
||||
+11:00 VU 35901 Pacific/Efate
|
||||
+12:00 FJ 334403 Pacific/Fiji
|
||||
+12:00 TV 15000 Pacific/Funafuti
|
||||
-06:00 EC 15000 Pacific/Galapagos
|
||||
-09:00 PF 15000 Pacific/Gambier
|
||||
+11:00 SB 194707 Pacific/Guadalcanal
|
||||
+10:00 GU 121094 Pacific/Guam
|
||||
-10:00 US 1569453 Pacific/Honolulu
|
||||
+13:00 KI 15000 Pacific/Kanton
|
||||
+14:00 KI 15000 Pacific/Kiritimati
|
||||
+11:00 FM 15000 Pacific/Kosrae
|
||||
+12:00 MH 15000 Pacific/Kwajalein
|
||||
+12:00 MH 45701 Pacific/Majuro
|
||||
-09:30 PF 15000 Pacific/Marquesas
|
||||
+12:00 NR 15000 Pacific/Nauru
|
||||
-11:00 NU 15000 Pacific/Niue
|
||||
+11:00 NF 15000 Pacific/Norfolk
|
||||
+11:00 NC 137086 Pacific/Noumea
|
||||
-11:00 AS 15000 Pacific/Pago_Pago
|
||||
+09:00 PW 15000 Pacific/Palau
|
||||
-08:00 PN 15000 Pacific/Pitcairn
|
||||
+11:00 FM 15000 Pacific/Pohnpei
|
||||
+10:00 PG 588589 Pacific/Port_Moresby
|
||||
-10:00 CK 15000 Pacific/Rarotonga
|
||||
+10:00 MP 48220 Pacific/Saipan
|
||||
-10:00 PF 81958 Pacific/Tahiti
|
||||
+12:00 KI 40311 Pacific/Tarawa
|
||||
+13:00 TO 22400 Pacific/Tongatapu
|
||||
+12:00 WF 15000 Pacific/Wallis
|
||||
|
@@ -1 +0,0 @@
|
||||
{ "format": "{tld}", "rows": "tld.tsv", "key": "tld", "name": "unicode" }
|
||||
-1439
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
{ "format": "{ua}", "rows": "useragent.tsv" }
|
||||
["Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:124.0) Gecko/20100101 Firefox/124.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15", "Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Mobile/15E148 Safari/604.1", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0"]
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
browser device os ua
|
||||
Chrome mobile Android Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Mobile Safari/537.36
|
||||
Chrome mobile Android Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Mobile Safari/537.36
|
||||
Chrome mobile Android Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Mobile Safari/537.36
|
||||
Chrome mobile Android Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Mobile Safari/537.36
|
||||
Chrome mobile Android Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Mobile Safari/537.36
|
||||
Chrome mobile Android Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36
|
||||
Chrome mobile Android Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.0.0 Mobile Safari/537.36
|
||||
Chrome mobile Android Mozilla/5.0 (Linux; Android 12; Pixel 6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36
|
||||
Chrome desktop ChromeOS Mozilla/5.0 (X11; CrOS x86_64 14541.0.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
|
||||
Chrome desktop Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
|
||||
Chrome desktop Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36
|
||||
Chrome desktop Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36
|
||||
Chrome desktop Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
|
||||
Chrome desktop Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36
|
||||
Chrome desktop Linux Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 YaBrowser/26.8.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.7922.34 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36
|
||||
Chrome desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.0.0 Safari/537.36
|
||||
Chrome mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 26_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/152.0.7977.64 Mobile/15E148 Safari/604.1
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36
|
||||
Chrome desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.0.0 Safari/537.36
|
||||
Edge desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36 Edg/151.0.0.0
|
||||
Edge desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36 Edg/152.0.0.0
|
||||
Edge desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/153.0.0.0 Safari/537.36 Edg/153.0.0.0
|
||||
Edge desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/152.0.0.0 Safari/537.36 Edg/152.0.0.0
|
||||
Firefox desktop Linux Mozilla/5.0 (X11; Linux x86_64; rv:155.0) Gecko/20100101 Firefox/155.0
|
||||
Firefox desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:126.0) Gecko/20100101 Firefox/126.0
|
||||
Firefox desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:154.0) Gecko/20100101 Firefox/154.0
|
||||
Firefox desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:155.0) Gecko/20100101 Firefox/155.0
|
||||
Firefox desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:154.0) Gecko/20100101 Firefox/154.0
|
||||
Firefox desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:155.0) Gecko/20100101 Firefox/155.0
|
||||
Opera desktop Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36 OPR/135.0.0.0
|
||||
Opera desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 OPR/134.0.0.0
|
||||
Safari mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1
|
||||
Safari mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.7.5 Mobile/15E148 Safari/604.1
|
||||
Safari mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1
|
||||
Safari mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.3 Mobile/15E148 Safari/604.1
|
||||
Safari mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Mobile/15E148 Safari/604.1
|
||||
Safari mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5.2 Mobile/15E148 Safari/604.1
|
||||
Safari mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Mobile/15E148 Safari/604.1
|
||||
Safari mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6.1 Mobile/15E148 Safari/604.1
|
||||
Safari mobile iOS Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/27.0 Mobile/15E148 Safari/604.1
|
||||
Safari desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15
|
||||
Safari desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.6 Safari/605.1.15
|
||||
Safari desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Safari/605.1.15
|
||||
Safari desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5 Safari/605.1.15
|
||||
Safari desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.5.2 Safari/605.1.15
|
||||
Safari desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6 Safari/605.1.15
|
||||
Safari desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6.1 Safari/605.1.15
|
||||
Safari desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.6.2 Safari/605.1.15
|
||||
Safari desktop macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/27.0 Safari/605.1.15
|
||||
Samsung Internet mobile Android Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) SamsungBrowser/30.0 Chrome/143.0.0.0 Mobile Safari/537.36
|
||||
|
+18
-4
@@ -1,7 +1,21 @@
|
||||
{
|
||||
"format": "{street} {street-number}\n{postal-code} {locality}",
|
||||
"street": "{/geo.SE.address.street}",
|
||||
"street-number": "{/geo.SE.address.street-number}",
|
||||
"postal-code": "{/geo.SE.address.postal-code}",
|
||||
"locality": "{/geo.SE.address.locality}"
|
||||
"street": [
|
||||
{
|
||||
"format": "{first}{last}",
|
||||
"first": ["Ängs", "Bergs", "Björk", "Drottning", "Eke", "Furu", "Hamn", "Köpmans", "Kungs", "Linné", "Norra", "Nybro", "Oden", "Öster", "Park", "Skogs", "Skol", "Slotts", "Söder", "Stations", "Stor", "Strand", "Trädgårds", "Vasa", "Väster"],
|
||||
"last": ["gatan", "vägen", "stigen", "gränd", "backen", "torget", "allén"]
|
||||
},
|
||||
["Avenyn", "Birger Jarlsgatan", "Promenaden", "Staby", "Sveavägen", "Vintjärn"]
|
||||
],
|
||||
"street-number": [
|
||||
"{int(1,9)}",
|
||||
"{int(10,99)}",
|
||||
{ "format": "{int(100,999)}", "weight": 0.2 },
|
||||
{ "format": "{int(1,9)}{upper(1)}", "weight": 0.2 },
|
||||
{ "format": "{int(10,99)}{upper(1)}", "weight": 0.1 },
|
||||
{ "format": "{int(100,999)}{upper(1)}", "weight": 0.05 }
|
||||
],
|
||||
"postal-code": "{int(100,999)} {int(10,99)}",
|
||||
"locality": ["Alingsås", "Alvesta", "Ängelholm", "Arboga", "Arvika", "Avesta", "Boden", "Bollnäs", "Borås", "Borlänge", "Enköping", "Eskilstuna", "Eslöv", "Fagersta", "Falkenberg", "Falköping", "Falun", "Finspång", "Gällivare", "Gävle", "Göteborg", "Halmstad", "Haparanda", "Härnösand", "Hässleholm", "Helsingborg", "Huddinge", "Hudiksvall", "Jönköping", "Kalmar", "Karlshamn", "Karlskoga", "Karlskrona", "Karlstad", "Katrineholm", "Kiruna", "Köping", "Kramfors", "Kristianstad", "Kristinehamn", "Landskrona", "Lidingö", "Lidköping", "Lindesberg", "Linköping", "Ljungby", "Ludvika", "Luleå", "Lund", "Lycksele", "Malmö", "Mariestad", "Mjölby", "Mölndal", "Mora", "Motala", "Nacka", "Nässjö", "Norrköping", "Norrtälje", "Nyköping", "Nynäshamn", "Örebro", "Örnsköldsvik", "Oskarshamn", "Östersund", "Piteå", "Rabbalshede", "Ronneby", "Säffle", "Sandviken", "Sävsjö", "Sigtuna", "Skara", "Skellefteå", "Skövde", "Söderhamn", "Södertälje", "Sollentuna", "Solna", "Sölvesborg", "Stockholm", "Strängnäs", "Sundbyberg", "Sundsvall", "Täby", "Tierp", "Tranås", "Trelleborg", "Trollhättan", "Uddevalla", "Ulricehamn", "Umeå", "Upplands Väsby", "Uppsala", "Vänersborg", "Varberg", "Värnamo", "Västerås", "Västervik", "Växjö", "Vetlanda", "Vimmerby", "Visby", "Ystad"]
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user