コード例 #1
0
    def __init__(self):
        self.dirs = dirs

        user_config = configuration.Config(self.dirs.config_file)
        # Apply defaults where no custom values have been set
        for key, value in default_config.items():
            if key not in user_config:
                user_config[key] = value
        self.config = user_config
        self.config.save_state()

        logging.info("Running in portable mode: %s" % self.dirs.portable)

        self.month = None
        self.date = None
        self.months = {}

        # The dir name is the title
        self.title = ""

        # show instructions at first start
        self.is_first_start = self.config.read("firstStart")
        self.config["firstStart"] = 0
        logging.info("First Start: %s" % bool(self.is_first_start))

        logging.info("RedNotebook version: %s" % info.version)
        logging.info(filesystem.get_platform_info())

        self.actual_date = self.get_start_date()

        # Let components check if the MainWindow has been created
        self.frame = None
        self.frame = MainWindow(self)

        journal_path = self.get_journal_path()
        if not self.dirs.is_valid_journal_path(journal_path):
            logging.error(
                "Invalid directory: %s. Using default journal." % journal_path
            )
            self.show_message(
                _("You cannot use this directory for your journal:")
                + " %s" % journal_path
                + ". "
                + _("Opening default journal."),
                error=True,
            )
            journal_path = self.dirs.default_data_dir
        self.open_journal(journal_path)

        self.archiver = backup.Archiver(self)
        GObject.idle_add(self.archiver.check_last_backup_date)

        # Check for a new version
        if self.config.read("checkForNewVersion") == 1:
            utils.check_new_version(self, info.version, startup=True)

        # Automatically save the content after a period of time
        GObject.timeout_add_seconds(600, self.save_to_disk)
コード例 #2
0
ファイル: journal.py プロジェクト: davidbrenner/rednotebook-1
    def __init__(self):
        self.dirs = dirs

        user_config = configuration.Config(self.dirs.config_file)
        # Apply defaults where no custom values have been set
        for key, value in default_config.items():
            if key not in user_config:
                user_config[key] = value
        self.config = user_config
        self.config.save_state()

        logging.info('Running in portable mode: %s' % self.dirs.portable)

        self.month = None
        self.date = None
        self.months = {}

        # The dir name is the title
        self.title = ''

        # show instructions at first start
        self.is_first_start = self.config.read('firstStart')
        self.config['firstStart'] = 0
        logging.info('First Start: %s' % bool(self.is_first_start))

        logging.info('RedNotebook version: %s' % info.version)
        logging.info(filesystem.get_platform_info())

        utils.set_environment_variables(self.config)

        self.actual_date = self.get_start_date()

        # Let components check if the MainWindow has been created
        self.frame = None
        self.frame = MainWindow(self)

        journal_path = self.get_journal_path()
        if not self.dirs.is_valid_journal_path(journal_path):
            logging.error('Invalid directory: %s. Using default journal.' % journal_path)
            self.show_message(_('You cannot use this directory for your journal:') +
                              ' %s' % journal_path + '. ' + _('Opening default journal.'),
                              error=True)
            journal_path = self.dirs.default_data_dir
        self.open_journal(journal_path)

        self.archiver = backup.Archiver(self)
        # TODO: Enable backup check.
        # self.archiver.check_last_backup_date()

        # Check for a new version
        if self.config.read('checkForNewVersion') == 1:
            utils.check_new_version(self, info.version, startup=True)

        # Automatically save the content after a period of time
        gobject.timeout_add_seconds(600, self.save_to_disk)
