Beispiel #1
0
 def test_parse_local_fuzzy_dates(self):
     """ Parse fuzzy dates in their localized version """
     self.assertEqual(Date(_("now")), Date.now())
     self.assertEqual(Date(_("soon")), Date.soon())
     self.assertEqual(Date(_("later")), Date.someday())
     self.assertEqual(Date(_("someday")), Date.someday())
     self.assertEqual(Date(""), Date.no_date())
Beispiel #2
0
 def test_parse_fuzzy_dates(self):
     """ Parse fuzzy dates like now, soon, later, someday """
     self.assertEqual(Date("now"), Date.now())
     self.assertEqual(Date("soon"), Date.soon())
     self.assertEqual(Date("later"), Date.someday())
     self.assertEqual(Date("someday"), Date.someday())
     self.assertEqual(Date(""), Date.no_date())
Beispiel #3
0
    def check_commands(commands_list):
        """ Execute search commands

        This method is recursive for !or and !and """

        def fulltext_search(task, word):
            """ check if task contains the word """
            word = word.lower()
            text = task.get_excerpt(strip_tags=False).lower()
            title = task.get_title().lower()

            return word in text or word in title

        value_checks = {
            'after': lambda t, v: task.get_due_date() > v,
            'before': lambda t, v: task.get_due_date() < v,
            'tag': lambda t, v: v in task.get_tags_name(),
            'word': fulltext_search,
            'today': lambda task, v: task.get_due_date() == Date.today(),
            'tomorrow': lambda task, v: task.get_due_date() == Date.tomorrow(),
            'nodate': lambda task, v: task.get_due_date() == Date.no_date(),
            'now': lambda task, v: task.get_due_date() == Date.now(),
            'soon': lambda task, v: task.get_due_date() == Date.soon(),
            'someday': lambda task, v: task.get_due_date() == Date.someday(),
            'notag': lambda task, v: task.get_tags() == [],
        }

        for command in commands_list:
            cmd, positive, args = command[0], command[1], command[2:]
            result = False

            if cmd == 'or':
                for sub_cmd in args[0]:
                    if check_commands([sub_cmd]):
                        result = True
                        break
            elif value_checks.get(cmd, None):
                if len(args) > 0:
                    args = args[0]
                result = value_checks[cmd](task, args)

            if (positive and not result) or (not positive and result):
                return False

        return True
