Пример #1
0
class AncestorTree(Report):
    """ AncestorTree Report """
    def __init__(self, database, options, user):
        """
        Create AncestorTree object that produces the report.

        The arguments are:

        database        - the GRAMPS database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance
        """
        Report.__init__(self, database, options, user)

        self.options = options
        self._user = user

        lang = options.menu.get_option_by_name('trans').get_value()
        self._locale = self.set_locale(lang)
        stdoptions.run_private_data_option(self, options.menu)
        stdoptions.run_living_people_option(self, options.menu, self._locale)
        self.database = CacheProxyDb(self.database)
        stdoptions.run_name_format_option(self, options.menu)
        self._nd = self._name_display

    def begin_report(self):
        """
        This report needs the following parameters (class variables)
        that come in the options class.

        max_generations - Maximum number of generations to include.
        pagebbg      - Whether to include page breaks between generations.
        dispf        - Display format for the output box.
        scale_report - Whether to scale the report to fit the width or all.
        indblank     - Whether to include blank pages.
        compress     - Whether to compress chart.
        incl_private - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death

        We will
        1. a canvas in its full one-page size
        2. a page that we wish to print on
        scale up/down either or both of the above as needed/desired.
        almost all of this should be moved into Canvas!
        """

        database = self.database

        self.connect = GUIConnect()
        self.connect.set__opts(self.options.menu, self._locale, self._nd)

        #Set up the canvas that we will print on.
        style_sheet = self.doc.get_style_sheet()
        font_normal = style_sheet.get_paragraph_style("AC2-Normal").get_font()

        #The canvas that we will put our report on and print off of
        self.canvas = Canvas(self.doc,
                             ReportOptions(self.doc, font_normal, 'AC2-line'))

        self.canvas.report_opts.box_shadow *= \
                        self.connect.get_val('shadowscale')
        self.canvas.report_opts.box_pgap *= self.connect.get_val('box_Yscale')
        self.canvas.report_opts.box_mgap *= self.connect.get_val('box_Yscale')

        with self._user.progress(_('Ancestor Tree'), _('Making the Tree...'),
                                 4) as step:

            #make the tree onto the canvas
            ## inlc_marr = self.connect.get_val("inc_marr")
            self.max_generations = self.connect.get_val('maxgen')
            tree = MakeAncestorTree(database, self.canvas)
            tree.start(self.connect.get_val('pid'))
            tree = None

            step()

            #Title
            title = self.connect.title_class(self.doc)
            center = self.database.get_person_from_gramps_id(
                self.connect.get_val('pid'))
            title.calc_title(center)
            self.canvas.add_title(title)

            #make the report as big as it wants to be.
            report = MakeReport(database, self.doc, self.canvas, font_normal)
            report.start()
            self.max_generations = report.get_generations()  #already know
            report = None

            step()

            #Note?
            if self.connect.get_val("inc_note"):
                note_box = NoteBox(self.doc, "AC2-note-box",
                                   self.connect.get_val("note_place"))
                subst = SubstKeywords(self.database, self._locale, self._nd,
                                      None, None)
                note_box.text = subst.replace_and_clean(
                    self.connect.get_val('note_disp'))
                self.canvas.add_note(note_box)

            #Now we have the report in its full size.
            #Do we want to scale the report?
            one_page = self.connect.get_val("resize_page")
            scale_report = self.connect.get_val("scale_tree")

            scale = self.canvas.scale_report(one_page, scale_report != 0,
                                             scale_report == 2)

            step()

            if scale != 1 or self.connect.get_val('shadowscale') != 1.0:
                self.scale_styles(scale)

    def write_report(self):

        one_page = self.connect.get_val("resize_page")
        #scale_report = self.connect.get_val("scale_tree")

        #inlc_marr = self.connect.get_val("inc_marr")
        inc_border = self.connect.get_val('inc_border')
        incblank = self.connect.get_val("inc_blank")
        prnnum = self.connect.get_val("inc_pagenum")

        #####################
        #Setup page information

        colsperpage = self.doc.get_usable_width()
        colsperpage += self.canvas.report_opts.col_width
        colsperpage = int(colsperpage /
                          (self.canvas.report_opts.max_box_width +
                           self.canvas.report_opts.col_width))
        colsperpage = colsperpage or 1

        #####################
        #Vars
        if prnnum:
            page_num_box = PageNumberBox(self.doc, 'AC2-box', self._locale)

        #TODO - Here

        #####################
        #ok, everyone is now ready to print on the canvas.  Paginate?
        self.canvas.paginate(colsperpage, one_page)

        #####################
        #Yeah!!!
        #lets finally make some pages!!!
        #####################
        pages = self.canvas.page_count(incblank)
        with self._user.progress(_('Ancestor Tree'), _('Printing the Tree...'),
                                 pages) as step:

            for page in self.canvas.page_iter_gen(incblank):

                self.doc.start_page()

                #do we need to print a border?
                if inc_border:
                    page.draw_border('AC2-line')

                #Do we need to print the page number?
                if prnnum:
                    page_num_box.display(page)

                #Print the individual people and lines
                page.display()

                step()
                self.doc.end_page()

    def scale_styles(self, scale):
        """
        Scale the styles for this report.
        """
        style_sheet = self.doc.get_style_sheet()

        graph_style = style_sheet.get_draw_style("AC2-box")
        graph_style.set_shadow(graph_style.get_shadow(),
                               self.canvas.report_opts.box_shadow * scale)
        graph_style.set_line_width(graph_style.get_line_width() * scale)
        style_sheet.add_draw_style("AC2-box", graph_style)

        graph_style = style_sheet.get_draw_style("AC2-fam-box")
        graph_style.set_shadow(graph_style.get_shadow(),
                               self.canvas.report_opts.box_shadow * scale)
        graph_style.set_line_width(graph_style.get_line_width() * scale)
        style_sheet.add_draw_style("AC2-fam-box", graph_style)

        graph_style = style_sheet.get_draw_style("AC2-note-box")
        #graph_style.set_shadow(graph_style.get_shadow(),
        #                       self.canvas.report_opts.box_shadow * scale)
        graph_style.set_line_width(graph_style.get_line_width() * scale)
        style_sheet.add_draw_style("AC2-note-box", graph_style)

        para_style = style_sheet.get_paragraph_style("AC2-Normal")
        font = para_style.get_font()
        font.set_size(font.get_size() * scale)
        para_style.set_font(font)
        style_sheet.add_paragraph_style("AC2-Normal", para_style)

        para_style = style_sheet.get_paragraph_style("AC2-Note")
        font = para_style.get_font()
        font.set_size(font.get_size() * scale)
        para_style.set_font(font)
        style_sheet.add_paragraph_style("AC2-Note", para_style)

        para_style = style_sheet.get_paragraph_style("AC2-Title")
        font = para_style.get_font()
        font.set_size(font.get_size() * scale)
        para_style.set_font(font)
        style_sheet.add_paragraph_style("AC2-Title", para_style)

        graph_style = GraphicsStyle()
        width = graph_style.get_line_width()
        width = width * scale
        graph_style.set_line_width(width)
        style_sheet.add_draw_style("AC2-line", graph_style)

        self.doc.set_style_sheet(style_sheet)
