andy-z / ged4py

GEDCOM tools for Python
MIT License
18 stars 7 forks source link

Extract year from birth date #36

Closed edmundo00 closed 3 years ago

edmundo00 commented 3 years ago

I'm trying to get the year only of a birth, and I'm getting the full date or "ABT year" or "AFT year" How do I extract the year from my data, currently to get the data I'm using:

MYANCESTOR.sub_tag_value("BIRT/DATE")

Sorry, I'm just a newbie, may a stupid question, thanks for your help.

andy-z commented 3 years ago

Hi @rabitojo,

briefly MYANCESTOR.sub_tag_value("BIRT/DATE") returns you the value of the DATE record and that value is a Python object of type ged4py.date.DateValue. DateValue is a base class for a bunch of sub-classes, each subclass corresponds to a particular date format, for ABT date this would be DateValueAbout class, for other date types it will be some other sub-class of DateValue. Most of those DateValue classes have one or two dates of CalendarDate type associated with them, and CalendarDate has a year and optionally month and day (and also a calendar type associated with that date).

How to get a CalendarDate from a DateValue depends on type of DateValue. For date types that only have a single date DateValue subclasses define date attribute which holds CalendarDate, for date types with two dates there are two attributes - date1 and date2.

To handle that complexity it is easier to use visitor pattern as in Example 3, but you need to provide your own DateValueVisitor subclass which extracts year from the date, the code might look like:

from ged4py.date import DateValueVisitor

class ExtractYear(DateValueVisitor):
    """Visitor class that extracts and returns year number (integer)
    from a date, None is returned if date has no CalendarDate.
    """
    def visitSimple(self, date):
        return date.date.year

    def visitPeriod(self, date):
        # return year of the first date
        return date.date1.year

    def visitFrom(self, date):
        return date.date.year

    def visitTo(self, date):
        return date.date.year

    def visitRange(self, date):
        return date.date1.year

    def visitBefore(self, date):
        return date.date.year

    def visitAfter(self, date):
        return date.date.year

    def visitAbout(self, date):
        return date.date.year

    def visitCalculated(self, date):
        return date.date.year

    def visitEstimated(self, date):
        return date.date.year

    def visitInterpreted(self, date):
        return date.date.year

    def visitPhrase(self, date):
        return None

# .....

year_visitor = ExtractYear()

dateValue = MYANCESTOR.sub_tag_value("BIRT/DATE")
if dateValue is not None:
    year = dateValue.accept(year_visitor)
else:
    year = None

If the above looks too complicated it's also possible to use somewhat hackish dynamic lookup based on the presense of date or date1 attributes, this does not need ExtractYear visitor class:

dateValue = MYANCESTOR.sub_tag_value("BIRT/DATE")
if hasattr(dateValue, "date"):
    year = dateValue.date.year
elif hasattr(dateValue, "date1"):
    year = dateValue.date1.year
else:
    year = None
edmundo00 commented 3 years ago

Thank you so much Andy, I've tried the first solution and it works great, exactly what I needed. I'm making some code to be able to create a family tree from a GEDCOM file in a vectorial format SVG, because nothing in the market convince me to create a big family tree with 10 generations in a printable size. So far very happy with your package, doing great!!

andy-z commented 3 years ago

Glad it works for you. Closing the issue.