コード例 #3
0
ファイル: journal.py プロジェクト: rakotomandimby/rednotebook
class Journal:
    def __init__(self):
        self.dirs = dirs

        user_config = configuration.Config(self.dirs.config_file)
        # Apply defaults where no custom values have been set
        for key, value in default_config.items():
            if key not in user_config:
                user_config[key] = value
        self.config = user_config
        self.config.save_state()

        logging.info('Running in portable mode: %s' % self.dirs.portable)

        self.month = None
        self.date = None
        self.months = {}

        self.search_index = index.Index()

        # The dir name is the title
        self.title = ''

        # show instructions at first start
        self.is_first_start = self.config.read('firstStart')
        self.config['firstStart'] = 0
        logging.info('First Start: %s' % bool(self.is_first_start))

        logging.info('RedNotebook version: %s' % info.version)
        logging.info(filesystem.get_platform_info())

        self.actual_date = self.get_start_date()

        # Let components check if the MainWindow has been created
        self.frame = None
        self.frame = MainWindow(self)

        journal_path = self.get_journal_path()
        if not self.dirs.is_valid_journal_path(journal_path):
            logging.error('Invalid directory: %s. Using default journal.' %
                          journal_path)
            self.show_message(
                _('You cannot use this directory for your journal:') +
                ' %s' % journal_path + '. ' + _('Opening default journal.'),
                error=True)
            journal_path = self.dirs.default_data_dir
        self.open_journal(journal_path)

        self.archiver = backup.Archiver(self)
        GObject.idle_add(self.archiver.check_last_backup_date)

        # Check for a new version
        if self.config.read('checkForNewVersion') == 1:
            utils.check_new_version(self, info.version, startup=True)

        # Automatically save the content after a period of time
        GObject.timeout_add_seconds(600, self.save_to_disk)

    def get_journal_path(self):
        '''
        Retrieve the path from optional args or return standard value if args
        not present
        '''
        if not args.journal:
            data_dir = self.config.read('dataDir', self.dirs.data_dir)
            if not os.path.isabs(data_dir):
                data_dir = os.path.join(self.dirs.app_dir, data_dir)
                data_dir = os.path.normpath(data_dir)
            return data_dir

        # path_arg can be e.g. data (under .rednotebook), data (elsewhere),
        # or an absolute path /home/username/myjournal
        # Try to find the journal under the standard location or at the given
        # absolute or relative location
        path_arg = args.journal

        logging.debug('Trying to find journal "%s"' % path_arg)

        paths_to_check = [
            path_arg,
            os.path.join(self.dirs.journal_user_dir, path_arg)
        ]

        for path in paths_to_check:
            if os.path.exists(path):
                if os.path.isdir(path):
                    return path
                else:
                    logging.warning('To open a journal you must specify a '
                                    'directory, not a file.')

        logging.error('The path "%s" is not a valid journal directory. '
                      'Execute "rednotebook -h" for instructions' % path_arg)
        sys.exit(2)

    def get_start_date(self):
        '''
        Retrieve the date from optional args or otherwise return 'today'
        '''
        if not args.start_date:
            return datetime.date.today()

        try:
            return dates.get_date_from_date_string(args.start_date)
        except ValueError:
            logging.error('Invalid date: %s (required format: YYYY-MM-DD).' %
                          args.start_date)
            sys.exit(2)

    def exit(self):
        self.frame.add_values_to_config()

        # Make it possible to stop the program from exiting
        # e.g. if the journal could not be saved
        self.is_allowed_to_exit = True
        self.save_to_disk(exit_imminent=True)

        if self.is_allowed_to_exit:
            logging.info('Goodbye!')
            # Informs the logging system to perform an orderly shutdown by
            # flushing and closing all handlers.
            logging.shutdown()
            Gtk.main_quit()

    def convert(self, text, target, headers=None, options=None):
        options = options or {}
        options['font'] = self.config.read('previewFont')
        return markup.convert(text,
                              target,
                              self.dirs.data_dir,
                              headers=headers,
                              options=options)

    def save_to_disk(self,
                     exit_imminent=False,
                     changing_journal=False,
                     saveas=False):
        self.save_old_day()

        try:
            filesystem.make_directory(self.dirs.data_dir)
        except (OSError, IOError) as err:
            logging.error('Creating journal directory failed: {}'.format(err))
            self.frame.show_save_error_dialog(exit_imminent)
            return True

        try:
            something_saved = storage.save_months_to_disk(
                self.months, self.dirs.data_dir, exit_imminent, saveas)
        except (IOError, OSError) as err:
            logging.error('Saving month files failed: {}'.format(err))
            self.frame.show_save_error_dialog(exit_imminent)
            something_saved = None

        if something_saved:
            self.show_message(_('The content has been saved to %s') %
                              self.dirs.data_dir,
                              error=False)
            logging.info('The content has been saved to %r' %
                         self.dirs.data_dir)
        elif something_saved is None:
            # Don't display this as an error, because we already show a dialog.
            self.show_message(_('The journal could not be saved'), error=False)
        else:
            self.show_message(_('Nothing to save'), error=False)

        self.config.save_to_disk()

        if not (exit_imminent or changing_journal) and something_saved:
            # Update cloud
            self.frame.cloud.update(force_update=True)

        # tell gobject to keep saving the content in regular intervals
        return True

    def open_journal(self, data_dir):
        if not os.path.exists(data_dir):
            logging.warning(
                'The dir %s does not exist. Select a different dir.' %
                data_dir)
            return

        if self.months:
            self.save_to_disk(changing_journal=True)

        logging.info('Opening journal at %r' % data_dir)
        self.dirs.data_dir = data_dir

        self.month = None
        self.months.clear()
        self.frame.search_box.clear()
        self.search_index.clear()

        self.months = storage.load_all_months_from_disk(data_dir)

        # Nothing to save before first day change
        self.load_day(self.actual_date)

        if self.is_first_start and not os.listdir(data_dir) and not self.days:
            self.add_instruction_content()

        # We can't use self.days here since it uses self.save_old_day.
        for month in self.months.values():
            for day in month.days.values():
                self.search_index.add(day.date, day.get_indexed_words())

        self.stats = Statistics(self)

        self.frame.cloud.update(force_update=True)

        self.frame.categories_tree_view.categories = self.categories
        # Add auto-completion for tag search
        self.frame.search_box.set_entries(
            ['#%s' % data.escape_tag(tag) for tag in self.categories])

        self.title = filesystem.get_journal_title(data_dir)

        # Set frame title
        self.set_frame_title()

        # Save the folder for next start
        if not self.dirs.portable:
            self.config['dataDir'] = data_dir
        else:
            rel_data_dir = filesystem.get_relative_path(
                self.dirs.app_dir, data_dir)
            self.config['dataDir'] = rel_data_dir

    def set_frame_title(self):
        parts = ['RedNotebook']
        if self.title != 'data':
            parts.append(self.title)
        parts.append(dates.format_date('%x', self.date))
        self.frame.main_frame.set_title(' - '.join(parts))

    def get_month(self, date):
        '''
        Returns the corresponding month if it has previously been visited,
        otherwise a new month is created and returned
        '''

        year_and_month = dates.get_year_and_month_from_date(date)

        # Selected month has not been loaded or created yet
        if year_and_month not in self.months:
            self.months[year_and_month] = Month(date.year, date.month)

        return self.months[year_and_month]

    def get_day(self, date):
        return self.get_month(date).get_day(date.day)

    def save_old_day(self):
        '''Order is important'''
        self.search_index.remove(self.day.date, self.day.get_indexed_words())
        old_content = self.day.content
        new_content = self.frame.categories_tree_view.get_day_content()
        new_content['text'] = self.frame.get_day_text()
        self.day.content = new_content
        self.search_index.add(self.day.date, self.day.get_indexed_words())

        content_changed = (old_content != new_content)
        if content_changed:
            self.month.edited = True

        self.frame.calendar.set_day_edited(self.date.day, not self.day.empty)

    def load_day(self, new_date):
        old_date = self.date
        self.date = new_date

        if not dates.same_month(new_date, old_date) or self.month is None:
            self.month = self.get_month(self.date)

        self.frame.set_date(self.month, self.date, self.day)

        self.set_frame_title()

    def merge_days(self, days):
        '''
        Method used by importers
        '''
        self.save_old_day()
        for new_day in days:
            date = new_day.date
            month = self.get_month(date)
            old_day = month.get_day(date.day)
            old_day.merge(new_day)
            month.edited = True

    @property
    def day(self):
        return self.month.get_day(self.date.day)

    def change_date(self, new_date):
        if new_date < datetime.date(1900, 1, 1):
            self.show_message('Only dates after 1900 are supported.',
                              title='Too Early',
                              error=True)
            return

        if new_date == self.date:
            return

        self.save_old_day()
        self.load_day(new_date)

    def go_to_next_day(self):
        next_date = self.date + dates.one_day
        following_edited_days = self.get_days_in_date_range(
            start_date=next_date)
        if following_edited_days:
            next_date = following_edited_days[0].date
        self.change_date(next_date)

    def go_to_prev_day(self):
        prev_date = self.date - dates.one_day
        previous_edited_days = self.get_days_in_date_range(end_date=prev_date)
        if previous_edited_days:
            prev_date = previous_edited_days[-1].date
        self.change_date(prev_date)

    def show_message(self, msg, title=None, error=False):
        if error and not title:
            title = _('Error')

        if error:
            msg_type = Gtk.MessageType.ERROR
            log_level = logging.ERROR
        else:
            msg_type = Gtk.MessageType.INFO
            log_level = logging.INFO

        self.frame.show_message(title, msg, msg_type)
        logging.log(log_level, '%s. %s' % (title, msg) if title else msg)

    @property
    def categories(self):
        return list(
            sorted(set(
                itertools.chain.from_iterable(day.categories
                                              for day in self.days)),
                   key=locale.strxfrm))

    def get_entries(self, category):
        entries = set()
        for day in self.days:
            entries |= set(day.get_entries(category))
        return sorted(entries)

    def search(self, text, tags):
        results = []
        # TODO: Allow using index with a configuration option?
        use_index = False
        if use_index:
            words = data.get_indexed_words(text)
            words.extend('#{}'.format(tag) for tag in tags)

            if not words:
                return []

            dates = self.search_index.find(words[0])
            for word in words[1:]:
                dates &= self.search_index.find(word)

            for date in sorted(dates, reverse=True):
                for word in words:
                    results.append(self.get_day(date).search(word, tags))
        else:
            days = self.get_days_with_tags(tags)
            for day in reversed(days):
                results.append(day.search(text, tags))
        return results

    def get_days_with_tags(self, tags):
        if not tags:
            return self.days
        days = []
        for day in self.days:
            day_tags = set(data.escape_tag(tag) for tag in day.categories)
            if all(tag in day_tags for tag in tags):
                days.append(day)
        return days

    def get_word_count_dict(self):
        """
        Return a dictionary mapping the words to their number of appearance.
        """
        word_dict = defaultdict(int)
        for day in self.days:
            words = day.get_words()
            for word in words:
                word_dict[word.lower()] += 1
        return word_dict

    @property
    def days(self):
        '''
        Returns all edited days ordered by their date
        '''
        # The day being edited counts too
        if self.frame:
            self.save_old_day()

        days = []
        for month in self.months.values():
            # Filter out days without content.
            days_in_month = [
                day for day in month.days.values() if not day.empty
            ]
            days.extend(days_in_month)

        # Sort days
        days = sorted(days, key=lambda day: day.date)
        return days

    def get_days_in_date_range(self, start_date=None, end_date=None):
        if not start_date:
            start_date = datetime.date.min
        if not end_date:
            end_date = datetime.date.max

        start_date, end_date = sorted([start_date, end_date])
        assert start_date <= end_date

        days_in_date_range = []
        for day in self.days:
            if day.date < start_date:
                continue
            elif start_date <= day.date <= end_date:
                days_in_date_range.append(day)
            elif day.date > end_date:
                break
        return days_in_date_range

    def add_instruction_content(self):
        self.change_date(datetime.date.today())
        current_date = self.date

        logging.info('Adding example content on %s' % current_date)

        for example_day in info.example_content:
            self.day.content = example_day
            self.frame.set_date(self.month, self.date, self.day)
            self.search_index.add(self.day.date, self.day.get_indexed_words())
            self.go_to_next_day()

        self.change_date(current_date)