Пример #2
0
class FanChart(Report):

    def __init__(self, database, options, user):
        """
        Create the FanChart object that produces the report.

        The arguments are:

        database     - the Gramps database instance
        options      - instance of the Options class for this report
        user         - a gen.user.User instance

        This report needs the following parameters (class variables)
        that come in the options class.

        maxgen       - Maximum number of generations to include.
        circle       - Draw a full circle, half circle, or quarter circle.
        background   - Background color is generation dependent or white.
        radial       - Print radial texts roundabout or as upright as possible.
        draw_empty   - draw background when there is no information
        same_style   - use the same style for all generation
        incl_private - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death
        """

        Report.__init__(self, database, options, user)

        menu = options.menu

        self.set_locale(options.menu.get_option_by_name('trans').get_value())

        stdoptions.run_private_data_option(self, menu)
        stdoptions.run_living_people_option(self, menu, self._locale)
        self.database = CacheProxyDb(self.database)

        self.max_generations = menu.get_option_by_name('maxgen').get_value()
        self.circle = menu.get_option_by_name('circle').get_value()
        self.background = menu.get_option_by_name('background').get_value()
        self.radial = menu.get_option_by_name('radial').get_value()
        pid = menu.get_option_by_name('pid').get_value()
        self.draw_empty = menu.get_option_by_name('draw_empty').get_value()
        self.same_style = menu.get_option_by_name('same_style').get_value()
        self.center_person = self.database.get_person_from_gramps_id(pid)
        if self.center_person is None:
            raise ReportError(_("Person %s is not in the Database") % pid)

        self.graphic_style = []
        self.text_style = []
        for i in range(0, self.max_generations):
            self.graphic_style.append('FC-Graphic' + '%02d' % i)
            self.text_style.append('FC-Text' + '%02d' % i)

        self.calendar = 0

        self.height = 0
        self.map = [None] * 2**self.max_generations
        self.text = {}

    def apply_filter(self, person_handle, index):
        """traverse the ancestors recursively until either the end
        of a line is found, or until we reach the maximum number of
        generations that we want to deal with"""

        if (not person_handle) or (index >= 2**self.max_generations):
            return
        self.map[index-1] = person_handle
        self.text[index-1] = self.get_info(person_handle, log2(index))

        person = self.database.get_person_from_handle(person_handle)
        family_handle = person.get_main_parents_family_handle()
        if family_handle:
            family = self.database.get_family_from_handle(family_handle)
            self.apply_filter(family.get_father_handle(), index*2)
            self.apply_filter(family.get_mother_handle(), (index*2)+1)

    def write_report(self):
        self.doc.start_page()

        self.apply_filter(self.center_person.get_handle(), 1)
        p_rn = self.center_person.get_primary_name().get_regular_name()

        if self.circle == FULL_CIRCLE:
            max_angle = 360.0
            start_angle = 90
            max_circular = 5
            _x_ = self.doc.get_usable_width() / 2.0
            _y_ = self.doc.get_usable_height() / 2.0
            min_xy = min(_x_, _y_)

        elif self.circle == HALF_CIRCLE:
            max_angle = 180.0
            start_angle = 180
            max_circular = 3
            _x_ = (self.doc.get_usable_width()/2.0)
            _y_ = self.doc.get_usable_height()
            min_xy = min(_x_, _y_)

        else:  # quarter circle
            max_angle = 90.0
            start_angle = 270
            max_circular = 2
            _x_ = 0
            _y_ = self.doc.get_usable_height()
            min_xy = min(self.doc.get_usable_width(), _y_)

        # choose  one line or two lines translation according to the width
        title = self._("%(generations)d Generation Fan Chart "
                       "for %(person)s") % {
                           'generations' : self.max_generations,
                           'person' : p_rn}
        title_nb_lines = 1
        style_sheet = self.doc.get_style_sheet()
        if style_sheet:
            p_style = style_sheet.get_paragraph_style('FC-Title')
            if p_style:
                font = p_style.get_font()
                if font:
                    title_width = utils.pt2cm(self.doc.string_width(font,
                                                                    title))
                    if title_width > self.doc.get_usable_width():
                        title = self._(
                            "%(generations)d Generation Fan Chart "
                            "for\n%(person)s") % {
                                'generations' : self.max_generations,
                                'person' : p_rn}
                        title_nb_lines = 2

        if self.circle == FULL_CIRCLE or self.circle == QUAR_CIRCLE:
            # adjust only if full circle or 1/4 circle in landscape mode
            if self.doc.get_usable_height() <= self.doc.get_usable_width():
                # Should be in Landscape now
                style_sheet = self.doc.get_style_sheet()
                p_style = style_sheet.get_paragraph_style('FC-Title')
                if p_style:
                    font = p_style.get_font()
                    if font:
                        fontsize = utils.pt2cm(font.get_size())
                        # _y_ is vertical distance to center of circle,
                        # move center down 1 fontsize
                        _y_ += fontsize*title_nb_lines
                        # min_XY is the diameter of the circle,
                        # subtract two fontsize
                        # so we dont draw outside bottom of the paper
                        min_xy = min(min_xy, _y_ - 2*fontsize*title_nb_lines)
        if self.max_generations > max_circular:
            block_size = min_xy / (self.max_generations * 2 - max_circular)
        else:
            block_size = min_xy / self.max_generations

        # adaptation of the fonts (title and others)
        optimized_style_sheet = self.get_optimized_style_sheet(
            title, max_circular, block_size, self.same_style,
            not self.same_style,
            # if same_style, use default generated colors
            self.background == BACKGROUND_WHITE)

        if optimized_style_sheet:
            self.doc.set_style_sheet(optimized_style_sheet)

        # title
        mark = IndexMark(title, INDEX_TYPE_TOC, 1)
        self.doc.center_text('FC-Graphic-title', title,
                             self.doc.get_usable_width() / 2, 0, mark)
        # wheel
        for generation in range(0, min(max_circular, self.max_generations)):
            self.draw_circular(_x_, _y_,
                               start_angle, max_angle, block_size, generation)
        for generation in range(max_circular, self.max_generations):
            self.draw_radial(_x_, _y_,
                             start_angle, max_angle, block_size, generation)
        self.doc.end_page()

    def get_info(self, person_handle, generation):
        """ get info about a person """
        person = self.database.get_person_from_handle(person_handle)
        p_pn = person.get_primary_name()
        self.calendar = config.get('preferences.calendar-format-report')

        birth = get_birth_or_fallback(self.database, person)
        bth = ""
        if birth:
            bth = birth.get_date_object()
            bth = str(bth.to_calendar(self.calendar).get_year())
            if bth == 0:
                bth = ""
            elif birth.get_type() != EventType.BIRTH:
                bth += '*'

        death = get_death_or_fallback(self.database, person)
        dth = ""
        if death:
            dth = death.get_date_object()
            dth = str(dth.to_calendar(self.calendar).get_year())
            if dth == 0:
                dth = ""
            elif death.get_type() != EventType.DEATH:
                dth += '*'
        if bth and dth:
            val = "%s - %s" % (str(bth), str(dth))
        elif bth:
            val = "* %s" % (str(bth))
        elif dth:
            val = "+ %s" % (str(dth))
        else:
            val = ""

        if generation > 7:
            if (p_pn.get_first_name() != "") and (p_pn.get_surname() != ""):
                name = p_pn.get_first_name() + " " + p_pn.get_surname()
            else:
                name = p_pn.get_first_name() + p_pn.get_surname()
            if (name != "") and (val != ""):
                string = name + ", " + val
            else:
                string = name + val
            return [string]
        elif generation == 7:
            if (p_pn.get_first_name() != "") and (p_pn.get_surname() != ""):
                name = p_pn.get_first_name() + " " + p_pn.get_surname()
            else:
                name = p_pn.get_first_name() + p_pn.get_surname()

            if self.circle == FULL_CIRCLE:
                return [name, val]
            elif self.circle == HALF_CIRCLE:
                return [name, val]
            else:
                if (name != "") and (val != ""):
                    string = name + ", " + val
                else:
                    string = name + val
                return [string]
        elif generation == 6:
            if self.circle == FULL_CIRCLE:
                return [p_pn.get_first_name(), p_pn.get_surname(), val]
            elif self.circle == HALF_CIRCLE:
                return [p_pn.get_first_name(), p_pn.get_surname(), val]
            else:
                if (p_pn.get_first_name() != "") and (p_pn.get_surname() != ""):
                    name = p_pn.get_first_name() + " " + p_pn.get_surname()
                else:
                    name = p_pn.get_first_name() + p_pn.get_surname()
                return [name, val]
        else:
            return [p_pn.get_first_name(), p_pn.get_surname(), val]

    def get_max_width_for_circles(self, rad1, rad2, max_centering_proportion):
        r"""
        (the "r" in the above line is to keep pylint happy)
           __
          /__\ <- compute the line width which is drawable between 2 circles.
         /  _ \   max_centering_proportion : 0, touching the circle1, 1,
        |  |_| |   touching the circle2, 0.5 : middle between the 2 circles
        |      |
         \    /
          \__/
                 basically, max_centering_proportion is
                 max_centering_proportion/nb_lines
        """
        # radius at the center of the 2 circles
        rmid = rad2 - (rad2-rad1)*max_centering_proportion
        return sin(acos(rmid/rad2)) * rad2 * 2

    def get_max_width_for_circles_line(self, rad1, rad2, line, nb_lines,
                                       centering=False):
        r"""
        (the "r" in the above line is to keep pylint happy)
           __
          /__\ <- compute the line width which is drawable between 2 circles.
         /  _ \   instead of a max_centering_proportion, you get a
        |  |_| |  line/nb_lines position.  (we suppose that lines have the
        |      |  same heights.) for example, if you've 2 lines to draw,
         \    /   line 2 max width is at the 2/3 between the 2 circles
          \__/
        """
        if centering:
            return self.get_max_width_for_circles(rad1, rad2, 1.0)
        else:
            return self.get_max_width_for_circles(rad1, rad2,
                                                  line/float(nb_lines+1))

    def get_optimized_font_size_for_text(self, rad1, rad2, text, font,
                                         centering=False):
        """
        a text can be several lines
        find the font size equals or lower than font.get_size() which fit
        between rad1 and rad2 to display the text.
        centering is a special case when you've the full circle
        available to draw the text in the middle of it
        """
        min_font_size = font.get_size()
        i = 1
        nb_lines = len(text)
        for line in text:
            font_size = self.get_optimized_font_size(
                line, font,
                self.get_max_width_for_circles_line(rad1, rad2, i, nb_lines,
                                                    centering))
            i += 1
            if min_font_size > font_size:
                min_font_size = font_size
        return min_font_size

    def get_optimized_font_size(self, line, font, max_width):
        """
        for a given width, guess the best font size which is equals
        or smaller than font which make line fit into max_width
        """
        test_font = FontStyle(font)
        width = utils.pt2cm(self.doc.string_width(test_font, line))
        while width > max_width and test_font.get_size() > 1:
            test_font.set_size(test_font.get_size() -1)
            width = utils.pt2cm(self.doc.string_width(test_font, line))
        return test_font.get_size()

    def get_optimized_style_sheet(self, title, max_circular, block_size,
                                  map_style_from_single,
                                  map_paragraphs_colors_to_graphics,
                                  make_background_white):
        """
        returns an optimized (modified) style sheet which make fanchart
        look nicer
        """
        new_style_sheet = self.doc.get_style_sheet()
        if not new_style_sheet:
            return self.doc.get_style_sheet()

        # update title font size
        pstyle_name = 'FC-Title'
        p_style = new_style_sheet.get_paragraph_style(pstyle_name)
        if p_style:
            title_font = p_style.get_font()
            if title_font:
                title_width = utils.pt2cm(
                    self.doc.string_multiline_width(title_font, title))
                while (title_width > self.doc.get_usable_width() and
                       title_font.get_size() > 1):
                    title_font.set_size(title_font.get_size()-1)
                    title_width = utils.pt2cm(
                        self.doc.string_multiline_width(title_font, title))
                new_style_sheet.add_paragraph_style(pstyle_name, p_style)

        # biggest font allowed is the one of the fist generation, after,
        # always lower than the previous one
        p_style = new_style_sheet.get_paragraph_style(self.text_style[0])
        font = None
        if p_style:
            font = p_style.get_font()
        if font:
            previous_generation_font_size = font.get_size()

            for generation in range(0, self.max_generations):
                gstyle_name = self.graphic_style[generation]
                pstyle_name = self.text_style[generation]
                g_style = new_style_sheet.get_draw_style(gstyle_name)

                # p_style is a copy of 'FC-Text' - use different style
                # to be able to auto change some fonts for some generations
                if map_style_from_single:
                    p_style = new_style_sheet.get_paragraph_style('FC-Text')
                else:
                    p_style = new_style_sheet.get_paragraph_style(pstyle_name)

                if g_style and p_style:
                    # set graphic colors to paragraph colors,
                    # while it's functionnaly
                    # the same for fanchart or make backgrounds white
                    if make_background_white:
                        g_style.set_fill_color((255, 255, 255))
                        new_style_sheet.add_draw_style(gstyle_name, g_style)
                    elif map_paragraphs_colors_to_graphics:
                        pstyle = new_style_sheet.get_paragraph_style(
                            pstyle_name)
                        if pstyle:
                            g_style.set_fill_color(
                                pstyle.get_background_color())
                            new_style_sheet.add_draw_style(gstyle_name,
                                                           g_style)

                    # adapt font size if too big
                    segments = 2**generation
                    if generation < min(max_circular, self.max_generations):
                        # adpatation for circular fonts
                        rad1, rad2 = self.get_circular_radius(
                            block_size, generation, self.circle)
                        font = p_style.get_font()
                        if font:
                            min_font_size = font.get_size()
                            # find the smallest font required
                            for index in range(segments - 1, 2*segments - 1):
                                if self.map[index]:
                                    font_size = \
                                        self.get_optimized_font_size_for_text(
                                            rad1, rad2, self.text[index],
                                            p_style.get_font(),
                                            (self.circle == FULL_CIRCLE and
                                             generation == 0)
                                            )
                                if font_size < min_font_size:
                                    min_font_size = font_size
                            font.set_size(min(previous_generation_font_size,
                                              min_font_size))
                    else:
                        # adaptation for radial fonts

                        # find the largest string for the generation
                        longest_line = ""
                        longest_width = 0
                        for index in range(segments - 1, 2*segments - 1):
                            if self.map[index]:
                                for line in self.text[index]:
                                    width = utils.pt2cm(
                                        self.doc.string_multiline_width(
                                            p_style.get_font(), line))
                                    if width > longest_width:
                                        longest_line = line
                                        longest_width = width

                        # determine maximum width allowed for this generation
                        rad1, rad2 = self.get_radial_radius(
                            block_size, generation, self.circle)
                        max_width = rad2 - rad1

                        # reduce the font so that longest_width
                        # fit into max_width
                        font = p_style.get_font()
                        if font:
                            font.set_size(min(previous_generation_font_size,
                                              self.get_optimized_font_size(
                                                  longest_line,
                                                  p_style.get_font(),
                                                  max_width)))

                    # redefine the style
                    new_style_sheet.add_paragraph_style(pstyle_name, p_style)
                    font = p_style.get_font()
        if font:
            previous_generation_font_size = font.get_size()

        # finished
        return new_style_sheet

    def draw_circular(self, _x_, _y_,
                      start_angle, max_angle, size, generation):
        segments = 2**generation
        delta = max_angle / segments
        end_angle = start_angle
        text_angle = start_angle - 270 + (delta / 2.0)
        rad1, rad2 = self.get_circular_radius(size, generation, self.circle)
        graphic_style = self.graphic_style[generation]

        for index in range(segments - 1, 2*segments - 1):
            start_angle = end_angle
            end_angle = start_angle + delta
            (_xc, _yc) = draw_wedge(self.doc, graphic_style, _x_, _y_, rad2,
                                    start_angle, end_angle,
                                    self.map[index] or self.draw_empty, rad1)
            if self.map[index]:
                if (generation == 0) and self.circle == FULL_CIRCLE:
                    _yc = _y_
                person = self.database.get_person_from_handle(self.map[index])
                mark = utils.get_person_mark(self.database, person)
                self.doc.rotate_text(graphic_style, self.text[index],
                                     _xc, _yc, text_angle, mark)
            text_angle += delta

    def get_radial_radius(self, size, generation, circle):
        """ determine the radius """
        if circle == FULL_CIRCLE:
            rad1 = size * ((generation * 2) - 5)
            rad2 = size * ((generation * 2) - 3)
        elif circle == HALF_CIRCLE:
            rad1 = size * ((generation * 2) - 3)
            rad2 = size * ((generation * 2) - 1)
        else:  # quarter circle
            rad1 = size * ((generation * 2) - 2)
            rad2 = size * (generation * 2)
        return rad1, rad2

    def get_circular_radius(self, size, generation, circle):
        """ determine the radius """
        return size * generation, size * (generation + 1)

    def draw_radial(self, _x_, _y_,
                    start_angle, max_angle, size, generation):
        segments = 2**generation
        delta = max_angle / segments
        end_angle = start_angle
        text_angle = start_angle - delta / 2.0
        graphic_style = self.graphic_style[generation]

        rad1, rad2 = self.get_radial_radius(size, generation, self.circle)
        for index in range(segments - 1, 2*segments - 1):
            start_angle = end_angle
            end_angle = start_angle + delta
            (_xc, _yc) = draw_wedge(self.doc, graphic_style, _x_, _y_, rad2,
                                    start_angle, end_angle,
                                    self.map[index] or self.draw_empty, rad1)
            text_angle += delta
            if self.map[index]:
                person = self.database.get_person_from_handle(self.map[index])
                mark = utils.get_person_mark(self.database, person)
                if (self.radial == RADIAL_UPRIGHT
                        and (start_angle >= 90)
                        and (start_angle < 270)):
                    self.doc.rotate_text(graphic_style, self.text[index],
                                         _xc, _yc, text_angle + 180, mark)
                else:
                    self.doc.rotate_text(graphic_style, self.text[index],
                                         _xc, _yc, text_angle, mark)
