def login(self):
     global _
     username = self.sign_in.username.get()
     password = self.sign_in.password.get()
     if not (username and password):
         messagebox.showinfo(message=_('Please enter your FamilySearch username and password.'))
         return
     self.btn_valid.config(state='disabled')
     self.info(_('Login to FamilySearch...'))
     self.logfile = open('download.log', 'w', encoding='utf-8')
     self.fs = Session(self.sign_in.username.get(), self.sign_in.password.get(), verbose=True, logfile=self.logfile, timeout=1)
     if not self.fs.logged:
         messagebox.showinfo(_('Error'), message=_('The username or password was incorrect'))
         self.btn_valid.config(state='normal')
         self.info('')
         return
     self.tree = Tree(self.fs)
     _ = self.fs._
     self.title.config(text=_('Options'))
     cache.delete('lang')
     cache.add('lang', self.fs.lang)
     self.options = Options(self.form, True)
     self.info('')
     self.sign_in.destroy()
     self.options.pack()
     self.master.change_lang()
     self.btn_valid.config(command=self.command_in_thread(self.download), state='normal', text=_('Download'))
     self.options.start_indis.add_indi(self.fs.get_userid())
     self.update_needed = False
Exemple #2
0
 def login(self):
     """ log in FamilySearch """
     global _
     username = self.sign_in.username.get()
     password = self.sign_in.password.get()
     if not (username and password):
         messagebox.showinfo(message=_(
             "Please enter your FamilySearch username and password."))
         return
     self.btn_valid.config(state="disabled")
     self.info(_("Login to FamilySearch..."))
     self.logfile = open("download.log", "w", encoding="utf-8")
     self.fs = Session(
         self.sign_in.username.get(),
         self.sign_in.password.get(),
         verbose=True,
         logfile=self.logfile,
         timeout=1,
     )
     if not self.fs.logged:
         messagebox.showinfo(
             _("Error"),
             message=_("The username or password was incorrect"))
         self.btn_valid.config(state="normal")
         self.info("")
         return
     self.tree = Tree(self.fs)
     _ = self.fs._
     self.title.config(text=_("Options"))
     cache.delete("lang")
     cache.add("lang", self.fs.lang)
     cache.delete("username")
     cache.add("username", username)
     url = "/service/tree/tree-data/reservations/person/%s/ordinances" % self.fs.fid
     lds_account = self.fs.get_url(url, {}).get("status") == "OK"
     self.options = Options(self.form, lds_account)
     self.info("")
     self.sign_in.destroy()
     self.options.pack()
     self.master.change_lang()
     self.btn_valid.config(command=self.command_in_thread(self.download),
                           state="normal",
                           text=_("Download"))
     self.options.start_indis.add_indi(self.fs.fid)
     self.update_needed = False
