Пример #1
0
class Finance(activity.Activity):
    def __init__(self, handle):
        activity.Activity.__init__(self, handle)
        self.set_title(_("Finance"))
        self.max_participants = 1

        # Initialize database.
        # data
        #   next_id
        #   transactions
        #     id, name, type, amount, date, category
        #   budgets
        #     category, period, amount, budget
        self.data = {'next_id': 0, 'transactions': [], 'budgets': {}}

        self.transaction_map = {}
        self.visible_transactions = []

        self.undo_transaction_map = []
        self.undo_id_map = []

        self.redo_id_map = []
        self.redo_transaction_map = []

        self.transaction_names = {}
        self.category_names = {}

        # Initialize view period to the first of the month.
        self.period = MONTH
        self.period_start = self.get_this_period()

        # Create screens.
        self.register = registerscreen.RegisterScreen(self)
        self.chart = chartscreen.ChartScreen(self)
        self.budget = budgetscreen.BudgetScreen(self)

        self.build_toolbox()

        self.screenbox = Gtk.VBox()

        self.headerbox = self.build_header()
        self._active_panel = None

        # Add the summary data.

        self.startlabel = Gtk.Label()
        self.startamountlabel = Gtk.Label()
        self.creditslabel = Gtk.Label()
        self.debitslabel = Gtk.Label()
        self.balancelabel = Gtk.Label()
        self.balancelabel.props.margin_left = style.DEFAULT_SPACING
        self.balancelabel.props.margin_right = style.DEFAULT_SPACING

        font_size = int(style.FONT_SIZE * 1.25)
        font = Pango.FontDescription("Sans %d" % font_size)
        for label in (self.startlabel, self.startamountlabel,
                      self.creditslabel, self.debitslabel):
            label.modify_font(font)
            label.set_hexpand(True)
            label.set_halign(Gtk.Align.START)
            label.props.margin = style.DEFAULT_PADDING

        self.balance_evbox = Gtk.EventBox()
        self.balance_evbox.add(self.balancelabel)
        summarybox = Gtk.Grid()
        summarybox.attach(self.startlabel, 0, 0, 1, 1)
        summarybox.attach(self.startamountlabel, 0, 1, 1, 1)
        summarybox.attach(self.creditslabel, 1, 0, 1, 1)
        summarybox.attach(self.debitslabel, 1, 1, 1, 1)
        summarybox.attach(self.balance_evbox, 2, 0, 1, 2)
        self.balance_evbox.set_halign(Gtk.Align.END)

        summary_evbox = Gtk.EventBox()
        summary_evbox.add(summarybox)
        summary_evbox.modify_bg(Gtk.StateType.NORMAL,
                                style.Color('#666666').get_gdk_color())
        summary_evbox.set_size_request(-1, style.GRID_CELL_SIZE)

        vbox = Gtk.VBox()

        vbox.pack_start(self.headerbox, False, False, 0)
        vbox.pack_start(self.screenbox, True, True, 0)
        vbox.pack_start(summary_evbox, False, False, 0)

        # Start with the empty screen.
        self.empty_panel = emptypanel.create_empty_panel(
            'row-insert-credit', _('Add some credit or debit to get started!'),
            _('Add credit'), self.__empty_panel_btn_cb)

        self._set_internal_panel(self.empty_panel)

        # This has to happen last, because it calls the read_file
        # method when restoring from the Journal.
        self.set_canvas(vbox)

        self.show_all()
        self.show_header_controls()

        if os.getenv('FINANCE_TEST'):
            self.create_test_data()
            self._set_internal_panel(self.register)

    def build_toolbox(self):

        view_tool_group = None
        registerbtn = RadioToolButton()
        registerbtn.props.icon_name = 'view-list'
        registerbtn.props.label = _('Register')
        registerbtn.set_tooltip(_("Register"))
        registerbtn.props.group = view_tool_group
        view_tool_group = registerbtn
        registerbtn.props.accelerator = '<Ctrl>1'
        registerbtn.connect('clicked', self.register_cb)

        budgetbtn = RadioToolButton()
        budgetbtn.props.icon_name = 'budget'
        budgetbtn.props.label = _('Budget')
        budgetbtn.set_tooltip(_("Budget"))
        budgetbtn.props.group = view_tool_group
        budgetbtn.props.accelerator = '<Ctrl>2'
        budgetbtn.connect('clicked', self.budget_cb)

        chartbtn = RadioToolButton()
        chartbtn.props.icon_name = 'chart'
        chartbtn.props.label = _('Chart')
        chartbtn.set_tooltip(_("Chart"))
        chartbtn.props.group = view_tool_group
        chartbtn.props.accelerator = '<Ctrl>3'
        chartbtn.connect('clicked', self.chart_cb)

        helpbutton = self._create_help_button()
        helpbutton.show_all()

        self.toolbar_box = ToolbarBox()

        activity_button = ActivityToolbarButton(self)
        self.toolbar_box.toolbar.insert(activity_button, 0)
        activity_button.show()

        self.toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.toolbar_box.toolbar.insert(registerbtn, -1)
        self.toolbar_box.toolbar.insert(budgetbtn, -1)
        self.toolbar_box.toolbar.insert(chartbtn, -1)

        self.toolbar_box.toolbar.insert(Gtk.SeparatorToolItem(), -1)

        self.toolbar_box.toolbar.insert(helpbutton, -1)

        separator = Gtk.SeparatorToolItem()
        separator.props.draw = False
        separator.set_expand(True)
        self.toolbar_box.toolbar.insert(separator, -1)

        self.toolbar_box.toolbar.insert(StopButton(self), -1)
        self.set_toolbar_box(self.toolbar_box)

        activity_button.page.insert(self._create_export_button(), -1)

        self.toolbar_box.show_all()

    def _create_export_button(self):
        # Add expoprt button
        export_data = ToolButton('save-as-data')

        export_data.props.tooltip = _('Export data')
        export_data.props.hide_tooltip_on_click = False
        export_data.palette_invoker.props.toggle_palette = True
        export_data.show()

        menu_box = PaletteMenuBox()
        export_data.props.palette.set_content(menu_box)

        menu_item = PaletteMenuItem(text_label=_('Export credits by day'))
        menu_item.connect('activate', self.__export_data_to_chart_cb, 'credit',
                          DAY)
        menu_box.append_item(menu_item)

        menu_item = PaletteMenuItem(text_label=_('Export debits by day'))
        menu_item.connect('activate', self.__export_data_to_chart_cb, 'debit',
                          DAY)
        menu_box.append_item(menu_item)

        menu_item = PaletteMenuItem(text_label=_('Export credits by month'))
        menu_item.connect('activate', self.__export_data_to_chart_cb, 'credit',
                          MONTH)
        menu_box.append_item(menu_item)

        menu_item = PaletteMenuItem(text_label=_('Export debits by month'))
        menu_item.connect('activate', self.__export_data_to_chart_cb, 'debit',
                          MONTH)
        menu_box.append_item(menu_item)

        menu_box.show_all()
        return export_data

    def _create_help_button(self):
        helpitem = HelpButton()
        helpitem.add_section(_('Register'), icon='view-list')
        helpitem.add_paragraph(registerscreen.REGISTER_HELP)
        helpitem.add_section(_('Budget'), icon='budget')
        helpitem.add_paragraph(budgetscreen.BUDGET_HELP)
        helpitem.add_section(_('Chart'), icon='chart')
        helpitem.add_paragraph(chartscreen.CHART_HELP)
        return helpitem

    def build_header(self):
        # Add the header.
        self.periodlabel = Gtk.Label()
        self.periodlabel.set_padding(10, 0)
        self._period_label_item = Gtk.ToolItem()
        self._period_label_item.add(self.periodlabel)
        self.periodlabel.set_halign(Gtk.Align.END)
        self._period_label_item.set_size_request(style.GRID_CELL_SIZE * 5, -1)

        headerbox = Gtk.Toolbar()
        headerbox.modify_bg(Gtk.StateType.NORMAL,
                            style.Color('#424242').get_gdk_color())
        headerbox.set_size_request(-1, style.GRID_CELL_SIZE)

        # register buttons
        self.newcreditbtn = ToolButton('row-insert-credit')
        self.newcreditbtn.set_tooltip(_("New Credit"))
        self.newcreditbtn.props.accelerator = '<Ctrl>A'
        self.newcreditbtn.connect('clicked', self.__newcredit_cb)

        self.newdebitbtn = ToolButton('row-insert-debit')
        self.newdebitbtn.set_tooltip(_("New Debit"))
        self.newdebitbtn.props.accelerator = '<Ctrl>D'
        self.newdebitbtn.connect('clicked', self.__newdebit_cb)

        self.undoactionbtn = UndoButton()
        self.undoactionbtn.connect('clicked', self.__undoaction_cb)

        self.redoactionbtn = RedoButton()
        self.redoactionbtn.connect('clicked', self.__redoaction_cb)

        self.eraseitembtn = ToolButton('basket')
        self.eraseitembtn.set_tooltip(_("Erase Transaction"))
        self.eraseitembtn.props.accelerator = '<Ctrl>E'
        self.eraseitembtn.connect('clicked', self.__eraseitem_cb)

        headerbox.insert(self.newcreditbtn, -1)
        headerbox.insert(self.newdebitbtn, -1)
        headerbox.insert(self.eraseitembtn, -1)
        headerbox.insert(self.undoactionbtn, -1)
        headerbox.insert(self.redoactionbtn, -1)

        self.header_separator_visible = Gtk.SeparatorToolItem()
        headerbox.insert(self.header_separator_visible, -1)

        self.export_image = ToolButton('save-as-image')
        self.export_image.set_tooltip(_("Save as Image"))
        self.export_image.connect('clicked', self.__save_image_cb)
        headerbox.insert(self.export_image, -1)

        self._header_separator = Gtk.SeparatorToolItem()
        self._header_separator.props.draw = False
        self._header_separator.set_expand(True)
        headerbox.insert(self._header_separator, -1)

        # period controls
        self.thisperiodbtn = ToolButton('go-down')
        self.thisperiodbtn.props.accelerator = '<Ctrl>Down'
        self.thisperiodbtn.connect('clicked', self.thisperiod_cb)

        self.prevperiodbtn = ToolButton('go-previous-paired')
        self.prevperiodbtn.props.accelerator = '<Ctrl>Left'
        self.prevperiodbtn.connect('clicked', self.prevperiod_cb)

        self.nextperiodbtn = ToolButton('go-next-paired')
        self.nextperiodbtn.props.accelerator = '<Ctrl>Right'
        self.nextperiodbtn.connect('clicked', self.nextperiod_cb)

        period_options = {
            DAY: _('Day'),
            WEEK: _('Week'),
            MONTH: _('Month'),
            YEAR: _('Year'),
            FOREVER: _('Forever')
        }
        periodcombo = FilterToolItem('calendar', MONTH, period_options,
                                     _('Select period'))

        periodcombo.connect('changed', self.__period_changed_cb)

        headerbox.insert(periodcombo, -1)
        headerbox.insert(self.prevperiodbtn, -1)
        headerbox.insert(self.nextperiodbtn, -1)
        headerbox.insert(self.thisperiodbtn, -1)

        headerbox.insert(self._period_label_item, -1)
        return headerbox

    def show_header_controls(self):
        for child in self.headerbox.get_children():
            child.show()
            if self._active_panel in (self.register, self.empty_panel):
                if child in (self.header_separator_visible, self.export_image):
                    child.hide()
            elif self._active_panel == self.budget:
                if child in (self.newcreditbtn, self.newdebitbtn,
                             self.eraseitembtn, self.undoactionbtn,
                             self.redoactionbtn, self.header_separator_visible,
                             self.export_image):
                    child.hide()
            elif self._active_panel == self.chart:
                # Use NOT here
                if child not in (self.newcreditbtn, self.newdebitbtn,
                                 self.header_separator_visible,
                                 self.export_image):
                    child.hide()

    def register_cb(self, widget):
        self._set_internal_panel(self.register)
        self.show_header_controls()
        self.newcreditbtn.set_tooltip(_("New Credit"))
        self.newdebitbtn.set_tooltip(_("New Debit"))

    def budget_cb(self, widget):
        self._set_internal_panel(self.budget)
        self.show_header_controls()

    def chart_cb(self, widget):
        self._set_internal_panel(self.chart)
        self.show_header_controls()
        self.newcreditbtn.set_tooltip(_("Show Credits"))
        self.newdebitbtn.set_tooltip(_("Show Debits"))

    def _set_internal_panel(self, widget):
        if self.screenbox.get_children():
            self.screenbox.remove(self.screenbox.get_children()[0])
        self.screenbox.pack_start(widget, True, True, 0)
        widget.show_all()
        self._active_panel = widget
        self.build_screen()

    def build_screen(self):
        self.build_visible_transactions()

        if hasattr(self._active_panel, 'build'):
            self._active_panel.build()

        self.update_header()
        self.update_summary()
        self.update_toolbar()

    def __empty_panel_btn_cb(self, button):
        self._set_internal_panel(self.register)
        self.register.new_credit()

    def __newcredit_cb(self, widget):
        if self._active_panel == self.chart:
            # in the case of chart, select the graphic
            self.chart.set_mode(self.chart.CHART_CREDIT)
            return

        # this check is used when the emptypanel is visible
        if self._active_panel != self.register:
            self._set_internal_panel(self.register)
        self.register.new_credit()

    def __newdebit_cb(self, widget):
        if self._active_panel == self.chart:
            # in the case of chart, select the graphic
            self.chart.set_mode(self.chart.CHART_DEBIT)
            return

        # this check is used when the emptypanel is visible
        if self._active_panel != self.register:
            self._set_internal_panel(self.register)
        self.register.new_debit()

    def __undoaction_cb(self, widget):
        self.undo_transaction()
        self.build_screen()

    def __redoaction_cb(self, widget):
        self.redo_transaction()
        self.build_screen()

    def __eraseitem_cb(self, widget):
        self.register.erase_item()
        self.build_screen()

    def update_header(self):
        if self.period == DAY:
            # TRANS: representation of the "Day" period
            text = self.period_start.strftime(_("%B %d, %Y"))

        elif self.period == WEEK:
            # TRANS: representation of the "Week of" period
            text = _('Week of') + self.period_start.strftime(_(" %B %d, %Y"))

        elif self.period == MONTH:
            # TRANS: representation of the "Month" period
            text = self.period_start.strftime(_("%B, %Y"))

        elif self.period == YEAR:
            # TRANS: representation of the "Year" period
            text = self.period_start.strftime(_("%Y"))

        elif self.period == FOREVER:
            text = _('Forever')

        self.periodlabel.set_markup("<span size='xx-large' color='white'><b>" +
                                    text + "</b></span>")

    def update_summary(self):
        # Calculate starting balance.
        start = 0.0
        for t in self.data['transactions']:
            d = t['date']
            if d < self.period_start.toordinal():
                if t['type'] == 'credit':
                    start += t['amount']
                else:
                    start -= t['amount']

        # Calculate totals for this period.
        credit_count = 0
        credit_total = 0.0
        debit_count = 0
        debit_total = 0.0
        total = start
        for t in self.visible_transactions:
            if t['type'] == 'credit':
                credit_count += 1
                credit_total += t['amount']
                total += t['amount']
            else:
                debit_count += 1
                debit_total += t['amount']
                total -= t['amount']

        # Update Balance.
        if total >= 0.0:
            balancecolor = colors.CREDIT_COLOR
        else:
            balancecolor = colors.DEBIT_COLOR
        balance = \
            "<span size='xx-large' foreground='white'><b>%s %s</b></span>" % \
            (_('Balance: '), locale.currency(total))
        self.balancelabel.set_markup(balance)

        self.balance_evbox.modify_bg(Gtk.StateType.NORMAL,
                                     style.Color(balancecolor).get_gdk_color())

        self.startlabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            _('Starting Balance:'))
        self.startamountlabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            locale.currency(start))

        self.creditslabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            (_('%(credit_total)s in %(credit_count)d credits') % {
                'credit_total': locale.currency(credit_total),
                'credit_count': credit_count
            }))

        self.debitslabel.set_markup(
            "<span foreground='white'><b>%s</b></span>" %
            (_('%(debit_total)s in %(debit_count)d debits') % {
                'debit_total': locale.currency(debit_total),
                'debit_count': debit_count
            }))

    def update_toolbar(self):
        # Disable the navigation when Forever is selected.
        next_prev = self.period != FOREVER
        self.prevperiodbtn.set_sensitive(next_prev)
        self.thisperiodbtn.set_sensitive(next_prev)
        self.nextperiodbtn.set_sensitive(next_prev)

        # This is a HACK to translate the string properly
        # http://bugs.sugarlabs.org/ticket/3190
        if self.period == MONTH:
            text_previous_period = _('Previous Month')
            text_this_period = _('This Month')
            text_next_period = _('Next Month')
        elif self.period == WEEK:
            text_previous_period = _('Previous Week')
            text_this_period = _('This Week')
            text_next_period = _('Next Week')
        elif self.period == DAY:
            text_previous_period = _('Previous Day')
            text_this_period = _('This Day')
            text_next_period = _('Next Day')
        elif self.period == YEAR:
            text_previous_period = _('Previous Year')
            text_this_period = _('This Year')
            text_next_period = _('Next Year')

        if self.period != FOREVER:
            self.prevperiodbtn.set_tooltip(text_previous_period)
            self.thisperiodbtn.set_tooltip(text_this_period)
            self.nextperiodbtn.set_tooltip(text_next_period)

    # Update the label self.period to reflect the period.
    def get_this_period(self):
        today = datetime.date.today()

        if self.period == DAY:
            return today

        elif self.period == WEEK:
            while today.weekday() != 0:
                today -= datetime.timedelta(days=1)
            return today

        elif self.period == MONTH:
            return datetime.date(today.year, today.month, 1)

        elif self.period == YEAR:
            return datetime.date(today.year, 1, 1)

        elif self.period == FOREVER:
            return datetime.date(1900, 1, 1)

    def get_next_period(self, start):
        if self.period == DAY:
            return start + datetime.timedelta(days=1)

        elif self.period == WEEK:
            return start + datetime.timedelta(days=7)

        elif self.period == MONTH:
            if start.month == 12:
                return datetime.date(start.year + 1, 1, 1)
            else:
                return datetime.date(start.year, start.month + 1, 1)

        elif self.period == YEAR:
            return datetime.date(start.year + 1, 1, 1)

    def get_prev_period(self, start):
        if self.period == DAY:
            return start - datetime.timedelta(days=1)

        elif self.period == WEEK:
            return start - datetime.timedelta(days=7)

        elif self.period == MONTH:
            if start.month == 1:
                return datetime.date(start.year - 1, 12, 1)
            else:
                return datetime.date(start.year, start.month - 1, 1)

        elif self.period == YEAR:
            return datetime.date(start.year - 1, 1, 1)

    def thisperiod_cb(self, widget):
        if self.period != FOREVER:
            self.period_start = self.get_this_period()
            self.build_screen()

    def nextperiod_cb(self, widget):
        if self.period != FOREVER:
            self.period_start = self.get_next_period(self.period_start)
            self.build_screen()

    def prevperiod_cb(self, widget):
        if self.period != FOREVER:
            self.period_start = self.get_prev_period(self.period_start)
            self.build_screen()

    def __period_changed_cb(self, widget, value):
        self.period = int(value)
        self.update_toolbar()

        # Jump to 'this period'.
        self.period_start = self.get_this_period()
        self.build_screen()

    def build_visible_transactions(self):
        self.build_undo_buttons()

        if self.period == FOREVER:
            self.visible_transactions = self.data['transactions']

        else:
            period_start_ord = self.period_start.toordinal()
            period_end_ord = self.get_next_period(
                self.period_start).toordinal()

            self.visible_transactions = []
            for t in self.data['transactions']:
                d = t['date']
                if d >= period_start_ord and d < period_end_ord:
                    self.visible_transactions.append(t)

        self.visible_transactions.sort(key=lambda a: a['date'])

    def build_transaction_map(self):
        self.transaction_map = {}
        for t in self.data['transactions']:
            self.transaction_map[t['id']] = t

    def create_transaction(self,
                           name='',
                           type='debit',
                           amount=0,
                           category='',
                           date=datetime.date.today()):
        id = self.data['next_id']
        self.data['next_id'] += 1

        t = {
            'id': id,
            'name': name,
            'type': type,
            'amount': amount,
            'date': date.toordinal(),
            'category': category
        }
        self.data['transactions'].append(t)
        self.transaction_map[id] = t

        self.undo_id_map.append(id)
        self.undo_transaction_map.append('Erase')
        self.redo_transaction_map = []
        self.redo_id_map = []

        self.build_visible_transactions()

        return id

    def destroy_transaction(self, id):
        t = self.transaction_map[id]
        self.data['transactions'].remove(t)
        del self.transaction_map[id]

    def undo_redo_action(self, id, t, isin=False):
        # if we're updating the transaction
        if t == 'Erase':
            self.destroy_transaction(id)
        elif isin:
            for i in range(len(self.data['transactions'])):
                if id == self.data['transactions'][i]['id']:
                    self.data['transactions'][i] = t
                    break
        else:
            # Have to insert it back into the right position
            flag = 1
            for i in range(len(self.data['transactions'])):
                if id < self.data['transactions'][i]['id']:
                    self.data['transactions'].insert(i, t)
                    flag = 0
                    break
            if flag:
                self.data['transactions'].append(t)

    def undo_transaction(self):
        if len(self.undo_transaction_map) == 0:
            return False

        id = self.undo_id_map.pop()
        t = self.undo_transaction_map.pop()

        self.redo_id_map.append(id)
        isin = False
        if id in self.transaction_map.keys():
            self.redo_transaction_map.append(
                copy.deepcopy(self.transaction_map[id]))
            isin = True
        else:
            self.redo_transaction_map.append('Erase')

        copy_t = copy.deepcopy(t)
        self.undo_redo_action(id, copy_t, isin)

        if t != 'Erase':
            self.transaction_map[id] = copy_t
        return True

    def redo_transaction(self):
        if len(self.redo_transaction_map) == 0:
            return False

        id = self.redo_id_map.pop()
        t = self.redo_transaction_map.pop()

        self.undo_id_map.append(id)
        isin = False
        if id in self.transaction_map.keys():
            self.undo_transaction_map.append(
                copy.deepcopy(self.transaction_map[id]))
            isin = True
        else:
            self.undo_transaction_map.append('Erase')

        self.undo_redo_action(id, t, isin)

        if t != 'Erase':
            self.transaction_map[id] = copy.deepcopy(t)
        return True

    def build_undo_buttons(self):
        if len(self.undo_transaction_map) == 0:
            self.undoactionbtn.set_sensitive(False)
        else:
            self.undoactionbtn.set_sensitive(True)
        if len(self.redo_transaction_map) == 0:
            self.redoactionbtn.set_sensitive(False)
        else:
            self.redoactionbtn.set_sensitive(True)

    def build_names(self):
        self.transaction_names = {}
        self.category_names = {}
        for t in self.data['transactions']:
            self.transaction_names[t['name']] = 1
            self.category_names[t['category']] = 1

    def create_test_data(self):
        cur_date = datetime.date.today()
        cur_date = datetime.date(cur_date.year, cur_date.month, 1)
        self.create_transaction('Initial Balance',
                                type='credit',
                                amount=632,
                                category='Initial Balance',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Fix Car',
                                amount=75.84,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Adopt Cat',
                                amount=100,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('New Coat',
                                amount=25.53,
                                category='Clothing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Pay Rent',
                                amount=500,
                                category='Housing',
                                date=cur_date)

        cur_date += datetime.timedelta(days=1)
        self.create_transaction('Funky Cafe',
                                amount=5.20,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Groceries',
                                amount=50.92,
                                category='Food',
                                date=cur_date)
        self.create_transaction('Cat Food',
                                amount=5.40,
                                category='Pets',
                                date=cur_date)

        cur_date += datetime.timedelta(days=4)
        self.create_transaction('Paycheck',
                                type='credit',
                                amount=700,
                                category='Paycheck',
                                date=cur_date)
        self.create_transaction('Gas',
                                amount=21.20,
                                category='Transportation',
                                date=cur_date)

        cur_date += datetime.timedelta(days=2)
        self.create_transaction('Cat Toys',
                                amount=10.95,
                                category='Pets',
                                date=cur_date)
        self.create_transaction('Gift for Sister',
                                amount=23.20,
                                category='Gifts',
                                date=cur_date)

        self.build_transaction_map()
        self.build_names()

    def read_file(self, file_path):
        if self.metadata['mime_type'] != 'text/plain':
            return

        fd = open(file_path, 'r')
        try:
            text = fd.read()
            self.data = json.loads(text)
        finally:
            fd.close()

        if self.data['transactions']:
            self._set_internal_panel(self.register)
        self.show_header_controls()

        self.build_transaction_map()
        self.build_names()
        self.build_screen()

    def write_file(self, file_path):
        if not self.metadata['mime_type']:
            self.metadata['mime_type'] = 'text/plain'

        fd = open(file_path, 'w')
        try:
            text = json.dumps(self.data)
            fd.write(text)
        finally:
            fd.close()

    def __save_image_cb(self, widget):
        image_file = tempfile.NamedTemporaryFile(mode='w+b', suffix='.png')
        journal_entry = datastore.create()
        journal_entry.metadata['title'] = self.chart.title
        journal_entry.metadata['keep'] = '0'
        journal_entry.metadata['mime_type'] = 'image/png'

        # generate the image
        self.chart.generate_image(image_file.file, 800, 600)
        image_file.file.close()
        journal_entry.file_path = image_file.name

        # generate the preview
        preview_str = io.BytesIO()
        self.chart.generate_image(preview_str, activity.PREVIEW_SIZE[0],
                                  activity.PREVIEW_SIZE[1])
        journal_entry.metadata['preview'] = dbus.ByteArray(
            preview_str.getvalue())

        logging.debug('Create %s image file', image_file.name)
        datastore.write(journal_entry)
        self._show_journal_alert(_('Chart created'), _('Open in the Journal'),
                                 journal_entry.object_id)

    def _show_journal_alert(self, title, msg, object_id):
        open_alert = Alert()
        open_alert.props.title = title
        open_alert.props.msg = msg
        open_icon = Icon(icon_name='zoom-activity')
        open_alert.add_button(Gtk.ResponseType.APPLY, _('Show in Journal'),
                              open_icon)
        open_icon.show()
        ok_icon = Icon(icon_name='dialog-ok')
        open_alert.add_button(Gtk.ResponseType.OK, _('Ok'), ok_icon)
        ok_icon.show()
        # Remove other alerts
        for alert in self._alerts:
            self.remove_alert(alert)

        self.add_alert(open_alert)
        open_alert.connect('response', self.__open_response_cb, object_id)
        open_alert.show()

    def __open_response_cb(self, alert, response_id, object_id):
        if response_id is Gtk.ResponseType.APPLY:
            activity.show_object_in_journal(object_id)
        self.remove_alert(alert)

    def __export_data_to_chart_cb(self, widget, type_movement, period):
        """
        type_movement = 'debit' or 'credit'
        period
            DAY = 0
            MONTH = 2
        """
        logging.debug('export data %s %s', type_movement, period)

        chart_params = {}
        axis = {
            'tickFont': 'Sans',
            'labelFont': 'Sans',
            'labelFontSize': 14,
            'labelColor': '#666666',
            'lineColor': '#b3b3b3',
            'tickColor': '#000000',
            'tickFontSize': 12
        }
        chart_params['font_options'] = {
            'titleFont': 'Sans',
            'titleFontSize': 12,
            'titleColor': '#000000',
            'axis': axis
        }

        if type_movement == 'credit':
            what_filter = 'Credits'
        elif type_movement == 'debit':
            what_filter = 'Debits'
        else:
            logging.debug('ERROR type_movement should be credit or debit')

        if period == DAY:
            when = 'day'
        elif period == MONTH:
            when = 'month'
        else:
            logging.debug('ERROR period should be DAY or MONTH')

        title = _('%(what_filter)s by %(when)s') % {
            'what_filter': what_filter,
            'when': when
        }
        chart_params['title'] = title
        chart_params['x_label'] = ''
        chart_params['y_label'] = ''
        chart_params['current_chart.type'] = 1
        xo_color = profile.get_color()
        chart_params['chart_line_color'] = xo_color.get_stroke_color()
        chart_params['chart_color'] = xo_color.get_fill_color()
        """
        'chart_data': [
            ['hello', 200.0],
            ['mrch', 100.0]],
        """
        transactions = self.data['transactions']

        groups = {}
        for transaction in transactions:
            if transaction['type'] == type_movement:
                date = transaction['date']
                if period == DAY:
                    group = date
                elif period == MONTH:
                    d = datetime.date.fromordinal(date)
                    group = datetime.date(d.year, d.month, 1).toordinal()
                if group in list(groups.keys()):
                    groups[group] = groups[group] + transaction['amount']
                else:
                    groups[group] = transaction['amount']

        data = []
        for group in sorted(groups.keys()):
            if period == DAY:
                label = datetime.date.fromordinal(group).isoformat()
            elif period == MONTH:
                d = datetime.date.fromordinal(group)
                label = '%s-%s' % (d.year, d.month)

            data.append([label, groups[group]])

        chart_params['chart_data'] = data

        logging.debug('chart_data %s', chart_params)

        # save to the journal
        data_file = tempfile.NamedTemporaryFile(mode='w', suffix='.json')
        journal_entry = datastore.create()
        journal_entry.metadata['title'] = title
        journal_entry.metadata['keep'] = '0'
        journal_entry.metadata['mime_type'] = 'application/x-chart-activity'
        journal_entry.metadata['activity'] = 'org.sugarlabs.SimpleGraph'

        json.dump(chart_params, data_file.file)
        data_file.file.close()
        journal_entry.file_path = data_file.name

        logging.debug('Create %s data file', data_file.name)
        datastore.write(journal_entry)
        self._show_journal_alert(_('Exported data'), _('Open in the Journal'),
                                 journal_entry.object_id)
Пример #2
0
    def __init__(self, pc, toolbar_box):

        GObject.GObject.__init__(self)

        self._abiword_canvas = pc.abiword_canvas

        copy = CopyButton()
        copy.props.accelerator = '<Ctrl>C'
        copy.connect('clicked', lambda button: pc.abiword_canvas.copy())
        self.insert(copy, -1)
        copy.show()

        paste = PasteButton()
        paste.props.accelerator = '<Ctrl>V'
        paste.connect('clicked', self.__paste_button_cb)
        self.insert(paste, -1)
        paste.show()

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        undo = UndoButton(sensitive=False)
        undo.connect('clicked', lambda button: pc.abiword_canvas.undo())
        pc.abiword_canvas.connect("can-undo", lambda abi, can_undo:
                                  undo.set_sensitive(can_undo))
        self.insert(undo, -1)
        undo.show()

        redo = RedoButton(sensitive=False)
        redo.connect('clicked', lambda button: pc.abiword_canvas.redo())
        pc.abiword_canvas.connect("can-redo", lambda abi, can_redo:
                                  redo.set_sensitive(can_redo))
        self.insert(redo, -1)
        redo.show()

        pc.abiword_canvas.connect('text-selected', lambda abi, b:
                                  copy.set_sensitive(True))
        pc.abiword_canvas.connect('image-selected', lambda abi, b:
                                  copy.set_sensitive(True))
        pc.abiword_canvas.connect('selection-cleared', lambda abi, b:
                                  copy.set_sensitive(False))

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        search_label = Gtk.Label(label=_("Search") + ": ")
        search_label.show()
        search_item_page_label = Gtk.ToolItem()
        search_item_page_label.add(search_label)
        self.insert(search_item_page_label, -1)
        search_item_page_label.show()

        # setup the search options
        self._search_entry = iconentry.IconEntry()
        self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
                                              'system-search')
        self._search_entry.connect('activate', self._search_entry_activated_cb)
        self._search_entry.connect('changed', self._search_entry_changed_cb)
        self._search_entry.add_clear_button()
        self._add_widget(self._search_entry, expand=True)

        self._findprev = ToolButton('go-previous-paired')
        self._findprev.set_tooltip(_('Find previous'))
        self.insert(self._findprev, -1)
        self._findprev.show()
        self._findprev.connect('clicked', self._findprev_cb)

        self._findnext = ToolButton('go-next-paired')
        self._findnext.set_tooltip(_('Find next'))
        self.insert(self._findnext, -1)
        self._findnext.show()
        self._findnext.connect('clicked', self._findnext_cb)

        # set the initial state of the search controls
        # note: we won't simple call self._search_entry_changed_cb
        # here, as that will call into the abiword_canvas, which
        # is not mapped on screen here, causing the set_find_string
        # call to fail
        self._findprev.set_sensitive(False)
        self._findnext.set_sensitive(False)