Пример #3
0
class AncestorReport(Report):
    """
    Ancestor Report class
    """
    def __init__(self, database, options, user):
        """
        Create the AncestorReport object that produces the Ahnentafel report.

        The arguments are:

        database        - the Gramps database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance

        This report needs the following parameters (class variables)
        that come in the options class.

        gen       - Maximum number of generations to include.
        pagebbg   - Whether to include page breaks between generations.
        name_format   - Preferred format to display names
        incl_private  - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death
        """
        Report.__init__(self, database, options, user)

        self.map = {}
        menu = options.menu

        lang = menu.get_option_by_name('trans').get_value()
        rlocale = self.set_locale(lang)

        stdoptions.run_private_data_option(self, menu)
        stdoptions.run_living_people_option(self, menu, rlocale)
        self.database = CacheProxyDb(self.database)

        self.max_generations = menu.get_option_by_name('maxgen').get_value()
        self.pgbrk = menu.get_option_by_name('pagebbg').get_value()
        self.opt_namebrk = menu.get_option_by_name('namebrk').get_value()
        pid = menu.get_option_by_name('pid').get_value()
        self.center_person = self.database.get_person_from_gramps_id(pid)
        if (self.center_person == None):
            raise ReportError(_("Person %s is not in the Database") % pid)

        stdoptions.run_name_format_option(self, menu)

        self.__narrator = Narrator(self.database,
                                   use_fulldate=True,
                                   nlocale=rlocale)

    def apply_filter(self, person_handle, index, generation=1):
        """
        Recursive function to walk back all parents of the current person.
        When max_generations are hit, we stop the traversal.
        """

        # check for end of the current recursion level. This happens
        # if the person handle is None, or if the max_generations is hit

        if not person_handle or generation > self.max_generations:
            return

        # store the person in the map based off their index number
        # which is passed to the routine.
        self.map[index] = person_handle

        # retrieve the Person instance from the database from the
        # passed person_handle and find the parents from the list.
        # Since this report is for natural parents (birth parents),
        # we have to handle that parents may not

        person = self.database.get_person_from_handle(person_handle)
        if person is None:
            return

        father_handle = None
        mother_handle = None
        for family_handle in person.get_parent_family_handle_list():
            family = self.database.get_family_from_handle(family_handle)

            # filter the child_ref_list to find the reference that matches
            # the passed person. There should be exactly one, but there is
            # nothing that prevents the same child in the list multiple times.

            ref = [
                c for c in family.get_child_ref_list()
                if c.get_reference_handle() == person_handle
            ]
            if ref:

                # If the father_handle is not defined and the relationship is
                # BIRTH, then we have found the birth father. Same applies to
                # the birth mother. If for some reason, the we have multiple
                # people defined as the birth parents, we will select based on
                # priority in the list

                if not father_handle and \
                   ref[0].get_father_relation() == ChildRefType.BIRTH:
                    father_handle = family.get_father_handle()
                if not mother_handle and \
                   ref[0].get_mother_relation() == ChildRefType.BIRTH:
                    mother_handle = family.get_mother_handle()

        # Recursively call the function. It is okay if the handle is None,
        # since routine handles a handle of None

        self.apply_filter(father_handle, index * 2, generation + 1)
        self.apply_filter(mother_handle, (index * 2) + 1, generation + 1)

    def write_report(self):
        """
        The routine the actually creates the report. At this point, the document
        is opened and ready for writing.
        """

        # Call apply_filter to build the self.map array of people in the
        # database that match the ancestry.

        self.apply_filter(self.center_person.get_handle(), 1)

        # Write the title line. Set in INDEX marker so that this section will be
        # identified as a major category if this is included in a Book report.

        name = self._name_display.display_formal(self.center_person)
        # feature request 2356: avoid genitive form
        title = self._("Ahnentafel Report for %s") % name
        mark = IndexMark(title, INDEX_TYPE_TOC, 1)
        self.doc.start_paragraph("AHN-Title")
        self.doc.write_text(title, mark)
        self.doc.end_paragraph()

        # get the entries out of the map, and sort them.

        generation = 0

        for key in sorted(self.map):

            # check the index number to see if we need to start a new generation
            if generation == log2(key):

                # generate a page break if requested
                if self.pgbrk and generation > 0:
                    self.doc.page_break()
                generation += 1

                # Create the Generation title, set an index marker
                gen_text = self._("Generation %d") % generation
                mark = None  # don't need any with no page breaks
                if self.pgbrk:
                    mark = IndexMark(gen_text, INDEX_TYPE_TOC, 2)
                self.doc.start_paragraph("AHN-Generation")
                self.doc.write_text(gen_text, mark)
                self.doc.end_paragraph()

            # Build the entry

            self.doc.start_paragraph("AHN-Entry", "%d." % key)
            person = self.database.get_person_from_handle(self.map[key])
            if person is None:
                continue
            name = self._name_display.display(person)
            mark = utils.get_person_mark(self.database, person)

            # write the name in bold
            self.doc.start_bold()
            self.doc.write_text(name.strip(), mark)
            self.doc.end_bold()

            # terminate with a period if it is not already terminated.
            # This can happen if the person's name ends with something 'Jr.'
            if name[-1:] == '.':
                self.doc.write_text(" ")
            else:
                self.doc.write_text(". ")

            # Add a line break if requested (not implemented yet)
            if self.opt_namebrk:
                self.doc.write_text('\n')

            self.__narrator.set_subject(person)
            self.doc.write_text(self.__narrator.get_born_string())
            self.doc.write_text(self.__narrator.get_baptised_string())
            self.doc.write_text(self.__narrator.get_christened_string())
            self.doc.write_text(self.__narrator.get_died_string())
            self.doc.write_text(self.__narrator.get_buried_string())

            self.doc.end_paragraph()