Exemple #3
0
 def login(self):
     global _
     username = self.sign_in.username.get()
     password = self.sign_in.password.get()
     if not (username and password):
         messagebox.showinfo(message=_(
             'Please enter your FamilySearch username and password.'))
         return
     self.btn_valid.config(state='disabled')
     self.info(_('Login to FamilySearch...'))
     self.logfile = open('download.log', 'w', encoding='utf-8')
     self.fs = Session(self.sign_in.username.get(),
                       self.sign_in.password.get(),
                       verbose=True,
                       logfile=self.logfile,
                       timeout=1)
     if not self.fs.logged:
         messagebox.showinfo(
             _('Error'),
             message=_('The username or password was incorrect'))
         self.btn_valid.config(state='normal')
         self.info('')
         return
     self.tree = Tree(self.fs)
     _ = self.fs._
     self.title.config(text=_('Options'))
     cache.delete('lang')
     cache.add('lang', self.fs.lang)
     lds_account = self.fs.get_url(
         '/platform/tree/persons/%s/ordinances.json' %
         self.fs.get_userid()) != 'error'
     self.options = Options(self.form, lds_account)
     self.info('')
     self.sign_in.destroy()
     self.options.pack()
     self.master.change_lang()
     self.btn_valid.config(command=self.command_in_thread(self.download),
                           state='normal',
                           text=_('Download'))
     self.options.start_indis.add_indi(self.fs.get_userid())
     self.update_needed = False
        parser.add_argument('-i', metavar = '<FILE>', nargs = '+', type = argparse.FileType('r', encoding='UTF-8'), default = sys.stdin, help = 'input GEDCOM files [stdin]')
        parser.add_argument('-o', metavar = '<FILE>', nargs = '?', type = argparse.FileType('w', encoding='UTF-8'), default = sys.stdout, help = 'output GEDCOM files [stdout]')
    except TypeError:
        sys.stderr.write('Python >= 3.4 is required to run this script\n')
        sys.stderr.write('(see https://docs.python.org/3/whatsnew/3.4.html#argparse)\n')
        exit(2)

    # extract arguments from the command line
    try:
        parser.error = parser.exit
        args = parser.parse_args()
    except SystemExit:
        parser.print_help()
        exit(2)

    tree = Tree()

    indi_counter = 0
    fam_counter = 0

    # read the GEDCOM data
    for file in args.i:
        ged = Gedcom(file)

        # add informations about individuals
        for num in ged.indi:
            fid = ged.indi[num].fid
            if fid not in tree.indi:
                indi_counter += 1
                tree.indi[fid] = Indi(num = indi_counter)
                tree.indi[fid].fid = ged.indi[num].fid
            help="output GEDCOM files [stdout]",
        )
    except TypeError:
        sys.stderr.write("Python >= 3.4 is required to run this script\n")
        sys.stderr.write("(see https://docs.python.org/3/whatsnew/3.4.html#argparse)\n")
        exit(2)

    # extract arguments from the command line
    try:
        parser.error = parser.exit
        args = parser.parse_args()
    except SystemExit:
        parser.print_help()
        exit(2)

    tree = Tree()

    indi_counter = 0
    fam_counter = 0

    # read the GEDCOM data
    for file in args.i:
        ged = Gedcom(file)

        # add informations about individuals
        for num in ged.indi:
            fid = ged.indi[num].fid
            if fid not in tree.indi:
                indi_counter += 1
                tree.indi[fid] = Indi(num=indi_counter)
                tree.indi[fid].fid = ged.indi[num].fid