Пример #3
0
    def build_header(self):
        # Add the header.
        self.periodlabel = Gtk.Label()
        self.periodlabel.set_padding(10, 0)
        self._period_label_item = Gtk.ToolItem()
        self._period_label_item.add(self.periodlabel)
        self.periodlabel.set_halign(Gtk.Align.END)
        self._period_label_item.set_size_request(style.GRID_CELL_SIZE * 5, -1)

        headerbox = Gtk.Toolbar()
        headerbox.modify_bg(Gtk.StateType.NORMAL,
                            style.Color('#424242').get_gdk_color())
        headerbox.set_size_request(-1, style.GRID_CELL_SIZE)

        # register buttons
        self.newcreditbtn = ToolButton('row-insert-credit')
        self.newcreditbtn.set_tooltip(_("New Credit"))
        self.newcreditbtn.props.accelerator = '<Ctrl>A'
        self.newcreditbtn.connect('clicked', self.__newcredit_cb)

        self.newdebitbtn = ToolButton('row-insert-debit')
        self.newdebitbtn.set_tooltip(_("New Debit"))
        self.newdebitbtn.props.accelerator = '<Ctrl>D'
        self.newdebitbtn.connect('clicked', self.__newdebit_cb)

        self.undoactionbtn = UndoButton()
        self.undoactionbtn.connect('clicked', self.__undoaction_cb)

        self.redoactionbtn = RedoButton()
        self.redoactionbtn.connect('clicked', self.__redoaction_cb)

        self.eraseitembtn = ToolButton('basket')
        self.eraseitembtn.set_tooltip(_("Erase Transaction"))
        self.eraseitembtn.props.accelerator = '<Ctrl>E'
        self.eraseitembtn.connect('clicked', self.__eraseitem_cb)

        headerbox.insert(self.newcreditbtn, -1)
        headerbox.insert(self.newdebitbtn, -1)
        headerbox.insert(self.eraseitembtn, -1)
        headerbox.insert(self.undoactionbtn, -1)
        headerbox.insert(self.redoactionbtn, -1)

        self.header_separator_visible = Gtk.SeparatorToolItem()
        headerbox.insert(self.header_separator_visible, -1)

        self.export_image = ToolButton('save-as-image')
        self.export_image.set_tooltip(_("Save as Image"))
        self.export_image.connect('clicked', self.__save_image_cb)
        headerbox.insert(self.export_image, -1)

        self._header_separator = Gtk.SeparatorToolItem()
        self._header_separator.props.draw = False
        self._header_separator.set_expand(True)
        headerbox.insert(self._header_separator, -1)

        # period controls
        self.thisperiodbtn = ToolButton('go-down')
        self.thisperiodbtn.props.accelerator = '<Ctrl>Down'
        self.thisperiodbtn.connect('clicked', self.thisperiod_cb)

        self.prevperiodbtn = ToolButton('go-previous-paired')
        self.prevperiodbtn.props.accelerator = '<Ctrl>Left'
        self.prevperiodbtn.connect('clicked', self.prevperiod_cb)

        self.nextperiodbtn = ToolButton('go-next-paired')
        self.nextperiodbtn.props.accelerator = '<Ctrl>Right'
        self.nextperiodbtn.connect('clicked', self.nextperiod_cb)

        period_options = {
            DAY: _('Day'),
            WEEK: _('Week'),
            MONTH: _('Month'),
            YEAR: _('Year'),
            FOREVER: _('Forever')
        }
        periodcombo = FilterToolItem('calendar', MONTH, period_options,
                                     _('Select period'))

        periodcombo.connect('changed', self.__period_changed_cb)

        headerbox.insert(periodcombo, -1)
        headerbox.insert(self.prevperiodbtn, -1)
        headerbox.insert(self.nextperiodbtn, -1)
        headerbox.insert(self.thisperiodbtn, -1)

        headerbox.insert(self._period_label_item, -1)
        return headerbox