Пример #4
0
class EndOfLineReport(Report):
    """ EndOfLine Report """

    def __init__(self, database, options, user):
        """
        Create the EndOfLineReport object that produces the report.

        The arguments are:

        database        - the Gramps database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance

        This report needs the following parameters (class variables)
        that come in the options class.
        name_format   - Preferred format to display names
        incl_private  - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death
        """
        Report.__init__(self, database, options, user)

        menu = options.menu

        self.set_locale(menu.get_option_by_name('trans').get_value())

        stdoptions.run_date_format_option(self, menu)

        stdoptions.run_private_data_option(self, menu)
        stdoptions.run_living_people_option(self, menu, self._locale)
        self.database = CacheProxyDb(self.database)

        pid = menu.get_option_by_name('pid').get_value()
        self.center_person = self.database.get_person_from_gramps_id(pid)
        if self.center_person is None:
            raise ReportError(_("Person %s is not in the Database") % pid)

        stdoptions.run_name_format_option(self, menu)

        # eol_map is a map whose:
        #   keys are the generations of the people
        #   values are a map whose:
        #      keys are person handles
        #      values are an array whose:
        #         elements are an array of ancestor person handles that link
        #         the eol person handle to the person or interest
        # eol_map[generation][person_handle][pedigree_idx][ancestor_handle_idx]
        #
        # There is an array of pedigrees because one person could show up twice
        # in one generation (descendants marrying). Most people only have one
        # pedigree.
        #
        # eol_map is populated by get_eol() which calls itself recursively.
        self.eol_map = {}
        self.get_eol(self.center_person, 1, [])

    def get_eol(self, person, gen, pedigree):
        """
        Recursively find the end of the line for each person
        """
        person_handle = person.get_handle()
        new_pedigree = list(pedigree) + [person_handle]
        person_is_eol = False
        families = person.get_parent_family_handle_list()

        if person_handle in pedigree:
            # This is a severe error!
            # It indicates a loop in ancestry: A -> B -> A
            person_is_eol = True
        elif not families:
            person_is_eol = True
        else:
            for family_handle in families:
                family = self.database.get_family_from_handle(family_handle)
                father_handle = family.get_father_handle()
                mother_handle = family.get_mother_handle()
                if father_handle:
                    father = self.database.get_person_from_handle(father_handle)
                    self.get_eol(father, gen+1, new_pedigree)
                if mother_handle:
                    mother = self.database.get_person_from_handle(mother_handle)
                    self.get_eol(mother, gen+1, new_pedigree)

                if not father_handle or not mother_handle:
                    person_is_eol = True

        if person_is_eol:
            # This person is the end of a line
            if gen not in self.eol_map:
                self.eol_map[gen] = {}
            if person_handle not in self.eol_map[gen]:
                self.eol_map[gen][person_handle] = []
            self.eol_map[gen][person_handle].append(new_pedigree)

    def write_report(self):
        """
        The routine that actually creates the report.
        At this point, the document is opened and ready for writing.
        """
        pname = self._name_display.display(self.center_person)

        self.doc.start_paragraph("EOL-Title")
        # feature request 2356: avoid genitive form
        title = self._("End of Line Report for %s") % pname
        mark = IndexMark(title, INDEX_TYPE_TOC, 1)
        self.doc.write_text(title, mark)
        self.doc.end_paragraph()

        self.doc.start_paragraph("EOL-Subtitle")
        # feature request 2356: avoid genitive form
        title = self._("All the ancestors of %s who are missing a parent"
                      ) % pname
        self.doc.write_text(title)
        self.doc.end_paragraph()

        self.doc.start_table('EolTable', 'EOL-Table')
        for generation, handles in sorted(self.eol_map.items()):
            self.write_generation_row(generation)
            for person_handle, pedigrees in handles.items():
                self.write_person_row(person_handle)
                list(map(self.write_pedigree_row, pedigrees))
        self.doc.end_table()

    def write_generation_row(self, generation):
        """
        Write out a row in the table showing the generation.
        """
        self.doc.start_row()
        self.doc.start_cell('EOL_GenerationCell', 2)
        self.doc.start_paragraph('EOL-Generation')
        self.doc.write_text(self._("Generation %d") % generation)
        self.doc.end_paragraph()
        self.doc.end_cell()
        self.doc.end_row()

    def write_person_row(self, person_handle):
        """
        Write a row in the table with information about the given person.
        """
        person = self.database.get_person_from_handle(person_handle)

        name = self._name_display.display(person)
        mark = utils.get_person_mark(self.database, person)
        birth_date = ""
        birth_ref = person.get_birth_ref()
        if birth_ref:
            event = self.database.get_event_from_handle(birth_ref.ref)
            birth_date = self._get_date(event.get_date_object())
        death_date = ""
        death_ref = person.get_death_ref()
        if death_ref:
            event = self.database.get_event_from_handle(death_ref.ref)
            death_date = self._get_date(event.get_date_object())
        dates = ''
        if birth_date or death_date:
            dates = " (%(birth_date)s - %(death_date)s)" % {
                               'birth_date' : birth_date,
                               'death_date' : death_date}

        self.doc.start_row()
        self.doc.start_cell('EOL-TableCell', 2)
        self.doc.start_paragraph('EOL-Normal')
        self.doc.write_text(name, mark)
        self.doc.write_text(dates)
        self.doc.end_paragraph()
        self.doc.end_cell()
        self.doc.end_row()

    def write_pedigree_row(self, pedigree):
        """
        Write a row in the table with with the person's family line.

        pedigree is an array containing the names of the people in the pedigree
        """
        names = []
        for person_handle in pedigree:
            person = self.database.get_person_from_handle(person_handle)
            names.append(self._name_display.display(person))
        text = " -- ".join(names)
        self.doc.start_row()
        self.doc.start_cell('EOL-TableCell')
        self.doc.end_cell()
        self.doc.start_cell('EOL-TableCell')
        self.doc.start_paragraph('EOL-Pedigree')
        self.doc.write_text(text)
        self.doc.end_paragraph()
        self.doc.end_cell()
        self.doc.end_row()