class Download(Frame):
    def __init__(self, master, **kwargs):
        super(Download, self).__init__(master, borderwidth=20, **kwargs)
        self.fs = None
        self.tree = None
        self.logfile = None

        # User informations
        self.info_tree = False
        self.start_time = None
        info = Frame(self, borderwidth=10)
        self.info_label = Label(info, wraplength=350, borderwidth=20, justify='center', font=('a', 10, 'bold'))
        self.info_indis = Label(info)
        self.info_fams = Label(info)
        self.info_sources = Label(info)
        self.info_notes = Label(info)
        self.time = Label(info)
        self.info_label.grid(row=0, column=0, columnspan=2)
        self.info_indis.grid(row=1, column=0)
        self.info_fams.grid(row=1, column=1)
        self.info_sources.grid(row=2, column=0)
        self.info_notes.grid(row=2, column=1)
        self.time.grid(row=3, column=0, columnspan=2)

        self.form = Frame(self)
        self.sign_in = SignIn(self.form)
        self.options = None
        self.title = Label(self, text=_('Sign In to FamilySearch'), font=('a', 12, 'bold'))
        buttons = Frame(self)
        self.btn_quit = Button(buttons, text=_('Quit'), command=Thread(target=self.quit).start)
        self.btn_valid = Button(buttons, text=_('Sign In'), command=self.command_in_thread(self.login))
        self.title.pack()
        self.sign_in.pack()
        self.form.pack()
        self.btn_quit.pack(side='left', padx=(0, 40))
        self.btn_valid.pack(side='right', padx=(40, 0))
        info.pack()
        buttons.pack(side='bottom')
        self.pack()
        self.update_needed = False

    def info(self, text):
        self.info_label.config(text=text)

    def save(self):
        filename = filedialog.asksaveasfilename(title=_('Save as'), defaultextension='.ged', filetypes=(('GEDCOM', '.ged'), (_('All files'), '*.*')))
        if not filename:
            return
        with open(filename, 'w', encoding='utf-8') as file:
            self.tree.print(file)

    def login(self):
        global _
        username = self.sign_in.username.get()
        password = self.sign_in.password.get()
        if not (username and password):
            messagebox.showinfo(message=_('Please enter your FamilySearch username and password.'))
            return
        self.btn_valid.config(state='disabled')
        self.info(_('Login to FamilySearch...'))
        self.logfile = open('download.log', 'w', encoding='utf-8')
        self.fs = Session(self.sign_in.username.get(), self.sign_in.password.get(), verbose=True, logfile=self.logfile, timeout=1)
        if not self.fs.logged:
            messagebox.showinfo(_('Error'), message=_('The username or password was incorrect'))
            self.btn_valid.config(state='normal')
            self.info('')
            return
        self.tree = Tree(self.fs)
        _ = self.fs._
        self.title.config(text=_('Options'))
        cache.delete('lang')
        cache.add('lang', self.fs.lang)
        self.options = Options(self.form, True)
        self.info('')
        self.sign_in.destroy()
        self.options.pack()
        self.master.change_lang()
        self.btn_valid.config(command=self.command_in_thread(self.download), state='normal', text=_('Download'))
        self.options.start_indis.add_indi(self.fs.get_userid())
        self.update_needed = False

    def quit(self):
        self.update_needed = False
        if self.logfile:
            self.logfile.close()
        super(Download, self).quit()
        # prevent exception during download
        os._exit(1)

    def download(self):
        todo = [self.options.start_indis.indis[key] for key in sorted(self.options.start_indis.indis)]
        for fid in todo:
            if not re.match(r'[A-Z0-9]{4}-[A-Z0-9]{3}', fid):
                messagebox.showinfo(_('Error'), message=_('Invalid FamilySearch ID: ') + fid)
                return
        self.start_time = time.time()
        self.options.destroy()
        self.form.destroy()
        self.title.config(text='FamilySearch to GEDCOM')
        self.btn_valid.config(state='disabled')
        self.info(_('Download starting individuals...'))
        self.info_tree = True
        self.tree.add_indis(todo)
        todo = set(todo)
        done = set()
        for i in range(self.options.ancestors.get()):
            if not todo:
                break
            done |= todo
            self.info(_('Download ') + str(i + 1) + _('th generation of ancestors...'))
            todo = self.tree.add_parents(todo) - done

        todo = set(self.tree.indi.keys())
        done = set()
        for i in range(self.options.descendants.get()):
            if not todo:
                break
            done |= todo
            self.info(_('Download ') + str(i + 1) + _('th generation of descendants...'))
            todo = self.tree.add_children(todo) - done

        if self.options.spouses.get():
            self.info(_('Download spouses and marriage information...'))
            todo = set(self.tree.indi.keys())
            self.tree.add_spouses(todo)
        ordi = self.options.ordinances.get()
        cont = self.options.contributors.get()

        async def download_stuff(loop):
            futures = set()
            for fid, indi in self.tree.indi.items():
                futures.add(loop.run_in_executor(None, indi.get_notes))
                if ordi:
                    futures.add(loop.run_in_executor(None, self.tree.add_ordinances, fid))
                if cont:
                    futures.add(loop.run_in_executor(None, indi.get_contributors))
            for fam in self.tree.fam.values():
                futures.add(loop.run_in_executor(None, fam.get_notes))
                if cont:
                    futures.add(loop.run_in_executor(None, fam.get_contributors))
            for future in futures:
                await future

        loop = asyncio.get_event_loop()
        self.info(_('Download notes') + (((',' if cont else _(' and')) + _(' ordinances')) if ordi else '') + (_(' and contributors') if cont else '') + '...')
        loop.run_until_complete(download_stuff(loop))

        self.tree.reset_num()
        self.btn_valid.config(command=self.save, state='normal', text=_('Save'))
        self.info(text=_('Success ! Click below to save your GEDCOM file'))
        self.update_info_tree()
        self.update_needed = False

    def command_in_thread(self, func):
        def res():
            self.update_needed = True
            Thread(target=self.update_gui).start()
            Thread(target=func).start()
        return res

    def update_info_tree(self):
        if self.info_tree and self.start_time and self.tree:
            self.info_indis.config(text=_('Individuals: %s') % len(self.tree.indi))
            self.info_fams.config(text=_('Families: %s') % len(self.tree.fam))
            self.info_sources.config(text=_('Sources: %s') % len(self.tree.sources))
            self.info_notes.config(text=_('Notes: %s') % len(self.tree.notes))
            t = round(time.time() - self.start_time)
            minutes = t // 60
            seconds = t % 60
            self.time.config(text=_('Elapsed time: %s:%s') % (minutes, '00%s'[len(str(seconds)):] % seconds))

    def update_gui(self):
        while self.update_needed:
            self.update_info_tree()
            self.master.update()
            time.sleep(0.1)
    def save(self):
        if not self.files_to_merge.files:
            messagebox.showinfo(_('Error'), message=_('Please add GEDCOM files'))
            return

        filename = filedialog.asksaveasfilename(title=_('Save as'), defaultextension='.ged', filetypes=(('GEDCOM', '.ged'), (_('All files'), '*.*')))
        tree = Tree()

        indi_counter = 0
        fam_counter = 0

        # read the GEDCOM data
        for file in self.files_to_merge.files.values():
            ged = Gedcom(file, tree)

            # add informations about individuals
            for num in ged.indi:
                fid = ged.indi[num].fid
                if fid not in tree.indi:
                    indi_counter += 1
                    tree.indi[fid] = Indi(tree=tree, num=indi_counter)
                    tree.indi[fid].tree = tree
                    tree.indi[fid].fid = ged.indi[num].fid
                tree.indi[fid].fams_fid |= ged.indi[num].fams_fid
                tree.indi[fid].famc_fid |= ged.indi[num].famc_fid
                tree.indi[fid].name = ged.indi[num].name
                tree.indi[fid].birthnames = ged.indi[num].birthnames
                tree.indi[fid].nicknames = ged.indi[num].nicknames
                tree.indi[fid].aka = ged.indi[num].aka
                tree.indi[fid].married = ged.indi[num].married
                tree.indi[fid].gender = ged.indi[num].gender
                tree.indi[fid].facts = ged.indi[num].facts
                tree.indi[fid].notes = ged.indi[num].notes
                tree.indi[fid].sources = ged.indi[num].sources
                tree.indi[fid].memories = ged.indi[num].memories
                tree.indi[fid].baptism = ged.indi[num].baptism
                tree.indi[fid].confirmation = ged.indi[num].confirmation
                tree.indi[fid].endowment = ged.indi[num].endowment
                if not (tree.indi[fid].sealing_child and tree.indi[fid].sealing_child.famc):
                    tree.indi[fid].sealing_child = ged.indi[num].sealing_child

            # add informations about families
            for num in ged.fam:
                husb, wife = (ged.fam[num].husb_fid, ged.fam[num].wife_fid)
                if (husb, wife) not in tree.fam:
                    fam_counter += 1
                    tree.fam[(husb, wife)] = Fam(husb, wife, tree, fam_counter)
                    tree.fam[(husb, wife)].tree = tree
                tree.fam[(husb, wife)].chil_fid |= ged.fam[num].chil_fid
                tree.fam[(husb, wife)].fid = ged.fam[num].fid
                tree.fam[(husb, wife)].facts = ged.fam[num].facts
                tree.fam[(husb, wife)].notes = ged.fam[num].notes
                tree.fam[(husb, wife)].sources = ged.fam[num].sources
                tree.fam[(husb, wife)].sealing_spouse = ged.fam[num].sealing_spouse

        # merge notes by text
        tree.notes = sorted(tree.notes, key=lambda x: x.text)
        for i, n in enumerate(tree.notes):
            if i == 0:
                n.num = 1
                continue
            if n.text == tree.notes[i - 1].text:
                n.num = tree.notes[i - 1].num
            else:
                n.num = tree.notes[i - 1].num + 1

        # compute number for family relationships and print GEDCOM file
        tree.reset_num()
        with open(filename, 'w', encoding='utf-8') as file:
            tree.print(file)
        messagebox.showinfo(_('Info'), message=_('Files successfully merged'))
