run: make lint
if: runner.os == 'Linux'
+ - name: Test [Install]
+ run: make test/install
+
- name: Test [Unit]
run: make test/unit
# @grep -Eh '\s##\s' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
-# -include .env
-
-
-.PHONY: test/unit
-test/unit: ##H@@ Run Unit tests only
- $(PYTHON) -m coverage run -p -m pytest getmyancestors/tests
-
-# Installation
-.PHONY: deps
-deps: ##H@@ Install dependencies
- $(PYTHON) -m pip install --no-user ".[dev]"
-
-# Installation tests
-.PHONY: test/install
-test/install: ##H@@ Run installation tests
- $(PYTHON) -m coverage run -p -m pytest tests/test_installation.py
-
-.PHONY: test/offline
-test/offline: ##H@@ Run offline verification (requires fixtures)
- $(PYTHON) -m pytest tests/offline_test.py
-
-
-# Generate targets for all test files (enables autocomplete)
-TEST_FILES := $(wildcard getmyancestors/tests/test_*.py)
-TEST_TARGETS := $(patsubst getmyancestors/tests/%.py,test/unit/%,$(TEST_FILES))
-
-.PHONY: $(TEST_TARGETS)
-$(TEST_TARGETS): test/unit/%:
- pytest getmyancestors/tests/$*.py -v
-
-.PHONY: test/
-test/: ##H@@ Run unit & E2E tests
-test/: test/unit test/offline test/cov
-
-.PHONY: test/cov
-test/cov: ##H@@ Combine all coverage data and show report
- -$(PYTHON) -m coverage combine
- $(PYTHON) -m coverage report
-
-
REMOTE_HEAD ?= origin/master
PY_CHANGED_FILES ?= $(shell git diff --name-only --diff-filter=MACU $(REMOTE_HEAD) '*.py')
PY_CHANGED_FILES_FLAG ?= $(if $(PY_CHANGED_FILES),1,)
fi
+# -include .env
+
+# Installation
+.PHONY: deps
+deps: ##H@@ Install dependencies
+ $(PYTHON) -m pip install --no-user ".[dev]"
+
+
+.PHONY: test/unit
+test/unit: ##H@@ Run Unit tests only
+ $(PYTHON) -m coverage run -p -m pytest getmyancestors/tests
+
+
+# Generate targets for all test files (enables autocomplete)
+TEST_FILES := $(wildcard getmyancestors/tests/test_*.py)
+TEST_TARGETS := $(patsubst getmyancestors/tests/%.py,test/unit/%,$(TEST_FILES))
+
+.PHONY: $(TEST_TARGETS)
+$(TEST_TARGETS): test/unit/%:
+ pytest getmyancestors/tests/$*.py -v
+
+
+.PHONY: test
+test: ##H@@ Run unit & E2E tests
+test: test/unit test/offline test/install test/cov
+
+.PHONY:test/
+test/:test
+
+.PHONY: test/cov
+test/cov: ##H@@ Combine all coverage data and show report
+ -$(PYTHON) -m coverage combine
+ $(PYTHON) -m coverage report
+
+
+.PHONY: test/install
+test/install: ##H@@ Run installation tests
+ $(PYTHON) -m coverage run -p -m pytest tests/test_installation.py
+
+
+.PHONY: test/offline
+test/offline: ##H@@ Run offline verification (requires fixtures)
+ $(PYTHON) -m pytest tests/offline_test.py
+
+
.PHONY: clean
clean: ##H@@ Clean up build files/cache
rm -rf *.egg-info build dist .coverage .coverage.*
-import os
-import sys
from typing import Optional
from getmyancestors.classes.constants import FACT_TYPES, ORDINANCES
Ordinance,
Source,
)
-
-
-def _warn(msg: str):
- """Write a warning message to stderr with optional color (if TTY)."""
- use_color = sys.stderr.isatty() or os.environ.get("FORCE_COLOR", "")
- if use_color:
- sys.stderr.write(f"\033[33m{msg}\033[0m\n")
- else:
- sys.stderr.write(f"{msg}\n")
+from getmyancestors.utils import _error, _warn
class Gedcom:
f"Warning: Family @F{num}@ ({husb_name} & {wife_name}) missing _FSFTID tag, "
f"using GEDCOM pointer as fallback."
)
+ if husb_name != "Unknown" and wife_name != "Unknown":
+ _error(
+ f"Error: Family @F{num}@ ({husb_name} & {wife_name}) has NO _FSFTID tag! "
+ "This may imply a problem with the FamilySearch data. You may need to investigate it."
+ )
fam.fid = num # Use GEDCOM pointer ID as fallback
for _num, fam in self.fam.items():
def print(self, file=sys.stdout):
"""print individual in GEDCOM format"""
file.write("0 @I%s@ INDI\n" % self.id)
+ printed_names = set()
if self.name:
self.name.print(file)
+ printed_names.add(self.name)
for nick in sorted(
self.nicknames,
key=lambda x: (
),
):
file.write(cont("2 NICK %s %s" % (nick.given, nick.surname)))
+ printed_names.add(nick)
for birthname in sorted(
self.birthnames,
key=lambda x: (
x.note.text if x.note else "",
),
):
- birthname.print(file)
+ if birthname not in printed_names:
+ birthname.print(file)
+ printed_names.add(birthname)
for aka in sorted(
self.aka,
key=lambda x: (
x.note.text if x.note else "",
),
):
- aka.print(file, "aka")
+ if aka not in printed_names:
+ aka.print(file, "aka")
+ printed_names.add(aka)
for married_name in sorted(
self.married,
key=lambda x: (
x.note.text if x.note else "",
),
):
- married_name.print(file, "married")
+ if married_name not in printed_names:
+ married_name.print(file, "married")
+ printed_names.add(married_name)
if self.gender:
file.write("1 SEX %s\n" % self.gender)
for fact in sorted(
)
-def _warn(msg: str):
- """Write a warning message to stderr with optional color (if TTY)."""
- use_color = sys.stderr.isatty() or os.environ.get("FORCE_COLOR", "")
- if use_color:
- sys.stderr.write(f"\033[33m{msg}\033[0m\n")
- else:
- sys.stderr.write(f"{msg}\n")
-
-
@app.command()
def main(
files: Annotated[
target_set.clear()
seen = set()
for n in all_names:
- s = str(n)
- if s not in seen:
+ # Use all relevant fields for deduplication key, similar to sort key
+ # but using the object's hash/eq properties if possible, or a tuple representation
+ key = (
+ n.given,
+ n.surname,
+ n.prefix,
+ n.suffix,
+ n.kind,
+ n.alternative,
+ n.note.text if hasattr(n, "note") and n.note else None,
+ )
+ if key not in seen:
target_set.add(n)
- seen.add(s)
+ seen.add(key)
# Helper for whitespace normalization in quotes
def norm_space(s):
-
-
from getmyancestors.classes.tree.core import Indi, Tree
--- /dev/null
+import os
+import sys
+
+
+def _warn(msg: str):
+ """Write a yellow warning message to stderr with optional color (if TTY)."""
+ use_color = sys.stderr.isatty() or os.environ.get("FORCE_COLOR", "")
+ if use_color:
+ sys.stderr.write(f"\033[33m{msg}\033[0m\n")
+ else:
+ sys.stderr.write(f"{msg}\n")
+
+
+def _error(msg: str):
+ """Write a red error message to stderr with optional color (if TTY)."""
+ use_color = sys.stderr.isatty() or os.environ.get("FORCE_COLOR", "")
+ if use_color:
+ sys.stderr.write(f"\033[31m{msg}\033[0m\n")
+ else:
+ sys.stderr.write(f"{msg}\n")
data_file = ".tmp/.coverage"
[tool.coverage.report]
-fail_under = 67.98
+fail_under = 68.55
precision = 2
show_missing = true
-Subproject commit 741c02ced0efd708044045e855cb7fafd6eb6aa8
+Subproject commit 8166780fe62343decac53b265364e2576cdbf195