def instance_from_struct(cls, struct): """ Given a struct with metadata, create a Gramps object. self is class when called as a classmethod. """ from gramps.gen.lib import (Person, Family, Event, Source, Place, Citation, Repository, Media, Note, Tag, Date) if isinstance(struct, dict): if "_class" in struct.keys(): if struct["_class"] == "Person": return Person.create(Person.from_struct(struct)) elif struct["_class"] == "Family": return Family.create(Family.from_struct(struct)) elif struct["_class"] == "Event": return Event.create(Event.from_struct(struct)) elif struct["_class"] == "Source": return Source.create(Source.from_struct(struct)) elif struct["_class"] == "Place": return Place.create(Place.from_struct(struct)) elif struct["_class"] == "Citation": return Citation.create(Citation.from_struct(struct)) elif struct["_class"] == "Repository": return Repository.create(Repository.from_struct(struct)) elif struct["_class"] == "Media": return Media.create(Media.from_struct(struct)) elif struct["_class"] == "Note": return Note.create(Note.from_struct(struct)) elif struct["_class"] == "Tag": return Tag.create(Tag.from_struct(struct)) elif struct["_class"] == "Date": return Date().unserialize( Date.from_struct(struct, full=True)) raise AttributeError("invalid struct: %s" % struct)
def match_dates( db_handle: DbReadBase, gramps_class_name: str, handles: List[Handle], date_mask: str ): """Match dates based on a date mask or range.""" check_range = False if "-" in date_mask: check_range = True start, end = date_mask.split("-") if "/" in start: year, month, day = start.split("/") start_date = Date((int(year), int(month), int(day))) else: start_date = None if "/" in end: year, month, day = end.split("/") end_date = Date((int(year), int(month), int(day))) else: end_date = None query_method = db_handle.method("get_%s_from_handle", gramps_class_name) result = [] for handle in handles: obj = query_method(handle) date = obj.get_date_object() if date.is_valid(): if check_range: if match_date_range(date, start_date, end_date): result.append(handle) else: if match_date(date, date_mask): result.append(handle) return result
def double_click(self, obj): # bring up events on this day year, month, day = self.gui.calendar.get_date() date = Date() date.set_yr_mon_day(year, month + 1, day) run_quick_report_by_name(self.gui.dbstate, self.gui.uistate, 'onthisday', date)
def set_end_date(self, date: Union[Date, str]): """Set optional timeline end date.""" if isinstance(date, str): year, month, day = date.split("/") self.end_date = Date((int(year), int(month), int(day))) else: self.end_date = date
def write_report(self): """ The short method that runs through each month and creates a page. """ # initialize the dict to fill: self.calendar = {} # get the information, first from holidays: if self.country != 0: self.__get_holidays() # get data from database: self.collect_data() # generate the report: self.doc.start_paragraph('BIR-Title') if self.titletext == _(_TITLE0): title = self._("%(str1)s: %(str2)s") % { 'str1': self._(_TITLE0), 'str2': self._get_date(Date(self.year)) } # localized year else: title = self._("%(str1)s: %(str2)s") % { 'str1': str(self.titletext), 'str2': self._get_date(Date(self.year)) } mark = IndexMark(title, INDEX_TYPE_TOC, 1) self.doc.write_text(title, mark) self.doc.end_paragraph() if self.text1.strip() != "": self.doc.start_paragraph('BIR-Text1style') text1 = str(self.text1) if text1 == _(_TITLE1): text1 = self._(_TITLE1) self.doc.write_text(text1) self.doc.end_paragraph() if self.text2.strip() != "": self.doc.start_paragraph('BIR-Text2style') text2 = str(self.text2) if text2 == _(_TITLE2): text2 = self._(_TITLE2) self.doc.write_text(text2) self.doc.end_paragraph() if self.text3.strip() != "": self.doc.start_paragraph('BIR-Text3style') self.doc.write_text(str(self.text3)) self.doc.end_paragraph() if self.relationships: name = self.center_person.get_primary_name() self.doc.start_paragraph('BIR-Text3style') mark = utils.get_person_mark(self.database, self.center_person) # feature request 2356: avoid genitive form self.doc.write_text( self._("Relationships shown are to %s") % self._name_display.display_name(name), mark) self.doc.end_paragraph() with self._user.progress(_('Birthday and Anniversary Report'), _('Formatting months...'), 12) as step: for month in range(1, 13): step() self.print_page(month)
def match_date_range(date: Date, start_date: Date, end_date: Date) -> bool: """Check if date falls in given range.""" if start_date: if start_date.match(date, comparison=">"): return False if end_date: if end_date.match(date, comparison="<"): return False return True
def by_person_birthdate_key(self, handle): """Compare by birth date, if equal sorts by name.""" obj = self.query_method(handle) birth = get_birth_or_fallback(self.database, obj) if birth: date = birth.get_date_object() else: date = Date() return "%08d" % date.get_sort_value() + str( self.by_person_surname_key(handle))
def match_date(date: Date, mask: str) -> bool: """Check if date matches mask.""" if date is not None and date.is_valid(): year_mask, month_mask, day_mask = mask.split("/") date = gregorian(date) year = date.get_year() if year_mask == "*" or year == int(year_mask): month = date.get_month() if month_mask == "*" or month == int(month_mask): day = date.get_day() if day_mask == "*" or day == int(day_mask): return True return False
def instance_from_struct(cls, struct): """ Given a struct with metadata, create a Gramps object. self is class when called as a classmethod. """ from gramps.gen.lib import (Person, Family, Event, Source, Place, Citation, Repository, Media, Note, Tag, Date) if isinstance(struct, dict): if "_class" in struct.keys(): if struct["_class"] == "Person": return Person.create(Person.from_struct(struct)) elif struct["_class"] == "Family": return Family.create(Family.from_struct(struct)) elif struct["_class"] == "Event": return Event.create(Event.from_struct(struct)) elif struct["_class"] == "Source": return Source.create(Source.from_struct(struct)) elif struct["_class"] == "Place": return Place.create(Place.from_struct(struct)) elif struct["_class"] == "Citation": return Citation.create(Citation.from_struct(struct)) elif struct["_class"] == "Repository": return Repository.create(Repository.from_struct(struct)) elif struct["_class"] == "Media": return Media.create(Media.from_struct(struct)) elif struct["_class"] == "Note": return Note.create(Note.from_struct(struct)) elif struct["_class"] == "Tag": return Tag.create(Tag.from_struct(struct)) elif struct["_class"] == "Date": return Date().unserialize(Date.from_struct(struct, full=True)) raise AttributeError("invalid struct: %s" % struct)
def format_datetime(datestring): """ Convert an exif timestamp into a string for display, using the standard Gramps date format. """ try: timestamp = datetime.strptime(datestring, '%Y:%m:%d %H:%M:%S') except ValueError: return _('Invalid format') date_part = Date() date_part.set_yr_mon_day(timestamp.year, timestamp.month, timestamp.day) date_str = displayer.display(date_part) time_str = _('%(hr)02d:%(min)02d:%(sec)02d') % {'hr': timestamp.hour, 'min': timestamp.minute, 'sec': timestamp.second} return _('%(date)s %(time)s') % {'date': date_str, 'time': time_str}
def __init__( self, db_handle: DbReadBase, dates: Optional[str] = None, events: Optional[List[str]] = None, ratings: bool = False, relatives: Optional[List[str]] = None, relative_events: Optional[List[str]] = None, discard_empty: bool = True, omit_anchor: bool = True, precision: int = 1, locale: GrampsLocale = glocale, ): """Initialize timeline.""" self.db_handle = db_handle self.timeline: List[Tuple[Event, Person, str, str]] = [] self.dates = dates self.start_date = None self.end_date = None self.ratings = ratings self.discard_empty = discard_empty self.precision = precision self.locale = locale self.anchor_person = None self.omit_anchor = omit_anchor self.depth = 1 self.eligible_events: Set[str] = set() self.event_filters: List[str] = events or [] self.eligible_relative_events: Set[str] = set() self.relative_event_filters: List[str] = relative_events or [] self.relative_filters: List[str] = relatives or [] self.set_event_filters(self.event_filters) self.set_relative_event_filters(self.relative_event_filters) self.birth_dates: Dict[str, Date] = {} if dates and "-" in dates: start, end = dates.split("-") if "/" in start: year, month, day = start.split("/") self.start_date = Date((int(year), int(month), int(day))) else: self.start_date = None if "/" in end: year, month, day = end.split("/") self.end_date = Date((int(year), int(month), int(day))) else: self.end_date = None
def __get_sortvals(self, date): """ Get the sort values representing the start and end of a Date object. """ start = None stop = None if date.modifier == Date.MOD_NONE: start = date.sortval stop = date.sortval elif date.modifier == Date.MOD_AFTER: start = date.sortval elif date.modifier == Date.MOD_BEFORE: stop = date.sortval elif date.is_compound(): date1, date2 = date.get_start_stop_range() start = Date(*date1).sortval stop = Date(*date2).sortval return (start, stop)
def draw_year_headings(self, year_low, year_high, start_pos, stop_pos): """ Draws the column headings (years) for the page. """ style_sheet = self.doc.get_style_sheet() label_font = style_sheet.get_paragraph_style('TLG-Label').get_font() label_y = self.header - (utils.pt2cm(label_font.get_size()) * 1.2) incr = (year_high - year_low) / 5 delta = (stop_pos - start_pos) / 5 for val in range(0, 6): xpos = start_pos + (val * delta) year_str = self._get_date(Date(year_low + int(incr * val))) self.doc.center_text('TLG-label', year_str, xpos, label_y)
def parse_date(self, field): if field == "0": return None date = Date() matches = _text_parse.match(field) if matches: groups = matches.groups() date.set_as_text(groups[0]) date.set_modifier(Date.MOD_TEXTONLY) return date matches = _date_parse.match(field) if matches: groups = matches.groups() mod = _mod_map.get(groups[0], Date.MOD_NONE) if groups[3] == "..": mod = Date.MOD_SPAN cal2 = _cal_map.get(groups[5], Date.CAL_GREGORIAN) sub2 = self.sub_date(groups[4]) else: sub2 = (0, 0, 0) cal1 = _cal_map.get(groups[2], Date.CAL_GREGORIAN) sub1 = self.sub_date(groups[1]) try: date.set(Date.QUAL_NONE, mod, cal1, (sub1[0], sub1[1], sub1[2], 0, sub2[0], sub2[1], sub2[2], 0)) except DateError as e: # TRANSLATORS: leave the {date} and {gw_snippet} untranslated # in the format string, but you may re-order them if needed. LOG.warning( _("Invalid date {date} in {gw_snippet}, " "preserving date as text.").format( date=e.date.to_struct(), gw_snippet=field)) date.set(modifier=Date.MOD_TEXTONLY, text=field) return date else: return None
def create_widget(self): cell = Gtk.CellRendererText() cell.set_property('width', self._FILTER_WIDTH) cell.set_property('ellipsize', self._FILTER_ELLIPSIZE) self.generic.pack_start(cell, True) self.generic.add_attribute(cell, 'text', 0) self.on_filters_changed('Person') cell = Gtk.CellRendererText() cell.set_property('width', self._FILTER_WIDTH) cell.set_property('ellipsize', self._FILTER_ELLIPSIZE) self.tag.pack_start(cell, True) self.tag.add_attribute(cell, 'text', 0) self.etype.get_child().set_width_chars(5) exdate1 = Date() exdate2 = Date() exdate1.set(Date.QUAL_NONE, Date.MOD_RANGE, Date.CAL_GREGORIAN, (0, 0, 1800, False, 0, 0, 1900, False)) exdate2.set(Date.QUAL_NONE, Date.MOD_BEFORE, Date.CAL_GREGORIAN, (0, 0, 1850, False)) msg1 = displayer.display(exdate1) msg2 = displayer.display(exdate2) self.add_text_entry(_('Name'), self.filter_name) self.add_text_entry(_('ID'), self.filter_id) self.add_entry(_('Gender'), self.filter_gender) self.add_text_entry( _('Birth date'), self.filter_birth, _('example: "%(msg1)s" or "%(msg2)s"') % { 'msg1': msg1, 'msg2': msg2 }) self.add_text_entry( _('Death date'), self.filter_death, _('example: "%(msg1)s" or "%(msg2)s"') % { 'msg1': msg1, 'msg2': msg2 }) self.add_entry(_('Event'), self.etype) self.add_text_entry(_('Note'), self.filter_note) self.add_entry(_('Tag'), self.tag) self.add_filter_entry(_('Custom filter'), self.generic) self.add_regex_entry(self.filter_regex)
def parse_date(self,field): if field == "0": return None date = Date() matches = _text_parse.match(field) if matches: groups = matches.groups() date.set_as_text(groups[0]) date.set_modifier(Date.MOD_TEXTONLY) return date matches = _date_parse.match(field) if matches: groups = matches.groups() mod = _mod_map.get(groups[0],Date.MOD_NONE) if groups[3] == "..": mod = Date.MOD_SPAN cal2 = _cal_map.get(groups[5],Date.CAL_GREGORIAN) sub2 = self.sub_date(groups[4]) else: sub2 = (0,0,0) cal1 = _cal_map.get(groups[2],Date.CAL_GREGORIAN) sub1 = self.sub_date(groups[1]) try: date.set(Date.QUAL_NONE,mod, cal1, (sub1[0],sub1[1],sub1[2],0,sub2[0],sub2[1],sub2[2],0)) except DateError as e: # TRANSLATORS: leave the {date} and {gw_snippet} untranslated # in the format string, but you may re-order them if needed. LOG.warning(_( "Invalid date {date} in {gw_snippet}, " "preserving date as text." ).format(date=e.date.to_struct(), gw_snippet=field)) date.set(modifier=Date.MOD_TEXTONLY, text=field) return date else: return None
def _geteventdate(self, event_handle): """ Get the event date @param: event_handle -- The handle for the event to use """ event_date = Date.EMPTY event = self.r_db.get_event_from_handle(event_handle) if event: date = event.get_date_object() if date: # returns the date in YYYY-MM-DD format return Date(date.get_year_calendar("Gregorian"), date.get_month(), date.get_day()) # return empty date string return event_date
def add_birthday(self, fields, data): """Read the BDAY property of a VCard.""" date_str = data.strip() date_match = VCardParser.DATE_RE.match(date_str) date = Date() if date_match: if date_match.group(2): date_str = "%s-%s-%s" % (date_match.group(2), date_match.group(3), date_match.group(4)) else: date_str = date_match.group(1) y, m, d = [int(x, 10) for x in date_str.split('-')] try: date.set(value=(d, m, y, False)) except DateError: # TRANSLATORS: leave the {vcard_snippet} untranslated # in the format string, but you may re-order it if needed. self.__add_msg( _("Invalid date in BDAY {vcard_snippet}, " "preserving date as text.").format(vcard_snippet=data), self.line_num - 1) date.set(modifier=Date.MOD_TEXTONLY, text=data) else: if date_str: # TRANSLATORS: leave the {vcard_snippet} untranslated. self.__add_msg( _("Date {vcard_snippet} not in appropriate format " "yyyy-mm-dd, preserving date as text.").format( vcard_snippet=date_str), self.line_num - 1) date.set(modifier=Date.MOD_TEXTONLY, text=date_str) else: # silently ignore an empty BDAY record return event = Event() event.set_type(EventType(EventType.BIRTH)) event.set_date_object(date) self.database.add_event(event, self.trans) event_ref = EventRef() event_ref.set_reference_handle(event.get_handle()) self.person.set_birth_ref(event_ref)
def get_date_string(self, event): """ return date string for an event label. Based on the data availability and preferences, we select one of the following for a given event: year only complete date empty string """ if event and event.get_date_object() is not None: event_date = event.get_date_object() if event_date.get_year_valid(): if self.event_choice in [4, 5]: return self.get_date( # localized year Date(event_date.get_year())) elif self.event_choice in [1, 2, 3, 7]: return self.get_date(event_date) return ''
def add_birthday(self, fields, data): """Read the BDAY property of a VCard.""" date_str = data.strip() date_match = VCardParser.DATE_RE.match(date_str) date = Date() if date_match: if date_match.group(2): date_str = "%s-%s-%s" % (date_match.group(2), date_match.group(3), date_match.group(4)) else: date_str = date_match.group(1) y, m, d = [int(x, 10) for x in date_str.split('-')] try: date.set(value=(d, m, y, False)) except DateError: # TRANSLATORS: leave the {vcard_snippet} untranslated # in the format string, but you may re-order it if needed. self.__add_msg(_( "Invalid date in BDAY {vcard_snippet}, " "preserving date as text." ).format(vcard_snippet=data), self.line_num - 1) date.set(modifier=Date.MOD_TEXTONLY, text=data) else: if date_str: # TRANSLATORS: leave the {vcard_snippet} untranslated. self.__add_msg(_( "Date {vcard_snippet} not in appropriate format " "yyyy-mm-dd, preserving date as text." ).format(vcard_snippet=date_str), self.line_num - 1) date.set(modifier=Date.MOD_TEXTONLY, text=date_str) else: # silently ignore an empty BDAY record return event = Event() event.set_type(EventType(EventType.BIRTH)) event.set_date_object(date) self.database.add_event(event, self.trans) event_ref = EventRef() event_ref.set_reference_handle(event.get_handle()) self.person.set_birth_ref(event_ref)
def create_widget(self): cell = Gtk.CellRendererText() cell.set_property('width', self._FILTER_WIDTH) cell.set_property('ellipsize', self._FILTER_ELLIPSIZE) self.generic.pack_start(cell, True) self.generic.add_attribute(cell, 'text', 0) self.on_filters_changed('Person') cell = Gtk.CellRendererText() cell.set_property('width', self._FILTER_WIDTH) cell.set_property('ellipsize', self._FILTER_ELLIPSIZE) self.tag.pack_start(cell, True) self.tag.add_attribute(cell, 'text', 0) self.etype.get_child().set_width_chars(5) exdate1 = Date() exdate2 = Date() exdate1.set(Date.QUAL_NONE, Date.MOD_RANGE, Date.CAL_GREGORIAN, (0, 0, 1800, False, 0, 0, 1900, False)) exdate2.set(Date.QUAL_NONE, Date.MOD_BEFORE, Date.CAL_GREGORIAN, (0, 0, 1850, False)) msg1 = displayer.display(exdate1) msg2 = displayer.display(exdate2) self.add_text_entry(_('Name'), self.filter_name) self.add_text_entry(_('ID'), self.filter_id) self.add_entry(_('Gender'), self.filter_gender) self.add_text_entry(_('Birth date'), self.filter_birth, _('example: "%(msg1)s" or "%(msg2)s"') % {'msg1':msg1, 'msg2':msg2}) self.add_text_entry(_('Death date'), self.filter_death, _('example: "%(msg1)s" or "%(msg2)s"') % {'msg1':msg1, 'msg2':msg2}) self.add_entry(_('Event'), self.etype) self.add_text_entry(_('Note'), self.filter_note) self.add_entry(_('Tag'), self.tag) self.add_filter_entry(_('Custom filter'), self.generic) self.add_regex_entry(self.filter_regex)
def collect_data(self): """ This method runs through the data, and collects the relevant dates and text. """ db = self.database people = db.iter_person_handles() with self._user.progress(_('Calendar Report'), _('Applying Filter...'), db.get_number_of_people()) as step: people = self.filter.apply(self.database, people, step) ngettext = self._locale.translation.ngettext # to see "nearby" comments with self._user.progress(_('Calendar Report'), _('Reading database...'), len(people)) as step: for person_handle in people: step() person = db.get_person_from_handle(person_handle) mark = ReportUtils.get_person_mark(db, person) birth_ref = person.get_birth_ref() birth_date = None if birth_ref: birth_event = db.get_event_from_handle(birth_ref.ref) birth_date = birth_event.get_date_object() if (self.birthdays and birth_date is not None and birth_date.is_valid()): birth_date = gregorian(birth_date) year = birth_date.get_year() month = birth_date.get_month() day = birth_date.get_day() prob_alive_date = Date(self.year, month, day) nyears = self.year - year # add some things to handle maiden name: father_lastname = None # husband, actually if self.maiden_name in ['spouse_first', 'spouse_last' ]: # get husband's last name: if person.get_gender() == Person.FEMALE: family_list = person.get_family_handle_list() if family_list: if self.maiden_name == 'spouse_first': fhandle = family_list[0] else: fhandle = family_list[-1] fam = db.get_family_from_handle(fhandle) father_handle = fam.get_father_handle() mother_handle = fam.get_mother_handle() if mother_handle == person_handle: if father_handle: father = db.get_person_from_handle( father_handle) if father: father_lastname = father.get_primary_name( ).get_surname() short_name = self.get_name(person, father_lastname) alive = probably_alive(person, db, prob_alive_date) if not self.alive or alive: if nyears == 0: text = self._('%(person)s, birth') % { 'person': short_name } else: # translators: leave all/any {...} untranslated text = ngettext('{person}, {age}', '{person}, {age}', nyears).format(person=short_name, age=nyears) self.add_day_item(text, month, day, marks=[mark]) if self.anniversaries: family_list = person.get_family_handle_list() for fhandle in family_list: fam = db.get_family_from_handle(fhandle) father_handle = fam.get_father_handle() mother_handle = fam.get_mother_handle() if father_handle == person.get_handle(): spouse_handle = mother_handle else: continue # with next person if the father is not "person" # this will keep from duplicating the anniversary if spouse_handle: spouse = db.get_person_from_handle(spouse_handle) if spouse: s_m = ReportUtils.get_person_mark(db, spouse) spouse_name = self.get_name(spouse) short_name = self.get_name(person) # TEMP: this will handle ordered events # Gramps 3.0 will have a new mechanism for start/stop events are_married = None for event_ref in fam.get_event_ref_list(): event = db.get_event_from_handle( event_ref.ref) et = EventType rt = EventRoleType if event.type in [et.MARRIAGE, et.MARR_ALT] and \ (event_ref.get_role() == rt.FAMILY or event_ref.get_role() == rt.PRIMARY ): are_married = event elif event.type in [et.DIVORCE, et.ANNULMENT, et.DIV_FILING] and \ (event_ref.get_role() == rt.FAMILY or event_ref.get_role() == rt.PRIMARY ): are_married = None if are_married is not None: for event_ref in fam.get_event_ref_list(): event = db.get_event_from_handle( event_ref.ref) event_obj = event.get_date_object() if event_obj.is_valid(): event_obj = gregorian(event_obj) year = event_obj.get_year() month = event_obj.get_month() day = event_obj.get_day() prob_alive_date = Date( self.year, month, day) nyears = self.year - year if nyears == 0: text = self._( '%(spouse)s and\n %(person)s, wedding' ) % { 'spouse': spouse_name, 'person': short_name, } else: # translators: leave all/any {...} untranslated text = ngettext( "{spouse} and\n {person}, {nyears}", "{spouse} and\n {person}, {nyears}", nyears).format( spouse=spouse_name, person=short_name, nyears=nyears) alive1 = probably_alive( person, self.database, prob_alive_date) alive2 = probably_alive( spouse, self.database, prob_alive_date) if ((self.alive and alive1 and alive2) or not self.alive): self.add_day_item( text, month, day, marks=[mark, s_m])
def makedate(year, month=0, day=0, about=False): d = GrampsDate() d.set_yr_mon_day(year, month, day) if about: d.set_modifier(GrampsDate.MOD_ABOUT) return DateProxy(d)
def run_tool(self): self.progress = ProgressMeter(_('Running Date Test'),'', parent=self.parent_window) self.progress.set_pass(_('Generating dates'), 4) dates = [] # first some valid dates calendar = Date.CAL_GREGORIAN for quality in (Date.QUAL_NONE, Date.QUAL_ESTIMATED, Date.QUAL_CALCULATED): for modifier in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER, Date.MOD_ABOUT): for slash1 in (False,True): for month in range(0,13): for day in (0,5,27): if not month and day: continue d = Date() d.set(quality,modifier,calendar,(day,month,1789,slash1),"Text comment") dates.append( d) for modifier in (Date.MOD_RANGE, Date.MOD_SPAN): for slash1 in (False,True): for slash2 in (False,True): for month in range(0,13): for day in (0,5,27): if not month and day: continue d = Date() d.set(quality,modifier,calendar,(day,month,1789,slash1,day,month,1876,slash2),"Text comment") dates.append( d) if not month: continue d = Date() d.set(quality,modifier,calendar,(day,month,1789,slash1,day,13-month,1876,slash2),"Text comment") dates.append( d) if not day: continue d = Date() d.set(quality,modifier,calendar,(day,month,1789,slash1,32-day,month,1876,slash2),"Text comment") dates.append( d) d = Date() d.set(quality,modifier,calendar,(day,month,1789,slash1,32-day,13-month,1876,slash2),"Text comment") dates.append( d) modifier = Date.MOD_TEXTONLY d = Date() d.set(quality,modifier,calendar,Date.EMPTY, "This is a textual date") dates.append( d) self.progress.step() # test invalid dates #dateval = (4,7,1789,False,5,8,1876,False) #for l in range(1,len(dateval)): # d = Date() # try: # d.set(Date.QUAL_NONE,Date.MOD_NONE, # Date.CAL_GREGORIAN,dateval[:l],"Text comment") # dates.append( d) # except DateError, e: # d.set_as_text("Date identified value correctly as invalid.\n%s" % e) # dates.append( d) # except: # d = Date() # d.set_as_text("Date.set Exception %s" % ("".join(traceback.format_exception(*sys.exc_info())),)) # dates.append( d) #for l in range(1,len(dateval)): # d = Date() # try: # d.set(Date.QUAL_NONE,Date.MOD_SPAN,Date.CAL_GREGORIAN,dateval[:l],"Text comment") # dates.append( d) # except DateError, e: # d.set_as_text("Date identified value correctly as invalid.\n%s" % e) # dates.append( d) # except: # d = Date() # d.set_as_text("Date.set Exception %s" % ("".join(traceback.format_exception(*sys.exc_info())),)) # dates.append( d) #self.progress.step() #d = Date() #d.set(Date.QUAL_NONE,Date.MOD_NONE, # Date.CAL_GREGORIAN,(44,7,1789,False),"Text comment") #dates.append( d) #d = Date() #d.set(Date.QUAL_NONE,Date.MOD_NONE, # Date.CAL_GREGORIAN,(4,77,1789,False),"Text comment") #dates.append( d) #d = Date() #d.set(Date.QUAL_NONE,Date.MOD_SPAN, # Date.CAL_GREGORIAN, # (4,7,1789,False,55,8,1876,False),"Text comment") #dates.append( d) #d = Date() #d.set(Date.QUAL_NONE,Date.MOD_SPAN, # Date.CAL_GREGORIAN, # (4,7,1789,False,5,88,1876,False),"Text comment") #dates.append( d) with DbTxn(_("Date Test Plugin"), self.db, batch=True) as self.trans: self.db.disable_signals() self.progress.set_pass(_('Generating dates'), len(dates)) # create pass and fail tags pass_handle = self.create_tag(_('Pass'), '#0000FFFF0000') fail_handle = self.create_tag(_('Fail'), '#FFFF00000000') # now add them as birth to new persons i = 1 for dateval in dates: person = Person() surname = Surname() surname.set_surname("DateTest") name = Name() name.add_surname(surname) name.set_first_name("Test %d" % i) person.set_primary_name(name) self.db.add_person(person, self.trans) bevent = Event() bevent.set_type(EventType.BIRTH) bevent.set_date_object(dateval) bevent.set_description("Date Test %d (source)" % i) bevent_h = self.db.add_event(bevent, self.trans) bevent_ref = EventRef() bevent_ref.set_reference_handle(bevent_h) # for the death event display the date as text and parse it back to a new date ndate = None try: datestr = _dd.display( dateval) try: ndate = _dp.parse( datestr) if not ndate: ndate = Date() ndate.set_as_text("DateParser None") person.add_tag(fail_handle) else: person.add_tag(pass_handle) except: ndate = Date() ndate.set_as_text("DateParser Exception %s" % ("".join(traceback.format_exception(*sys.exc_info())),)) person.add_tag(fail_handle) else: person.add_tag(pass_handle) except: ndate = Date() ndate.set_as_text("DateDisplay Exception: %s" % ("".join(traceback.format_exception(*sys.exc_info())),)) person.add_tag(fail_handle) if dateval.get_modifier() != Date.MOD_TEXTONLY \ and ndate.get_modifier() == Date.MOD_TEXTONLY: # parser was unable to correctly parse the string ndate.set_as_text( "TEXTONLY: "+ndate.get_text()) person.add_tag(fail_handle) if dateval.get_modifier() == Date.MOD_TEXTONLY \ and dateval.get_text().count("Traceback") \ and pass_handle in person.get_tag_list(): person.add_tag(fail_handle) devent = Event() devent.set_type(EventType.DEATH) devent.set_date_object(ndate) devent.set_description("Date Test %d (result)" % i) devent_h = self.db.add_event(devent, self.trans) devent_ref = EventRef() devent_ref.set_reference_handle(devent_h) person.set_birth_ref(bevent_ref) person.set_death_ref(devent_ref) self.db.commit_person(person, self.trans) i = i + 1 self.progress.step() self.db.enable_signals() self.db.request_rebuild() self.progress.close()
def run(self): BUTTONS = ((_("Select All"), self.select_all), (_("Select None"), self.select_none), (_("Toggle Selection"), self.toggle_select), (_("Add Selected Events"), self.apply_selection), ) if hasattr(self, "table") and self.table: self.reselect = False if self.options.handler.options_dict['remove']: QuestionDialog(_("Remove Events, Notes, and Source and Reselect Data"), _("Are you sure you want to remove previous events, notes, and source and reselect data?"), _("Remove and Run Select Again"), self.set_reselect, self.window) else: QuestionDialog(_("Reselect Data"), _("Are you sure you want to reselect data?"), _("Run Select Again"), self.set_reselect, self.window) if not self.reselect: return current_date = Date() current_date.set_yr_mon_day(*time.localtime(time.time())[0:3]) self.action = {} widget = self.add_results_frame(_("Select")) document = TextBufDoc(make_basic_stylesheet(), None) document.dbstate = self.dbstate document.uistate = self.uistate document.open("", container=widget) self.sdb = SimpleAccess(self.db) sdoc = SimpleDoc(document) stab = QuickTable(self.sdb) self.table = stab stab.columns(_("Select"), _("Person"), _("Action"), _("Birth Date"), _("Death Date"), _("Evidence"), _("Relative")) self.results_write(_("Processing...\n")) self.filter_option = self.options.menu.get_option_by_name('filter') self.filter = self.filter_option.get_filter() # the actual filter people = self.filter.apply(self.db, self.db.iter_person_handles()) num_people = self.db.get_number_of_people() source_text = self.options.handler.options_dict['source_text'] source = None add_birth = self.options.handler.options_dict['add_birth'] add_death = self.options.handler.options_dict['add_death'] remove_old = self.options.handler.options_dict['remove'] self.MAX_SIB_AGE_DIFF = self.options.handler.options_dict['MAX_SIB_AGE_DIFF'] self.MAX_AGE_PROB_ALIVE = self.options.handler.options_dict['MAX_AGE_PROB_ALIVE'] self.AVG_GENERATION_GAP = self.options.handler.options_dict['AVG_GENERATION_GAP'] if remove_old: with DbTxn("", self.db, batch=True) as self.trans: self.db.disable_signals() self.results_write(_("Removing old estimations... ")) self.progress.set_pass((_("Removing '%s'...") % source_text), num_people) supdate = None for person_handle in people: self.progress.step() pupdate = 0 person = self.db.get_person_from_handle(person_handle) birth_ref = person.get_birth_ref() if birth_ref: birth = self.db.get_event_from_handle(birth_ref.ref) for citation_handle in birth.get_citation_list(): citation = self.db.get_citation_from_handle(citation_handle) source_handle = citation.get_reference_handle() #print "birth handle:", source_handle source = self.db.get_source_from_handle(source_handle) if source: if source.get_title() == source_text: #print("birth event removed from:", # person.gramps_id) person.set_birth_ref(None) person.remove_handle_references('Event',[birth_ref.ref]) # remove note note_list = birth.get_referenced_note_handles() birth.remove_handle_references('Note', [note_handle for (obj_type, note_handle) in note_list]) for (obj_type, note_handle) in note_list: self.db.remove_note(note_handle, self.trans) self.db.remove_event(birth_ref.ref, self.trans) self.db.remove_citation(citation_handle, self.trans) pupdate = 1 supdate = source # found the source. break death_ref = person.get_death_ref() if death_ref: death = self.db.get_event_from_handle(death_ref.ref) for citation_handle in death.get_citation_list(): citation = self.db.get_citation_from_handle(citation_handle) source_handle = citation.get_reference_handle() #print "death handle:", source_handle source = self.db.get_source_from_handle(source_handle) if source: if source.get_title() == source_text: #print("death event removed from:", # person.gramps_id) person.set_death_ref(None) person.remove_handle_references('Event',[death_ref.ref]) # remove note note_list = death.get_referenced_note_handles() death.remove_handle_references('Note', [note_handle for (obj_type, note_handle) in note_list]) for (obj_type, note_handle) in note_list: self.db.remove_note(note_handle, self.trans) self.db.remove_event(death_ref.ref, self.trans) self.db.remove_citation(citation_handle, self.trans) pupdate = 1 supdate = source # found the source. break if pupdate == 1: self.db.commit_person(person, self.trans) if supdate: self.db.remove_source(supdate.handle, self.trans) self.results_write(_("done!\n")) self.db.enable_signals() self.db.request_rebuild() if add_birth or add_death: self.results_write(_("Selecting... \n\n")) self.progress.set_pass(_('Selecting...'), num_people) row = 0 for person_handle in people: self.progress.step() person = self.db.get_person_from_handle(person_handle) birth_ref = person.get_birth_ref() death_ref = person.get_death_ref() add_birth_event, add_death_event = False, False if not birth_ref or not death_ref: date1, date2, explain, other = self.calc_estimates(person) if birth_ref: ev = self.db.get_event_from_handle(birth_ref.ref) date1 = ev.get_date_object() elif not birth_ref and add_birth and date1: if date1.match( current_date, "<"): add_birth_event = True date1.make_vague() else: date1 = Date() else: date1 = Date() if death_ref: ev = self.db.get_event_from_handle(death_ref.ref) date2 = ev.get_date_object() elif not death_ref and add_death and date2: if date2.match( current_date, "<"): add_death_event = True date2.make_vague() else: date2 = Date() else: date2 = Date() # Describe if add_birth_event and add_death_event: action = _("Add birth and death events") elif add_birth_event: action = _("Add birth event") elif add_death_event: action = _("Add death event") else: continue #stab.columns(_("Select"), _("Person"), _("Action"), # _("Birth Date"), _("Death Date"), _("Evidence"), _("Relative")) if add_birth == 1 and not birth_ref: # no date date1 = Date() if add_death == 1 and not death_ref: # no date date2 = Date() if person == other: other = None stab.row("checkbox", person, action, date1, date2, explain or "", other or "") if add_birth_event: stab.set_cell_markup(3, row, "<b>%s</b>" % date_displayer.display(date1)) if add_death_event: stab.set_cell_markup(4, row, "<b>%s</b>" % date_displayer.display(date2)) self.action[person.handle] = (add_birth_event, add_death_event) row += 1 if row > 0: self.results_write(" ") for text, function in BUTTONS: self.make_button(text, function, widget) self.results_write("\n") stab.write(sdoc) self.results_write(" ") for text, function in BUTTONS: self.make_button(text, function, widget) self.results_write("\n") else: self.results_write(_("No events to be added.")) self.results_write("\n") self.results_write("\n") self.set_current_frame(_("Select"))
def __create_gui(self): vbox = Gtk.VBox(orientation=Gtk.Orientation.VERTICAL) vbox.set_spacing(4) label = Gtk.Label( _("This gramplet allows setting properties for multiple places at the same time" )) label.set_halign(Gtk.Align.START) label.set_line_wrap(True) vbox.pack_start(label, False, True, 0) pt_label = Gtk.Label(_('Place type:')) pt_label.set_halign(Gtk.Align.START) self.typecombo = Gtk.ComboBoxText.new_with_entry() self.typecombo.set_tooltip_text( _("New place type for the selected places")) self.__fill_combo(self.typecombo, list(self.__typenames()), wrap_width=4) tag_label = Gtk.Label(_('Tag:')) tag_label.set_halign(Gtk.Align.START) self.tagcombo = Gtk.ComboBoxText.new_with_entry() self.tagcombo.set_tooltip_text(_("New tag for the selected places")) self.__fill_combo(self.tagcombo, list(self.__tagnames())) date_label = Gtk.Label(_('Timespan:')) date_label.set_halign(Gtk.Align.START) self.date_entry = ValidatableMaskedEntry() date_button = Gtk.Button.new_from_icon_name("gramps-date", -1) timespan_tooltip = _("Set timespan for enclosing places") date_label.set_tooltip_text(timespan_tooltip) date_button.set_tooltip_text(timespan_tooltip) self.date_entry.set_tooltip_text(timespan_tooltip) self.date_object = Date() self.track = [] self.date_field = MonitoredDate(self.date_entry, date_button, self.date_object, self.uistate, self.track) label1 = Gtk.Label(_("New enclosing place:")) label1.set_halign(Gtk.Align.START) label1.set_line_wrap(True) self.label1 = label1 self.enclosing_place = Gtk.Label(_("None")) self.enclosing_place.set_halign(Gtk.Align.START) self.enclosing_place.set_tooltip_text( _("New enclosing place for the selected places")) pt_grid = Gtk.Grid(column_spacing=10, row_spacing=2) pt_grid.attach(pt_label, 0, 0, 1, 1) pt_grid.attach(self.typecombo, 1, 0, 1, 1) pt_grid.attach(tag_label, 0, 1, 1, 1) pt_grid.attach(self.tagcombo, 1, 1, 1, 1) pt_grid.attach(date_label, 0, 2, 1, 1) pt_grid.attach(self.date_entry, 1, 2, 1, 1) pt_grid.attach(date_button, 2, 2, 1, 1) pt_grid.attach(label1, 0, 3, 1, 1) pt_grid.attach(self.enclosing_place, 1, 3, 1, 1) vbox.pack_start(pt_grid, False, True, 0) but_set_enclosing = Gtk.Button(label=_('Select enclosing place')) but_set_enclosing.connect("clicked", self.cb_select) but_set_enclosing.set_tooltip_text( _("Select a new enclosing place for the selected places")) vbox.pack_start(but_set_enclosing, False, True, 10) self.clear_enclosing = Gtk.CheckButton( _("Clear original enclosing places")) self.clear_enclosing.set_tooltip_text( _("If checked then any previous enclosing place will be removed from the selected places" )) vbox.pack_start(self.clear_enclosing, False, True, 0) self.clear_tags = Gtk.CheckButton(_("Clear tags")) self.clear_tags.set_tooltip_text( _("If checked then any previous tag will be removed from the selected places" )) vbox.pack_start(self.clear_tags, False, True, 0) self.generate_hierarchy = Gtk.CheckButton(_("Generate hierarchy")) self.generate_hierarchy.connect("clicked", self.cb_select_generate_hierarchy) self.generate_hierarchy.set_tooltip_text( _("If checked then a place hierarchy will be generated, based on the names of the original places" )) vbox.pack_start(self.generate_hierarchy, False, True, 0) box1 = Gtk.VBox() box1.set_margin_left(20) box2 = Gtk.HBox() box2.pack_start(Gtk.Label(_("Separator:")), False, True, 0) group = None rownum = 0 group = Gtk.RadioButton.new_with_label_from_widget(group, _("Comma")) group.connect("toggled", self.cb_set_sep, ',') group.set_tooltip_text( _("If selected then the original names will be split at commas to create the new hierarchy" )) group.set_sensitive(False) box2.pack_start(group, False, True, 0) group.set_active(True) rownum += 1 group = Gtk.RadioButton.new_with_label_from_widget(group, _("Space")) group.connect("toggled", self.cb_set_sep, None) group.set_tooltip_text( _("If selected then the original names will be split at blank spaces to create the new hierarchy" )) group.set_sensitive(False) box2.pack_start(group, False, True, 0) self.sep_group = group box1.pack_start(box2, False, True, 0) self.reverse = Gtk.CheckButton(_("reverse hierarchy")) self.reverse.set_tooltip_text( _("If checked then the original name is assumed to start with the largest place in the hierarchy" )) self.reverse.set_sensitive(False) box1.pack_start(self.reverse, False, True, 0) vbox.pack_start(box1, False, True, 0) self.replace_text = Gtk.CheckButton(_("Replace text")) self.replace_text.set_tooltip_text( _("If checked then a specified text is replaced with a new text in the selected place names" )) self.replace_text.connect("clicked", self.cb_select_replace_text) self.use_regex = Gtk.CheckButton(_("Use regex")) self.use_regex.set_tooltip_text( _("If checked then the old text can be a regular expression and the new text can contain substitutions (\\1, \\2 etc.)" )) self.use_regex.set_sensitive(False) replace_text_box = Gtk.HBox() replace_text_box.pack_start(self.replace_text, False, True, 0) replace_text_box.pack_start(self.use_regex, False, True, 0) vbox.pack_start(replace_text_box, False, True, 0) old_text_label = Gtk.Label(_("Old text:")) self.old_text = Gtk.Entry() self.old_text.set_sensitive(False) self.old_text.set_tooltip_text( _("The original text (or regular expression) to be replaced")) new_text_label = Gtk.Label(_("New text:")) self.new_text = Gtk.Entry() self.new_text.set_sensitive(False) self.new_text.set_tooltip_text( _("The new text that will replace the old text")) replace_grid = Gtk.Grid(column_spacing=10) replace_grid.set_margin_left(20) replace_grid.attach(old_text_label, 1, 0, 1, 1) replace_grid.attach(self.old_text, 2, 0, 1, 1) replace_grid.attach(new_text_label, 1, 1, 1, 1) replace_grid.attach(self.new_text, 2, 1, 1, 1) vbox.pack_start(replace_grid, False, True, 0) but_clear = Gtk.Button(label=_('Clear selections')) but_clear.connect("clicked", self.cb_clear) but_clear.set_tooltip_text(_("Clears all settings")) vbox.pack_start(but_clear, False, True, 10) but_apply = Gtk.Button(label=_('Apply to selected places')) but_apply.connect("clicked", self.cb__apply) but_apply.set_tooltip_text( _("Executes all specified operations for selected places")) vbox.pack_start(but_apply, False, True, 0) vbox.show_all() return vbox
class Timeline: """Timeline class.""" def __init__( self, db_handle: DbReadBase, dates: Optional[str] = None, events: Optional[List[str]] = None, ratings: bool = False, relatives: Optional[List[str]] = None, relative_events: Optional[List[str]] = None, discard_empty: bool = True, omit_anchor: bool = True, precision: int = 1, locale: GrampsLocale = glocale, ): """Initialize timeline.""" self.db_handle = db_handle self.timeline: List[Tuple[Event, Person, str, str]] = [] self.dates = dates self.start_date = None self.end_date = None self.ratings = ratings self.discard_empty = discard_empty self.precision = precision self.locale = locale self.anchor_person = None self.omit_anchor = omit_anchor self.depth = 1 self.eligible_events: Set[str] = set() self.event_filters: List[str] = events or [] self.eligible_relative_events: Set[str] = set() self.relative_event_filters: List[str] = relative_events or [] self.relative_filters: List[str] = relatives or [] self.set_event_filters(self.event_filters) self.set_relative_event_filters(self.relative_event_filters) self.birth_dates: Dict[str, Date] = {} if dates and "-" in dates: start, end = dates.split("-") if "/" in start: year, month, day = start.split("/") self.start_date = Date((int(year), int(month), int(day))) else: self.start_date = None if "/" in end: year, month, day = end.split("/") self.end_date = Date((int(year), int(month), int(day))) else: self.end_date = None def set_start_date(self, date: Union[Date, str]): """Set optional timeline start date.""" if isinstance(date, str): year, month, day = date.split("/") self.start_date = Date((int(year), int(month), int(day))) else: self.start_date = date def set_end_date(self, date: Union[Date, str]): """Set optional timeline end date.""" if isinstance(date, str): year, month, day = date.split("/") self.end_date = Date((int(year), int(month), int(day))) else: self.end_date = date def set_discard_empty(self, discard_empty: bool): """Set discard empty identifier.""" self.discard_empty = discard_empty def set_precision(self, precision: int): """Set optional precision for span.""" self.precision = precision def set_locale(self, locale: str): """Set optional locale for span.""" self.locale = get_locale_for_language(locale, default=True) def set_event_filters(self, filters: Optional[List[str]] = None): """Prepare the event filter table.""" self.event_filters = filters or [] self.eligible_events = self._prepare_eligible_events( self.event_filters) def set_relative_event_filters(self, filters: Optional[List[str]] = None): """Prepare the relative event filter table.""" self.relative_event_filters = filters or [] self.eligible_relative_events = self._prepare_eligible_events( self.relative_event_filters) def _prepare_eligible_events(self, event_filters: List[str]): """Prepare an event filter list.""" eligible_events = {"Birth", "Death"} event_type = EventType() default_event_types = event_type.get_standard_xml() default_event_map = event_type.get_map() custom_event_types = self.db_handle.get_event_types() for key in event_filters: if key in default_event_types: eligible_events.add(key) continue if key in custom_event_types: eligible_events.add(key) continue if key not in EVENT_CATEGORIES: raise ValueError( f"{key} is not a valid event or event category") for entry in event_type.get_menu_standard_xml(): event_key = entry[0].lower().replace("life events", "vital") if event_key in event_filters: for event_id in entry[1]: if event_id in default_event_map: eligible_events.add(default_event_map[event_id]) break if "custom" in event_filters: for event_name in custom_event_types: eligible_events.add(event_name) return eligible_events def get_age(self, start_date: str, date: str): """Return calculated age or empty string otherwise.""" age = "" if start_date: span = Span(start_date, date) if span.is_valid(): age = str( span.format(precision=self.precision, dlocale=self.locale).strip("()")) return age def is_death_indicator(self, event: Event) -> bool: """Check if an event indicates death timeframe.""" if event.type in DEATH_INDICATORS: return True for event_name in [ "Funeral", "Interment", "Reinterment", "Inurnment", "Memorial", "Visitation", "Wake", "Shiva", ]: if self.locale.translation.sgettext(event_name) == str(event.type): return True return False def is_eligible(self, event: Event, relative: bool): """Check if an event is eligible for the timeline.""" if relative: if self.relative_event_filters == []: return True return str(event.get_type()) in self.eligible_relative_events if self.event_filters == []: return True return str(event.get_type()) in self.eligible_events def add_event(self, event: Tuple[Event, Person, str, str], relative: bool = False): """Add event to timeline if needed.""" if self.discard_empty: if event[0].date.sortval == 0: return if self.end_date: if self.end_date.match(event[0].date, comparison="<"): return if self.start_date: if self.start_date.match(event[0].date, comparison=">"): return for item in self.timeline: if item[0].handle == event[0].handle: return if self.is_eligible(event[0], relative): if self.ratings: count, confidence = get_rating(self.db_handle, event[0]) event[0].citations = count event[0].confidence = confidence self.timeline.append(event) def add_person( self, handle: Handle, anchor: bool = False, start: bool = True, end: bool = True, ancestors: int = 1, offspring: int = 1, ): """Add events for a person to the timeline.""" if self.anchor_person and handle == self.anchor_person.handle: return person = self.db_handle.get_person_from_handle(handle) if person.handle not in self.birth_dates: event = get_birth_or_fallback(self.db_handle, person) if event: self.birth_dates.update({person.handle: event.date}) for event_ref in person.event_ref_list: event = self.db_handle.get_event_from_handle(event_ref.ref) role = event_ref.get_role().xml_str() self.add_event((event, person, "self", role)) if anchor and not self.anchor_person: self.anchor_person = person self.depth = max(ancestors, offspring) + 1 if self.start_date is None and self.end_date is None: if len(self.timeline) > 0: if start or end: self.timeline.sort(key=lambda x: x[0].get_date_object( ).get_sort_value()) if start: self.start_date = self.timeline[0][0].date if end: if self.is_death_indicator(self.timeline[-1][0]): self.end_date = self.timeline[-1][0].date else: data = probably_alive_range( person, self.db_handle) self.end_date = data[1] for family in person.parent_family_list: self.add_family(family, ancestors=ancestors) for family in person.family_list: self.add_family(family, anchor=person, ancestors=ancestors, offspring=offspring) else: for family in person.family_list: self.add_family(family, anchor=person, events_only=True) def add_relative(self, handle: Handle, ancestors: int = 1, offspring: int = 1): """Add events for a relative of the anchor person.""" person = self.db_handle.get_person_from_handle(handle) calculator = get_relationship_calculator(reinit=True, clocale=self.locale) calculator.set_depth(self.depth) relationship = calculator.get_one_relationship(self.db_handle, self.anchor_person, person) if self.relative_filters: found = False for relative in self.relative_filters: if relative in relationship: found = True break if not found: return if self.relative_event_filters: for event_ref in person.event_ref_list: event = self.db_handle.get_event_from_handle(event_ref.ref) role = event_ref.get_role().xml_str() self.add_event((event, person, relationship, role), relative=True) event = get_birth_or_fallback(self.db_handle, person) if event: self.add_event((event, person, relationship, "Primary"), relative=True) if person.handle not in self.birth_dates: self.birth_dates.update({person.handle: event.date}) event = get_death_or_fallback(self.db_handle, person) if event: self.add_event((event, person, relationship, "Primary"), relative=True) for family_handle in person.family_list: family = self.db_handle.get_family_from_handle(family_handle) event = get_marriage_or_fallback(self.db_handle, family) if event: self.add_event((event, person, relationship, "Family"), relative=True) event = get_divorce_or_fallback(self.db_handle, family) if event: self.add_event((event, person, relationship, "Family"), relative=True) if offspring > 1: for child_ref in family.child_ref_list: self.add_relative(child_ref.ref, offspring=offspring - 1) if ancestors > 1: if "father" in relationship or "mother" in relationship: for family_handle in person.parent_family_list: self.add_family(family_handle, include_children=False, ancestors=ancestors - 1) def add_family( self, handle: Handle, anchor: Optional[Person] = None, include_children: bool = True, ancestors: int = 1, offspring: int = 1, events_only: bool = False, ): """Add events for all family members to the timeline.""" family = self.db_handle.get_family_from_handle(handle) if anchor: for event_ref in family.event_ref_list: event = self.db_handle.get_event_from_handle(event_ref.ref) role = event_ref.get_role().xml_str() self.add_event((event, anchor, "self", role)) if events_only: return if self.anchor_person: if (family.father_handle and family.father_handle != self.anchor_person.handle): self.add_relative(family.father_handle, ancestors=ancestors) if (family.mother_handle and family.mother_handle != self.anchor_person.handle): self.add_relative(family.mother_handle, ancestors=ancestors) if include_children: for child in family.child_ref_list: if child.ref != self.anchor_person.handle: self.add_relative(child.ref, offspring=offspring) else: if family.father_handle: self.add_person(family.father_handle) if family.mother_handle: self.add_person(family.mother_handle) for child in family.child_ref_list: self.add_person(child.ref) def profile(self, page=0, pagesize=20): """Return a profile for the timeline.""" profiles = [] self.timeline.sort( key=lambda x: x[0].get_date_object().get_sort_value()) events = self.timeline if page > 0: offset = (page - 1) * pagesize events = events[offset:offset + pagesize] for (event, person_object, relationship, role) in events: label = self.locale.translation.sgettext(str(event.type)) if (person_object and self.anchor_person and self.anchor_person.handle != person_object.handle and relationship not in ["self", "", None]): label = f"{label} ({relationship.title()})" try: obj = self.db_handle.get_place_from_handle(event.place) place = get_place_profile_for_object(self.db_handle, obj, locale=self.locale) place["display_name"] = pd.display_event(self.db_handle, event) place["handle"] = event.place except HandleError: place = {} age = "" person = {} if person_object is not None: person_age = "" get_person = True if self.anchor_person: if self.anchor_person.handle in self.birth_dates: age = self.get_age( self.birth_dates[self.anchor_person.handle], event.date) if self.anchor_person.handle == person_object.handle: person_age = age if self.omit_anchor: get_person = False if get_person: person = get_person_profile_for_object(self.db_handle, person_object, {}, locale=self.locale) if not person_age and person_object.handle in self.birth_dates: person_age = self.get_age( self.birth_dates[person_object.handle], event.date) if not age: age = person_age person["age"] = person_age profile = { "date": self.locale.date_displayer.display(event.date), "description": event.description, "gramps_id": event.gramps_id, "handle": event.handle, "label": self.locale.translation.sgettext(label), "media": [x.ref for x in event.media_list], "person": person, "place": place, "age": age, "type": event.type, "role": self.locale.translation.gettext(role), } profile["person"]["relationship"] = str(relationship) if self.ratings: profile["citations"] = event.citations profile["confidence"] = event.confidence profiles.append(profile) return profiles
def write_people(self): """ write the people """ self.doc.add_comment('') # If we're going to attempt to include images, then use the HTML style # of .gv file. use_html_output = False if self._incimages: use_html_output = True # loop through all the people we need to output for handle in sorted(self._people): # enable a diff person = self._db.get_person_from_handle(handle) name = self._name_display.display(person) p_id = person.get_gramps_id() # figure out what colour to use gender = person.get_gender() colour = self._colorunknown if gender == Person.MALE: colour = self._colormales elif gender == Person.FEMALE: colour = self._colorfemales # see if we have surname colours that match this person surname = person.get_primary_name().get_surname() surname = surname.encode('iso-8859-1', 'xmlcharrefreplace') if surname in self._surnamecolors: colour = self._surnamecolors[surname] # see if we have a birth/death or fallback dates we can use if self._incdates or self._incplaces: bth_event = get_birth_or_fallback(self._db, person) dth_event = get_death_or_fallback(self._db, person) else: bth_event = None dth_event = None # output the birth or fallback event birth_str = None if bth_event and self._incdates: date = bth_event.get_date_object() if self._just_years and date.get_year_valid(): birth_str = self._get_date( # localized year Date(date.get_year())) else: birth_str = self._get_date(date) # get birth place (one of: hamlet, village, town, city, parish, # county, province, region, state or country) birthplace = None if bth_event and self._incplaces: birthplace = self.get_event_place(bth_event) # see if we have a deceased date we can use death_str = None if dth_event and self._incdates: date = dth_event.get_date_object() if self._just_years and date.get_year_valid(): death_str = self._get_date( # localized year Date(date.get_year())) else: death_str = self._get_date(date) # get death place (one of: hamlet, village, town, city, parish, # county, province, region, state or country) deathplace = None if dth_event and self._incplaces: deathplace = self.get_event_place(dth_event) # see if we have an image to use for this person image_path = None if self._incimages: media_list = person.get_media_list() if len(media_list) > 0: media_handle = media_list[0].get_reference_handle() media = self._db.get_media_from_handle(media_handle) media_mime_type = media.get_mime_type() if media_mime_type[0:5] == "image": image_path = get_thumbnail_path( media_path_full(self._db, media.get_path()), rectangle=media_list[0].get_rectangle(), size=self._imagesize) # put the label together and output this person label = "" line_delimiter = '\\n' if use_html_output: line_delimiter = '<BR/>' # if we have an image, then start an HTML table; # remember to close the table afterwards! if image_path: label = ('<TABLE BORDER="0" CELLSPACING="2" CELLPADDING="0" ' 'CELLBORDER="0"><TR><TD><IMG SRC="%s"/></TD>' % image_path) if self._imageonside == 0: label += '</TR><TR>' label += '<TD>' # at the very least, the label must have the person's name name = name.replace('"', '"') label += name.replace('<', '<').replace('>', '>') if self.includeid == 1: # same line label += " (%s)" % p_id elif self.includeid == 2: # own line label += "%s(%s)" % (line_delimiter, p_id) if birth_str or death_str: label += '%s(' % line_delimiter if birth_str: label += '%s' % birth_str label += ' – ' if death_str: label += '%s' % death_str label += ')' if birthplace or deathplace: if birthplace == deathplace: deathplace = None # no need to print the same name twice label += '%s' % line_delimiter if birthplace: label += '%s' % birthplace if birthplace and deathplace: label += ' / ' if deathplace: label += '%s' % deathplace # see if we have a table that needs to be terminated if image_path: label += '</TD></TR></TABLE>' else: # non html label is enclosed by "" so escape other " label = label.replace('"', '\\\"') shape = "box" style = "solid" border = colour fill = colour # do not use colour if this is B&W outline if self._colorize == 'outline': border = "" fill = "" if gender == person.FEMALE and ("f" in self._useroundedcorners): style = "rounded" elif gender == person.MALE and ("m" in self._useroundedcorners): style = "rounded" elif gender == person.UNKNOWN: shape = "hexagon" # if we're filling the entire node: if self._colorize == 'filled': style += ",filled" border = "" # we're done -- add the node self.doc.add_node(p_id, label=label, shape=shape, color=border, style=style, fillcolor=fill, htmloutput=use_html_output)
def run(self): BUTTONS = ((_("Select All"), self.select_all), (_("Select None"), self.select_none), (_("Toggle Selection"), self.toggle_select), (_("Add Selected Events"), self.apply_selection), ) if hasattr(self, "table") and self.table: self.reselect = False if self.options.handler.options_dict['remove']: QuestionDialog(_("Remove Events, Notes, and Source and Reselect Data"), _("Are you sure you want to remove previous events, notes, and source and reselect data?"), _("Remove and Run Select Again"), self.set_reselect, self.window) else: QuestionDialog(_("Reselect Data"), _("Are you sure you want to reselect data?"), _("Run Select Again"), self.set_reselect, self.window) if not self.reselect: return current_date = Date() current_date.set_yr_mon_day(*time.localtime(time.time())[0:3]) self.action = {} widget = self.add_results_frame(_("Select")) document = TextBufDoc(make_basic_stylesheet(), None) document.dbstate = self.dbstate document.uistate = self.uistate document.open("", container=widget) self.sdb = SimpleAccess(self.db) sdoc = SimpleDoc(document) stab = QuickTable(self.sdb) self.table = stab stab.columns(_("Select"), _("Person"), _("Action"), _("Birth Date"), _("Death Date"), _("Evidence"), _("Relative")) self.results_write(_("Processing...\n")) self.filter_option = self.options.menu.get_option_by_name('filter') self.filter = self.filter_option.get_filter() # the actual filter people = self.filter.apply(self.db, self.db.iter_person_handles()) num_people = self.db.get_number_of_people() source_text = self.options.handler.options_dict['source_text'] source = None add_birth = self.options.handler.options_dict['add_birth'] add_death = self.options.handler.options_dict['add_death'] remove_old = self.options.handler.options_dict['remove'] self.MAX_SIB_AGE_DIFF = self.options.handler.options_dict['MAX_SIB_AGE_DIFF'] self.MAX_AGE_PROB_ALIVE = self.options.handler.options_dict['MAX_AGE_PROB_ALIVE'] self.AVG_GENERATION_GAP = self.options.handler.options_dict['AVG_GENERATION_GAP'] if remove_old: with DbTxn("", self.db, batch=True) as self.trans: self.db.disable_signals() self.results_write(_("Removing old estimations... ")) self.progress.set_pass((_("Removing '%s'...") % source_text), num_people) for person_handle in people: self.progress.step() pupdate = 0 person = self.db.get_person_from_handle(person_handle) birth_ref = person.get_birth_ref() if birth_ref: birth = self.db.get_event_from_handle(birth_ref.ref) for citation_handle in birth.get_citation_list(): citation = self.db.get_citation_from_handle(citation_handle) source_handle = citation.get_reference_handle() #print "birth handle:", source_handle source = self.db.get_source_from_handle(source_handle) if source: #print "birth source:", source, source.get_title() if source.get_title() == source_text: person.set_birth_ref(None) person.remove_handle_references('Event',[birth_ref.ref]) # remove note note_list = birth.get_referenced_note_handles() birth.remove_handle_references('Note', [note_handle for (obj_type, note_handle) in note_list]) for (obj_type, note_handle) in note_list: self.db.remove_note(note_handle, self.trans) self.db.remove_event(birth_ref.ref, self.trans) self.db.commit_source(source, self.trans) pupdate = 1 break death_ref = person.get_death_ref() if death_ref: death = self.db.get_event_from_handle(death_ref.ref) for citation_handle in death.get_citation_list(): citation = self.db.get_citation_from_handle(citation_handle) source_handle = citation.get_reference_handle() #print "death handle:", source_handle source = self.db.get_source_from_handle(source_handle) if source: #print "death source:", source, source.get_title() if source.get_title() == source_text: person.set_death_ref(None) person.remove_handle_references('Event',[death_ref.ref]) # remove note note_list = death.get_referenced_note_handles() birth.remove_handle_references('Note', [note_handle for (obj_type, note_handle) in note_list]) for (obj_type, note_handle) in note_list: self.db.remove_note(note_handle, self.trans) self.db.remove_event(death_ref.ref, self.trans) self.db.commit_source(source, self.trans) pupdate = 1 break if pupdate == 1: self.db.commit_person(person, self.trans) if source: self.db.remove_source(source.handle, self.trans) self.results_write(_("done!\n")) self.db.enable_signals() self.db.request_rebuild() if add_birth or add_death: self.results_write(_("Selecting... \n\n")) self.progress.set_pass(_('Selecting...'), num_people) row = 0 for person_handle in people: self.progress.step() person = self.db.get_person_from_handle(person_handle) birth_ref = person.get_birth_ref() death_ref = person.get_death_ref() add_birth_event, add_death_event = False, False if not birth_ref or not death_ref: date1, date2, explain, other = self.calc_estimates(person) if birth_ref: ev = self.db.get_event_from_handle(birth_ref.ref) date1 = ev.get_date_object() elif not birth_ref and add_birth and date1: if date1.match( current_date, "<"): add_birth_event = True date1.make_vague() else: date1 = Date() else: date1 = Date() if death_ref: ev = self.db.get_event_from_handle(death_ref.ref) date2 = ev.get_date_object() elif not death_ref and add_death and date2: if date2.match( current_date, "<"): add_death_event = True date2.make_vague() else: date2 = Date() else: date2 = Date() # Describe if add_birth_event and add_death_event: action = _("Add birth and death events") elif add_birth_event: action = _("Add birth event") elif add_death_event: action = _("Add death event") else: continue #stab.columns(_("Select"), _("Person"), _("Action"), # _("Birth Date"), _("Death Date"), _("Evidence"), _("Relative")) if add_birth == 1 and not birth_ref: # no date date1 = Date() if add_death == 1 and not death_ref: # no date date2 = Date() if person == other: other = None stab.row("checkbox", person, action, date1, date2, explain or "", other or "") if add_birth_event: stab.set_cell_markup(3, row, "<b>%s</b>" % date_displayer.display(date1)) if add_death_event: stab.set_cell_markup(4, row, "<b>%s</b>" % date_displayer.display(date2)) self.action[person.handle] = (add_birth_event, add_death_event) row += 1 if row > 0: self.results_write(" ") for text, function in BUTTONS: self.make_button(text, function, widget) self.results_write("\n") stab.write(sdoc) self.results_write(" ") for text, function in BUTTONS: self.make_button(text, function, widget) self.results_write("\n") else: self.results_write(_("No events to be added.")) self.results_write("\n") self.results_write("\n") self.progress.close() self.set_current_frame(_("Select"))
def print_page(self, month): """ This method actually writes the calendar page. """ style_sheet = self.doc.get_style_sheet() ptitle = style_sheet.get_paragraph_style("CAL-Title") ptext = style_sheet.get_paragraph_style("CAL-Text") pdaynames = style_sheet.get_paragraph_style("CAL-Daynames") pnumbers = style_sheet.get_paragraph_style("CAL-Numbers") numpos = pt2cm(pnumbers.get_font().get_size()) ptext1style = style_sheet.get_paragraph_style("CAL-Text1style") long_days = self._ldd.long_days self.doc.start_page() width = self.doc.get_usable_width() height = self.doc.get_usable_height() header = 2.54 # one inch mark = None if month == 1: mark = IndexMark(self._('Calendar Report'), INDEX_TYPE_TOC, 1) self.draw_rectangle("CAL-Border", 0, 0, width, height) self.doc.draw_box("CAL-Title", "", 0, 0, width, header, mark) self.doc.draw_line("CAL-Border", 0, header, width, header) year = self.year # assume every calendar header in the world is "<month-name> <year>" title = "%s %s" % (self._ldd.long_months[month].capitalize(), self._get_date(Date(self.year))) # localized year mark = IndexMark(title, INDEX_TYPE_TOC, 2) font_height = pt2cm(ptitle.get_font().get_size()) self.doc.center_text("CAL-Title", title, width/2, font_height * 0.25, mark) cell_width = width / 7 cell_height = (height - header)/ 6 current_date = datetime.date(year, month, 1) spacing = pt2cm(1.25 * ptext.get_font().get_size()) # 158 if current_date.isoweekday() != g2iso(self.start_dow + 1): # Go back to previous first day of week, and start from there current_ord = (current_date.toordinal() - ((current_date.isoweekday() + 7) - g2iso(self.start_dow + 1)) % 7) else: current_ord = current_date.toordinal() for day_col in range(7): font_height = pt2cm(pdaynames.get_font().get_size()) self.doc.center_text("CAL-Daynames", long_days[(day_col+ g2iso(self.start_dow + 1)) % 7 + 1].capitalize(), day_col * cell_width + cell_width/2, header - font_height * 1.5) for week_row in range(6): something_this_week = 0 for day_col in range(7): thisday = current_date.fromordinal(current_ord) if thisday.month == month: something_this_week = 1 self.draw_rectangle("CAL-Border", day_col * cell_width, header + week_row * cell_height, (day_col + 1) * cell_width, header + (week_row + 1) * cell_height) last_edge = (day_col + 1) * cell_width self.doc.center_text("CAL-Numbers", str(thisday.day), day_col * cell_width + cell_width/2, header + week_row * cell_height) list_ = self.calendar.get(month, {}).get(thisday.day, []) list_.sort() # to get CAL-Holiday on bottom position = spacing for (format, p, m_list) in list_: for line in reversed(p.split("\n")): # make sure text will fit: if position - 0.1 >= cell_height - numpos: # font daynums break font = ptext.get_font() line = string_trim(font, line, cm2pt(cell_width + 0.2)) self.doc.draw_text(format, line, day_col * cell_width + 0.1, header + (week_row + 1) * cell_height - position - 0.1, m_list[0]) if len(m_list) > 1: # index the spouse too self.doc.draw_text(format, "",0,0, m_list[1]) position += spacing current_ord += 1 if not something_this_week: last_edge = 0 font_height = pt2cm(1.5 * ptext1style.get_font().get_size()) x = last_edge + (width - last_edge)/2 text1 = str(self.text1) if text1 == _(_TITLE1): text1 = self._(_TITLE1) self.doc.center_text("CAL-Text1style", text1, x, height - font_height * 3) text2 = str(self.text2) if text2 == _(_TITLE2): text2 = self._(_TITLE2) self.doc.center_text("CAL-Text2style", text2, x, height - font_height * 2) self.doc.center_text("CAL-Text3style", self.text3, x, height - font_height * 1) self.doc.end_page()
def find_records(db, filter, top_size, callname, trans_text=glocale.translation.sgettext, name_format=None, living_mode=LivingProxyDb.MODE_INCLUDE_ALL): """ @param trans_text: allow deferred translation of strings @type trans_text: a GrampsLocale sgettext instance trans_text is a defined keyword (see po/update_po.py, po/genpot.sh) :param name_format: optional format to control display of person's name :type name_format: None or int :param living_mode: enable optional control of living people's records :type living_mode: int """ def get_unfiltered_person_from_handle(person_handle): if living_mode == LivingProxyDb.MODE_INCLUDE_ALL: return db.get_person_from_handle(person_handle) else: # we are in the proxy so get the person before proxy changes return db.get_unfiltered_person(person_handle) today = datetime.date.today() today_date = Date(today.year, today.month, today.day) # Person records person_youngestliving = [] person_oldestliving = [] person_youngestdied = [] person_oldestdied = [] person_youngestmarried = [] person_oldestmarried = [] person_youngestdivorced = [] person_oldestdivorced = [] person_youngestfather = [] person_youngestmother = [] person_oldestfather = [] person_oldestmother = [] person_handle_list = db.iter_person_handles() if filter: person_handle_list = filter.apply(db, person_handle_list) for person_handle in person_handle_list: person = db.get_person_from_handle(person_handle) unfil_person = get_unfiltered_person_from_handle(person_handle) if person is None: continue # FIXME this should check for a "fallback" birth also/instead birth_ref = person.get_birth_ref() if not birth_ref: # No birth event, so we can't calculate any age. continue birth = db.get_event_from_handle(birth_ref.ref) birth_date = birth.get_date_object() death_date = _find_death_date(db, person) if not _good_date(birth_date): # Birth date unknown or incomplete, so we can't calculate any age. continue name = _get_styled_primary_name(person, callname, trans_text=trans_text, name_format=name_format) if death_date is None: if probably_alive(unfil_person, db): # Still living, look for age records _record(person_youngestliving, person_oldestliving, today_date - birth_date, name, 'Person', person_handle, top_size) elif _good_date(death_date): # Already died, look for age records _record(person_youngestdied, person_oldestdied, death_date - birth_date, name, 'Person', person_handle, top_size) for family_handle in person.get_family_handle_list(): family = db.get_family_from_handle(family_handle) marriage_date = None divorce_date = None for event_ref in family.get_event_ref_list(): event = db.get_event_from_handle(event_ref.ref) if (event.get_type().is_marriage() and (event_ref.get_role().is_family() or event_ref.get_role().is_primary())): marriage_date = event.get_date_object() elif (event.get_type().is_divorce() and (event_ref.get_role().is_family() or event_ref.get_role().is_primary())): divorce_date = event.get_date_object() if _good_date(marriage_date): _record(person_youngestmarried, person_oldestmarried, marriage_date - birth_date, name, 'Person', person_handle, top_size) if _good_date(divorce_date): _record(person_youngestdivorced, person_oldestdivorced, divorce_date - birth_date, name, 'Person', person_handle, top_size) for child_ref in family.get_child_ref_list(): if person.get_gender() == person.MALE: relation = child_ref.get_father_relation() elif person.get_gender() == person.FEMALE: relation = child_ref.get_mother_relation() else: continue if relation != ChildRefType.BIRTH: continue child = db.get_person_from_handle(child_ref.ref) # FIXME this should check for a "fallback" birth also/instead child_birth_ref = child.get_birth_ref() if not child_birth_ref: continue child_birth = db.get_event_from_handle(child_birth_ref.ref) child_birth_date = child_birth.get_date_object() if not _good_date(child_birth_date): continue if person.get_gender() == person.MALE: _record(person_youngestfather, person_oldestfather, child_birth_date - birth_date, name, 'Person', person_handle, top_size) elif person.get_gender() == person.FEMALE: _record(person_youngestmother, person_oldestmother, child_birth_date - birth_date, name, 'Person', person_handle, top_size) # Family records family_mostchildren = [] family_youngestmarried = [] family_oldestmarried = [] family_shortest = [] family_longest = [] family_smallestagediff = [] family_biggestagediff = [] for family in db.iter_families(): #family = db.get_family_from_handle(family_handle) if living_mode != LivingProxyDb.MODE_INCLUDE_ALL: # FIXME no iter_families method in LivingProxyDb so do it this way family = db.get_family_from_handle(family.get_handle()) father_handle = family.get_father_handle() if not father_handle: continue mother_handle = family.get_mother_handle() if not mother_handle: continue # Test if either father or mother are in filter if filter: if not filter.apply(db, [father_handle, mother_handle]): continue father = db.get_person_from_handle(father_handle) unfil_father = get_unfiltered_person_from_handle(father_handle) if father is None: continue mother = db.get_person_from_handle(mother_handle) unfil_mother = get_unfiltered_person_from_handle(mother_handle) if mother is None: continue name = StyledText(trans_text("%(father)s and %(mother)s")) % { 'father': _get_styled_primary_name(father, callname, trans_text=trans_text, name_format=name_format), 'mother': _get_styled_primary_name(mother, callname, trans_text=trans_text, name_format=name_format) } if (living_mode == LivingProxyDb.MODE_INCLUDE_ALL or (not probably_alive(unfil_father, db) and not probably_alive(unfil_mother, db))): _record(None, family_mostchildren, len(family.get_child_ref_list()), name, 'Family', family.handle, top_size) father_birth_ref = father.get_birth_ref() if father_birth_ref: father_birth_date = db.get_event_from_handle( father_birth_ref.ref).get_date_object() else: father_birth_date = None mother_birth_ref = mother.get_birth_ref() if mother_birth_ref: mother_birth_date = db.get_event_from_handle( mother_birth_ref.ref).get_date_object() else: mother_birth_date = None if _good_date(father_birth_date) and _good_date(mother_birth_date): if father_birth_date >> mother_birth_date: _record(family_smallestagediff, family_biggestagediff, father_birth_date - mother_birth_date, name, 'Family', family.handle, top_size) elif mother_birth_date >> father_birth_date: _record(family_smallestagediff, family_biggestagediff, mother_birth_date - father_birth_date, name, 'Family', family.handle, top_size) marriage_date = None divorce = None divorce_date = None for event_ref in family.get_event_ref_list(): event = db.get_event_from_handle(event_ref.ref) if (event.get_type().is_marriage() and (event_ref.get_role().is_family() or event_ref.get_role().is_primary())): marriage_date = event.get_date_object() if (event and event.get_type().is_divorce() and (event_ref.get_role().is_family() or event_ref.get_role().is_primary())): divorce = event divorce_date = event.get_date_object() father_death_date = _find_death_date(db, father) mother_death_date = _find_death_date(db, mother) if not _good_date(marriage_date): # Not married or marriage date unknown continue if divorce is not None and not _good_date(divorce_date): # Divorced but date unknown or inexact continue if (not probably_alive(unfil_father, db) and not _good_date(father_death_date)): # Father died but death date unknown or inexact continue if (not probably_alive(unfil_mother, db) and not _good_date(mother_death_date)): # Mother died but death date unknown or inexact continue if (divorce_date is None and father_death_date is None and mother_death_date is None): # Still married and alive if (probably_alive(unfil_father, db) and probably_alive(unfil_mother, db)): _record(family_youngestmarried, family_oldestmarried, today_date - marriage_date, name, 'Family', family.handle, top_size) elif (_good_date(divorce_date) or _good_date(father_death_date) or _good_date(mother_death_date)): end = None if _good_date(father_death_date) and _good_date(mother_death_date): end = min(father_death_date, mother_death_date) elif _good_date(father_death_date): end = father_death_date elif _good_date(mother_death_date): end = mother_death_date if _good_date(divorce_date): if end: end = min(end, divorce_date) else: end = divorce_date duration = end - marriage_date _record(family_shortest, family_longest, duration, name, 'Family', family.handle, top_size) #python 3 workaround: assign locals to tmp so we work with runtime version tmp = locals() return [(trans_text(text), varname, tmp[varname]) for (text, varname, default) in RECORDS]
def column_date(self,data): if data[10]: date = Date() date.unserialize(data[10]) return displayer.display(date) return ''
def _convert_date(self, date_in): """Convert the change date to the preferred date format and return a string""" change_date = Date() change_date.set_yr_mon_day(*time.localtime(date_in)[0:3]) return gramps.gen.datehandler.displayer.display(change_date)
def collect_data(self): """ This method runs through the data, and collects the relevant dates and text. """ people = self.database.iter_person_handles() people = self.filter.apply(self.database, people, user=self._user) ngettext = self._locale.translation.ngettext # to see "nearby" comments rel_calc = get_relationship_calculator(reinit=True, clocale=self._locale) with self._user.progress(_('Birthday and Anniversary Report'), _('Reading database...'), len(people)) as step: for person_handle in people: step() person = self.database.get_person_from_handle(person_handle) birth_ref = person.get_birth_ref() birth_date = None if birth_ref: birth_event = self.database.get_event_from_handle( birth_ref.ref) birth_date = birth_event.get_date_object() if (self.birthdays and birth_date is not None and birth_date.is_valid()): birth_date = gregorian(birth_date) year = birth_date.get_year() month = birth_date.get_month() day = birth_date.get_day() prob_alive_date = Date(self.year, month, day) nyears = self.year - year # add some things to handle maiden name: father_lastname = None # husband, actually if self.maiden_name in ['spouse_first', 'spouse_last' ]: # get husband's last name: if person.get_gender() == Person.FEMALE: family_list = person.get_family_handle_list() if len(family_list) > 0: if self.maiden_name == 'spouse_first': fhandle = family_list[0] else: fhandle = family_list[-1] fam = self.database.get_family_from_handle( fhandle) father_handle = fam.get_father_handle() mother_handle = fam.get_mother_handle() if mother_handle == person_handle: if father_handle: father = self.database.get_person_from_handle( father_handle) if father is not None: primary_name = father.get_primary_name( ) if primary_name: father_lastname = Surname.get_surname( primary_name. get_primary_surname()) short_name = self.get_name(person, father_lastname) alive = probably_alive(person, self.database, prob_alive_date) if ((self.alive and alive) or not self.alive): comment = "" if self.relationships: relation = rel_calc.get_one_relationship( self.database, self.center_person, person, olocale=self._locale) if relation: # FIXME this won't work for RTL languages comment = " --- %s" % relation if nyears == 0: text = self._('%(person)s, birth%(relation)s') % { 'person': short_name, 'relation': comment } else: # translators: leave all/any {...} untranslated text = ngettext('{person}, {age}{relation}', '{person}, {age}{relation}', nyears).format(person=short_name, age=nyears, relation=comment) self.add_day_item(text, month, day, person) if self.anniversaries: family_list = person.get_family_handle_list() for fhandle in family_list: fam = self.database.get_family_from_handle(fhandle) father_handle = fam.get_father_handle() mother_handle = fam.get_mother_handle() if father_handle == person.get_handle(): spouse_handle = mother_handle else: continue # with next person if the father is not "person" # this will keep from duplicating the anniversary if spouse_handle: spouse = self.database.get_person_from_handle( spouse_handle) if spouse: spouse_name = self.get_name(spouse) short_name = self.get_name(person) # TEMP: this will handle ordered events # Gramps 3.0 will have a new mechanism for start/stop events are_married = None for event_ref in fam.get_event_ref_list(): event = self.database.get_event_from_handle( event_ref.ref) if event.type in [ EventType.MARRIAGE, EventType.MARR_ALT ]: are_married = event elif event.type in [ EventType.DIVORCE, EventType.ANNULMENT, EventType.DIV_FILING ]: are_married = None if are_married is not None: for event_ref in fam.get_event_ref_list(): event = self.database.get_event_from_handle( event_ref.ref) event_obj = event.get_date_object() if event_obj is not Date.EMPTY and event_obj.is_valid( ): event_obj = gregorian(event_obj) year = event_obj.get_year() month = event_obj.get_month() day = event_obj.get_day() nyears = self.year - year if event_obj.is_valid(): if nyears == 0: text = self._( "%(spouse)s and\n %(person)s, wedding" ) % { 'spouse': spouse_name, 'person': short_name } else: # translators: leave all/any {...} untranslated text = ngettext( "{spouse} and\n {person}, {nyears}", "{spouse} and\n {person}, {nyears}", nyears).format( spouse=spouse_name, person=short_name, nyears=nyears) prob_alive_date = Date( self.year, month, day) alive1 = probably_alive( person, self.database, prob_alive_date) alive2 = probably_alive( spouse, self.database, prob_alive_date) if (self.alive and alive1 and alive2 ) or not self.alive: self.add_day_item( text, month, day, spouse)
def write_families(self): """ write the families """ self.doc.add_comment('') ngettext = self._locale.translation.ngettext # to see "nearby" comments # loop through all the families we need to output for family_handle in sorted(self._families): # enable a diff family = self._db.get_family_from_handle(family_handle) fgid = family.get_gramps_id() # figure out a wedding date or placename we can use wedding_date = None wedding_place = None if self._incdates or self._incplaces: for event_ref in family.get_event_ref_list(): event = self._db.get_event_from_handle(event_ref.ref) if (event.get_type() == EventType.MARRIAGE and (event_ref.get_role() == EventRoleType.FAMILY or event_ref.get_role() == EventRoleType.PRIMARY)): # get the wedding date if self._incdates: date = event.get_date_object() if self._just_years and date.get_year_valid(): wedding_date = self._get_date( # localized year Date(date.get_year())) else: wedding_date = self._get_date(date) # get the wedding location if self._incplaces: wedding_place = self.get_event_place(event) break # figure out the number of children (if any) children_str = None if self._incchildcount: child_count = len(family.get_child_ref_list()) if child_count >= 1: # translators: leave all/any {...} untranslated children_str = ngettext("{number_of} child", "{number_of} children", child_count ).format(number_of=child_count) label = '' fgid_already = False if wedding_date: if label != '': label += '\\n' label += '%s' % wedding_date if self.includeid == 1 and not fgid_already: # same line label += " (%s)" % fgid fgid_already = True if wedding_place: if label != '': label += '\\n' label += '%s' % wedding_place if self.includeid == 1 and not fgid_already: # same line label += " (%s)" % fgid fgid_already = True if self.includeid == 1 and not label: label = "(%s)" % fgid fgid_already = True elif self.includeid == 2 and not label: # own line label = "(%s)" % fgid fgid_already = True elif self.includeid == 2 and label and not fgid_already: label += "\\n(%s)" % fgid fgid_already = True if children_str: if label != '': label += '\\n' label += '%s' % children_str if self.includeid == 1 and not fgid_already: # same line label += " (%s)" % fgid fgid_already = True shape = "ellipse" style = "solid" border = self._colorfamilies fill = self._colorfamilies # do not use colour if this is B&W outline if self._colorize == 'outline': border = "" fill = "" # if we're filling the entire node: if self._colorize == 'filled': style += ",filled" border = "" # we're done -- add the node self.doc.add_node(fgid, label, shape, border, style, fill) # now that we have the families written, # go ahead and link the parents and children to the families for family_handle in self._families: # get the parents for this family family = self._db.get_family_from_handle(family_handle) fgid = family.get_gramps_id() father_handle = family.get_father_handle() mother_handle = family.get_mother_handle() self.doc.add_comment('') if self._usesubgraphs and father_handle and mother_handle: self.doc.start_subgraph(fgid) # see if we have a father to link to this family if father_handle: if father_handle in self._people: father = self._db.get_person_from_handle(father_handle) father_rn = father.get_primary_name().get_regular_name() comment = self._("father: %s") % father_rn self.doc.add_link(father.get_gramps_id(), fgid, "", self._arrowheadstyle, self._arrowtailstyle, comment=comment) # see if we have a mother to link to this family if mother_handle: if mother_handle in self._people: mother = self._db.get_person_from_handle(mother_handle) mother_rn = mother.get_primary_name().get_regular_name() comment = self._("mother: %s") % mother_rn self.doc.add_link(mother.get_gramps_id(), fgid, "", self._arrowheadstyle, self._arrowtailstyle, comment=comment) if self._usesubgraphs and father_handle and mother_handle: self.doc.end_subgraph() # link the children to the family for childref in family.get_child_ref_list(): if childref.ref in self._people: child = self._db.get_person_from_handle(childref.ref) child_rn = child.get_primary_name().get_regular_name() comment = self._("child: %s") % child_rn self.doc.add_link(fgid, child.get_gramps_id(), "", self._arrowheadstyle, self._arrowtailstyle, comment=comment)
def run_tool(self): self.progress = ProgressMeter(_('Running Date Test'), '', parent=self.parent_window) self.progress.set_pass(_('Generating dates'), 4) dates = [] # first some valid dates calendar = Date.CAL_GREGORIAN for quality in (Date.QUAL_NONE, Date.QUAL_ESTIMATED, Date.QUAL_CALCULATED): for modifier in (Date.MOD_NONE, Date.MOD_BEFORE, Date.MOD_AFTER, Date.MOD_ABOUT): for slash1 in (False, True): for month in range(0, 13): for day in (0, 5, 27): if not month and day: continue d = Date() d.set(quality, modifier, calendar, (day, month, 1789, slash1), "Text comment") dates.append(d) for modifier in (Date.MOD_RANGE, Date.MOD_SPAN): for slash1 in (False, True): for slash2 in (False, True): for month in range(0, 13): for day in (0, 5, 27): if not month and day: continue d = Date() d.set(quality, modifier, calendar, (day, month, 1789, slash1, day, month, 1876, slash2), "Text comment") dates.append(d) if not month: continue d = Date() d.set(quality, modifier, calendar, (day, month, 1789, slash1, day, 13 - month, 1876, slash2), "Text comment") dates.append(d) if not day: continue d = Date() d.set(quality, modifier, calendar, (day, month, 1789, slash1, 32 - day, month, 1876, slash2), "Text comment") dates.append(d) d = Date() d.set(quality, modifier, calendar, (day, month, 1789, slash1, 32 - day, 13 - month, 1876, slash2), "Text comment") dates.append(d) modifier = Date.MOD_TEXTONLY d = Date() d.set(quality, modifier, calendar, Date.EMPTY, "This is a textual date") dates.append(d) self.progress.step() # test invalid dates #dateval = (4,7,1789,False,5,8,1876,False) #for l in range(1,len(dateval)): # d = Date() # try: # d.set(Date.QUAL_NONE,Date.MOD_NONE, # Date.CAL_GREGORIAN,dateval[:l],"Text comment") # dates.append( d) # except DateError, e: # d.set_as_text("Date identified value correctly as invalid.\n%s" % e) # dates.append( d) # except: # d = Date() # d.set_as_text("Date.set Exception %s" % ("".join(traceback.format_exception(*sys.exc_info())),)) # dates.append( d) #for l in range(1,len(dateval)): # d = Date() # try: # d.set(Date.QUAL_NONE,Date.MOD_SPAN,Date.CAL_GREGORIAN,dateval[:l],"Text comment") # dates.append( d) # except DateError, e: # d.set_as_text("Date identified value correctly as invalid.\n%s" % e) # dates.append( d) # except: # d = Date() # d.set_as_text("Date.set Exception %s" % ("".join(traceback.format_exception(*sys.exc_info())),)) # dates.append( d) #self.progress.step() #d = Date() #d.set(Date.QUAL_NONE,Date.MOD_NONE, # Date.CAL_GREGORIAN,(44,7,1789,False),"Text comment") #dates.append( d) #d = Date() #d.set(Date.QUAL_NONE,Date.MOD_NONE, # Date.CAL_GREGORIAN,(4,77,1789,False),"Text comment") #dates.append( d) #d = Date() #d.set(Date.QUAL_NONE,Date.MOD_SPAN, # Date.CAL_GREGORIAN, # (4,7,1789,False,55,8,1876,False),"Text comment") #dates.append( d) #d = Date() #d.set(Date.QUAL_NONE,Date.MOD_SPAN, # Date.CAL_GREGORIAN, # (4,7,1789,False,5,88,1876,False),"Text comment") #dates.append( d) with DbTxn(_("Date Test Plugin"), self.db, batch=True) as self.trans: self.db.disable_signals() self.progress.set_pass(_('Generating dates'), len(dates)) # create pass and fail tags pass_handle = self.create_tag(_('Pass'), '#0000FFFF0000') fail_handle = self.create_tag(_('Fail'), '#FFFF00000000') # now add them as birth to new persons i = 1 for dateval in dates: person = Person() surname = Surname() surname.set_surname("DateTest") name = Name() name.add_surname(surname) name.set_first_name("Test %d" % i) person.set_primary_name(name) self.db.add_person(person, self.trans) bevent = Event() bevent.set_type(EventType.BIRTH) bevent.set_date_object(dateval) bevent.set_description("Date Test %d (source)" % i) bevent_h = self.db.add_event(bevent, self.trans) bevent_ref = EventRef() bevent_ref.set_reference_handle(bevent_h) # for the death event display the date as text and parse it back to a new date ndate = None try: datestr = _dd.display(dateval) try: ndate = _dp.parse(datestr) if not ndate: ndate = Date() ndate.set_as_text("DateParser None") person.add_tag(fail_handle) else: person.add_tag(pass_handle) except: ndate = Date() ndate.set_as_text("DateParser Exception %s" % ("".join( traceback.format_exception(*sys.exc_info())), )) person.add_tag(fail_handle) else: person.add_tag(pass_handle) except: ndate = Date() ndate.set_as_text("DateDisplay Exception: %s" % ("".join( traceback.format_exception(*sys.exc_info())), )) person.add_tag(fail_handle) if dateval.get_modifier() != Date.MOD_TEXTONLY \ and ndate.get_modifier() == Date.MOD_TEXTONLY: # parser was unable to correctly parse the string ndate.set_as_text("TEXTONLY: " + ndate.get_text()) person.add_tag(fail_handle) if dateval.get_modifier() == Date.MOD_TEXTONLY \ and dateval.get_text().count("Traceback") \ and pass_handle in person.get_tag_list(): person.add_tag(fail_handle) devent = Event() devent.set_type(EventType.DEATH) devent.set_date_object(ndate) devent.set_description("Date Test %d (result)" % i) devent_h = self.db.add_event(devent, self.trans) devent_ref = EventRef() devent_ref.set_reference_handle(devent_h) person.set_birth_ref(bevent_ref) person.set_death_ref(devent_ref) self.db.commit_person(person, self.trans) i = i + 1 self.progress.step() self.db.enable_signals() self.db.request_rebuild() self.progress.close()