Exemple #8
0
class Download(Frame):
    def __init__(self, master, **kwargs):
        super(Download, self).__init__(master, borderwidth=20, **kwargs)
        self.fs = None
        self.tree = None
        self.logfile = None

        # User informations
        self.info_tree = False
        self.start_time = None
        info = Frame(self, borderwidth=10)
        self.info_label = Label(info,
                                wraplength=350,
                                borderwidth=20,
                                justify='center',
                                font=('a', 10, 'bold'))
        self.info_indis = Label(info)
        self.info_fams = Label(info)
        self.info_sources = Label(info)
        self.info_notes = Label(info)
        self.time = Label(info)
        self.info_label.grid(row=0, column=0, columnspan=2)
        self.info_indis.grid(row=1, column=0)
        self.info_fams.grid(row=1, column=1)
        self.info_sources.grid(row=2, column=0)
        self.info_notes.grid(row=2, column=1)
        self.time.grid(row=3, column=0, columnspan=2)

        self.form = Frame(self)
        self.sign_in = SignIn(self.form)
        self.options = None
        self.title = Label(self,
                           text=_('Sign In to FamilySearch'),
                           font=('a', 12, 'bold'))
        buttons = Frame(self)
        self.btn_quit = Button(buttons,
                               text=_('Quit'),
                               command=Thread(target=self.quit).start)
        self.btn_valid = Button(buttons,
                                text=_('Sign In'),
                                command=self.command_in_thread(self.login))
        self.title.pack()
        self.sign_in.pack()
        self.form.pack()
        self.btn_quit.pack(side='left', padx=(0, 40))
        self.btn_valid.pack(side='right', padx=(40, 0))
        info.pack()
        buttons.pack(side='bottom')
        self.pack()
        self.update_needed = False

    def info(self, text):
        self.info_label.config(text=text)

    def save(self):
        filename = filedialog.asksaveasfilename(title=_('Save as'),
                                                defaultextension='.ged',
                                                filetypes=(('GEDCOM', '.ged'),
                                                           (_('All files'),
                                                            '*.*')))
        if not filename:
            return
        with open(filename, 'w', encoding='utf-8') as file:
            self.tree.print(file)

    def login(self):
        global _
        username = self.sign_in.username.get()
        password = self.sign_in.password.get()
        if not (username and password):
            messagebox.showinfo(message=_(
                'Please enter your FamilySearch username and password.'))
            return
        self.btn_valid.config(state='disabled')
        self.info(_('Login to FamilySearch...'))
        self.logfile = open('download.log', 'w', encoding='utf-8')
        self.fs = Session(self.sign_in.username.get(),
                          self.sign_in.password.get(),
                          verbose=True,
                          logfile=self.logfile,
                          timeout=1)
        if not self.fs.logged:
            messagebox.showinfo(
                _('Error'),
                message=_('The username or password was incorrect'))
            self.btn_valid.config(state='normal')
            self.info('')
            return
        self.tree = Tree(self.fs)
        _ = self.fs._
        self.title.config(text=_('Options'))
        cache.delete('lang')
        cache.add('lang', self.fs.lang)
        lds_account = self.fs.get_url(
            '/platform/tree/persons/%s/ordinances.json' %
            self.fs.get_userid()) != 'error'
        self.options = Options(self.form, lds_account)
        self.info('')
        self.sign_in.destroy()
        self.options.pack()
        self.master.change_lang()
        self.btn_valid.config(command=self.command_in_thread(self.download),
                              state='normal',
                              text=_('Download'))
        self.options.start_indis.add_indi(self.fs.get_userid())
        self.update_needed = False

    def quit(self):
        self.update_needed = False
        if self.logfile:
            self.logfile.close()
        super(Download, self).quit()
        # prevent exception during download
        os._exit(1)

    def download(self):
        todo = [
            self.options.start_indis.indis[key]
            for key in sorted(self.options.start_indis.indis)
        ]
        for fid in todo:
            if not re.match(r'[A-Z0-9]{4}-[A-Z0-9]{3}', fid):
                messagebox.showinfo(_('Error'),
                                    message=_('Invalid FamilySearch ID: ') +
                                    fid)
                return
        self.start_time = time.time()
        self.options.destroy()
        self.form.destroy()
        self.title.config(text='FamilySearch to GEDCOM')
        self.btn_valid.config(state='disabled')
        self.info(_('Download starting individuals...'))
        self.info_tree = True
        self.tree.add_indis(todo)
        todo = set(todo)
        done = set()
        for i in range(self.options.ancestors.get()):
            if not todo:
                break
            done |= todo
            self.info(
                _('Download ') + str(i + 1) +
                _('th generation of ancestors...'))
            todo = self.tree.add_parents(todo) - done

        todo = set(self.tree.indi.keys())
        done = set()
        for i in range(self.options.descendants.get()):
            if not todo:
                break
            done |= todo
            self.info(
                _('Download ') + str(i + 1) +
                _('th generation of descendants...'))
            todo = self.tree.add_children(todo) - done

        if self.options.spouses.get():
            self.info(_('Download spouses and marriage information...'))
            todo = set(self.tree.indi.keys())
            self.tree.add_spouses(todo)
        ordi = self.options.ordinances.get()
        cont = self.options.contributors.get()

        async def download_stuff(loop):
            futures = set()
            for fid, indi in self.tree.indi.items():
                futures.add(loop.run_in_executor(None, indi.get_notes))
                if ordi:
                    futures.add(
                        loop.run_in_executor(None, self.tree.add_ordinances,
                                             fid))
                if cont:
                    futures.add(
                        loop.run_in_executor(None, indi.get_contributors))
            for fam in self.tree.fam.values():
                futures.add(loop.run_in_executor(None, fam.get_notes))
                if cont:
                    futures.add(
                        loop.run_in_executor(None, fam.get_contributors))
            for future in futures:
                await future

        loop = asyncio.get_event_loop()
        self.info(
            _('Download notes') + (((',' if cont else _(' and')) +
                                    _(' ordinances')) if ordi else '') +
            (_(' and contributors') if cont else '') + '...')
        loop.run_until_complete(download_stuff(loop))

        self.tree.reset_num()
        self.btn_valid.config(command=self.save,
                              state='normal',
                              text=_('Save'))
        self.info(text=_('Success ! Click below to save your GEDCOM file'))
        self.update_info_tree()
        self.update_needed = False

    def command_in_thread(self, func):
        def res():
            self.update_needed = True
            Thread(target=self.update_gui).start()
            Thread(target=func).start()

        return res

    def update_info_tree(self):
        if self.info_tree and self.start_time and self.tree:
            self.info_indis.config(text=_('Individuals: %s') %
                                   len(self.tree.indi))
            self.info_fams.config(text=_('Families: %s') % len(self.tree.fam))
            self.info_sources.config(text=_('Sources: %s') %
                                     len(self.tree.sources))
            self.info_notes.config(text=_('Notes: %s') % len(self.tree.notes))
            t = round(time.time() - self.start_time)
            minutes = t // 60
            seconds = t % 60
            self.time.config(text=_('Elapsed time: %s:%s') %
                             (minutes, '00%s'[len(str(seconds)):] % seconds))

    def update_gui(self):
        while self.update_needed:
            self.update_info_tree()
            self.master.update()
            time.sleep(0.1)