コード例 #4
0
class Journal:
    def __init__(self):
        self.dirs = dirs

        user_config = configuration.Config(self.dirs.config_file)
        # Apply defaults where no custom values have been set
        for key, value in default_config.items():
            if key not in user_config:
                user_config[key] = value
        self.config = user_config
        self.config.save_state()

        logging.info('Running in portable mode: %s' % self.dirs.portable)

        self.month = None
        self.date = None
        self.months = {}

        # The dir name is the title
        self.title = ''

        # show instructions at first start
        self.is_first_start = self.config.read('firstStart', 1)
        self.config['firstStart'] = 0
        logging.info('First Start: %s' % bool(self.is_first_start))

        logging.info('RedNotebook version: %s' % info.version)
        logging.info(filesystem.get_platform_info())

        utils.set_environment_variables(self.config)

        self.actual_date = self.get_start_date()

        # Let components check if the MainWindow has been created
        self.frame = None
        self.frame = MainWindow(self)

        journal_path = self.get_journal_path()
        if not self.dirs.is_valid_journal_path(journal_path):
            logging.error('Invalid directory: %s. Using default journal.' % journal_path)
            self.show_message(_('You cannot use this directory for your journal:') +
                              ' %s' % journal_path + '. ' + _('Opening default journal.'),
                              error=True)
            journal_path = self.dirs.default_data_dir
        self.open_journal(journal_path)

        self.archiver = backup.Archiver(self)
        #self.archiver.check_last_backup_date()

        # Check for a new version
        if self.config.read('checkForNewVersion', 0) == 1:
            utils.check_new_version(self, info.version, startup=True)

        # Automatically save the content after a period of time
        gobject.timeout_add_seconds(600, self.save_to_disk)


    def get_journal_path(self):
        '''
        Retrieve the path from optional args or return standard value if args
        not present
        '''
        if not args.journal:
            data_dir = self.config.read('dataDir', self.dirs.data_dir)
            if not os.path.isabs(data_dir):
                data_dir = os.path.join(self.dirs.app_dir, data_dir)
                data_dir = os.path.normpath(data_dir)
            return data_dir

        # path_arg can be e.g. data (under .rednotebook), data (elsewhere),
        # or an absolute path /home/username/myjournal
        # Try to find the journal under the standard location or at the given
        # absolute or relative location
        path_arg = args.journal

        logging.debug('Trying to find journal "%s"' % path_arg)

        paths_to_check = [path_arg, os.path.join(self.dirs.journal_user_dir, path_arg)]

        for path in paths_to_check:
            if os.path.exists(path):
                if os.path.isdir(path):
                    return path
                else:
                    logging.warning('To open a journal you must specify a '
                                    'directory, not a file.')

        logging.error('The path "%s" is not a valid journal directory. '
                      'Execute "rednotebook -h" for instructions' % path_arg)
        sys.exit(1)

    def get_start_date(self):
        '''
        Retrieve the date from optional args or otherwise return 'today'
        '''
        if not args.start_date:
            return datetime.date.today()

        try:
            return dates.get_date_from_date_string(args.start_date)
        except ValueError:
            logging.error('Invalid date: %s (required format: YYYY-MM-DD).' % args.start_date)
            sys.exit(2)

    def exit(self):
        self.frame.add_values_to_config()

        # Make it possible to stop the program from exiting
        # e.g. if the journal could not be saved
        self.is_allowed_to_exit = True
        self.save_to_disk(exit_imminent=True)

        if self.is_allowed_to_exit:
            logging.info('Goodbye!')
            # Informs the logging system to perform an orderly shutdown by
            # flushing and closing all handlers.
            logging.shutdown()
            gtk.main_quit()

    def convert(self, text, target, headers=None, options=None):
        options = options or {}
        options['font'] = self.config.read('previewFont', 'Ubuntu, sans-serif')
        return markup.convert(text, target, self.dirs.data_dir, headers=headers, options=options)

    def save_to_disk(self, exit_imminent=False, changing_journal=False, saveas=False):
        self.save_old_day()

        try:
            filesystem.make_directory(self.dirs.data_dir)
        except (OSError, IOError):
            self.frame.show_save_error_dialog(exit_imminent)
            return True

        try:
            something_saved = storage.save_months_to_disk(
                self.months, self.dirs.data_dir, exit_imminent, saveas)
        except (IOError, OSError):
            self.frame.show_save_error_dialog(exit_imminent)
            something_saved = None

        if something_saved:
            self.show_message(_('The content has been saved to %s') % self.dirs.data_dir, error=False)
            logging.info('The content has been saved to %r' % self.dirs.data_dir)
        elif something_saved is None:
            # Don't display this as an error, because we already show a dialog.
            self.show_message(_('The journal could not be saved'), error=False)
        else:
            self.show_message(_('Nothing to save'), error=False)

        self.config.save_to_disk()

        if not (exit_imminent or changing_journal) and something_saved:
            # Update cloud
            self.frame.cloud.update(force_update=True)

        # tell gobject to keep saving the content in regular intervals
        return True


    def open_journal(self, data_dir):
        if not os.path.exists(data_dir):
            logging.warning('The dir %s does not exist. Select a different dir.'
                            % data_dir)
            return

        if self.months:
            self.save_to_disk(changing_journal=True)

        logging.info('Opening journal at %r' % data_dir)
        self.dirs.data_dir = data_dir

        self.month = None
        self.months.clear()

        self.months = storage.load_all_months_from_disk(data_dir)

        # Nothing to save before first day change
        self.load_day(self.actual_date)

        self.stats = Statistics(self)

        if self.is_first_start and not os.listdir(data_dir) and len(self.days) == 0:
            self.add_instruction_content()

        self.frame.cloud.update(force_update=True)

        # Reset Search
        self.frame.search_box.clear()

        self.frame.categories_tree_view.categories = self.categories
        # Add auto-completion for tag search
        self.frame.search_box.set_entries([u'#%s' % self.normalize_tag(tag)
                                           for tag in self.categories])

        self.title = filesystem.get_journal_title(data_dir)

        # Set frame title
        self.set_frame_title()

        # Save the folder for next start
        if not self.dirs.portable:
            self.config['dataDir'] = data_dir
        else:
            rel_data_dir = filesystem.get_relative_path(self.dirs.app_dir, data_dir)
            self.config['dataDir'] = rel_data_dir

    def set_frame_title(self):
        parts = ['RedNotebook']
        if self.title != 'data':
            parts.append(self.title)
        parts.append(dates.format_date('%x', self.date))
        self.frame.main_frame.set_title(' - '.join(parts))

    def get_month(self, date):
        '''
        Returns the corresponding month if it has previously been visited,
        otherwise a new month is created and returned
        '''

        year_and_month = dates.get_year_and_month_from_date(date)

        # Selected month has not been loaded or created yet
        if year_and_month not in self.months:
            self.months[year_and_month] = Month(date.year, date.month)

        return self.months[year_and_month]


    def save_old_day(self):
        '''Order is important'''
        old_content = self.day.content
        self.day.content = self.frame.categories_tree_view.get_day_content()
        self.day.text = self.frame.get_day_text()

        content_changed = (old_content != self.day.content)
        if content_changed:
            self.month.edited = True

        self.frame.calendar.set_day_edited(self.date.day, not self.day.empty)


    def load_day(self, new_date):
        old_date = self.date
        self.date = new_date

        if not Month.same_month(new_date, old_date) or self.month is None:
            self.month = self.get_month(self.date)

        self.frame.set_date(self.month, self.date, self.day)

        self.set_frame_title()


    def merge_days(self, days):
        '''
        Method used by importers
        '''
        self.save_old_day()
        for new_day in days:
            date = new_day.date
            month = self.get_month(date)
            old_day = month.get_day(date.day)
            old_day.merge(new_day)
            month.edited = True


    @property
    def day(self):
        return self.month.get_day(self.date.day)


    def change_date(self, new_date):
        if new_date < datetime.date(1900, 1, 1):
            self.show_message('Only dates after 1900 are supported.',
                              title='Too Early', error=True)
            return

        if new_date == self.date:
            return

        self.save_old_day()
        self.load_day(new_date)


    def go_to_next_day(self):
        next_date = self.date + dates.one_day
        following_edited_days = self.get_days_in_date_range(start_date=next_date)
        if following_edited_days:
            next_date = following_edited_days[0].date
        self.change_date(next_date)


    def go_to_prev_day(self):
        prev_date = self.date - dates.one_day
        previous_edited_days = self.get_days_in_date_range(end_date=prev_date)
        if previous_edited_days:
            prev_date = previous_edited_days[-1].date
        self.change_date(prev_date)


    def show_message(self, msg, title=None, error=False):
        if error and not title:
            title = _('Error')

        if error:
            msg_type = gtk.MESSAGE_ERROR
            log_level = logging.ERROR
        else:
            msg_type = gtk.MESSAGE_INFO
            log_level = logging.INFO

        self.frame.show_message(title, msg, msg_type)
        logging.log(log_level, '%s. %s' % (title, msg) if title else msg)


    @property
    def categories(self):
        return list(sorted(set(itertools.chain.from_iterable(
            day.categories for day in self.days)), cmp=locale.strcoll))


    def normalize_tag(self, tag):
        return tag.replace(' ', '_').lower()


    def get_entries(self, category):
        entries = set()
        for day in self.days:
            entries |= set(day.get_entries(category))
        return sorted(entries)


    def search(self, text, tags):
        days = self.get_days_with_tags(tags)
        results = []
        for day in reversed(days):
            results.append(day.search(text, tags))
        return results


    def get_days_with_tags(self, tags):
        if not tags:
            return self.days
        days = []
        for day in self.days:
            day_tags = set(data.escape_tag(tag) for tag in day.categories)
            if all(tag in day_tags for tag in tags):
                days.append(day)
        return days


    def get_word_count_dict(self):
        '''
        Returns a dictionary mapping the words to their number of appearance
        '''
        # TODO: Use collections.Counter in Python2.7
        # TODO: Check if concatenating all text and using a regex is faster.
        word_dict = defaultdict(int)
        for day in self.days:
            words = day.get_words()
            for word in words:
                word_dict[word.lower()] += 1
        return word_dict


    @property
    def days(self):
        '''
        Returns all edited days ordered by their date
        '''
        # The day being edited counts too
        if self.frame:
            self.save_old_day()

        days = []
        for month in self.months.values():
            days_in_month = month.days.values()

            # Filter out days without content
            days_in_month = [day for day in days_in_month if not day.empty]
            days.extend(days_in_month)

        # Sort days
        days = sorted(days, key=lambda day: day.date)
        return days


    def get_days_in_date_range(self, start_date=None, end_date=None):
        if not start_date:
            start_date = datetime.date.min
        if not end_date:
            end_date = datetime.date.max

        start_date, end_date = sorted([start_date, end_date])
        assert start_date <= end_date

        days_in_date_range = []
        for day in self.days:
            if day.date < start_date:
                continue
            elif start_date <= day.date <= end_date:
                days_in_date_range.append(day)
            elif day.date > end_date:
                break
        return days_in_date_range


    def add_instruction_content(self):
        self.change_date(datetime.date.today())
        current_date = self.date

        logging.info('Adding example content on %s' % current_date)

        for example_day in info.example_content:
            self.day.content = example_day
            self.frame.set_date(self.month, self.date, self.day)
            self.go_to_next_day()

        self.change_date(current_date)