Beispiel #4
0
    def __init__(self, requester, app, task, thisisnew=False, clipboard=None):
        """
        req is the requester
        app is the view manager
        thisisnew is True when a new task is created and opened
        """
        self.req = requester
        self.app = app
        self.browser_config = self.req.get_config('browser')
        self.config = self.req.get_task_config(task.get_id())
        self.time = None
        self.clipboard = clipboard
        self.builder = Gtk.Builder()
        self.builder.add_from_file(self.EDITOR_UI_FILE)
        self.donebutton = self.builder.get_object("mark_as_done")
        self.undonebutton = self.builder.get_object("mark_as_undone")
        self.dismissbutton = self.builder.get_object("dismiss")
        self.undismissbutton = self.builder.get_object("undismiss")
        self.add_subtask = self.builder.get_object("add_subtask")
        self.tag_store = self.builder.get_object("tag_store")
        self.parent_button = self.builder.get_object("parent")

        # Closed date
        self.closed_popover = self.builder.get_object("closed_popover")
        self.closed_entry = self.builder.get_object("closeddate_entry")
        self.closed_calendar = self.builder.get_object("calendar_closed")

        # Start date
        self.start_popover = self.builder.get_object("start_popover")
        self.start_entry = self.builder.get_object("startdate_entry")
        self.start_calendar = self.builder.get_object("calendar_start")

        # Due date
        self.due_popover = self.builder.get_object("due_popover")
        self.due_entry = self.builder.get_object("duedate_entry")
        self.due_calendar = self.builder.get_object("calendar_due")

        # Recurrence
        self.recurring_menu = RecurringMenu(self.req, task.tid, self.builder)

        # Create our dictionary and connect it
        dic = {
            "on_tags_popover":
            self.open_tags_popover,
            "on_tag_toggled":
            self.on_tag_toggled,
            "on_move":
            self.on_move,
            "set_recurring_term_every_day":
            self.set_recurring_term_every_day,
            "set_recurring_term_every_otherday":
            self.set_recurring_term_every_otherday,
            "set_recurring_term_every_week":
            self.set_recurring_term_every_week,
            "set_recurring_term_every_month":
            self.set_recurring_term_every_month,
            "set_recurring_term_every_year":
            self.set_recurring_term_every_year,
            "set_recurring_term_week_day":
            self.set_recurring_term_week_day,
            "set_recurring_term_calender_month":
            self.set_recurring_term_month,
            "set_recurring_term_calender_year":
            self.set_recurring_term_year,
            "toggle_recurring_status":
            self.toggle_recurring_status,
            "on_repeat_icon_toggled":
            self.on_repeat_icon_toggled,
            "show_popover_start":
            self.show_popover_start,
            "startingdate_changed":
            lambda w: self.date_changed(w, GTGCalendar.DATE_KIND_START),
            "startdate_cleared":
            lambda w: self.on_date_cleared(w, GTGCalendar.DATE_KIND_START),
            "startdate_focus_out":
            lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_START
                                             ),
            "show_popover_due":
            self.show_popover_due,
            "duedate_changed":
            lambda w: self.date_changed(w, GTGCalendar.DATE_KIND_DUE),
            "duedate_now_selected":
            lambda w: self.on_duedate_fuzzy(w, Date.now()),
            "duedate_soon_selected":
            lambda w: self.on_duedate_fuzzy(w, Date.soon()),
            "duedate_someday_selected":
            lambda w: self.on_duedate_fuzzy(w, Date.someday()),
            "duedate_cleared":
            lambda w: self.on_date_cleared(w, GTGCalendar.DATE_KIND_DUE),
            "duedate_focus_out":
            lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_DUE),
            "show_popover_closed":
            self.show_popover_closed,
            "closeddate_changed":
            lambda w: self.date_changed(w, GTGCalendar.DATE_KIND_CLOSED),
            "closeddate_focus_out":
            lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_CLOSED
                                             ),
        }

        self.window = self.builder.get_object("TaskEditor")
        self.builder.connect_signals(dic)
        self.window.set_application(app)

        if task.has_parent():
            self.parent_button.set_label(_('Open Parent'))
        else:
            self.parent_button.set_label(_('Add Parent'))

        # Connect signals for the calendar
        self.start_handle = self.start_calendar.connect(
            'day-selected',
            lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_START))

        self.due_handle = self.due_calendar.connect(
            'day-selected',
            lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_DUE))

        self.closed_handle = self.closed_calendar.connect(
            'day-selected',
            lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_CLOSED))

        # Removing the Normal textview to replace it by our own
        # So don't try to change anything with glade, this is a home-made
        # widget
        textview = self.builder.get_object("textview")
        scrolled = self.builder.get_object("scrolledtask")
        scrolled.remove(textview)
        self.textview = TaskView(self.req, self.clipboard)
        self.textview.show()
        scrolled.add(self.textview)
        conf_font_value = self.browser_config.get("font_name")
        if conf_font_value != "":
            self.textview.override_font(Pango.FontDescription(conf_font_value))

        self.textview.browse_tag_cb = app.select_tag
        self.textview.new_subtask_cb = self.new_subtask
        self.textview.get_subtasks_cb = task.get_children
        self.textview.delete_subtask_cb = self.remove_subtask
        self.textview.rename_subtask_cb = self.rename_subtask
        self.textview.open_subtask_cb = self.open_subtask
        self.textview.save_cb = self.light_save
        self.textview.add_tasktag_cb = task.tag_added
        self.textview.remove_tasktag_cb = task.remove_tag
        self.textview.refresh_cb = self.refresh_editor
        self.textview.get_tagslist_cb = task.get_tags_name
        self.textview.tid = task.tid

        # Voila! it's done
        self.textview.connect('focus-in-event', self.on_textview_focus_in)
        self.textview.connect('focus-out-event', self.on_textview_focus_out)
        """
        TODO(jakubbrindza): Once all the functionality in editor is back and
        working, bring back also the accelerators! Dayleft_label needs to be
        brought back, however its position is unsure.
        """
        # self.dayleft_label = self.builder.get_object("dayleft")

        self.task = task
        tags = task.get_tags()
        text = self.task.get_text()
        title = self.task.get_title()

        self.textview.buffer.set_text(f"{title}\n")

        if text:
            self.textview.insert(text)

            # Insert any remaining tags
            if tags:
                tag_names = [t.get_name() for t in tags]
                self.textview.insert_tags(tag_names)
        else:
            # If not text, we insert tags
            if tags:
                tag_names = [t.get_name() for t in tags]
                self.textview.insert_tags(tag_names)
                start = self.textview.buffer.get_end_iter()
                self.textview.buffer.insert(start, '\n')

        # Insert subtasks if they weren't inserted in the text
        subtasks = task.get_children()
        for sub in subtasks:
            if sub not in self.textview.subtasks['tags']:
                self.textview.insert_existing_subtask(sub)

        if thisisnew:
            self.textview.select_title()
        else:
            self.task.set_to_keep()

        self.window.connect("destroy", self.destruction)

        # Connect search field to tags popup
        self.tags_entry = self.builder.get_object("tags_entry")
        self.tags_tree = self.builder.get_object("tags_tree")

        self.tags_tree.set_search_entry(self.tags_entry)
        self.tags_tree.set_search_equal_func(self.search_function, None)

        # plugins
        self.pengine = PluginEngine()
        self.plugin_api = PluginAPI(self.req, self.app, self)
        self.pengine.register_api(self.plugin_api)
        self.pengine.onTaskLoad(self.plugin_api)

        # Putting the refresh callback at the end make the start a lot faster
        self.refresh_editor()
        self.textview.grab_focus()

        self.init_dimensions()

        self.window.insert_action_group('app', app)
        self.window.insert_action_group('win', app.browser)

        self.textview.set_editable(True)
        self.window.set_transient_for(self.app.browser)
        self.window.show()