Exemple #9
0
    def save(self):
        if not self.files_to_merge.files:
            messagebox.showinfo(_('Error'),
                                message=_('Please add GEDCOM files'))
            return

        filename = filedialog.asksaveasfilename(title=_('Save as'),
                                                defaultextension='.ged',
                                                filetypes=(('GEDCOM', '.ged'),
                                                           (_('All files'),
                                                            '*.*')))
        tree = Tree()

        indi_counter = 0
        fam_counter = 0

        # read the GEDCOM data
        for file in self.files_to_merge.files.values():
            ged = Gedcom(file, tree)

            # add informations about individuals
            for num in ged.indi:
                fid = ged.indi[num].fid
                if fid not in tree.indi:
                    indi_counter += 1
                    tree.indi[fid] = Indi(tree=tree, num=indi_counter)
                    tree.indi[fid].tree = tree
                    tree.indi[fid].fid = ged.indi[num].fid
                tree.indi[fid].fams_fid |= ged.indi[num].fams_fid
                tree.indi[fid].famc_fid |= ged.indi[num].famc_fid
                tree.indi[fid].name = ged.indi[num].name
                tree.indi[fid].birthnames = ged.indi[num].birthnames
                tree.indi[fid].nicknames = ged.indi[num].nicknames
                tree.indi[fid].aka = ged.indi[num].aka
                tree.indi[fid].married = ged.indi[num].married
                tree.indi[fid].gender = ged.indi[num].gender
                tree.indi[fid].facts = ged.indi[num].facts
                tree.indi[fid].notes = ged.indi[num].notes
                tree.indi[fid].sources = ged.indi[num].sources
                tree.indi[fid].memories = ged.indi[num].memories
                tree.indi[fid].baptism = ged.indi[num].baptism
                tree.indi[fid].confirmation = ged.indi[num].confirmation
                tree.indi[fid].endowment = ged.indi[num].endowment
                if not (tree.indi[fid].sealing_child
                        and tree.indi[fid].sealing_child.famc):
                    tree.indi[fid].sealing_child = ged.indi[num].sealing_child

            # add informations about families
            for num in ged.fam:
                husb, wife = (ged.fam[num].husb_fid, ged.fam[num].wife_fid)
                if (husb, wife) not in tree.fam:
                    fam_counter += 1
                    tree.fam[(husb, wife)] = Fam(husb, wife, tree, fam_counter)
                    tree.fam[(husb, wife)].tree = tree
                tree.fam[(husb, wife)].chil_fid |= ged.fam[num].chil_fid
                tree.fam[(husb, wife)].fid = ged.fam[num].fid
                tree.fam[(husb, wife)].facts = ged.fam[num].facts
                tree.fam[(husb, wife)].notes = ged.fam[num].notes
                tree.fam[(husb, wife)].sources = ged.fam[num].sources
                tree.fam[(husb,
                          wife)].sealing_spouse = ged.fam[num].sealing_spouse

        # merge notes by text
        tree.notes = sorted(tree.notes, key=lambda x: x.text)
        for i, n in enumerate(tree.notes):
            if i == 0:
                n.num = 1
                continue
            if n.text == tree.notes[i - 1].text:
                n.num = tree.notes[i - 1].num
            else:
                n.num = tree.notes[i - 1].num + 1

        # compute number for family relationships and print GEDCOM file
        tree.reset_num()
        with open(filename, 'w', encoding='utf-8') as file:
            tree.print(file)
        messagebox.showinfo(_('Info'), message=_('Files successfully merged'))