Пример #5
0
class KinshipReport(Report):
    """ Kinship Report """

    def __init__(self, database, options, user):
        """
        Create the KinshipReport object that produces the report.

        The arguments are:

        database        - the GRAMPS database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance

        This report needs the following parameters (class variables)
        that come in the options class.

        maxdescend    - Maximum generations of descendants to include.
        maxascend     - Maximum generations of ancestors to include.
        incspouses    - Whether to include spouses.
        inccousins    - Whether to include cousins.
        incaunts      - Whether to include aunts/uncles/nephews/nieces.
        pid           - The Gramps ID of the center person for the report.
        name_format   - Preferred format to display names
        incl_private  - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death
        """
        Report.__init__(self, database, options, user)
        menu = options.menu

        lang = menu.get_option_by_name('trans').get_value()
        rlocale = self.set_locale(lang)

        stdoptions.run_private_data_option(self, menu)
        stdoptions.run_living_people_option(self, menu, rlocale)
        self.database = CacheProxyDb(self.database)
        self.__db = self.database

        self.max_descend = menu.get_option_by_name('maxdescend').get_value()
        self.max_ascend = menu.get_option_by_name('maxascend').get_value()
        self.inc_spouses = menu.get_option_by_name('incspouses').get_value()
        self.inc_cousins = menu.get_option_by_name('inccousins').get_value()
        self.inc_aunts = menu.get_option_by_name('incaunts').get_value()
        pid = menu.get_option_by_name('pid').get_value()
        self.person = self.database.get_person_from_gramps_id(pid)
        if self.person is None:
            raise ReportError(_("Person %s is not in the Database") % pid)

        stdoptions.run_name_format_option(self, menu)

        self.rel_calc = get_relationship_calculator(reinit=True,
                                                    clocale=rlocale)

        self.kinship_map = {}
        self.spouse_map = {}

    def write_report(self):
        """
        The routine the actually creates the report. At this point, the document
        is opened and ready for writing.
        """
        pname = self._name_display.display(self.person)

        self.doc.start_paragraph("KIN-Title")
        # feature request 2356: avoid genitive form
        title = self._("Kinship Report for %s") % pname
        mark = IndexMark(title, INDEX_TYPE_TOC, 1)
        self.doc.write_text(title, mark)
        self.doc.end_paragraph()

        if self.inc_spouses:
            spouse_handles = self.get_spouse_handles(self.person.get_handle())
            if spouse_handles:
                self.write_people(self._("Spouses"), spouse_handles)

        # Collect all descendants of the person
        self.traverse_down(self.person.get_handle(), 0, 1)

        # Collect all ancestors/aunts/uncles/nephews/cousins of the person
        self.traverse_up(self.person.get_handle(), 1, 0)

        # Write Kin
        for Ga, Gbs in self.kinship_map.items():
            for Gb in Gbs:
                # To understand these calculations, see:
                # http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions
                _x_ = min(Ga, Gb)
                _y_ = abs(Ga - Gb)
                # Skip unrequested people
                if _x_ == 1 and _y_ > 0 and not self.inc_aunts:
                    continue
                elif _x_ > 1 and not self.inc_cousins:
                    continue

                get_rel_str = self.rel_calc.get_plural_relationship_string

                title = get_rel_str(Ga, Gb, in_law_b=False)
                self.write_people(self._(title), self.kinship_map[Ga][Gb])

                if (self.inc_spouses and
                        Ga in self.spouse_map and
                        Gb in self.spouse_map[Ga]):
                    title = get_rel_str(Ga, Gb, in_law_b=True)
                    self.write_people(self._(title), self.spouse_map[Ga][Gb])

    def traverse_down(self, person_handle, Ga, Gb, skip_handle=None):
        """
        Populate a map of arrays containing person handles for the descendants
        of the passed person. This function calls itself recursively until it
        reaches max_descend.

        Parameters:
        person_handle: the handle of the person to go to next
        Ga: The number of generations from the main person to the common
           ancestor. This should be incremented when going up generations, and
           left alone when going down generations.
        Gb: The number of generations from this person (person_handle) to the
           common ancestor. This should be incremented when going down
           generations and set back to zero when going up generations.
        skip_handle: an optional handle to skip when going down. This is useful
           to skip the descendant that brought you this generation in the first
           place.
        """
        for child_handle in self.get_children_handles(person_handle):
            if child_handle != skip_handle:
                self.add_kin(child_handle, Ga, Gb)

                if self.inc_spouses:
                    for spouse_handle in self.get_spouse_handles(child_handle):
                        self.add_spouse(spouse_handle, Ga, Gb)

                if Gb < self.max_descend:
                    self.traverse_down(child_handle, Ga, Gb+1)

    def traverse_up(self, person_handle, Ga, Gb):
        """
        Populate a map of arrays containing person handles for the ancestors
        of the passed person. This function calls itself recursively until it
        reaches max_ascend.

        Parameters:
        person_handle: the handle of the person to go to next
        Ga: The number of generations from the main person to the common
           ancestor. This should be incremented when going up generations, and
           left alone when going down generations.
        Gb: The number of generations from this person (person_handle) to the
           common ancestor. This should be incremented when going down
           generations and set back to zero when going up generations.
        """
        parent_handles = self.get_parent_handles(person_handle)
        for parent_handle in parent_handles:
            self.add_kin(parent_handle, Ga, Gb)
            self.traverse_down(parent_handle, Ga, Gb+1, person_handle)
            if Ga < self.max_ascend:
                self.traverse_up(parent_handle, Ga+1, 0)

    def add_kin(self, person_handle, Ga, Gb):
        """
        Add a person handle to the kin map.
        """
        if Ga not in self.kinship_map:
            self.kinship_map[Ga] = {}
        if Gb not in self.kinship_map[Ga]:
            self.kinship_map[Ga][Gb] = []
        if person_handle not in self.kinship_map[Ga][Gb]:
            self.kinship_map[Ga][Gb].append(person_handle)

    def add_spouse(self, spouse_handle, Ga, Gb):
        """
        Add a person handle to the spouse map.
        """
        if Ga not in self.spouse_map:
            self.spouse_map[Ga] = {}
        if Gb not in self.spouse_map[Ga]:
            self.spouse_map[Ga][Gb] = []
        if spouse_handle not in self.spouse_map[Ga][Gb]:
            self.spouse_map[Ga][Gb].append(spouse_handle)

    def get_parent_handles(self, person_handle):
        """
        Return an array of handles for all the parents of the
        given person handle.
        """
        parent_handles = []
        person = self.__db.get_person_from_handle(person_handle)
        family_handle = person.get_main_parents_family_handle()
        if family_handle:
            family = self.__db.get_family_from_handle(family_handle)
            father_handle = family.get_father_handle()
            if father_handle:
                parent_handles.append(father_handle)
            mother_handle = family.get_mother_handle()
            if mother_handle:
                parent_handles.append(mother_handle)
        return parent_handles

    def get_spouse_handles(self, person_handle):
        """
        Return an array of handles for all the spouses of the
        given person handle.
        """
        spouses = []
        person = self.__db.get_person_from_handle(person_handle)
        for family_handle in person.get_family_handle_list():
            family = self.__db.get_family_from_handle(family_handle)
            father_handle = family.get_father_handle()
            mother_handle = family.get_mother_handle()
            spouse_handle = None
            if mother_handle and father_handle == person_handle:
                spouse_handle = mother_handle
            elif father_handle and mother_handle == person_handle:
                spouse_handle = father_handle

            if spouse_handle and spouse_handle not in spouses:
                spouses.append(spouse_handle)
        return spouses

    def get_children_handles(self, person_handle):
        """
        Return an array of handles for all the children of the
        given person handle.
        """
        children = []
        person = self.__db.get_person_from_handle(person_handle)
        for family_handle in person.get_family_handle_list():
            family = self.__db.get_family_from_handle(family_handle)
            for child_ref in family.get_child_ref_list():
                children.append(child_ref.get_reference_handle())
        return children

    def write_people(self, title, people_handles):
        """
        Write information about a group of people - including the title.
        """
        cap_title = title[0].upper() + title[1:]
        subtitle = "%s (%d)" % (cap_title, len(people_handles))
        self.doc.start_paragraph("KIN-Subtitle")
        mark = IndexMark(cap_title, INDEX_TYPE_TOC, 2)
        self.doc.write_text(subtitle, mark)
        self.doc.end_paragraph()
        list(map(self.write_person, people_handles))

    def write_person(self, person_handle):
        """
        Write information about the given person.
        """
        person = self.database.get_person_from_handle(person_handle)

        name = self._name_display.display(person)
        mark = utils.get_person_mark(self.database, person)
        birth_date = ""
        birth = get_birth_or_fallback(self.database, person)
        if birth:
            birth_date = self._get_date(birth.get_date_object())

        death_date = ""
        death = get_death_or_fallback(self.database, person)
        if death:
            death_date = self._get_date(death.get_date_object())
        dates = ''
        if birth_date or death_date:
            dates = self._(" (%(birth_date)s - %(death_date)s)"
                          ) % {'birth_date' : birth_date,
                               'death_date' : death_date}

        self.doc.start_paragraph('KIN-Normal')
        self.doc.write_text(name, mark)
        self.doc.write_text(dates)
        self.doc.end_paragraph()
Пример #6
0
class DescendantReport(Report):
    """ Descendant report """

    def __init__(self, database, options, user):
        """
        Create the DescendantReport object that produces the report.

        The arguments are:

        database        - the Gramps database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance

        This report needs the following parameters (class variables)
        that come in the options class.

        gen           - Maximum number of generations to include.
        name_format   - Preferred format to display names
        dups          - Whether to include duplicate descendant trees
        incl_private  - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death
        inc_id        - Whether to include Gramps IDs
        """

        Report.__init__(self, database, options, user)

        menu = options.menu

        self.set_locale(menu.get_option_by_name('trans').get_value())

        stdoptions.run_date_format_option(self, menu)

        stdoptions.run_private_data_option(self, menu)
        stdoptions.run_living_people_option(self, menu, self._locale)
        self.database = CacheProxyDb(self.database)

        self.max_generations = menu.get_option_by_name('gen').get_value()
        self.want_ids = menu.get_option_by_name('inc_id').get_value()

        pid = menu.get_option_by_name('pid').get_value()
        self.center_person = self.database.get_person_from_gramps_id(pid)
        if self.center_person is None:
            raise ReportError(_("Person %s is not in the Database") % pid)

        #Initialize the Printinfo class
        self._showdups = menu.get_option_by_name('dups').get_value()
        numbering = menu.get_option_by_name('numbering').get_value()
        if numbering == "Simple":
            obj = PrintSimple(self._showdups)
        elif numbering == "Henry":
            obj = PrintHenry()
        elif numbering == "Modified Henry":
            obj = PrintHenry(modified=True)
        elif numbering == "d'Aboville":
            obj = PrintDAboville()
        elif numbering == "de Villiers/Pama":
            obj = PrintVilliers()
        elif numbering == "Meurgey de Tupigny":
            obj = PrintMeurgey()
        else:
            raise AttributeError("no such numbering: '%s'" % numbering)

        marrs = menu.get_option_by_name('marrs').get_value()
        divs = menu.get_option_by_name('divs').get_value()

        stdoptions.run_name_format_option(self, menu)

        self.obj_print = Printinfo(self.doc, self.database, obj, marrs, divs,
                                   self._name_display, self._locale,
                                   self.want_ids)

    def write_report(self):
        self.doc.start_paragraph("DR-Title")
        name = self._name_display.display(self.center_person)
        # feature request 2356: avoid genitive form
        title = self._("Descendants of %s") % name
        mark = IndexMark(title, INDEX_TYPE_TOC, 1)
        self.doc.write_text(title, mark)
        self.doc.end_paragraph()

        recurse = RecurseDown(self.max_generations, self.database,
                              self.obj_print, self._showdups, self._locale)
        recurse.recurse(1, self.center_person, None)