Beispiel #5
0
    def get_node_bgcolor(self, node):
        """ This method checks the urgency of a node (task) and returns its
         urgency background color"""
        sdate = node.get_start_date()
        ddate = node.get_due_date()
        daysleft = ddate.days_left()

        # Dates undefined
        if (ddate == Date.today()):
            return self._get_color(2)  # High urgency
        elif ddate != Date.no_date():  # Has a due date
            if daysleft < 0:
                return self._get_color(3)  # Overdue
            elif (sdate == Date.no_date() and  # Has no start date
                  not ddate.is_fuzzy()):  # Due date is not fuzzy
                return self._get_color(1)  # Normal

        # Fuzzy dates (now, soon, someday)
        # These can ignore the start date
        if (ddate == Date.now()):
            return self._get_color(2)
        elif (ddate == Date.soon()):
            return self._get_color(1)
        elif (ddate == Date.someday()):
            return self._get_color(0)

        # Dates fully defined. Calculate gradient color
        elif (sdate != Date.no_date() != ddate):
            dayspan = (ddate - sdate).days
            redf = self._pref_data['reddays']
            reddays = int(ceil(redf / 100.0 * dayspan))

            # Gradient variables
            grad_dayspan = dayspan - reddays
            grad_half_dayspan = grad_dayspan / 2.0

            # Default to low urgency color
            color = self._get_color(0)

            # CL : low urgency color
            # CN : normal urgency color
            # CH : high urgency color
            # CO : overdue color
            # To understand this section, it is easier to draw out a
            # timeline divided into 3 sections: CL to CN, CN to CH and
            # the reddays section. Then point out the spans of the
            # different variables (dayspan, daysleft, reddays,
            # grad_dayspan, grad_half_dayspan)
            if daysleft < 0:  # CO
                color = self._get_color(3)
            elif daysleft <= reddays:  # CH
                color = self._get_color(2)
            elif daysleft <= (dayspan - grad_half_dayspan):
                # Gradient CN to CH
                # Has to be float so division by it is non-zero
                steps = float(grad_half_dayspan)
                step = grad_half_dayspan - (daysleft - reddays)
                color = self._get_gradient_color(self._get_color(1),
                                                 self._get_color(2),
                                                 step / steps)
            elif daysleft <= dayspan:
                # Gradient CL to CN
                steps = float(grad_half_dayspan)
                step = grad_half_dayspan - (daysleft - reddays -
                                            grad_half_dayspan)
                color = self._get_gradient_color(self._get_color(0),
                                                 self._get_color(1),
                                                 step / steps)

            return color

        # Insufficient data to determine urgency
        else:
            return None
