Esempio n. 1
0
class oDeskScreenlet(screenlets.Screenlet):
    """A simple example of how to create a Screenlet"""

    # default meta-info for Screenlets (should be removed and put into metainfo)
    __name__ = 'oDeskScreenlet'
    __version__ = '0.0.1+++'
    __author__ = 'Kirill Pekarov'
    __desc__ = __doc__    # set description to docstring of class
    __timeout = None
    text_colour = [1.0, 1.0, 1.0, 1.0]
    back_colour = [0, 0, 0, 1.0]
    show_image = True

    update_interval = 5
    odesk_key = ''
    odesk_secret = ''
    odesk_access_token_0 = ''
    odesk_access_token_1 = ''

    #keys = {'key': 'a4bf017a804d46a9695218f40a952e73', 'secret': '4493f04f50bbe387'}
    #keys['access_token_0'] = 'c664f9275a8871709ed6069dcc8aa07c'
    #keys['access_token_1'] = 'a7b6db2f1af73fe6'

    ODESK_WORKED_ON_FORMAT = '%Y%m%d'

    timereport_data = None

    def __init__(self, **keyword_args):
        # Create screenlet area
        screenlets.Screenlet.__init__(self, width=200, height=300, uses_theme=True, **keyword_args)

        self.theme_name = "default"

        self.add_options_group(_('Options'), _('Appearence Options'))
        self.add_option(ColorOption(_('Options'), 'text_colour', self.text_colour, _('Text Colour'), ''))
        self.add_option(ColorOption(_('Options'), 'back_colour', self.back_colour, _('Back Colour'), ''))

        self.add_options_group(_('oDesk'), _('oDesk Options'))
        self.add_option(IntOption('oDesk', 'update_interval',
                                  self.update_interval, 'Update interval:',
                                  'Update interval in minutes',
                                  min=5, max=360))
        self.add_option(StringOption('oDesk', 'odesk_key', '', 'API Key:', 'The oDesk API Key'))
        self.add_option(StringOption('oDesk', 'odesk_secret', '', 'API Secret:', 'The oDesk API Secret'), realtime=False)

        self.add_option(StringOption('oDesk', 'odesk_access_token_0', '', 'Access Token 1:', 'The oDesk API Token 0'))
        self.add_option(StringOption('oDesk', 'odesk_access_token_1', '', 'Access Token 2:', 'The oDesk API Token 1'))

        self.add_default_menuitems()
        self.update_interval = self.update_interval

    def get_client(self):
        """Authenticate using keys stored in ``keys.json`` and return
        the ``Client`` instance ready for work.

        """
        if len(self.odesk_key) > 0 and len(self.odesk_secret) > 0 \
            and len(self.odesk_access_token_0) > 0 and len(self.odesk_access_token_1) > 0:

            self.client = Client(self.odesk_key, self.odesk_secret,
                        oauth_access_token=self.odesk_access_token_0,
                        oauth_access_token_secret=self.odesk_access_token_1 )

    def get_auth_info(self):
        return self.client.get('https://www.odesk.com/api/auth/v1/info')

    def get_auth_user_uid(self):
        auth_info = self.get_auth_info()
        self.odesk_uid = auth_info['auth_user']['uid']

    def get_timereport(self, from_date=None, to_date=None):
        """Return parsed JSON data with timereport for given period.
        Fields are default.

        :from_date:    date object
        :to_date:      date object

        If empty - timereport from now to the begining of the current week.

        """

        if hasattr(self, 'client') is False:
            self.get_client()

        if hasattr(self, 'client'):
            now = datetime.now()
            if not to_date:
                to_date = now.date()
                if not from_date:
                    from_date = now.date() - timedelta(days=now.weekday())

            query = Query(
                select=Query.DEFAULT_TIMEREPORT_FIELDS,
                where=(Q('worked_on') >= from_date) & (Q('worked_on') <= to_date))

            self.get_auth_user_uid()

            return self.client.timereport.get_provider_report(self.odesk_uid, query)

    def get_today_and_this_week_times(self, data):
        """Return mapping of teams and hours worked for each team today
        and this week.

        :data:    parsed json data returned from ``get_timereport()`` function

        """
        now = datetime.now()

        date_idx = data['table']['cols'].index(
            {'type': 'date', 'label': 'worked_on'})
        team_idx = data['table']['cols'].index(
            {'type': 'string', 'label': 'team_name'})
        hours_idx = data['table']['cols'].index(
            {'type': 'number', 'label': 'hours'})

        teams = {}

        for row in data['table']['rows']:
            team_name = row['c'][team_idx]['v']
            hours = float(row['c'][hours_idx]['v'])
            date_worked_on = row['c'][date_idx]['v']
            if team_name not in teams:
                teams[team_name] = {'today_hours': 0.0, 'week_hours': 0.0}
            teams[team_name]['week_hours'] += hours
            if date_worked_on == now.strftime(self.ODESK_WORKED_ON_FORMAT):
                teams[team_name]['today_hours'] += hours
        return teams

    def get_timereport_layout(self, data):
        """Render text widged showing timereport data.

        :data:        parsed json data returned from ``get_timereport()`` function
        :odesk_uid:   oDesk user UID

        """
        row_template = '{0}:\n\t{1:.2f} hrs today\n\t{2:.2f} hrs this week\n'
        rows_rendered = '\n'.join(
            [row_template.format(team_name,
                                 team_data['today_hours'],
                                 team_data['week_hours'])
             for team_name, team_data
             in self.get_today_and_this_week_times(data).items()]
        )
        if not rows_rendered:
            rows_rendered = "\nNo worked hours yet"
        template = 'User: {odesk_uid}\n{rows}'
        return template.format(
            odesk_uid=self.odesk_uid,
            rows=rows_rendered
        )

    def on_draw(self, ctx):
        ctx.scale(self.scale, self.scale)

        if self.theme:
            data_start_position = 10
            data = self.get_timereport()
            if data is not None:
                data_text = self.get_timereport_layout(data)
            else:
                data_text = ''

            rectwidth = data_start_position + (len(data_text) * 13)
            # Background
            ctx.set_source_rgba(self.back_colour[0], self.back_colour[1], self.back_colour[2], 0.7)
            self.theme.draw_rounded_rectangle(ctx, 0, 0, 10, rectwidth, self.height, fill=True)

            # text
            if len(data_text) > 0:
                ctx.set_source_rgba(self.text_colour[0], self.text_colour[1], self.text_colour[2], 1.0)
                self.theme.draw_text(ctx, data_text, data_start_position, 23, 'Free Sans', 12, rectwidth - data_start_position,
                                     pango.ALIGN_LEFT)

    def on_draw_shape(self, ctx):
        self.on_draw(ctx)

    def show_edit_dialog(self):
        client = Client(self.odesk_key, self.odesk_secret)
        # create dialog
        dialog = gtk.Dialog(_("Verification Code"), self.window)
        dialog.resize(300, 200)

        link = gtk.LinkButton(format(client.auth.get_authorize_url()), "Press here and copy verification code here:")
        dialog.vbox.add(link)

        dialog.add_buttons(gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
        entrybox = gtk.Entry()
        dialog.vbox.add(entrybox)

        dialog.show_all()
        # run dialog
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            verification_code = entrybox.get_text()
            self.odesk_access_token_0, self.odesk_access_token_1 = client.auth.get_access_token(verification_code)
        dialog.hide()
        self.update()

    def update(self):
        gobject.idle_add(self.redraw_canvas)
        return True

    def __setattr__(self, name, value):
        # call Screenlet.__setattr__ in baseclass
        screenlets.Screenlet.__setattr__(self, name, value)

        # check for this Screenlet's attributes, we are interested in:
        if name == "update_interval":
            if value > 0:
                self.__dict__['update_interval'] = value
                if self.__timeout:
                    gobject.source_remove(self.__timeout)
                self.__timeout = gobject.timeout_add(value * 60 * 1000, self.update)
            else:
                pass
        elif name == "odesk_secret":
            if len(value) > 0 and len(self.odesk_key) > 0:
                self.__dict__['odesk_secret'] = value

                # if there is no tokens, show window with url and edit field
                # for enter verification code
                if len(self.odesk_access_token_0) == 0 or len(self.odesk_access_token_1) == 0:
                    self.show_edit_dialog()