Skip to content
This repository has been archived by the owner on Jun 14, 2018. It is now read-only.

Commit

Permalink
add event notification, increase release number
Browse files Browse the repository at this point in the history
  • Loading branch information
Benoit HERVIER committed Jun 20, 2013
1 parent 4a6a9e5 commit bcd8aff
Show file tree
Hide file tree
Showing 6 changed files with 316 additions and 14 deletions.
8 changes: 5 additions & 3 deletions bitpurse/__init__.py
Expand Up @@ -29,7 +29,7 @@

__author__ = 'Benoit HERVIER (Khertan)'
__email__ = 'khertan@khertan.net'
__version__ = '2.0.2'
__version__ = '2.1.0'
__build__ = '1'
__upgrade__ = '''0.9.0: First beta release
0.9.1: Second beta release, add missing python-crypto dependancy
Expand All @@ -56,7 +56,9 @@
Remove transaction without confirmation older than 7 days
Add a copy address item menu in address page menu
2.0.2: Fix issue #4, avoid putting a change address with a null value
2.0.3: Workarround to try to fix tracker index for shareui'''
2.0.3: Workarround to try to fix tracker index for shareui
2.1.0: Fix a bug in storage of old transactions
Add event notification on new transaction'''


class BitPurse(QApplication):
Expand Down Expand Up @@ -129,4 +131,4 @@ def __init__(self):
self.walletController.settings.numberOfLaunch += 1

if __name__ == '__main__':
sys.exit(BitPurse().exec_())
sys.exit(BitPurse().exec_())
4 changes: 2 additions & 2 deletions bitpurse/address.py
Expand Up @@ -73,7 +73,7 @@ def __init__(self, jsondict={}):
for tx in jsondict['txs']:
if (tx['timestamp']
+ (60 * 60 * 24 * 7)
> time.time()) and (tx['confirmations'] >= 1):
> time.time()) or (tx['confirmations'] >= 1):

self.transactions.append(TransactionHist(
tx['hash'],
Expand Down Expand Up @@ -179,4 +179,4 @@ def __init__(self, txhash, timestamp, address, amount, confirmations):
.strftime('%c'), 'utf-8')
self.address = address
self.amount = amount
self.confirmations = confirmations
self.confirmations = confirmations
31 changes: 31 additions & 0 deletions bitpurse/dbusproxy.py
@@ -0,0 +1,31 @@
import dbus
import dbus.service

def safe_str(txt):
if txt:
return txt.encode()
else:
return ''

def safe_first_line(txt):
txt = safe_str(txt)
lines = util.remove_html_tags(txt).strip().splitlines()
if not lines or lines[0] == '':
return ''
else:
return lines[0]

class DBusProxy(dbus.service.Object):
#DBusPodcastsProxy(lambda: self.channels, self.on_itemUpdate_activate(), self.playback_episodes, self.download_episode_list, bus_name)
def __init__(self, sendTo, busname):
self._send_to = sendTo
dbus.service.Object.__init__(self, \
object_path='/', \
bus_name=busname)


@dbus.service.method(dbus_interface='net.khertan.bitpurse', in_signature='s', out_signature='')
def sendWithLink(self, btcaddr):
self._send_to(btcaddr)
return

248 changes: 248 additions & 0 deletions bitpurse/eventfeed.py
@@ -0,0 +1,248 @@
# -*- coding: utf-8 -*-

"""
A library to post events to the MeeGo 1.2 Harmattan Event Feed
This library is intended to be used by N950, N9 application or
service developers who want to post their own content to the
MeeGo 1.2 Harmattan UX Event Feed screen.
"""

__license__ = """
Copyright (c) 2011, Thomas Perl <m@thp.io>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
"""

__version__ = '1.0'
__author__ = 'Thomas Perl <thp.io/about>'
__url__ = 'http://thp.io/2011/eventfeed/'

__version_info__ = tuple(int(x) for x in __version__.split('.'))

# Dependency on PySide for encoding/decoding like MRemoteAction
from PySide.QtCore import QBuffer, QIODevice, QDataStream, QByteArray

# Python D-Bus Library dependency for communcating with the service
import dbus
import dbus.service
import dbus.mainloop
import dbus.glib

import datetime
import logging


logger = logging.getLogger(__name__)

# When the user clicks on "Refresh", this signal gets sent via D-Bus:
# signal sender=:1.8 -> dest=(null destination) serial=855 path=/eventfeed; interface=com.nokia.home.EventFeed; member=refreshRequested
# TODO: Implement support for receiving this signal

# MRemoteAction::toString()
# http://apidocs.meego.com/1.0/mtf/mremoteaction_8cpp_source.html
def qvariant_encode(value):
buffer = QBuffer()
buffer.open(QIODevice.ReadWrite)
stream = QDataStream(buffer)
stream.writeQVariant(value)
buffer.close()
return buffer.buffer().toBase64().data().strip()

# MRemoteAction::fromString()
# http://apidocs.meego.com/1.0/mtf/mremoteaction_8cpp_source.html
def qvariant_decode(data):
byteArray = QByteArray.fromBase64(data)
buffer = QBuffer(byteArray)
buffer.open(QIODevice.ReadOnly)
stream = QDataStream(buffer)
result = stream.readQVariant()
buffer.close()
return result


class EventFeedItem(object):
"""One item that can be posted to the event feed"""

def __init__(self, icon, title, timestamp=None):
"""Create a new event feed item
:param icon: Icon name or path to icon file (can be a URL)
:param title: The title text describing this item
:param timestamp: datetime.datetime object when the item happened (optional)
"""
if timestamp is None:
timestamp = datetime.datetime.now()

timestamp = timestamp.strftime('%Y-%m-%d %H:%M:%S')

self.args = {
'icon': icon,
'title': title,
'timestamp': timestamp,
}

# ID assigned when showing item
self.id = -1

# Callback for when the action is clicked
self.callback = None

# Action data (custom list of stuff for callback)
self.action_data = None

def set_body(self, body):
"""Body text of the item (string)"""
self.args['body'] = body

def set_image_list(self, image_list):
"""List of image filenames/URLs (list of strings)"""
self.args['imageList'] = image_list

def set_footer(self, footer):
"""Footer text, displayed near the time (string)"""
self.args['footer'] = footer

def set_video(self, video):
"""Flag to overlay a play button on the thumbnail (bool)"""
self.args['video'] = video

def set_url(self, url):
"""The URL to be opened when the item is clicked (string)"""
self.args['url'] = url

def set_action_data(self, *args):
"""The data to be sent when clicked (list of str, int, bool)"""
self.action_data = args

def set_custom_action(self, callback):
"""The action to be executed when clicked (callable)"""
self.callback = callback


class EventFeedService(dbus.service.Object):
EVENT_FEED_NAME = 'com.nokia.home.EventFeed'
EVENT_FEED_PATH = '/eventfeed'
EVENT_FEED_INTF = 'com.nokia.home.EventFeed'
EVENT_FEED_CALL = 'addItem'

DEFAULT_NAME = 'com.thpinfo.meego.EventFeedService'
DEFAULT_PATH = '/EventFeedService'
DEFAULT_INTF = 'com.thpinfo.meego.EventFeedService'

def __init__(self, source_name, source_display_name, on_data_received=None):
"""Create a new client to the event feed service
:param source_name: SLUG for the application (used in the service name)
:param source_display_name: User-visible application name (in the UI)
:param on_data_received: Callback for received data (optional)
"""
dbus_main_loop = dbus.glib.DBusGMainLoop(set_as_default=True)
session_bus = dbus.SessionBus(dbus_main_loop)

self.local_name = '.'.join([self.DEFAULT_NAME, source_name])
logger.debug('Local D-Bus name: %s', self.local_name)
bus_name = dbus.service.BusName(self.local_name, bus=session_bus)

dbus.service.Object.__init__(self,
object_path=self.DEFAULT_PATH,
bus_name=bus_name)

self.next_action_id = 1
self.actions = {}
self.source_name = source_name
self.source_display_name = source_display_name
self.on_data_received = on_data_received

o = session_bus.get_object(self.EVENT_FEED_NAME, self.EVENT_FEED_PATH)
self.event_feed = dbus.Interface(o, self.EVENT_FEED_INTF)

@dbus.service.method(DEFAULT_INTF)
def ReceiveActionCallback(self, action_id):
action_id = int(action_id)
logger.debug('Received callback ID %d', action_id)
callable = self.actions[action_id]
callable()

@dbus.service.method(DEFAULT_INTF)
def ReceiveActionData(self, *args):
logger.debug('Received data: %r', args)
if self.on_data_received is not None:
self.on_data_received(*args)

def add_item(self, item):
"""Send a EventFeedItem to the service to be displayed
:param item: EventFeedItem to be displayed
"""
if item.id != -1:
logger.debug('Message %d already shown - updating footer.', item.id)
self.update_item(item)
return item.id

action = item.callback
action_data = item.action_data
data = item.args.copy()

data['sourceName'] = self.source_name
data['sourceDisplayName'] = self.source_display_name

if action is not None or action_data is not None:
remote_action = [
self.local_name,
self.DEFAULT_PATH,
self.DEFAULT_INTF,
]

if action is not None:
action_id = self.next_action_id
self.next_action_id += 1
self.actions[action_id] = action
remote_action.extend([
'ReceiveActionCallback',
qvariant_encode(action_id),
])
else: # action_data is not None
remote_action.append('ReceiveActionData')
remote_action.extend([qvariant_encode(x) for x in action_data])

data['action'] = ' '.join(remote_action)

item.id = self.event_feed.addItem(data)

return item.id

def update_item(self, item):
"""Update a previously-sent EventFeedItem
:param item: EventFeedItem to be updated
"""
if item.id == -1:
return False

if 'footer' not in item.args:
return False

data = {'footer': item.args['footer']}
data['sourceName'] = self.source_name
data['sourceDisplayName'] = self.source_display_name

self.event_feed.updateItem(item.id, data)
return True

def remove_items(self):
"""Remove all items """
self.event_feed.removeItemsBySourceName(self.source_name)
# No need to remember action IDs, because all items were removed
self.actions = {}

15 changes: 8 additions & 7 deletions bitpurse/wallet.py
Expand Up @@ -13,7 +13,7 @@
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.

from PySide.QtCore import Slot
from PySide.QtCore import Slot, Signal, QObject
from PySide.QtGui import QApplication
import json
from PBKDF2 import PBKDF2
Expand All @@ -38,8 +38,11 @@ class DataError(Exception):
pass


class Wallet(object):
class Wallet(QObject):
onNewTransaction = Signal(str, str, int)

def __init__(self,):
QObject.__init__(self)
self.addresses = []
self.balance = 0
self.settings = Settings()
Expand Down Expand Up @@ -318,10 +321,11 @@ def addTransactionHistForAddress(self, addr, transaction):
tx.amount = transaction.amount
tx.date = transaction.date
return
self.onNewTransaction.emit(addr, transaction.address, transaction.amount)
address.transactions.append(transaction)
return

def getPrivKeyForAddress(self, addr, secondPassword=None):
def getPrivKeyFor Address(self, addr, secondPassword=None):
'''Return private key for an address'''
for address in self.addresses:
if addr == address.addr:
Expand Down Expand Up @@ -437,15 +441,12 @@ def load_txs_from_blockchain(self,):
except KeyError:
print 'None tx in json data'

@Slot(unicode)
def remove(self, addr):
del self.addresses[self.getIndex(addr)]

@Slot(unicode)
def getLabelForAddr(self, addr):
return self.addresses[self.getIndex(addr)].label

@Slot(unicode, unicode)
def setLabelForAddr(self, addr, label):
self.addresses[self.getIndex(addr)].label = label

Expand All @@ -457,4 +458,4 @@ def update(self, passKey):
except:
import traceback
traceback.print_exc()
raise
raise

0 comments on commit bcd8aff

Please sign in to comment.