Beispiel #6
0
    def __init__(self, requester, app, task, thisisnew=False, clipboard=None):
        """
        req is the requester
        app is the view manager
        thisisnew is True when a new task is created and opened
        """
        self.req = requester
        self.app = app
        self.browser_config = self.req.get_config('browser')
        self.config = self.req.get_task_config(task.get_id())
        self.time = None
        self.clipboard = clipboard
        self.builder = Gtk.Builder()
        self.builder.add_from_file(self.EDITOR_UI_FILE)
        self.donebutton = self.builder.get_object("mark_as_done")
        self.undonebutton = self.builder.get_object("mark_as_undone")
        self.dismissbutton = self.builder.get_object("dismiss")
        self.undismissbutton = self.builder.get_object("undismiss")
        self.add_subtask = self.builder.get_object("add_subtask")
        self.tag_store = self.builder.get_object("tag_store")
        self.parent_button = self.builder.get_object("parent")

        # Closed date
        self.closed_popover = self.builder.get_object("closed_popover")
        self.closed_entry = self.builder.get_object("closeddate_entry")
        self.closed_calendar = self.builder.get_object("calendar_closed")

        # Start date
        self.start_popover = self.builder.get_object("start_popover")
        self.start_entry = self.builder.get_object("startdate_entry")
        self.start_calendar = self.builder.get_object("calendar_start")

        # Due date
        self.due_popover = self.builder.get_object("due_popover")
        self.due_entry = self.builder.get_object("duedate_entry")
        self.due_calendar = self.builder.get_object("calendar_due")

        # Create our dictionary and connect it
        dic = {
            "on_tags_popover":
            self.open_tags_popover,
            "on_tag_toggled":
            self.on_tag_toggled,
            "on_move":
            self.on_move,
            "show_popover_start":
            self.show_popover_start,
            "startingdate_changed":
            lambda w: self.date_changed(w, GTGCalendar.DATE_KIND_START),
            "startdate_cleared":
            lambda w: self.on_date_cleared(w, GTGCalendar.DATE_KIND_START),
            "startdate_focus_out":
            lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_START
                                             ),
            "show_popover_due":
            self.show_popover_due,
            "duedate_changed":
            lambda w: self.date_changed(w, GTGCalendar.DATE_KIND_DUE),
            "duedate_now_selected":
            lambda w: self.on_duedate_fuzzy(w, Date.now()),
            "duedate_soon_selected":
            lambda w: self.on_duedate_fuzzy(w, Date.soon()),
            "duedate_someday_selected":
            lambda w: self.on_duedate_fuzzy(w, Date.someday()),
            "duedate_cleared":
            lambda w: self.on_date_cleared(w, GTGCalendar.DATE_KIND_DUE),
            "duedate_focus_out":
            lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_DUE),
            "show_popover_closed":
            self.show_popover_closed,
            "closeddate_changed":
            lambda w: self.date_changed(w, GTGCalendar.DATE_KIND_CLOSED),
            "closeddate_focus_out":
            lambda w, e: self.date_focus_out(w, e, GTGCalendar.DATE_KIND_CLOSED
                                             ),
        }

        self.builder.connect_signals(dic)
        self.window = self.builder.get_object("TaskEditor")
        self.window.set_application(app)

        # Connect signals for the calendar
        self.start_handle = self.start_calendar.connect(
            'day-selected',
            lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_START))

        self.due_handle = self.due_calendar.connect(
            'day-selected',
            lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_DUE))

        self.closed_handle = self.closed_calendar.connect(
            'day-selected',
            lambda c: self.on_date_selected(c, GTGCalendar.DATE_KIND_CLOSED))

        # Removing the Normal textview to replace it by our own
        # So don't try to change anything with glade, this is a home-made
        # widget
        textview = self.builder.get_object("textview")
        scrolled = self.builder.get_object("scrolledtask")
        scrolled.remove(textview)
        self.textview = TaskView(self.req, self.clipboard)
        self.textview.show()
        self.textview.set_subtask_callback(self.new_subtask)
        self.textview.open_task_callback(self.app.open_task)
        self.textview.set_left_margin(20)
        self.textview.set_right_margin(20)
        scrolled.add(self.textview)
        conf_font_value = self.browser_config.get("font_name")
        if conf_font_value != "":
            self.textview.override_font(Pango.FontDescription(conf_font_value))
        # Voila! it's done
        """
        TODO(jakubbrindza): Once all the functionality in editor is back and
        working, bring back also the accelerators! Dayleft_label needs to be
        brought back, however its position is unsure.
        """
        # self.dayleft_label = self.builder.get_object("dayleft")

        self.task = task
        tags = task.get_tags()
        self.textview.subtasks_callback(task.get_children)
        self.textview.removesubtask_callback(task.remove_child)
        self.textview.set_get_tagslist_callback(task.get_tags_name)
        self.textview.set_add_tag_callback(task.add_tag)
        self.textview.set_remove_tag_callback(task.remove_tag)
        self.textview.save_task_callback(self.light_save)

        texte = self.task.get_text()
        title = self.task.get_title()
        # the first line is the title
        self.textview.set_text(f"{title}\n")
        # we insert the rest of the task
        if texte:
            self.textview.insert(f"{texte}")
        else:
            # If not text, we insert tags
            if tags:
                for t in tags:
                    self.textview.insert_text("%s, " % t.get_name())
                self.textview.insert_text("\n")
            # If we don't have text, we still need to insert subtasks if any
            subtasks = task.get_children()
            if subtasks:
                self.textview.insert_subtasks(subtasks)
        # We select the title if it's a new task
        if thisisnew:
            self.textview.select_title()
        else:
            self.task.set_to_keep()
        self.textview.modified(full=True)
        self.window.connect("destroy", self.destruction)

        # Connect search field to tags popup
        self.tags_entry = self.builder.get_object("tags_entry")
        self.tags_tree = self.builder.get_object("tags_tree")

        self.tags_tree.set_search_entry(self.tags_entry)
        self.tags_tree.set_search_equal_func(self.search_function, None)

        # plugins
        self.pengine = PluginEngine()
        self.plugin_api = PluginAPI(self.req, self.app, self)
        self.pengine.register_api(self.plugin_api)
        self.pengine.onTaskLoad(self.plugin_api)

        # Putting the refresh callback at the end make the start a lot faster
        self.textview.refresh_callback(self.refresh_editor)
        self.refresh_editor()
        self.textview.grab_focus()

        self.init_dimensions()

        self.window.insert_action_group('app', app)
        self.window.insert_action_group('win', app.browser)
        self._set_actions()

        self.textview.set_editable(True)
        self.window.set_transient_for(self.app.browser)
        self.window.show()