Пример #7
0
class KinshipReport(Report):
    """ Kinship Report """
    def __init__(self, database, options, user):
        """
        Create the KinshipReport object that produces the report.

        The arguments are:

        database        - the Gramps database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance

        This report needs the following parameters (class variables)
        that come in the options class.

        maxdescend    - Maximum generations of descendants to include.
        maxascend     - Maximum generations of ancestors to include.
        incspouses    - Whether to include spouses.
        inccousins    - Whether to include cousins.
        incaunts      - Whether to include aunts/uncles/nephews/nieces.
        pid           - The Gramps ID of the center person for the report.
        name_format   - Preferred format to display names
        incl_private  - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death
        """
        Report.__init__(self, database, options, user)
        menu = options.menu

        lang = menu.get_option_by_name('trans').get_value()
        rlocale = self.set_locale(lang)

        stdoptions.run_private_data_option(self, menu)
        stdoptions.run_living_people_option(self, menu, rlocale)
        self.database = CacheProxyDb(self.database)
        self.__db = self.database

        self.max_descend = menu.get_option_by_name('maxdescend').get_value()
        self.max_ascend = menu.get_option_by_name('maxascend').get_value()
        self.inc_spouses = menu.get_option_by_name('incspouses').get_value()
        self.inc_cousins = menu.get_option_by_name('inccousins').get_value()
        self.inc_aunts = menu.get_option_by_name('incaunts').get_value()
        pid = menu.get_option_by_name('pid').get_value()
        self.person = self.database.get_person_from_gramps_id(pid)
        if self.person is None:
            raise ReportError(_("Person %s is not in the Database") % pid)

        stdoptions.run_name_format_option(self, menu)

        self.rel_calc = get_relationship_calculator(reinit=True,
                                                    clocale=rlocale)

        self.kinship_map = {}
        self.spouse_map = {}

    def write_report(self):
        """
        The routine the actually creates the report. At this point, the document
        is opened and ready for writing.
        """
        pname = self._name_display.display(self.person)

        self.doc.start_paragraph("KIN-Title")
        # feature request 2356: avoid genitive form
        title = self._("Kinship Report for %s") % pname
        mark = IndexMark(title, INDEX_TYPE_TOC, 1)
        self.doc.write_text(title, mark)
        self.doc.end_paragraph()

        if self.inc_spouses:
            spouse_handles = self.get_spouse_handles(self.person.get_handle())
            if spouse_handles:
                self.write_people(self._("Spouses"), spouse_handles)

        # Collect all descendants of the person
        self.traverse_down(self.person.get_handle(), 0, 1)

        # Collect all ancestors/aunts/uncles/nephews/cousins of the person
        self.traverse_up(self.person.get_handle(), 1, 0)

        # Write Kin
        for Ga, Gbs in self.kinship_map.items():
            for Gb in Gbs:
                # To understand these calculations, see:
                # http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions
                _x_ = min(Ga, Gb)
                _y_ = abs(Ga - Gb)
                # Skip unrequested people
                if _x_ == 1 and _y_ > 0 and not self.inc_aunts:
                    continue
                elif _x_ > 1 and not self.inc_cousins:
                    continue

                get_rel_str = self.rel_calc.get_plural_relationship_string

                title = get_rel_str(Ga, Gb, in_law_b=False)
                self.write_people(self._(title), self.kinship_map[Ga][Gb])

                if (self.inc_spouses and Ga in self.spouse_map
                        and Gb in self.spouse_map[Ga]):
                    title = get_rel_str(Ga, Gb, in_law_b=True)
                    self.write_people(self._(title), self.spouse_map[Ga][Gb])

    def traverse_down(self, person_handle, Ga, Gb, skip_handle=None):
        """
        Populate a map of arrays containing person handles for the descendants
        of the passed person. This function calls itself recursively until it
        reaches max_descend.

        Parameters:
        person_handle: the handle of the person to go to next
        Ga: The number of generations from the main person to the common
           ancestor. This should be incremented when going up generations, and
           left alone when going down generations.
        Gb: The number of generations from this person (person_handle) to the
           common ancestor. This should be incremented when going down
           generations and set back to zero when going up generations.
        skip_handle: an optional handle to skip when going down. This is useful
           to skip the descendant that brought you this generation in the first
           place.
        """
        for child_handle in self.get_children_handles(person_handle):
            if child_handle != skip_handle:
                self.add_kin(child_handle, Ga, Gb)

                if self.inc_spouses:
                    for spouse_handle in self.get_spouse_handles(child_handle):
                        self.add_spouse(spouse_handle, Ga, Gb)

                if Gb < self.max_descend:
                    self.traverse_down(child_handle, Ga, Gb + 1)

    def traverse_up(self, person_handle, Ga, Gb):
        """
        Populate a map of arrays containing person handles for the ancestors
        of the passed person. This function calls itself recursively until it
        reaches max_ascend.

        Parameters:
        person_handle: the handle of the person to go to next
        Ga: The number of generations from the main person to the common
           ancestor. This should be incremented when going up generations, and
           left alone when going down generations.
        Gb: The number of generations from this person (person_handle) to the
           common ancestor. This should be incremented when going down
           generations and set back to zero when going up generations.
        """
        parent_handles = self.get_parent_handles(person_handle)
        for parent_handle in parent_handles:
            self.add_kin(parent_handle, Ga, Gb)
            self.traverse_down(parent_handle, Ga, Gb + 1, person_handle)
            if Ga < self.max_ascend:
                self.traverse_up(parent_handle, Ga + 1, 0)

    def add_kin(self, person_handle, Ga, Gb):
        """
        Add a person handle to the kin map.
        """
        if Ga not in self.kinship_map:
            self.kinship_map[Ga] = {}
        if Gb not in self.kinship_map[Ga]:
            self.kinship_map[Ga][Gb] = []
        if person_handle not in self.kinship_map[Ga][Gb]:
            self.kinship_map[Ga][Gb].append(person_handle)

    def add_spouse(self, spouse_handle, Ga, Gb):
        """
        Add a person handle to the spouse map.
        """
        if Ga not in self.spouse_map:
            self.spouse_map[Ga] = {}
        if Gb not in self.spouse_map[Ga]:
            self.spouse_map[Ga][Gb] = []
        if spouse_handle not in self.spouse_map[Ga][Gb]:
            self.spouse_map[Ga][Gb].append(spouse_handle)

    def get_parent_handles(self, person_handle):
        """
        Return an array of handles for all the parents of the
        given person handle.
        """
        parent_handles = []
        person = self.__db.get_person_from_handle(person_handle)
        family_handle = person.get_main_parents_family_handle()
        if family_handle:
            family = self.__db.get_family_from_handle(family_handle)
            father_handle = family.get_father_handle()
            if father_handle:
                parent_handles.append(father_handle)
            mother_handle = family.get_mother_handle()
            if mother_handle:
                parent_handles.append(mother_handle)
        return parent_handles

    def get_spouse_handles(self, person_handle):
        """
        Return an array of handles for all the spouses of the
        given person handle.
        """
        spouses = []
        person = self.__db.get_person_from_handle(person_handle)
        for family_handle in person.get_family_handle_list():
            family = self.__db.get_family_from_handle(family_handle)
            father_handle = family.get_father_handle()
            mother_handle = family.get_mother_handle()
            spouse_handle = None
            if mother_handle and father_handle == person_handle:
                spouse_handle = mother_handle
            elif father_handle and mother_handle == person_handle:
                spouse_handle = father_handle

            if spouse_handle and spouse_handle not in spouses:
                spouses.append(spouse_handle)
        return spouses

    def get_children_handles(self, person_handle):
        """
        Return an array of handles for all the children of the
        given person handle.
        """
        children = []
        person = self.__db.get_person_from_handle(person_handle)
        for family_handle in person.get_family_handle_list():
            family = self.__db.get_family_from_handle(family_handle)
            for child_ref in family.get_child_ref_list():
                children.append(child_ref.get_reference_handle())
        return children

    def write_people(self, title, people_handles):
        """
        Write information about a group of people - including the title.
        """
        cap_title = title[0].upper() + title[1:]
        subtitle = "%s (%d)" % (cap_title, len(people_handles))
        self.doc.start_paragraph("KIN-Subtitle")
        mark = IndexMark(cap_title, INDEX_TYPE_TOC, 2)
        self.doc.write_text(subtitle, mark)
        self.doc.end_paragraph()
        list(map(self.write_person, people_handles))

    def write_person(self, person_handle):
        """
        Write information about the given person.
        """
        person = self.database.get_person_from_handle(person_handle)

        name = self._name_display.display(person)
        mark = utils.get_person_mark(self.database, person)
        birth_date = ""
        birth = get_birth_or_fallback(self.database, person)
        if birth:
            birth_date = self._get_date(birth.get_date_object())

        death_date = ""
        death = get_death_or_fallback(self.database, person)
        if death:
            death_date = self._get_date(death.get_date_object())
        dates = ''
        if birth_date or death_date:
            dates = " (%(birth_date)s - %(death_date)s)" % {
                'birth_date': birth_date,
                'death_date': death_date
            }

        self.doc.start_paragraph('KIN-Normal')
        self.doc.write_text(name, mark)
        self.doc.write_text(dates)
        self.doc.end_paragraph()