Exemple #10
0
class Download(Frame):
    """ Main widget """
    def __init__(self, master, **kwargs):
        super(Download, self).__init__(master, borderwidth=20, **kwargs)
        self.fs = None
        self.tree = None
        self.logfile = None

        # User informations
        self.info_tree = False
        self.start_time = None
        info = Frame(self, borderwidth=10)
        self.info_label = Label(info,
                                wraplength=350,
                                borderwidth=20,
                                justify="center",
                                font=("a", 10, "bold"))
        self.info_indis = Label(info)
        self.info_fams = Label(info)
        self.info_sources = Label(info)
        self.info_notes = Label(info)
        self.time = Label(info)
        self.info_label.grid(row=0, column=0, columnspan=2)
        self.info_indis.grid(row=1, column=0)
        self.info_fams.grid(row=1, column=1)
        self.info_sources.grid(row=2, column=0)
        self.info_notes.grid(row=2, column=1)
        self.time.grid(row=3, column=0, columnspan=2)

        self.form = Frame(self)
        self.sign_in = SignIn(self.form)
        self.options = None
        self.title = Label(self,
                           text=_("Sign In to FamilySearch"),
                           font=("a", 12, "bold"))
        buttons = Frame(self)
        self.btn_quit = Button(buttons,
                               text=_("Quit"),
                               command=Thread(target=self.quit).start)
        self.btn_valid = Button(buttons,
                                text=_("Sign In"),
                                command=self.command_in_thread(self.login))
        self.title.pack()
        self.sign_in.pack()
        self.form.pack()
        self.btn_quit.pack(side="left", padx=(0, 40))
        self.btn_valid.pack(side="right", padx=(40, 0))
        info.pack()
        buttons.pack(side="bottom")
        self.pack()
        self.update_needed = False

    def info(self, text):
        """ dislay informations """
        self.info_label.config(text=text)

    def save(self):
        """ save the GEDCOM file """
        filename = filedialog.asksaveasfilename(
            title=_("Save as"),
            defaultextension=".ged",
            filetypes=(("GEDCOM", ".ged"), (_("All files"), "*.*")),
        )
        if not filename:
            return
        with open(filename, "w", encoding="utf-8") as file:
            self.tree.print(file)

    def login(self):
        """ log in FamilySearch """
        global _
        username = self.sign_in.username.get()
        password = self.sign_in.password.get()
        if not (username and password):
            messagebox.showinfo(message=_(
                "Please enter your FamilySearch username and password."))
            return
        self.btn_valid.config(state="disabled")
        self.info(_("Login to FamilySearch..."))
        self.logfile = open("download.log", "w", encoding="utf-8")
        self.fs = Session(
            self.sign_in.username.get(),
            self.sign_in.password.get(),
            verbose=True,
            logfile=self.logfile,
            timeout=1,
        )
        if not self.fs.logged:
            messagebox.showinfo(
                _("Error"),
                message=_("The username or password was incorrect"))
            self.btn_valid.config(state="normal")
            self.info("")
            return
        self.tree = Tree(self.fs)
        _ = self.fs._
        self.title.config(text=_("Options"))
        cache.delete("lang")
        cache.add("lang", self.fs.lang)
        cache.delete("username")
        cache.add("username", username)
        url = "/service/tree/tree-data/reservations/person/%s/ordinances" % self.fs.fid
        lds_account = self.fs.get_url(url, {}).get("status") == "OK"
        self.options = Options(self.form, lds_account)
        self.info("")
        self.sign_in.destroy()
        self.options.pack()
        self.master.change_lang()
        self.btn_valid.config(command=self.command_in_thread(self.download),
                              state="normal",
                              text=_("Download"))
        self.options.start_indis.add_indi(self.fs.fid)
        self.update_needed = False

    def quit(self):
        """ prevent exception during download """
        self.update_needed = False
        if self.logfile:
            self.logfile.close()
        super(Download, self).quit()
        os._exit(1)

    def download(self):
        """ download family tree """
        todo = [
            self.options.start_indis.indis[key]
            for key in sorted(self.options.start_indis.indis)
        ]
        for fid in todo:
            if not re.match(r"[A-Z0-9]{4}-[A-Z0-9]{3}", fid):
                messagebox.showinfo(_("Error"),
                                    message=_("Invalid FamilySearch ID: ") +
                                    fid)
                return
        self.start_time = time.time()
        self.options.destroy()
        self.form.destroy()
        self.title.config(text="FamilySearch to GEDCOM")
        self.btn_valid.config(state="disabled")
        self.info(_("Downloading starting individuals..."))
        self.info_tree = True
        self.tree.add_indis(todo)
        todo = set(todo)
        done = set()
        for i in range(self.options.ancestors.get()):
            if not todo:
                break
            done |= todo
            self.info(
                _("Downloading %s. of generations of ancestors...") % (i + 1))
            todo = self.tree.add_parents(todo) - done

        todo = set(self.tree.indi.keys())
        done = set()
        for i in range(self.options.descendants.get()):
            if not todo:
                break
            done |= todo
            self.info(
                _("Downloading %s. of generations of descendants...") %
                (i + 1))
            todo = self.tree.add_children(todo) - done

        if self.options.spouses.get():
            self.info(_("Downloading spouses and marriage information..."))
            todo = set(self.tree.indi.keys())
            self.tree.add_spouses(todo)
        ordi = self.options.ordinances.get()
        cont = self.options.contributors.get()

        async def download_stuff(loop):
            futures = set()
            for fid, indi in self.tree.indi.items():
                futures.add(loop.run_in_executor(None, indi.get_notes))
                if ordi:
                    futures.add(
                        loop.run_in_executor(None, self.tree.add_ordinances,
                                             fid))
                if cont:
                    futures.add(
                        loop.run_in_executor(None, indi.get_contributors))
            for fam in self.tree.fam.values():
                futures.add(loop.run_in_executor(None, fam.get_notes))
                if cont:
                    futures.add(
                        loop.run_in_executor(None, fam.get_contributors))
            for future in futures:
                await future

        loop = asyncio.get_event_loop()
        self.info(
            _("Downloading notes") + ((("," if cont else _(" and")) +
                                       _(" ordinances")) if ordi else "") +
            (_(" and contributors") if cont else "") + "...")
        loop.run_until_complete(download_stuff(loop))

        self.tree.reset_num()
        self.btn_valid.config(command=self.save,
                              state="normal",
                              text=_("Save"))
        self.info(text=_("Success ! Click below to save your GEDCOM file"))
        self.update_info_tree()
        self.update_needed = False

    def command_in_thread(self, func):
        """ command to update widget in a new Thread """
        def res():
            self.update_needed = True
            Thread(target=self.update_gui).start()
            Thread(target=func).start()

        return res

    def update_info_tree(self):
        """ update informations """
        if self.info_tree and self.start_time and self.tree:
            self.info_indis.config(text=_("Individuals: %s") %
                                   len(self.tree.indi))
            self.info_fams.config(text=_("Families: %s") % len(self.tree.fam))
            self.info_sources.config(text=_("Sources: %s") %
                                     len(self.tree.sources))
            self.info_notes.config(text=_("Notes: %s") % len(self.tree.notes))
            t = round(time.time() - self.start_time)
            minutes = t // 60
            seconds = t % 60
            self.time.config(text=_("Elapsed time: %s:%s") %
                             (minutes, str(seconds).zfill(2)))

    def update_gui(self):
        """ update widget """
        while self.update_needed:
            self.update_info_tree()
            self.master.update()
            time.sleep(0.1)
        parser.add_argument('-i', metavar='<FILE>', nargs='+', type=argparse.FileType('r', encoding='UTF-8'), default=sys.stdin, help='input GEDCOM files [stdin]')
        parser.add_argument('-o', metavar='<FILE>', nargs='?', type=argparse.FileType('w', encoding='UTF-8'), default=sys.stdout, help='output GEDCOM files [stdout]')
    except TypeError:
        sys.stderr.write('Python >= 3.4 is required to run this script\n')
        sys.stderr.write('(see https://docs.python.org/3/whatsnew/3.4.html#argparse)\n')
        exit(2)

    # extract arguments from the command line
    try:
        parser.error = parser.exit
        args = parser.parse_args()
    except SystemExit:
        parser.print_help()
        exit(2)

    tree = Tree()

    indi_counter = 0
    fam_counter = 0
    note_counter = 0
    temp_note = None

    # read the GEDCOM data
    for file in args.i:
        ged = Gedcom(file, tree)

        # add informations about individuals
        for num in ged.indi:
            fid = ged.indi[num].fid
            if fid not in tree.indi:
                indi_counter += 1