Documentation
¶
Overview ¶
Package hupd provides utilities for building the HUPD patent ID index used by the analyze subcommand.
HUPD (Harvard USPTO Patent Dataset) distributes patent records as individual JSON files, one per application, organised under
<year>/<year>/<application_number>.json
The package supports two indexing strategies depending on what data is available locally:
Feather metadata scan (ScanMeta): reads the pre-built Feather file published alongside the dataset on HuggingFace. Column reads are performed with the Apache Arrow IPC reader; only four columns are consumed (application_number, patent_number, publication_number, filing_date), so memory usage is proportional to the index size, not the full record payloads.
JSON directory scan (ScanIDs): walks the on-disk directory tree and parses the patent_number / publication_number header fields from each JSON file in parallel. Use this when the Feather file is unavailable or when the on-disk dataset is a subset of the full release.
Both strategies produce the same output: a map from normalised US patent identifier to one or more JSON file paths. Normalisation strips the country prefix ("US"), any trailing kind code ("B2", "A1"), and the date suffix HUPD appends to publication_number values ("-YYYYMMDD"). The resulting bare numeric string matches the EPO record's patent_id after the same transformation is applied by NormalizeUSID.
Typical call sequence ¶
ids, err := hupd.ScanMeta(ctx, metaPath, metaURL, hupdDir, bar, &mu, log) // or, without Feather: ids, err := hupd.ScanIDs(ctx, hupdDir, workers, bar, &mu, log)
Thread safety ¶
ScanIDs and ScanMeta are safe to call from any goroutine. The bar and mu parameters are used only for progress reporting and must outlive the call. The returned map is created fresh on each call and is not shared internally.
Index ¶
- Constants
- Variables
- func EnsureMeta(ctx context.Context, metaPath, metaURL string, bar ProgressReporter, ...) error
- func FmtCount(n int64) string
- func FmtRate(perSec float64) string
- func NormalizeFamily(fps []string) []string
- func NormalizeHUPDPatentNumber(s string) string
- func NormalizeHUPDPublication(raw string) string
- func NormalizeUSID(id string) string
- func ReadIDs(path string) ([2]string, error)
- func ScanIDs(ctx context.Context, dir string, workers int, bar ProgressReporter, ...) (map[string][]string, error)
- func ScanMeta(ctx context.Context, metaPath, metaURL, hupdDir string, bar ProgressReporter, ...) (map[string][]string, error)
- type Header
- type ProgressReporter
Examples ¶
Constants ¶
const BarEvery = 250 * time.Millisecond
BarEvery throttles live spinner-message updates. Far tighter than ProgressEvery so the displayed counts move smoothly while log lines stay sparse.
const ProgressEvery = 5 * time.Second
ProgressEvery throttles progress log lines.
Variables ¶
var HUPDDateSuffixRE = regexp.MustCompile(`-\d{8}$`)
HUPDDateSuffixRE matches the trailing "-YYYYMMDD" segment HUPD appends to publication_number values.
Functions ¶
func EnsureMeta ¶
func EnsureMeta( ctx context.Context, metaPath, metaURL string, bar ProgressReporter, mu *sync.Mutex, log *slog.Logger, ) error
EnsureMeta downloads the HUPD metadata Feather file from metaURL to metaPath unless the file already exists on disk. The download is written to a sibling temp file and atomically renamed on success, so a partial download never leaves a corrupt file at metaPath.
bar and mu are used for progress reporting: mu must be held while calling bar methods so the caller's progress bar is updated from a single goroutine. log may be nil (no progress lines are emitted in that case).
func NormalizeFamily ¶
NormalizeFamily maps fps through NormalizeUSID, discarding empty results.
Example ¶
package main
import (
"fmt"
"github.com/Qubut/epo-processor/internal/hupd"
)
func main() {
// Filters a family-member list to US-normalised numeric IDs only.
// Non-US identifiers and unparseable entries are silently dropped.
ids := hupd.NormalizeFamily([]string{
"US9114971B2",
"EP1234567A1", // non-US, dropped
"US20120043352A1",
"", // empty, dropped
})
for _, id := range ids {
fmt.Println(id)
}
}
Output: 9114971 20120043352
func NormalizeHUPDPatentNumber ¶
NormalizeHUPDPatentNumber returns the granted-patent-number stem (bare numeric string in HUPD).
Example ¶
package main
import (
"fmt"
"github.com/Qubut/epo-processor/internal/hupd"
)
func main() {
// HUPD stores granted patent numbers as plain numeric strings.
// Normalisation trims whitespace; the string is returned unchanged
// if it is already a bare number.
fmt.Println(hupd.NormalizeHUPDPatentNumber(" 9114971 "))
fmt.Println(hupd.NormalizeHUPDPatentNumber("9114971"))
}
Output: 9114971 9114971
func NormalizeHUPDPublication ¶
NormalizeHUPDPublication converts a raw HUPD publication_number into the same canonical stem produced by NormalizeUSID for an EPO patent identifier.
HUPD stores publication numbers in USPTO format with a trailing application date, e.g. "US20100138160A1-20100603". USPTO uses an 11-digit body (4-digit year + 7-digit serial) whereas EPO/DOCDB uses 10 (year + 6-digit serial), dropping the serial's leading zero. Without this reconciliation a HUPD publication ("US20100138160A1") and the matching EPO patent_id ("US2010138160A1") normalise to different stems ("20100138160" vs "2010138160") and never match — which previously left the EPO↔HUPD self-link (DatasetRecord.EPOHUPDPaths) empty for every application-style US patent.
Mirrors the Python reference: publication_number.split('-')[0] then s[:6] + s[7:] (drop the extra serial digit at index 6).
Example ¶
package main
import (
"fmt"
"github.com/Qubut/epo-processor/internal/hupd"
)
func main() {
// HUPD stores publication numbers in USPTO format (11-digit body) with a
// trailing application date. EPO/DOCDB uses a 10-digit body, dropping the
// serial's leading zero. NormalizeHUPDPublication reconciles them so a HUPD
// publication and the matching EPO patent_id collapse to the same stem.
fmt.Println(hupd.NormalizeHUPDPublication("US20100138160A1-20100603"))
fmt.Println(hupd.NormalizeUSID("US2010138160A1")) // EPO patent_id, same stem
}
Output: 2010138160 2010138160
func NormalizeUSID ¶
NormalizeUSID returns the canonical numeric stem of a US-prefixed EPO identifier. "US9114971B2" → "9114971", "US20120043352A1" → "20120043352". Returns "" for non-US identifiers and unparseable inputs.
Example ¶
package main
import (
"fmt"
"github.com/Qubut/epo-processor/internal/hupd"
)
func main() {
// Strips the "US" prefix and kind code to yield the bare numeric string
// that matches the EPO patent_id after the same normalisation.
fmt.Println(hupd.NormalizeUSID("US9114971B2"))
fmt.Println(hupd.NormalizeUSID("US20120043352A1"))
fmt.Println(hupd.NormalizeUSID("EP1234567A1")) // non-US → empty
}
Output: 9114971 20120043352
func ReadIDs ¶
ReadIDs reads one HUPD JSON and returns its two normalized US identifiers (patent_number, publication_number). Either may be empty.
func ScanIDs ¶
func ScanIDs( ctx context.Context, dir string, workers int, bar ProgressReporter, mu *sync.Mutex, log *slog.Logger, ) (map[string][]string, error)
ScanIDs walks dir recursively, reads the patent_number and publication_number header fields from each .json file in parallel (up to workers goroutines), and returns a normalised-ID → []path index identical in shape to the one produced by ScanMeta.
Use ScanIDs when the Feather metadata file is unavailable or when only a subset of the full HUPD dataset is present on disk.
bar and mu are used for progress reporting (see EnsureMeta). log may be nil.
func ScanMeta ¶
func ScanMeta( ctx context.Context, metaPath, metaURL, hupdDir string, bar ProgressReporter, mu *sync.Mutex, log *slog.Logger, ) (map[string][]string, error)
ScanMeta builds the normalised-ID → []path index from the HUPD Feather metadata file. If metaPath does not exist it is downloaded first via EnsureMeta.
Columns consumed: application_number, patent_number, publication_number, filing_date. Per-row paths are inferred as hupdDir/<year>/<year>/<app>.json where year is derived from the filing_date column.
Each unique normalised ID (NormalizeUSID) maps to one or more paths; multiple paths arise when the same patent appears under more than one application number in the index.
bar and mu are used for progress reporting (see EnsureMeta).
Types ¶
type Header ¶
type Header struct {
PatentNumber string `json:"patent_number"`
PublicationNumber string `json:"publication_number"`
}
Header captures the only HUPD JSON fields needed for ID matching.
type ProgressReporter ¶
type ProgressReporter interface {
Describe(string)
}
ProgressReporter is the minimal interface for displaying scan progress. cmd.progressBar and cmd.noopProgressBar both satisfy it. All insight is carried in the Describe message.