Пример #4
0
    def __init__(self, pc, toolbar_box):

        GObject.GObject.__init__(self)

        self._abiword_canvas = pc.abiword_canvas

        copy = CopyButton()
        copy.props.accelerator = '<Ctrl>C'
        copy.connect('clicked', lambda button: pc.abiword_canvas.copy())
        self.insert(copy, -1)
        copy.show()

        paste = PasteButton()
        paste.props.accelerator = '<Ctrl>V'
        paste.connect('clicked', self.__paste_button_cb)
        self.insert(paste, -1)
        paste.show()

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        undo = UndoButton(sensitive=False)
        undo.connect('clicked', lambda button: pc.abiword_canvas.undo())
        pc.abiword_canvas.connect("can-undo", lambda abi, can_undo:
                                  undo.set_sensitive(can_undo))
        self.insert(undo, -1)
        undo.show()

        redo = RedoButton(sensitive=False)
        redo.connect('clicked', lambda button: pc.abiword_canvas.redo())
        pc.abiword_canvas.connect("can-redo", lambda abi, can_redo:
                                  redo.set_sensitive(can_redo))
        self.insert(redo, -1)
        redo.show()

        pc.abiword_canvas.connect('text-selected', lambda abi, b:
                                  copy.set_sensitive(True))
        pc.abiword_canvas.connect('image-selected', lambda abi, b:
                                  copy.set_sensitive(True))
        pc.abiword_canvas.connect('selection-cleared', lambda abi, b:
                                  copy.set_sensitive(False))

        separator = Gtk.SeparatorToolItem()
        self.insert(separator, -1)
        separator.show()

        search_label = Gtk.Label(label=_("Search") + ": ")
        search_label.show()
        search_item_page_label = Gtk.ToolItem()
        search_item_page_label.add(search_label)
        self.insert(search_item_page_label, -1)
        search_item_page_label.show()

        # setup the search options
        self._search_entry = iconentry.IconEntry()
        self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
                                              'system-search')
        self._search_entry.connect('activate', self._search_entry_activated_cb)
        self._search_entry.connect('changed', self._search_entry_changed_cb)
        self._search_entry.add_clear_button()
        self._add_widget(self._search_entry, expand=True)

        self._findprev = ToolButton('go-previous-paired')
        self._findprev.set_tooltip(_('Find previous'))
        self.insert(self._findprev, -1)
        self._findprev.show()
        self._findprev.connect('clicked', self._findprev_cb)

        self._findnext = ToolButton('go-next-paired')
        self._findnext.set_tooltip(_('Find next'))
        self.insert(self._findnext, -1)
        self._findnext.show()
        self._findnext.connect('clicked', self._findnext_cb)

        # set the initial state of the search controls
        # note: we won't simple call self._search_entry_changed_cb
        # here, as that will call into the abiword_canvas, which
        # is not mapped on screen here, causing the set_find_string
        # call to fail
        self._findprev.set_sensitive(False)
        self._findnext.set_sensitive(False)