Пример #8
0
class EndOfLineReport(Report):
    """ EndOfLine Report """
    def __init__(self, database, options, user):
        """
        Create the EndOfLineReport object that produces the report.

        The arguments are:

        database        - the GRAMPS database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance

        This report needs the following parameters (class variables)
        that come in the options class.
        name_format   - Preferred format to display names
        incl_private  - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death
        """
        Report.__init__(self, database, options, user)

        menu = options.menu

        lang = menu.get_option_by_name('trans').get_value()
        rlocale = self.set_locale(lang)

        stdoptions.run_private_data_option(self, menu)
        stdoptions.run_living_people_option(self, menu, rlocale)
        self.database = CacheProxyDb(self.database)

        pid = menu.get_option_by_name('pid').get_value()
        self.center_person = self.database.get_person_from_gramps_id(pid)
        if self.center_person is None:
            raise ReportError(_("Person %s is not in the Database") % pid)

        stdoptions.run_name_format_option(self, menu)

        # eol_map is a map whose:
        #   keys are the generations of the people
        #   values are a map whose:
        #      keys are person handles
        #      values are an array whose:
        #         elements are an array of ancestor person handles that link
        #         the eol person handle to the person or interest
        # eol_map[generation][person_handle][pedigree_idx][ancestor_handle_idx]
        #
        # There is an array of pedigrees because one person could show up twice
        # in one generation (descendants marrying). Most people only have one
        # pedigree.
        #
        # eol_map is populated by get_eol() which calls itself recursively.
        self.eol_map = {}
        self.get_eol(self.center_person, 1, [])

    def get_eol(self, person, gen, pedigree):
        """
        Recursively find the end of the line for each person
        """
        person_handle = person.get_handle()
        new_pedigree = list(pedigree) + [person_handle]
        person_is_eol = False
        families = person.get_parent_family_handle_list()

        if person_handle in pedigree:
            # This is a severe error!
            # It indicates a loop in ancestry: A -> B -> A
            person_is_eol = True
        elif not families:
            person_is_eol = True
        else:
            for family_handle in families:
                family = self.database.get_family_from_handle(family_handle)
                father_handle = family.get_father_handle()
                mother_handle = family.get_mother_handle()
                if father_handle:
                    father = self.database.get_person_from_handle(
                        father_handle)
                    self.get_eol(father, gen + 1, new_pedigree)
                if mother_handle:
                    mother = self.database.get_person_from_handle(
                        mother_handle)
                    self.get_eol(mother, gen + 1, new_pedigree)

                if not father_handle or not mother_handle:
                    person_is_eol = True

        if person_is_eol:
            # This person is the end of a line
            if gen not in self.eol_map:
                self.eol_map[gen] = {}
            if person_handle not in self.eol_map[gen]:
                self.eol_map[gen][person_handle] = []
            self.eol_map[gen][person_handle].append(new_pedigree)

    def write_report(self):
        """
        The routine that actually creates the report.
        At this point, the document is opened and ready for writing.
        """
        pname = self._name_display.display(self.center_person)

        self.doc.start_paragraph("EOL-Title")
        # feature request 2356: avoid genitive form
        title = self._("End of Line Report for %s") % pname
        mark = IndexMark(title, INDEX_TYPE_TOC, 1)
        self.doc.write_text(title, mark)
        self.doc.end_paragraph()

        self.doc.start_paragraph("EOL-Subtitle")
        # feature request 2356: avoid genitive form
        title = self._(
            "All the ancestors of %s who are missing a parent") % pname
        self.doc.write_text(title)
        self.doc.end_paragraph()

        self.doc.start_table('EolTable', 'EOL-Table')
        for generation, handles in self.eol_map.items():
            self.write_generation_row(generation)
            for person_handle, pedigrees in handles.items():
                self.write_person_row(person_handle)
                list(map(self.write_pedigree_row, pedigrees))
        self.doc.end_table()

    def write_generation_row(self, generation):
        """
        Write out a row in the table showing the generation.
        """
        self.doc.start_row()
        self.doc.start_cell('EOL_GenerationCell', 2)
        self.doc.start_paragraph('EOL-Generation')
        self.doc.write_text(self._("Generation %d") % generation)
        self.doc.end_paragraph()
        self.doc.end_cell()
        self.doc.end_row()

    def write_person_row(self, person_handle):
        """
        Write a row in the table with information about the given person.
        """
        person = self.database.get_person_from_handle(person_handle)

        name = self._name_display.display(person)
        mark = utils.get_person_mark(self.database, person)
        birth_date = ""
        birth_ref = person.get_birth_ref()
        if birth_ref:
            event = self.database.get_event_from_handle(birth_ref.ref)
            birth_date = self._get_date(event.get_date_object())
        death_date = ""
        death_ref = person.get_death_ref()
        if death_ref:
            event = self.database.get_event_from_handle(death_ref.ref)
            death_date = self._get_date(event.get_date_object())
        dates = ''
        if birth_date or death_date:
            dates = self._(" (%(birth_date)s - %(death_date)s)") % {
                'birth_date': birth_date,
                'death_date': death_date
            }

        self.doc.start_row()
        self.doc.start_cell('EOL-TableCell', 2)
        self.doc.start_paragraph('EOL-Normal')
        self.doc.write_text(name, mark)
        self.doc.write_text(dates)
        self.doc.end_paragraph()
        self.doc.end_cell()
        self.doc.end_row()

    def write_pedigree_row(self, pedigree):
        """
        Write a row in the table with with the person's family line.

        pedigree is an array containing the names of the people in the pedigree
        """
        names = []
        for person_handle in pedigree:
            person = self.database.get_person_from_handle(person_handle)
            names.append(self._name_display.display(person))
        text = " -- ".join(names)
        self.doc.start_row()
        self.doc.start_cell('EOL-TableCell')
        self.doc.end_cell()
        self.doc.start_cell('EOL-TableCell')
        self.doc.start_paragraph('EOL-Pedigree')
        self.doc.write_text(text)
        self.doc.end_paragraph()
        self.doc.end_cell()
        self.doc.end_row()
Пример #9
0
class DescendantReport(Report):
    """ Descendant report """
    def __init__(self, database, options, user):
        """
        Create the DescendantReport object that produces the report.

        The arguments are:

        database        - the Gramps database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance

        This report needs the following parameters (class variables)
        that come in the options class.

        gen           - Maximum number of generations to include.
        name_format   - Preferred format to display names
        dups          - Whether to include duplicate descendant trees
        incl_private  - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death
        inc_id        - Whether to include Gramps IDs
        """

        Report.__init__(self, database, options, user)

        menu = options.menu

        self.set_locale(menu.get_option_by_name('trans').get_value())

        stdoptions.run_date_format_option(self, menu)

        stdoptions.run_private_data_option(self, menu)
        stdoptions.run_living_people_option(self, menu, self._locale)
        self.database = CacheProxyDb(self.database)

        self.max_generations = menu.get_option_by_name('gen').get_value()
        self.want_ids = menu.get_option_by_name('inc_id').get_value()

        pid = menu.get_option_by_name('pid').get_value()
        self.center_person = self.database.get_person_from_gramps_id(pid)
        if self.center_person is None:
            raise ReportError(_("Person %s is not in the Database") % pid)

        #Initialize the Printinfo class
        self._showdups = menu.get_option_by_name('dups').get_value()
        numbering = menu.get_option_by_name('numbering').get_value()
        if numbering == "Simple":
            obj = PrintSimple(self._showdups)
        elif numbering == "Henry":
            obj = PrintHenry()
        elif numbering == "Modified Henry":
            obj = PrintHenry(modified=True)
        elif numbering == "d'Aboville":
            obj = PrintDAboville()
        elif numbering == "de Villiers/Pama":
            obj = PrintVilliers()
        elif numbering == "Meurgey de Tupigny":
            obj = PrintMeurgey()
        else:
            raise AttributeError("no such numbering: '%s'" % numbering)

        marrs = menu.get_option_by_name('marrs').get_value()
        divs = menu.get_option_by_name('divs').get_value()

        stdoptions.run_name_format_option(self, menu)

        pformat = menu.get_option_by_name("place_format").get_value()

        self.obj_print = Printinfo(self.doc, self.database, obj, marrs, divs,
                                   self._name_display, self._locale,
                                   self.want_ids, pformat)

    def write_report(self):
        self.doc.start_paragraph("DR-Title")
        name = self._name_display.display(self.center_person)
        # feature request 2356: avoid genitive form
        title = self._("Descendants of %s") % name
        mark = IndexMark(title, INDEX_TYPE_TOC, 1)
        self.doc.write_text(title, mark)
        self.doc.end_paragraph()

        recurse = RecurseDown(self.max_generations, self.database,
                              self.obj_print, self._showdups, self._locale)
        recurse.recurse(1, self.center_person, None)