コード例 #5
0
ファイル: journal.py プロジェクト: ipeikon/LaBook
    def __init__(self):
        self.dirs = dirs

        user_config = configuration.Config(self.dirs.config_file)
        # Apply defaults where no custom values have been set
        for key, value in default_config.items():
            if key not in user_config:
                user_config[key] = value
        self.config = user_config
        self.config.save_state()

        self.warn_if_second_instance()

        logging.info('Running in portable mode: %s' % self.dirs.portable)

        self.testing = False
        if options.debug:
            self.testing = True
            logging.debug('Debug Mode is on')

        # Allow starting minimized to tray
        # When we start minimized we have to set the tray icon visible
        self.start_minimized = options.minimized
        if self.start_minimized:
            self.config['closeToTray'] = 1

        self.month = None
        self.date = None
        self.months = {}

        # The dir name is the title
        self.title = ''

        # show instructions at first start
        self.is_first_start = self.config.read('firstStart', 1)
        self.config['firstStart'] = 0
        logging.info('First Start: %s' % bool(self.is_first_start))

        logging.info('RedNotebook version: %s' % info.version)
        logging.info(filesystem.get_platform_info())

        utils.set_environment_variables(self.config)

        self.actual_date = datetime.date.today()

        # Let components check if the MainWindow has been created
        self.frame = None
        self.frame = MainWindow(self)

        self.open_journal(self.get_journal_path())

        self.archiver = backup.Archiver(self)
        #self.archiver.check_last_backup_date()

        # Check for a new version
        if self.config.read('checkForNewVersion', 0) == 1:
            utils.check_new_version(self, info.version, startup=True)

        # Automatically save the content after a period of time
        if not self.testing:
            gobject.timeout_add_seconds(600, self.save_to_disk)
