<commit_before>"""
This module manages all market related activities
"""

import json
import logging
import hashlib
import random
from threading import Thread

from protocol import proto_page, query_page
from reputation import Reputation
from orders import Orders
import protocol
import lookup
from pymongo import MongoClient
from data_uri import DataURI
from PIL import Image, ImageOps
from StringIO import StringIO
import base64

class Market(object):

    def __init__(self, transport):

        """This class manages the active market for the application

        Attributes:
          _transport (CryptoTransportLayer): Transport layer for messaging between nodes.
          _dht (DHT): For storage across the network.
          _market_id (int): Indicates which local market we're working with.

        """

        # Current
        self._transport = transport
        self._dht = transport.getDHT()
        self._market_id = transport.getMarketID()
        self._myself = transport.getMyself()
        self._peers = self._dht.getActivePeers()

        # Legacy for now
        self.query_ident = None
        self.reputation = Reputation(self._transport)
        self.orders = Orders(self._transport)
        self.order_entries = self.orders._orders
        self.nicks = {}
        self.pages = {}
        self.welcome = False
        self.mypage = None
        self.signature = None
        self.nickname = None

        self._log = logging.getLogger('[%s] %s' % (self._market_id, self.__class__.__name__))
        self._log.info("Loading Market %s" % self._market_id)


        #MONGODB_URI = 'mongodb://localhost:27017'
        _dbclient = MongoClient()
        self._db = _dbclient.openbazaar

        self.settings = self._transport.settings

        welcome = True

        if self.settings:
            if  'welcome' in self.settings.keys() and self.settings['welcome']:
                welcome = False

        # Register callbacks for incoming events
        self._transport.add_callback('query_myorders', self.on_query_myorders)
        self._transport.add_callback('peer', self.on_peer)
        self._transport.add_callback('query_page', self.on_query_page)
        self._transport.add_callback('page', self.on_page)
        self._transport.add_callback('negotiate_pubkey', self.on_negotiate_pubkey)
        self._transport.add_callback('proto_response_pubkey', self.on_response_pubkey)

        self.load_page(welcome)





    def load_page(self, welcome):

        nickname = self.settings['nickname'] if self.settings.has_key("nickname") else ""
        store_description = self.settings['storeDescription'] if self.settings.has_key("storeDescription") else ""

        tagline = "%s: %s" % (nickname, store_description)
        self.mypage = tagline
        self.nickname = nickname
        self.signature = self._transport._myself.sign(tagline)

        if welcome:
            self._db.settings.update({}, {"$set":{"welcome":"noshow"}})
        else:
            self.welcome = False


    def save_product(self, msg):
        #self._log.debug("Saving product: %s" % msg)

        msg['market_id'] = self._market_id


        product_id = msg['id'] if msg.has_key("id") else ""

        if product_id == "":
            product_id = random.randint(0, 1000000)

        if not msg.has_key("productPrice") or not msg['productPrice'] > 0:
            msg['productPrice'] = 0

        if not msg.has_key("productQuantity") or not msg['productQuantity'] > 0:
            msg['productQuantity'] = 1


        if msg.has_key('productImageData'):

            uri = DataURI(msg['productImageData'])
            imageData = uri.data
            mime_type = uri.mimetype
            charset = uri.charset

            image = Image.open(StringIO(imageData))
            croppedImage = ImageOps.fit(image, (100, 100), centering=(0.5, 0.5))
            data = StringIO()
            croppedImage.save(data, format='PNG')


            new_uri = DataURI.make('image/png', charset=charset, base64=True, data=data.getvalue())
            data.close()
            msg['productImageData'] = new_uri

        # Save product listing to DHT
        listing = json.dumps(msg)
        listing_key = hashlib.sha1(listing).hexdigest()

        hash_value = hashlib.new('ripemd160')
        hash_value.update(listing_key)
        listing_key = hash_value.hexdigest()

        msg['key'] = listing_key

        self._db.products.update({'id':product_id}, {'$set':msg}, True)

        self._log.debug('New Listing Key: %s' % listing_key)

        # Store listing



        self._transport._dht.iterativeStore(self._transport, listing_key, listing, self._transport._guid)

        self.update_listings_index()

        # If keywords store them in the keyword index

    def republish_listing(self, msg):

        listing_id = msg.get('productID')
        listing = self._db.products.find_one({'id':listing_id})
        key = listing['key']

        listing = json.loads(listing)
        print listing

        self._transport._dht.iterativeStore(self._transport, key, listing, self._transport._guid)




    def update_listings_index(self):

        # Store to marketplace listing index
        listing_index_key = hashlib.sha1('listings-%s' % self._transport._guid).hexdigest()
        hashvalue = hashlib.new('ripemd160')
        hashvalue.update(listing_index_key)
        listing_index_key = hashvalue.hexdigest()

        # Calculate index of listings
        listing_ids = self._db.products.find({'market_id':self._transport._market_id}, {'key':1})
        my_listings = []
        for listing_id in listing_ids:
            my_listings.append(listing_id['key'])

        self._log.debug('My Listings: %s' % my_listings)

        # Sign listing index for validation and tamper resistance
        data_string = str({'guid':self._transport._guid, 'listings': my_listings})
        signature = self._myself.sign(data_string).encode('hex')

        value = {'signature': signature, 'data': {'guid':self._transport._guid, 'listings': my_listings}}

        # Pass off to thread to keep GUI snappy
        Thread(target=self._transport._dht.iterativeStore, args=(self._transport, listing_index_key, value, self._transport._guid,)).start()


    def remove_product(self, msg):
        self._log.info("Removing product: %s" % msg)
        self._db.products.remove({'id':msg['productID']})
        self.update_listings_index()


    def get_products(self):
        self._log.info('Getting products for market: %s' % self._transport._market_id)
        products = self._db.products.find({'market_id':self._transport._market_id})
        my_products = []

        for product in products:
            my_products.append({"productTitle":product['productTitle'] if product.has_key("productTitle") else "",
                                "id":product['id'] if product.has_key("id") else "",
                                "productDescription":product['productDescription'] if product.has_key("productDescription") else "",
                                "productPrice":product['productPrice'] if product.has_key("productPrice") else "",
                                "productShippingPrice":product['productShippingPrice'] if product.has_key("productShippingPrice") else "",
                                "productTags":product['productTags'] if product.has_key("productTags") else "",
                                "productImageData":product['productImageData'] if product.has_key("productImageData") else "",
                                "productQuantity":product['productQuantity'] if product.has_key("productQuantity") else "",
                                "key":product['key'] if product.has_key("key") else "",
                               })

        return {"products": my_products}




    # SETTINGS

    def save_settings(self, msg):
        self._log.info("Settings to save %s" % msg)
        self._log.info(self._transport)
        self._db.settings.update({'id':'%s'%self._transport._market_id}, {'$set':msg}, True)

    def get_settings(self):
        self._log.info(self._transport._market_id)
        settings = self._db.settings.find_one({'id':'%s'%self._transport._market_id})

        if settings:
            return {"bitmessage": settings['bitmessage'] if settings.has_key("bitmessage") else "",
                    "email": settings['email'] if settings.has_key("email") else "",
                    "PGPPubKey": settings['PGPPubKey'] if settings.has_key("PGPPubKey") else "",
                    "pubkey": settings['pubkey'] if settings.has_key("pubkey") else "",
                    "nickname": settings['nickname'] if settings.has_key("nickname") else "",
                    "secret": settings['secret'] if settings.has_key("secret") else "",
                    "welcome": settings['welcome'] if settings.has_key("welcome") else "",
                    "escrowAddresses": settings['escrowAddresses'] if settings.has_key("escrowAddresses") else "",
                    "storeDescription": settings['storeDescription'] if settings.has_key("storeDescription") else "",
                    "city": settings['city'] if settings.has_key("city") else "",
                    "stateRegion": settings['stateRegion'] if settings.has_key("stateRegion") else "",
                    "street1": settings['street1'] if settings.has_key("street1") else "",
                    "street2": settings['street2'] if settings.has_key("street2") else "",
                    "countryCode": settings['countryCode'] if settings.has_key("countryCode") else "",
                    "zip": settings['zip'] if settings.has_key("zip") else "",
                    "arbiterDescription": settings['arbiterDescription'] if settings.has_key("arbiterDescription") else "",
                    "arbiter": settings['arbiter'] if settings.has_key("arbiter") else "",
                   }