Пример #10
0
class AncestorTree(Report):
    """ AncestorTree Report """

    def __init__(self, database, options, user):
        """
        Create AncestorTree object that produces the report.

        The arguments are:

        database        - the Gramps database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance
        """
        Report.__init__(self, database, options, user)

        self.options = options
        self._user = user

        self.set_locale(options.menu.get_option_by_name('trans').get_value())
        stdoptions.run_date_format_option(self, options.menu)
        stdoptions.run_private_data_option(self, options.menu)
        stdoptions.run_living_people_option(self, options.menu, self._locale)
        self.database = CacheProxyDb(self.database)
        stdoptions.run_name_format_option(self, options.menu)
        self._nd = self._name_display

    def begin_report(self):
        """
        This report needs the following parameters (class variables)
        that come in the options class.

        max_generations - Maximum number of generations to include.
        pagebbg      - Whether to include page breaks between generations.
        dispf        - Display format for the output box.
        scale_report - Whether to scale the report to fit the width or all.
        indblank     - Whether to include blank pages.
        compress     - Whether to compress chart.
        incl_private - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death

        We will
        1. a canvas in its full one-page size
        2. a page that we wish to print on
        scale up/down either or both of the above as needed/desired.
        almost all of this should be moved into Canvas!
        """

        database = self.database

        self.connect = GUIConnect()
        self.connect.set__opts(self.options.menu, self._locale, self._nd)

        #Set up the canvas that we will print on.
        style_sheet = self.doc.get_style_sheet()
        font_normal = style_sheet.get_paragraph_style("AC2-Normal").get_font()

        #The canvas that we will put our report on and print off of
        self.canvas = Canvas(self.doc,
                             ReportOptions(self.doc, font_normal, 'AC2-line'))

        self.canvas.report_opts.box_shadow *= \
                        self.connect.get_val('shadowscale')
        self.canvas.report_opts.box_pgap *= self.connect.get_val('box_Yscale')
        self.canvas.report_opts.box_mgap *= self.connect.get_val('box_Yscale')

        with self._user.progress(_('Ancestor Tree'),
                                 _('Making the Tree...'), 4) as step:

            #make the tree onto the canvas
            ## inlc_marr = self.connect.get_val("inc_marr")
            self.max_generations = self.connect.get_val('maxgen')
            tree = MakeAncestorTree(database, self.canvas)
            tree.start(self.connect.get_val('pid'))
            tree = None

            step()

            #Title
            title = self.connect.title_class(self.doc)
            center = self.database.get_person_from_gramps_id(
                self.connect.get_val('pid'))
            title.calc_title(center)
            self.canvas.add_title(title)

            #make the report as big as it wants to be.
            report = MakeReport(database, self.doc, self.canvas, font_normal)
            report.start()
            self.max_generations = report.get_generations()  #already know
            report = None

            step()

            #Note?
            if self.connect.get_val("inc_note"):
                note_box = NoteBox(self.doc, "AC2-note-box",
                                   self.connect.get_val("note_place"))
                subst = SubstKeywords(self.database, self._locale, self._nd,
                                      None, None)
                note_box.text = subst.replace_and_clean(
                    self.connect.get_val('note_disp'))
                self.canvas.add_note(note_box)

            #Now we have the report in its full size.
            #Do we want to scale the report?
            one_page = self.connect.get_val("resize_page")
            scale_report = self.connect.get_val("scale_tree")

            scale = self.canvas.scale_report(one_page,
                                             scale_report != 0,
                                             scale_report == 2)

            step()

            if scale != 1 or self.connect.get_val('shadowscale') != 1.0:
                self.scale_styles(scale)

    def write_report(self):

        one_page = self.connect.get_val("resize_page")
        #scale_report = self.connect.get_val("scale_tree")

        #inlc_marr = self.connect.get_val("inc_marr")
        inc_border = self.connect.get_val('inc_border')
        incblank = self.connect.get_val("inc_blank")
        prnnum = self.connect.get_val("inc_pagenum")

        #####################
        #Setup page information

        colsperpage = self.doc.get_usable_width()
        colsperpage += self.canvas.report_opts.col_width
        colsperpage = int(colsperpage / (self.canvas.report_opts.max_box_width +
                                         self.canvas.report_opts.col_width))
        colsperpage = colsperpage or 1

        #####################
        #Vars
        if prnnum:
            page_num_box = PageNumberBox(self.doc, 'AC2-box', self._locale)

        #TODO - Here

        #####################
        #ok, everyone is now ready to print on the canvas.  Paginate?
        self.canvas.paginate(colsperpage, one_page)

        #####################
        #Yeah!!!
        #lets finally make some pages!!!
        #####################
        pages = self.canvas.page_count(incblank)
        with self._user.progress(_('Ancestor Tree'),
                                 _('Printing the Tree...'), pages) as step:

            for page in self.canvas.page_iter_gen(incblank):

                self.doc.start_page()

                #do we need to print a border?
                if inc_border:
                    page.draw_border('AC2-line')

                #Do we need to print the page number?
                if prnnum:
                    page_num_box.display(page)

                #Print the individual people and lines
                page.display()

                step()
                self.doc.end_page()

    def scale_styles(self, scale):
        """
        Scale the styles for this report.
        """
        style_sheet = self.doc.get_style_sheet()

        graph_style = style_sheet.get_draw_style("AC2-box")
        graph_style.set_shadow(graph_style.get_shadow(),
                               self.canvas.report_opts.box_shadow * scale)
        graph_style.set_line_width(graph_style.get_line_width() * scale)
        style_sheet.add_draw_style("AC2-box", graph_style)

        graph_style = style_sheet.get_draw_style("AC2-fam-box")
        graph_style.set_shadow(graph_style.get_shadow(),
                               self.canvas.report_opts.box_shadow * scale)
        graph_style.set_line_width(graph_style.get_line_width() * scale)
        style_sheet.add_draw_style("AC2-fam-box", graph_style)

        graph_style = style_sheet.get_draw_style("AC2-note-box")
        #graph_style.set_shadow(graph_style.get_shadow(),
        #                       self.canvas.report_opts.box_shadow * scale)
        graph_style.set_line_width(graph_style.get_line_width() * scale)
        style_sheet.add_draw_style("AC2-note-box", graph_style)

        para_style = style_sheet.get_paragraph_style("AC2-Normal")
        font = para_style.get_font()
        font.set_size(font.get_size() * scale)
        para_style.set_font(font)
        style_sheet.add_paragraph_style("AC2-Normal", para_style)

        para_style = style_sheet.get_paragraph_style("AC2-Note")
        font = para_style.get_font()
        font.set_size(font.get_size() * scale)
        para_style.set_font(font)
        style_sheet.add_paragraph_style("AC2-Note", para_style)

        para_style = style_sheet.get_paragraph_style("AC2-Title")
        font = para_style.get_font()
        font.set_size(font.get_size() * scale)
        para_style.set_font(font)
        style_sheet.add_paragraph_style("AC2-Title", para_style)

        graph_style = GraphicsStyle()
        width = graph_style.get_line_width()
        width = width * scale
        graph_style.set_line_width(width)
        style_sheet.add_draw_style("AC2-line", graph_style)

        self.doc.set_style_sheet(style_sheet)
Пример #11
0
class AncestorReport(Report):
    """
    Ancestor Report class
    """
    def __init__(self, database, options, user):
        """
        Create the AncestorReport object that produces the Ahnentafel report.

        The arguments are:

        database        - the GRAMPS database instance
        options         - instance of the Options class for this report
        user            - a gen.user.User() instance

        This report needs the following parameters (class variables)
        that come in the options class.

        gen       - Maximum number of generations to include.
        pagebbg   - Whether to include page breaks between generations.
        name_format   - Preferred format to display names
        incl_private  - Whether to include private data
        living_people - How to handle living people
        years_past_death - Consider as living this many years after death
        """
        Report.__init__(self, database, options, user)

        self.map = {}
        menu = options.menu

        lang = menu.get_option_by_name('trans').get_value()
        rlocale = self.set_locale(lang)

        stdoptions.run_private_data_option(self, menu)
        stdoptions.run_living_people_option(self, menu, rlocale)
        self.database = CacheProxyDb(self.database)

        self.max_generations = menu.get_option_by_name('maxgen').get_value()
        self.pgbrk = menu.get_option_by_name('pagebbg').get_value()
        self.opt_namebrk = menu.get_option_by_name('namebrk').get_value()
        pid = menu.get_option_by_name('pid').get_value()
        self.center_person = self.database.get_person_from_gramps_id(pid)
        if (self.center_person == None) :
            raise ReportError(_("Person %s is not in the Database") % pid )

        stdoptions.run_name_format_option(self, menu)

        self.__narrator = Narrator(self.database,  use_fulldate=True,
                                   nlocale=rlocale)

    def apply_filter(self, person_handle, index, generation=1):
        """
        Recursive function to walk back all parents of the current person.
        When max_generations are hit, we stop the traversal.
        """

        # check for end of the current recursion level. This happens
        # if the person handle is None, or if the max_generations is hit

        if not person_handle or generation > self.max_generations:
            return

        # store the person in the map based off their index number
        # which is passed to the routine.
        self.map[index] = person_handle

        # retrieve the Person instance from the database from the
        # passed person_handle and find the parents from the list.
        # Since this report is for natural parents (birth parents),
        # we have to handle that parents may not

        person = self.database.get_person_from_handle(person_handle)
        if person is None:
            return

        father_handle = None
        mother_handle = None
        for family_handle in person.get_parent_family_handle_list():
            family = self.database.get_family_from_handle(family_handle)

            # filter the child_ref_list to find the reference that matches
            # the passed person. There should be exactly one, but there is
            # nothing that prevents the same child in the list multiple times.

            ref = [ c for c in family.get_child_ref_list()
                    if c.get_reference_handle() == person_handle]
            if ref:

                # If the father_handle is not defined and the relationship is
                # BIRTH, then we have found the birth father. Same applies to
                # the birth mother. If for some reason, the we have multiple
                # people defined as the birth parents, we will select based on
                # priority in the list

                if not father_handle and \
                   ref[0].get_father_relation() == ChildRefType.BIRTH:
                    father_handle = family.get_father_handle()
                if not mother_handle and \
                   ref[0].get_mother_relation() == ChildRefType.BIRTH:
                    mother_handle = family.get_mother_handle()

        # Recursively call the function. It is okay if the handle is None,
        # since routine handles a handle of None

        self.apply_filter(father_handle, index*2, generation+1)
        self.apply_filter(mother_handle, (index*2)+1, generation+1)

    def write_report(self):
        """
        The routine the actually creates the report. At this point, the document
        is opened and ready for writing.
        """

        # Call apply_filter to build the self.map array of people in the
        # database that match the ancestry.

        self.apply_filter(self.center_person.get_handle(), 1)

        # Write the title line. Set in INDEX marker so that this section will be
        # identified as a major category if this is included in a Book report.

        name = self._name_display.display_formal(self.center_person)
        # feature request 2356: avoid genitive form
        title = self._("Ahnentafel Report for %s") % name
        mark = IndexMark(title, INDEX_TYPE_TOC, 1)
        self.doc.start_paragraph("AHN-Title")
        self.doc.write_text(title, mark)
        self.doc.end_paragraph()

        # get the entries out of the map, and sort them.

        generation = 0

        for key in sorted(self.map):

            # check the index number to see if we need to start a new generation
            if generation == log2(key):

                # generate a page break if requested
                if self.pgbrk and generation > 0:
                    self.doc.page_break()
                generation += 1

                # Create the Generation title, set an index marker
                gen_text = self._("Generation %d") % generation
                mark = None # don't need any with no page breaks
                if self.pgbrk:
                    mark = IndexMark(gen_text, INDEX_TYPE_TOC, 2)
                self.doc.start_paragraph("AHN-Generation")
                self.doc.write_text(gen_text, mark)
                self.doc.end_paragraph()

            # Build the entry

            self.doc.start_paragraph("AHN-Entry","%d." % key)
            person = self.database.get_person_from_handle(self.map[key])
            if person is None:
                continue
            name = self._name_display.display(person)
            mark = utils.get_person_mark(self.database, person)

            # write the name in bold
            self.doc.start_bold()
            self.doc.write_text(name.strip(), mark)
            self.doc.end_bold()

            # terminate with a period if it is not already terminated.
            # This can happen if the person's name ends with something 'Jr.'
            if name[-1:] == '.':
                self.doc.write_text(" ")
            else:
                self.doc.write_text(". ")

            # Add a line break if requested (not implemented yet)
            if self.opt_namebrk:
                self.doc.write_text('\n')

            self.__narrator.set_subject(person)
            self.doc.write_text(self.__narrator.get_born_string())
            self.doc.write_text(self.__narrator.get_baptised_string())
            self.doc.write_text(self.__narrator.get_christened_string())
            self.doc.write_text(self.__narrator.get_died_string())
            self.doc.write_text(self.__narrator.get_buried_string())

            self.doc.end_paragraph()