コード例 #6
0
ファイル: journal.py プロジェクト: ipeikon/LaBook
class Journal:
    def __init__(self):
        self.dirs = dirs

        user_config = configuration.Config(self.dirs.config_file)
        # Apply defaults where no custom values have been set
        for key, value in default_config.items():
            if key not in user_config:
                user_config[key] = value
        self.config = user_config
        self.config.save_state()

        self.warn_if_second_instance()

        logging.info('Running in portable mode: %s' % self.dirs.portable)

        self.testing = False
        if options.debug:
            self.testing = True
            logging.debug('Debug Mode is on')

        # Allow starting minimized to tray
        # When we start minimized we have to set the tray icon visible
        self.start_minimized = options.minimized
        if self.start_minimized:
            self.config['closeToTray'] = 1

        self.month = None
        self.date = None
        self.months = {}

        # The dir name is the title
        self.title = ''

        # show instructions at first start
        self.is_first_start = self.config.read('firstStart', 1)
        self.config['firstStart'] = 0
        logging.info('First Start: %s' % bool(self.is_first_start))

        logging.info('RedNotebook version: %s' % info.version)
        logging.info(filesystem.get_platform_info())

        utils.set_environment_variables(self.config)

        self.actual_date = datetime.date.today()

        # Let components check if the MainWindow has been created
        self.frame = None
        self.frame = MainWindow(self)

        self.open_journal(self.get_journal_path())

        self.archiver = backup.Archiver(self)
        #self.archiver.check_last_backup_date()

        # Check for a new version
        if self.config.read('checkForNewVersion', 0) == 1:
            utils.check_new_version(self, info.version, startup=True)

        # Automatically save the content after a period of time
        if not self.testing:
            gobject.timeout_add_seconds(600, self.save_to_disk)

    def warn_if_second_instance(self):
        '''Show warning when a second instance is started'''
        running = self.config.read('running', 0)
        if running:
            logging.warning('RedNotebook instance already running')
            text1 = _('RedNotebook appears to be already running.')
            text2 = _('This can happen if the application is hidden in the '
                      'system tray or was not shut down correctly.')
            text3 = _('Starting a second RedNotebook instance can lead to data '
                      'loss and it might be best to see if there is another '
                      'instance first.')
            text4 = _('Do you want to start a new RedNotebook instance nevertheless?')
            dialog = gtk.MessageDialog(type=gtk.MESSAGE_WARNING,
                                       buttons=gtk.BUTTONS_YES_NO, message_format=text1)
            dialog.format_secondary_text('%s %s\n\n%s' % (text2, text3, text4))
            answer = dialog.run()
            dialog.hide()
            if not answer == gtk.RESPONSE_YES:
                sys.exit()
        self.config['running'] = 1
        self.config.save_to_disk()


    def get_journal_path(self):
        '''
        Retrieve the path from optional args or return standard value if args
        not present
        '''
        if not args:
            data_dir = self.config.read('dataDir', self.dirs.data_dir)
            if not os.path.isabs(data_dir):
                data_dir = os.path.join(self.dirs.app_dir, data_dir)
                data_dir = os.path.normpath(data_dir)
            return data_dir

        # path_arg can be e.g. data (under .rednotebook), data (elsewhere),
        # or an absolute path /home/username/myjournal
        # Try to find the journal under the standard location or at the given
        # absolute or relative location
        path_arg = args[0]

        logging.debug('Trying to find journal "%s"' % path_arg)

        paths_to_check = [path_arg, os.path.join(self.dirs.journal_user_dir, path_arg)]

        for path in paths_to_check:
            if os.path.exists(path):
                if os.path.isdir(path):
                    return path
                else:
                    logging.warning('To open a journal you must specify a '
                                'directory, not a file.')

        logging.error('The path "%s" is no valid journal directory. '
                    'Execute "rednotebook -h" for instructions' % path_arg)
        sys.exit(1)


    def exit(self):
        self.frame.add_values_to_config()

        self.config['running'] = 0

        # Make it possible to stop the program from exiting
        # e.g. if the journal could not be saved
        self.is_allowed_to_exit = True
        self.save_to_disk(exit_imminent=True)

        if self.is_allowed_to_exit:
            logging.info('Goodbye!')
            gtk.main_quit()


    def save_to_disk(self, exit_imminent=False, changing_journal=False, saveas=False):
        #logging.info('Trying to save the journal')

        self.save_old_day()

        try:
            filesystem.make_directory(self.dirs.data_dir)
        except OSError:
            self.frame.show_save_error_dialog(exit_imminent)
            return True

        if not os.path.exists(self.dirs.data_dir):
            logging.error('Save path does not exist')
            self.frame.show_save_error_dialog(exit_imminent)
            return True

        something_saved = storage.save_months_to_disk(self.months,
            self.dirs.data_dir, self.frame, exit_imminent, changing_journal, saveas)

        if something_saved:
            self.show_message(_('The content has been saved to %s') % self.dirs.data_dir, error=False)
            logging.info('The content has been saved to %s' % self.dirs.data_dir)
        else:
            self.show_message(_('Nothing to save'), error=False)

        self.config.save_to_disk()

        if not (exit_imminent or changing_journal) and something_saved:
            # Update cloud
            self.frame.cloud.update(force_update=True)

        # tell gobject to keep saving the content in regular intervals
        return True


    def open_journal(self, data_dir, load_files=True):

        if self.months:
            self.save_to_disk(changing_journal=True)

        # Password Protection
        #password = self.config.read('password', '')

        logging.info('Opening journal at %s' % data_dir)

        if not os.path.exists(data_dir):
            logging.warning('The data dir %s does not exist. Select a different dir.'
                        % data_dir)

            self.frame.show_dir_chooser('open', dir_not_found=True)
            return

        data_dir_empty = not os.listdir(data_dir)

        if not load_files and not data_dir_empty:
            msg_part1 = _('The selected folder is not empty.')
            msg_part2 = _('To prevent you from overwriting data, the folder content has been imported into the new journal.')
            self.show_message('%s %s' % (msg_part1, msg_part2), error=False)
        elif load_files and data_dir_empty:
            self.show_message(_('The selected folder is empty. A new journal has been created.'),
                                error=False)

        self.dirs.data_dir = data_dir

        self.month = None
        self.months.clear()

        # We always want to load all files
        if load_files or True:
            self.months = storage.load_all_months_from_disk(data_dir)

        # Nothing to save before first day change
        self.load_day(self.actual_date)

        self.stats = Statistics(self)

        self.frame.categories_tree_view.categories = self.categories

        if self.is_first_start:
            self.add_instruction_content()

        # Notebook is only on page 1 here, if we are opening a journal the second time
        old_page = self.frame.search_notebook.get_current_page()
        new_page = self.config.read('cloudTabActive', 0)
        # 0 -> 0: search is cleared later
        # 0 -> 1: change to cloud, update automatically
        # 1 -> 0: change to search
        # 1 -> 1: update cloud

        # At tab change, cloud is updated automatically
        self.frame.search_notebook.set_current_page(new_page)
        if new_page == old_page:
            # Without tab change, force update
            self.frame.cloud.update(force_update=True)

        # Reset Search
        self.frame.search_box.clear()

        self.title = filesystem.get_journal_title(data_dir)

        # Set frame title
        if self.title == 'data':
            frame_title = 'RedNotebook'
        else:
            frame_title = 'RedNotebook - ' + self.title
        self.frame.main_frame.set_title(frame_title)

        # Save the folder for next start
        if not self.dirs.portable:
            self.config['dataDir'] = data_dir
        else:
            rel_data_dir = filesystem.get_relative_path(self.dirs.app_dir, data_dir)
            self.config['dataDir'] = rel_data_dir


    def get_month(self, date):
        '''
        Returns the corresponding month if it has previously been visited,
        otherwise a new month is created and returned
        '''

        year_and_month = dates.get_year_and_month_from_date(date)

        # Selected month has not been loaded or created yet
        if not year_and_month in self.months:
            self.months[year_and_month] = Month(date.year, date.month)

        return self.months[year_and_month]


    def save_old_day(self):
        '''Order is important'''
        old_content = self.day.content
        self.day.content = self.frame.categories_tree_view.get_day_content()
        self.day.text = self.frame.get_day_text()

        content_changed = not (old_content == self.day.content)
        if content_changed:
            self.month.edited = True

        self.frame.calendar.set_day_edited(self.date.day, not self.day.empty)


    def load_day(self, new_date):
        old_date = self.date
        self.date = new_date

        if not Month.same_month(new_date, old_date) or self.month is None:
            self.month = self.get_month(self.date)
            #self.month.visited = True

        self.frame.set_date(self.month, self.date, self.day)


    def merge_days(self, days):
        '''
        Method used by importers
        '''
        self.save_old_day()
        for new_day in days:
            date = new_day.date
            month = self.get_month(date)
            old_day = month.get_day(date.day)
            old_day.merge(new_day)
            month.edited = True


    @property
    def day(self):
        return self.month.get_day(self.date.day)


    def change_date(self, new_date):
        if new_date == self.date:
            return

        self.save_old_day()
        self.load_day(new_date)


    def go_to_next_day(self):
        next_date = self.date + dates.one_day
        following_edited_days = self.get_days_in_date_range(start_date=next_date)
        if following_edited_days:
            next_date = following_edited_days[0].date
        self.change_date(next_date)


    def go_to_prev_day(self):
        prev_date = self.date - dates.one_day
        previous_edited_days = self.get_days_in_date_range(end_date=prev_date)
        if previous_edited_days:
            prev_date = previous_edited_days[-1].date
        self.change_date(prev_date)


    def show_message(self, message_text, error=False, countdown=True):
        self.frame.statusbar.show_text(message_text, error, countdown)


    @property
    def categories(self):
        return list(sorted(set(itertools.chain.from_iterable(
                                        day.categories for day in self.days)),
                           key=utils.sort_asc))


    @property
    def tags(self):
        return self.get_entries('Tags')


    def get_entries(self, category):
        entries = set()
        for day in self.days:
            entries |= set(day.get_entries(category))
        return sorted(entries)


    def search(self, text=None, category=None, tag=None):
        results = []
        for day in reversed(self.days):
            result = None
            if text:
                result = day.search_text(text)
            elif category:
                result = day.search_category(category)
            elif tag:
                result = day.search_tag(tag)

            if result:
                if category:
                    results.extend(result)
                else:
                    results.append(result)

        return results


    @property
    def days(self):
        '''
        Returns all edited days ordered by their date
        '''
        # The day being edited counts too
        if self.frame:
            self.save_old_day()

        days = []
        for month in self.months.values():
            days_in_month = month.days.values()

            # Filter out days without content
            days_in_month = [day for day in days_in_month if not day.empty]
            days.extend(days_in_month)

        # Sort days
        days = sorted(days, key=lambda day: day.date)
        return days


    def get_word_count_dict(self, type):
        '''
        Returns a dictionary mapping the words to their number of appearance
        '''
        word_dict = collections.defaultdict(int)
        for day in self.days:
            if type == 'word':
                words = day.get_words()
            if type == 'category':
                words = day.categories
            if type == 'tag':
                words = day.tags

            for word in words:
                word_dict[word.lower()] += 1
        return word_dict


    def get_days_in_date_range(self, start_date=None, end_date=None):
        if not start_date:
            start_date = datetime.date.min
        if not end_date:
            end_date = datetime.date.max

        start_date, end_date = sorted([start_date, end_date])
        assert start_date <= end_date

        days_in_date_range = []
        for day in self.days:
            if day.date < start_date:
                continue
            elif start_date <= day.date <= end_date:
                days_in_date_range.append(day)
            elif day.date > end_date:
                break
        return days_in_date_range


    def go_to_first_empty_day(self):
        if len(self.days) == 0:
            return datetime.date.today()

        last_edited_day = self.days[-1]
        first_empty_date = last_edited_day.date + dates.one_day
        self.change_date(first_empty_date)


    def add_instruction_content(self):
        self.go_to_first_empty_day()
        current_date = self.date

        logging.info('Adding example content on %s' % current_date)

        for example_day in info.example_content:
            self.day.content = example_day
            self.frame.set_date(self.month, self.date, self.day)
            self.go_to_next_day()

        self.change_date(current_date)