<commit_msg>Market module now uses sqlite.<commit_after>"""
This module manages all market related activities
"""

import json
import logging
import hashlib
import random
from threading import Thread

from protocol import proto_page, query_page
from reputation import Reputation
from orders import Orders
import protocol
import lookup
from pymongo import MongoClient
from db_store import Obdb 
from data_uri import DataURI
from PIL import Image, ImageOps
from StringIO import StringIO
import base64

class Market(object):

    def __init__(self, transport):

        """This class manages the active market for the application

        Attributes:
          _transport (CryptoTransportLayer): Transport layer for messaging between nodes.
          _dht (DHT): For storage across the network.
          _market_id (int): Indicates which local market we're working with.

        """

        # Current
        self._transport = transport
        self._dht = transport.getDHT()
        self._market_id = transport.getMarketID()
        self._myself = transport.getMyself()
        self._peers = self._dht.getActivePeers()

        # Legacy for now
        self.query_ident = None
        self.reputation = Reputation(self._transport)
        self.orders = Orders(self._transport)
        self.order_entries = self.orders._orders
        self.nicks = {}
        self.pages = {}
        self.welcome = False
        self.mypage = None
        self.signature = None
        self.nickname = None

        self._log = logging.getLogger('[%s] %s' % (self._market_id, self.__class__.__name__))
        self._log.info("Loading Market %s" % self._market_id)

        self._db = Obdb()
        self.settings = self._transport.settings

        welcome = True

        if self.settings:
            if  'welcome' in self.settings.keys() and self.settings['welcome']:
                welcome = False

        # Register callbacks for incoming events
        self._transport.add_callback('query_myorders', self.on_query_myorders)
        self._transport.add_callback('peer', self.on_peer)
        self._transport.add_callback('query_page', self.on_query_page)
        self._transport.add_callback('page', self.on_page)
        self._transport.add_callback('negotiate_pubkey', self.on_negotiate_pubkey)
        self._transport.add_callback('proto_response_pubkey', self.on_response_pubkey)

        self.load_page(welcome)


    def load_page(self, welcome):

        nickname = self.settings['nickname'] if self.settings.has_key("nickname") else ""
        store_description = self.settings['storeDescription'] if self.settings.has_key("storeDescription") else ""

        tagline = "%s: %s" % (nickname, store_description)
        self.mypage = tagline
        self.nickname = nickname
        self.signature = self._transport._myself.sign(tagline)

        if welcome:
            self._db.updateEntries("settings", {'market_id': self._transport._market_id}, {"welcome":"noshow"})
        else:
            self.welcome = False


    def save_product(self, msg):
        #self._log.debug("Saving product: %s" % msg)

        msg['market_id'] = self._market_id


        product_id = msg['id'] if msg.has_key("id") else ""

        if product_id == "":
            product_id = random.randint(0, 1000000)

        if not msg.has_key("productPrice") or not msg['productPrice'] > 0:
            msg['productPrice'] = 0

        if not msg.has_key("productQuantity") or not msg['productQuantity'] > 0:
            msg['productQuantity'] = 1


        if msg.has_key('productImageData'):

            uri = DataURI(msg['productImageData'])
            imageData = uri.data
            mime_type = uri.mimetype
            charset = uri.charset

            image = Image.open(StringIO(imageData))
            croppedImage = ImageOps.fit(image, (100, 100), centering=(0.5, 0.5))
            data = StringIO()
            croppedImage.save(data, format='PNG')


            new_uri = DataURI.make('image/png', charset=charset, base64=True, data=data.getvalue())
            data.close()
            msg['productImageData'] = new_uri

        # Save product listing to DHT
        listing = json.dumps(msg)
        listing_key = hashlib.sha1(listing).hexdigest()

        hash_value = hashlib.new('ripemd160')
        hash_value.update(listing_key)
        listing_key = hash_value.hexdigest()

        msg['key'] = listing_key
        self._db.insertEntry("products", msg)
        self._log.debug('New Listing Key: %s' % listing_key)

        # Store listing



        self._transport._dht.iterativeStore(self._transport, listing_key, listing, self._transport._guid)

        self.update_listings_index()

        # If keywords store them in the keyword index

    def republish_listing(self, msg):

        listing_id = msg.get('productID')
        listing_ids = self._db.selectEntries("products", {"id": listing_id})
        key = listing['key']

        listing = json.loads(listing)

        self._transport._dht.iterativeStore(self._transport, key, listing, self._transport._guid)


    def update_listings_index(self):

        # Store to marketplace listing index
        listing_index_key = hashlib.sha1('listings-%s' % self._transport._guid).hexdigest()
        hashvalue = hashlib.new('ripemd160')
        hashvalue.update(listing_index_key)
        listing_index_key = hashvalue.hexdigest()

        # Calculate index of listings
        my_listings = []
        listing_ids = self._db.selectEntries("products", {"market_id": self._transport._market_id, 'key':1})
        for listing_id in listing_ids: 
            my_listings.append(listing_id['key'])

        self._log.debug('My Listings: %s' % my_listings)

        # Sign listing index for validation and tamper resistance
        data_string = str({'guid':self._transport._guid, 'listings': my_listings})
        signature = self._myself.sign(data_string).encode('hex')

        value = {'signature': signature, 'data': {'guid':self._transport._guid, 'listings': my_listings}}

        # Pass off to thread to keep GUI snappy
        Thread(target=self._transport._dht.iterativeStore, args=(self._transport, listing_index_key, value, self._transport._guid,)).start()


    def remove_product(self, msg):
        self._log.info("Removing product: %s" % msg)
        self._db.deleteEntries("products", {'id':msg['productID']})
        self.update_listings_index()


    def get_products(self):
        self._log.info('Getting products for market: %s' % self._transport._market_id)
        products = self._db.selectEntries("products", {"market_id": self._transport._market_id})
        return {"products": products}



    # SETTINGS

    def save_settings(self, msg):
        self._log.info("Settings to save %s" % msg)
        self._log.info(self._transport)
        self._db.updateEntries("settings", {'market_id': self._transport._market_id}, msg)

    def get_settings(self):
        self._log.info(self._transport._market_id) 
        settings = self._db.getOrCreate("settings", {"market_id": self._transport._market_id})
        if settings:
            return settings


{'fraction_non_alphanumeric': 0.11821067481777568, 'fraction_numerical': 0.004761344932988479, 'mean_word_length': 2.687256176853056, 'pattern_counts': {'":': 37, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5061507', 'n_tokens_mistral': 4958, 'n_tokens_neox': 4650, 'n_words': 1014}