Cleanup: best-guess sex from first name (offline dictionary)
A "Guess from first name" option in the Cleanup gender section: a bundled, curated given-name -> sex dictionary (weighted English + German for the first real tree) proposes sex for people who don't have it set. Deterministic, offline, no model. Genuinely ambiguous names (Marion, Frances, Jordan, …) are excluded from both sets so they're left for a human. Reuses the existing preview/apply gender flow, so every guess is reviewed before saving. No migration. 56 backend tests pass; frontend builds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,15 @@ export default function CleanupPage() {
|
||||
setGenSel(new Set(data.map((g) => g.person_id)));
|
||||
}
|
||||
}
|
||||
async function guessGender() {
|
||||
setGenMsg(null);
|
||||
const { data } = await api.GET("/api/v1/trees/{tree_id}/cleanup/gender/guess", {
|
||||
params: { path: { tree_id: treeId } },
|
||||
});
|
||||
setGender(data ?? []);
|
||||
setGenSel(new Set((data ?? []).map((g) => g.person_id)));
|
||||
}
|
||||
|
||||
async function applyGender() {
|
||||
const updates = (gender ?? [])
|
||||
.filter((g) => genSel.has(g.person_id))
|
||||
@@ -205,9 +214,18 @@ export default function CleanupPage() {
|
||||
onChange={previewGender}
|
||||
className="hidden"
|
||||
/>
|
||||
<Button variant="outline" onClick={() => genFile.current?.click()}>
|
||||
Choose source GEDCOM
|
||||
</Button>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="outline" onClick={() => genFile.current?.click()}>
|
||||
Choose source GEDCOM
|
||||
</Button>
|
||||
<Button variant="outline" onClick={guessGender}>
|
||||
Guess from first name
|
||||
</Button>
|
||||
</div>
|
||||
<p className="text-xs text-[var(--muted)]">
|
||||
“Guess from first name” uses a built-in name dictionary for people with no sex set;
|
||||
ambiguous names (Marion, Frances, …) are left for you to decide.
|
||||
</p>
|
||||
{genMsg && <p className="text-sm text-bronze">{genMsg}</p>}
|
||||
{gender && (
|
||||
<div className="space-y-2">
|
||||
|
||||
Vendored
+51
@@ -714,6 +714,26 @@ export interface paths {
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/v1/trees/{tree_id}/cleanup/gender/guess": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path?: never;
|
||||
cookie?: never;
|
||||
};
|
||||
/**
|
||||
* Guess Gender
|
||||
* @description Best-guess sex from first names (bundled dictionary) for people missing it.
|
||||
*/
|
||||
get: operations["guess_gender_api_v1_trees__tree_id__cleanup_gender_guess_get"];
|
||||
put?: never;
|
||||
post?: never;
|
||||
delete?: never;
|
||||
options?: never;
|
||||
head?: never;
|
||||
patch?: never;
|
||||
trace?: never;
|
||||
};
|
||||
"/api/v1/trees/{tree_id}/cleanup/gender": {
|
||||
parameters: {
|
||||
query?: never;
|
||||
@@ -3481,6 +3501,37 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
guess_gender_api_v1_trees__tree_id__cleanup_gender_guess_get: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
header?: never;
|
||||
path: {
|
||||
tree_id: string;
|
||||
};
|
||||
cookie?: never;
|
||||
};
|
||||
requestBody?: never;
|
||||
responses: {
|
||||
/** @description Successful Response */
|
||||
200: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["GenderProposal"][];
|
||||
};
|
||||
};
|
||||
/** @description Validation Error */
|
||||
422: {
|
||||
headers: {
|
||||
[name: string]: unknown;
|
||||
};
|
||||
content: {
|
||||
"application/json": components["schemas"]["HTTPValidationError"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
apply_gender_api_v1_trees__tree_id__cleanup_gender_post: {
|
||||
parameters: {
|
||||
query?: never;
|
||||
|
||||
@@ -2867,6 +2867,54 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/trees/{tree_id}/cleanup/gender/guess": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"cleanup"
|
||||
],
|
||||
"summary": "Guess Gender",
|
||||
"description": "Best-guess sex from first names (bundled dictionary) for people missing it.",
|
||||
"operationId": "guess_gender_api_v1_trees__tree_id__cleanup_gender_guess_get",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "tree_id",
|
||||
"in": "path",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"format": "uuid",
|
||||
"title": "Tree Id"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/GenderProposal"
|
||||
},
|
||||
"title": "Response Guess Gender Api V1 Trees Tree Id Cleanup Gender Guess Get"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/HTTPValidationError"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/trees/{tree_id}/cleanup/gender": {
|
||||
"post": {
|
||||
"tags": [
|
||||
|
||||
Reference in New Issue
Block a user