コード例 #7
0
ファイル: journal.py プロジェクト: abhinav3295/RedNotebook
    def __init__(self):
        self.dirs = dirs

        user_config = configuration.Config(self.dirs.config_file)
        # Apply defaults where no custom values have been set
        for key, value in default_config.items():
            if key not in user_config:
                user_config[key] = value
        self.config = user_config
        self.config.save_state()

        logging.info('Running in portable mode: %s' % self.dirs.portable)

        # Allow starting minimized to tray
        # When we start minimized we have to set the tray icon visible
        self.start_minimized = filesystem.HAS_TRAY and args.minimized
        if not filesystem.HAS_TRAY:
            self.config['closeToTray'] = 0
        elif self.start_minimized:
            self.config['closeToTray'] = 1

        self.month = None
        self.date = None
        self.months = {}

        # The dir name is the title
        self.title = ''

        # show instructions at first start
        self.is_first_start = self.config.read('firstStart', 1)
        self.config['firstStart'] = 0
        logging.info('First Start: %s' % bool(self.is_first_start))

        logging.info('RedNotebook version: %s' % info.version)
        logging.info(filesystem.get_platform_info())

        utils.set_environment_variables(self.config)

        self.actual_date = self.get_start_date()

        # Let components check if the MainWindow has been created
        self.frame = None
        self.frame = MainWindow(self)

        journal_path = self.get_journal_path()
        if not self.dirs.is_valid_journal_path(journal_path):
            logging.error('Invalid directory: %s. Using default journal.' %
                          journal_path)
            self.show_message(
                _('You cannot use this directory for your journal:') +
                ' %s' % journal_path + '. ' + _('Opening default journal.'),
                error=True)
            journal_path = self.dirs.default_data_dir
        self.open_journal(journal_path)

        self.archiver = backup.Archiver(self)
        #self.archiver.check_last_backup_date()

        # Check for a new version
        if self.config.read('checkForNewVersion', 0) == 1:
            utils.check_new_version(self, info.version, startup=True)

        # Automatically save the content after a period of time
        gobject.timeout_add_seconds(600, self.save_to_disk)