<s> import logging <EOL> LOG = logging . getLogger ( __name__ ) <EOL> from re import compile as regex_compile <EOL> from time import time as time_now <EOL> from os . path import exists as path_exists , join as join_path <EOL> from threading import Lock <EOL> from pylons import config <EOL> import yaml <EOL> from paste . deploy . converters import asbool <EOL> from turbulenz_local . tools import get_absolute_path , create_dir <EOL> from turbulenz_local . lib . validation import ValidationException <EOL> from turbulenz_local . lib . tools import create_id <EOL> from turbulenz_local . lib . money import Money , get_currency <EOL> STORE_PRICING_TYPES = [ '<STR_LIT>' , '<STR_LIT>' ] <EOL> DEFAULT_CURRENCY_TYPE = '<STR_LIT>' <EOL> DEFAULT_CURRENCY = get_currency ( DEFAULT_CURRENCY_TYPE ) <EOL> class StoreError ( Exception ) : <EOL> def __init__ ( self , value , response_code = <NUM_LIT> ) : <EOL> super ( StoreError , self ) . __init__ ( ) <EOL> self . value = value <EOL> self . response_code = response_code <EOL> def __str__ ( self ) : <EOL> return self . value <EOL> class StoreUnsupported ( StoreError ) : <EOL> def __init__ ( self ) : <EOL> super ( StoreUnsupported , self ) . __init__ ( '<STR_LIT>' , <NUM_LIT> ) <EOL> class StoreInvalidTransactionId ( StoreError ) : <EOL> def __init__ ( self ) : <EOL> super ( StoreInvalidTransactionId , self ) . __init__ ( '<STR_LIT>' , <NUM_LIT> ) <EOL> class StoreItem ( object ) : <EOL> validate_key = regex_compile ( '<STR_LIT>' ) <EOL> def __init__ ( self , game , meta_data , existing_keys ) : <EOL> self . errors = [ ] <EOL> self . warnings = [ ] <EOL> self . path = None <EOL> self . game = game <EOL> self . index = None <EOL> if not isinstance ( meta_data , dict ) : <EOL> raise StoreError ( '<STR_LIT>' ) <EOL> try : <EOL> key = meta_data [ '<STR_LIT:key>' ] <EOL> except KeyError : <EOL> raise StoreError ( '<STR_LIT>' ) <EOL> if not self . validate_key . match ( key ) : <EOL> self . error ( '<STR_LIT>' ) <EOL> self . key = key <EOL> if key in existing_keys : <EOL> self . error ( '<STR_LIT>' % key ) <EOL> existing_keys . add ( key ) <EOL> if '<STR_LIT:title>' not in meta_data or meta_data [ '<STR_LIT:title>' ] is None : <EOL> self . error ( '<STR_LIT>' % key ) <EOL> self . title = '<STR_LIT>' <EOL> else : <EOL> self . title = meta_data [ '<STR_LIT:title>' ] <EOL> if '<STR_LIT:description>' not in meta_data or meta_data [ '<STR_LIT:description>' ] is None : <EOL> self . error ( '<STR_LIT>' % key ) <EOL> self . description = '<STR_LIT>' <EOL> else : <EOL> self . description = meta_data [ '<STR_LIT:description>' ] <EOL> if '<STR_LIT>' in meta_data : <EOL> self . warning ( '<STR_LIT>' <EOL> '<STR_LIT>' % key ) <EOL> self . images = { <EOL> '<STR_LIT>' : meta_data . get ( '<STR_LIT>' , '<STR_LIT>' ) , <EOL> '<STR_LIT>' : meta_data . get ( '<STR_LIT>' , '<STR_LIT>' ) , <EOL> '<STR_LIT>' : meta_data . get ( '<STR_LIT>' , '<STR_LIT>' ) <EOL> } <EOL> def error ( self , msg ) : <EOL> self . errors . append ( msg ) <EOL> def warning ( self , msg ) : <EOL> self . warnings . append ( msg ) <EOL> class StoreOffering ( StoreItem ) : <EOL> def __init__ ( self , game , meta_data , offering_keys , resource_keys ) : <EOL> super ( StoreOffering , self ) . __init__ ( game , meta_data , offering_keys ) <EOL> prices = meta_data . get ( '<STR_LIT>' , meta_data . get ( '<STR_LIT>' ) ) <EOL> if prices is None : <EOL> self . error ( '<STR_LIT>' % self . key ) <EOL> prices = { DEFAULT_CURRENCY_TYPE : <NUM_LIT:1> } <EOL> self . prices = { } <EOL> for currency , currency_price in prices . items ( ) : <EOL> if currency_price <= <NUM_LIT:0> : <EOL> self . error ( '<STR_LIT>' % ( currency , self . key ) ) <EOL> try : <EOL> self . prices [ currency ] = Money ( get_currency ( currency ) , currency_price ) <EOL> except TypeError : <EOL> self . error ( '<STR_LIT>' <EOL> % ( currency , self . key , currency ) ) <EOL> self . prices [ currency ] = Money ( get_currency ( currency ) , <NUM_LIT:1> ) <EOL> output = meta_data . get ( '<STR_LIT>' ) <EOL> if output is None : <EOL> self . error ( '<STR_LIT>' % self . key ) <EOL> output = { } <EOL> if not isinstance ( output , dict ) : <EOL> self . error ( '<STR_LIT>' % self . key ) <EOL> output = { } <EOL> self . output = { } <EOL> for output_key , output_amount in output . items ( ) : <EOL> if output_key not in resource_keys : <EOL> self . error ( '<STR_LIT>' % output_key ) <EOL> elif not isinstance ( output_amount , int ) : <EOL> self . error ( '<STR_LIT>' % output_key ) <EOL> elif output_amount <= <NUM_LIT:0> : <EOL> self . error ( '<STR_LIT>' % output_key ) <EOL> else : <EOL> self . output [ output_key ] = output_amount <EOL> try : <EOL> self . available = asbool ( meta_data . get ( '<STR_LIT>' , True ) ) <EOL> except ValueError : <EOL> self . error ( '<STR_LIT>' ) <EOL> def to_dict ( self ) : <EOL> return { '<STR_LIT:index>' : self . index , <EOL> '<STR_LIT:title>' : self . title , <EOL> '<STR_LIT:description>' : self . description , <EOL> '<STR_LIT>' : self . images , <EOL> '<STR_LIT>' : self . output , <EOL> '<STR_LIT>' : dict ( ( k , v . get_minor_amount ( ) ) for k , v in self . prices . items ( ) ) , <EOL> '<STR_LIT>' : self . available } <EOL> def get_price ( self ) : <EOL> return self . prices [ DEFAULT_CURRENCY_TYPE ] <EOL> class StoreResource ( StoreItem ) : <EOL> def __init__ ( self , game , meta_data , resource_keys ) : <EOL> super ( StoreResource , self ) . __init__ ( game , meta_data , resource_keys ) <EOL> self . type = meta_data . get ( '<STR_LIT:type>' ) <EOL> if self . type not in STORE_PRICING_TYPES : <EOL> self . error ( '<STR_LIT>' % ( str ( STORE_PRICING_TYPES ) , self . key ) ) <EOL> self . type = '<STR_LIT>' <EOL> def to_dict ( self ) : <EOL> return { '<STR_LIT:index>' : self . index , <EOL> '<STR_LIT:title>' : self . title , <EOL> '<STR_LIT:description>' : self . description , <EOL> '<STR_LIT>' : self . images , <EOL> '<STR_LIT:type>' : self . type } <EOL> class StoreUserGameItems ( object ) : <EOL> def __init__ ( self , user , game , game_store_items ) : <EOL> self . user = user <EOL> self . game = game <EOL> self . game_store_items = game_store_items <EOL> self . user_items = { } <EOL> try : <EOL> path = config [ '<STR_LIT>' ] <EOL> except KeyError : <EOL> LOG . error ( '<STR_LIT>' ) <EOL> return <EOL> path = join_path ( path , self . game . slug ) <EOL> if not create_dir ( path ) : <EOL> raise StoreError ( '<STR_LIT>' % path ) <EOL> self . path = get_absolute_path ( path ) <EOL> self . lock = Lock ( ) <EOL> self . _read ( ) <EOL> def _read ( self ) : <EOL> with self . lock : <EOL> unicode_path = unicode ( '<STR_LIT>' % ( self . path , self . user . username ) ) <EOL> if path_exists ( unicode_path ) : <EOL> try : <EOL> with open ( unicode_path , '<STR_LIT:r>' ) as f : <EOL> file_store_items = yaml . load ( f ) <EOL> self . user_items = { } <EOL> if file_store_items : <EOL> for item in file_store_items : <EOL> item_amount = file_store_items [ item ] [ '<STR_LIT>' ] <EOL> self . user_items [ str ( item ) ] = { <EOL> '<STR_LIT>' : item_amount <EOL> } <EOL> except ( IOError , KeyError , yaml . YAMLError ) as e : <EOL> LOG . error ( '<STR_LIT>' , self . path , str ( e ) ) <EOL> raise StoreError ( '<STR_LIT>' % ( self . path , str ( e ) ) ) <EOL> else : <EOL> self . user_items = { } <EOL> def _write ( self ) : <EOL> with self . lock : <EOL> try : <EOL> with open ( unicode ( '<STR_LIT>' % ( self . path , self . user . username ) ) , '<STR_LIT:w>' ) as f : <EOL> yaml . dump ( self . user_items , f , default_flow_style = False ) <EOL> except IOError as e : <EOL> LOG . error ( '<STR_LIT>' , self . path , str ( e ) ) <EOL> raise StoreError ( '<STR_LIT>' % self . path ) <EOL> def get_items ( self ) : <EOL> return dict ( ( key , self . get_item ( key ) ) for key in self . user_items . keys ( ) ) <EOL> def get_item ( self , key ) : <EOL> try : <EOL> if self . game_store_items . get_resource ( key ) . type == '<STR_LIT>' : <EOL> user_item = self . user_items . get ( key ) <EOL> if user_item and user_item [ '<STR_LIT>' ] > <NUM_LIT:1> : <EOL> return { '<STR_LIT>' : <NUM_LIT:1> } <EOL> except StoreError : <EOL> pass <EOL> return self . user_items [ key ] <EOL> def remove_items ( self ) : <EOL> self . user_items = { } <EOL> self . _write ( ) <EOL> def transfer_items ( self , transaction ) : <EOL> for item_key , item in transaction . items . items ( ) : <EOL> for resource_key , output_amount in self . game_store_items . get_offering ( item_key ) . output . items ( ) : <EOL> amount = item [ '<STR_LIT>' ] * output_amount <EOL> user_item = self . user_items . get ( resource_key ) <EOL> if user_item : <EOL> user_item [ '<STR_LIT>' ] += amount <EOL> else : <EOL> self . user_items [ str ( resource_key ) ] = { <EOL> '<STR_LIT>' : amount <EOL> } <EOL> self . _write ( ) <EOL> def consume_items ( self , consume_transaction ) : <EOL> item_key = consume_transaction . key <EOL> try : <EOL> user_item_amount = self . user_items [ item_key ] [ '<STR_LIT>' ] <EOL> if user_item_amount < consume_transaction . consume_amount : <EOL> return False <EOL> except KeyError : <EOL> return False <EOL> new = user_item_amount - consume_transaction . consume_amount <EOL> if new == <NUM_LIT:0> : <EOL> del self . user_items [ item_key ] <EOL> else : <EOL> self . user_items [ item_key ] [ '<STR_LIT>' ] = new <EOL> self . _write ( ) <EOL> return True <EOL> def reset_all_transactions ( self ) : <EOL> self . user_items = { } <EOL> class StoreUserList ( object ) : <EOL> def __init__ ( self , game , game_store_items ) : <EOL> self . users = { } <EOL> self . game = game <EOL> self . game_store_items = game_store_items <EOL> def get ( self , user ) : <EOL> try : <EOL> return self . users [ user . username ] <EOL> except KeyError : <EOL> store_user = StoreUserGameItems ( user , self . game , self . game_store_items ) <EOL> self . users [ user . username ] = store_user <EOL> return store_user <EOL> class GameStoreItems ( object ) : <EOL> def __init__ ( self , game ) : <EOL> self . offerings = { } <EOL> self . resources = { } <EOL> self . issues = [ ] <EOL> total_yaml_errors = <NUM_LIT:0> <EOL> def add_infos ( key , item ) : <EOL> num_errors = len ( item . errors ) <EOL> if num_errors > <NUM_LIT:0> or len ( item . warnings ) > <NUM_LIT:0> : <EOL> self . issues . append ( ( key , { <EOL> '<STR_LIT>' : item . errors , <EOL> '<STR_LIT>' : item . warnings <EOL> } ) ) <EOL> return num_errors <EOL> yaml_path = unicode ( get_absolute_path ( join_path ( game . path , '<STR_LIT>' ) ) ) <EOL> if path_exists ( yaml_path ) : <EOL> try : <EOL> with open ( yaml_path , '<STR_LIT:r>' ) as f : <EOL> items_meta = yaml . load ( f ) <EOL> resource_keys = set ( ) <EOL> offering_keys = set ( ) <EOL> if isinstance ( items_meta , list ) : <EOL> for index , m in enumerate ( items_meta ) : <EOL> resource = StoreResource ( game , m , resource_keys ) <EOL> resource . index = index <EOL> total_yaml_errors += add_infos ( resource . key , resource ) <EOL> self . resources [ resource . key ] = resource <EOL> index = <NUM_LIT:0> <EOL> items_meta_end = len ( items_meta ) - <NUM_LIT:1> <EOL> for m in items_meta : <EOL> try : <EOL> m [ '<STR_LIT>' ] = { m [ '<STR_LIT:key>' ] : <NUM_LIT:1> } <EOL> except KeyError : <EOL> raise StoreError ( '<STR_LIT>' ) <EOL> offering = StoreOffering ( game , m , offering_keys , resource_keys ) <EOL> if offering . available : <EOL> offering . index = index <EOL> index += <NUM_LIT:1> <EOL> else : <EOL> offering . index = items_meta_end <EOL> items_meta_end -= <NUM_LIT:1> <EOL> total_yaml_errors += add_infos ( offering . key , offering ) <EOL> self . offerings [ offering . key ] = offering <EOL> elif isinstance ( items_meta , dict ) : <EOL> resource_meta = items_meta . get ( '<STR_LIT>' ) <EOL> if not isinstance ( resource_meta , list ) : <EOL> raise StoreError ( '<STR_LIT>' ) <EOL> for index , m in enumerate ( resource_meta ) : <EOL> resource = StoreResource ( game , m , resource_keys ) <EOL> resource . index = index <EOL> total_yaml_errors += add_infos ( resource . key , resource ) <EOL> self . resources [ resource . key ] = resource <EOL> offerings_meta = items_meta . get ( '<STR_LIT>' ) <EOL> if not isinstance ( offerings_meta , list ) : <EOL> raise StoreError ( '<STR_LIT>' ) <EOL> index = <NUM_LIT:0> <EOL> items_meta_end = len ( offerings_meta ) - <NUM_LIT:1> <EOL> for m in offerings_meta : <EOL> offering = StoreOffering ( game , m , offering_keys , resource_keys ) <EOL> if offering . available : <EOL> offering . index = index <EOL> index += <NUM_LIT:1> <EOL> else : <EOL> offering . index = items_meta_end <EOL> items_meta_end -= <NUM_LIT:1> <EOL> total_yaml_errors += add_infos ( offering . key , offering ) <EOL> self . offerings [ offering . key ] = offering <EOL> else : <EOL> raise StoreError ( '<STR_LIT>' ) <EOL> except ( IOError , yaml . YAMLError ) as e : <EOL> LOG . error ( '<STR_LIT>' , str ( e ) ) <EOL> raise StoreError ( '<STR_LIT>' % str ( e ) ) <EOL> else : <EOL> raise StoreUnsupported ( ) <EOL> if total_yaml_errors > <NUM_LIT:0> : <EOL> raise ValidationException ( self . issues ) <EOL> self . store_users = StoreUserList ( game , self ) <EOL> def get_offering ( self , key ) : <EOL> try : <EOL> return self . offerings [ key ] <EOL> except KeyError : <EOL> raise StoreError ( '<STR_LIT>' % key , <NUM_LIT> ) <EOL> def get_resource ( self , key ) : <EOL> try : <EOL> return self . resources [ key ] <EOL> except KeyError : <EOL> raise StoreError ( '<STR_LIT>' % key , <NUM_LIT> ) <EOL> def get_store_user ( self , user ) : <EOL> return self . store_users . get ( user ) <EOL> def read_meta ( self ) : <EOL> return dict ( ( offering . key , offering . to_dict ( ) ) for offering in self . offerings . values ( ) ) <EOL> def read_resources ( self ) : <EOL> return dict ( ( resource . key , resource . to_dict ( ) ) for resource in self . resources . values ( ) ) <EOL> class ConsumeTransaction ( object ) : <EOL> def __init__ ( self , user , game , resource_key , <EOL> consume_amount , gamesession_id , token ) : <EOL> self . user = user <EOL> self . game = game <EOL> self . id = create_id ( ) <EOL> self . key = resource_key <EOL> self . gamesession_id = gamesession_id <EOL> self . token = token <EOL> self . consumed = False <EOL> try : <EOL> consume_amount = int ( consume_amount ) <EOL> except ValueError : <EOL> raise StoreError ( '<STR_LIT>' % resource_key ) <EOL> self . consume_amount = consume_amount <EOL> if consume_amount <= <NUM_LIT:0> : <EOL> raise StoreError ( '<STR_LIT>' % resource_key ) <EOL> game_store_items = StoreList . get ( game ) <EOL> try : <EOL> resource_meta = game_store_items . get_resource ( resource_key ) <EOL> if resource_meta . type != '<STR_LIT>' : <EOL> raise StoreError ( '<STR_LIT>' % resource_key ) <EOL> except KeyError : <EOL> raise StoreError ( '<STR_LIT>' % resource_key ) <EOL> def check_match ( self , other ) : <EOL> return ( self . user . username == other . user . username and <EOL> self . game . slug == other . game . slug and <EOL> self . key == other . key and <EOL> self . gamesession_id == other . gamesession_id and <EOL> self . token == other . token and <EOL> self . consume_amount == other . consume_amount ) <EOL> def consume ( self ) : <EOL> game_store_items = StoreList . get ( self . game ) <EOL> store_user = game_store_items . get_store_user ( self . user ) <EOL> self . consumed = store_user . consume_items ( self ) <EOL> UserTransactionsList . get ( self . user ) . add_consume_transaction ( self . gamesession_id , self . token , self ) <EOL> class Transaction ( object ) : <EOL> def __init__ ( self , user , game , transaction_items ) : <EOL> self . user = user <EOL> self . game = game <EOL> self . id = create_id ( ) <EOL> self . items = transaction_items <EOL> total = <NUM_LIT:0> <EOL> game_store_items = StoreList . get ( game ) <EOL> resource_keys = set ( ) <EOL> if len ( transaction_items ) == <NUM_LIT:0> : <EOL> raise StoreError ( '<STR_LIT>' ) <EOL> for item_key , item in transaction_items . items ( ) : <EOL> try : <EOL> basket_amount = int ( item [ '<STR_LIT>' ] ) <EOL> basket_price = int ( item [ '<STR_LIT>' ] ) <EOL> except ( ValueError , KeyError , TypeError ) : <EOL> raise StoreError ( '<STR_LIT>' % item_key ) <EOL> if basket_amount == <NUM_LIT:0> : <EOL> continue <EOL> elif basket_amount < <NUM_LIT:0> : <EOL> raise StoreError ( '<STR_LIT>' % item_key ) <EOL> game_offering = game_store_items . get_offering ( item_key ) <EOL> minor_price = game_offering . get_price ( ) . get_minor_amount ( ) <EOL> if basket_price != minor_price : <EOL> raise StoreError ( '<STR_LIT>' % item_key ) <EOL> offering_output = game_offering . output <EOL> try : <EOL> basket_item_output = item [ '<STR_LIT>' ] <EOL> if not isinstance ( basket_item_output , dict ) : <EOL> raise ValueError ( ) <EOL> if len ( offering_output ) != len ( basket_item_output ) : <EOL> raise ValueError ( ) <EOL> for resource_key , amount in offering_output . items ( ) : <EOL> if amount != basket_item_output [ resource_key ] : <EOL> raise ValueError ( ) <EOL> resource_keys . add ( resource_key ) <EOL> except ( KeyError , ValueError ) : <EOL> raise StoreError ( '<STR_LIT>' % item_key ) <EOL> self . items [ item_key ] = { <EOL> '<STR_LIT>' : basket_price , <EOL> '<STR_LIT>' : basket_amount <EOL> } <EOL> total += minor_price * basket_amount <EOL> for key in resource_keys : <EOL> resource = game_store_items . get_resource ( key ) <EOL> if resource is not None and resource . type == '<STR_LIT>' : <EOL> user_store_items = game_store_items . get_store_user ( user ) <EOL> if user_store_items is not None : <EOL> try : <EOL> user_item = user_store_items . get_item ( key ) <EOL> if user_item [ '<STR_LIT>' ] > <NUM_LIT:0> : <EOL> raise StoreError ( '<STR_LIT>' ) <EOL> except KeyError : <EOL> pass <EOL> self . total = total <EOL> self . completed = False <EOL> self . completed_time = None <EOL> UserTransactionsList . get ( user ) . add_transaction ( self . id , self ) <EOL> def pay ( self ) : <EOL> if self . completed : <EOL> return <EOL> game_store_items = StoreList . get ( self . game ) <EOL> store_user = game_store_items . get_store_user ( self . user ) <EOL> store_user . transfer_items ( self ) <EOL> self . completed_time = time_now ( ) <EOL> self . completed = True <EOL> def status ( self ) : <EOL> if self . completed : <EOL> return { '<STR_LIT:status>' : '<STR_LIT>' } <EOL> else : <EOL> return { '<STR_LIT:status>' : '<STR_LIT>' } <EOL> class TransactionsList ( object ) : <EOL> def __init__ ( self , user ) : <EOL> self . transactions = { } <EOL> self . consume_transactions = { } <EOL> self . user = user <EOL> def add_transaction ( self , transaction_id , transaction ) : <EOL> self . transactions [ transaction_id ] = transaction <EOL> def add_consume_transaction ( self , gamesession_id , token , consume_transaction ) : <EOL> if gamesession_id in self . consume_transactions : <EOL> self . consume_transactions [ gamesession_id ] [ token ] = consume_transaction <EOL> else : <EOL> self . consume_transactions [ gamesession_id ] = { <EOL> token : consume_transaction <EOL> } <EOL> def get_transaction ( self , transaction_id ) : <EOL> try : <EOL> return self . transactions [ transaction_id ] <EOL> except KeyError : <EOL> raise StoreInvalidTransactionId ( ) <EOL> def get_consume_transaction ( self , gamesession_id , token ) : <EOL> try : <EOL> return self . consume_transactions [ gamesession_id ] [ token ] <EOL> except ( KeyError , TypeError ) : <EOL> return None <EOL> class UserTransactionsList ( object ) : <EOL> user_transactions = { } <EOL> @ classmethod <EOL> def load ( cls , user ) : <EOL> user_transactions_list = TransactionsList ( user . username ) <EOL> cls . user_transactions [ user . username ] = user_transactions_list <EOL> return user_transactions_list <EOL> @ classmethod <EOL> def get ( cls , user ) : <EOL> try : <EOL> return cls . user_transactions [ user . username ] <EOL> except KeyError : <EOL> return cls . load ( user ) <EOL> class StoreList ( object ) : <EOL> game_stores = { } <EOL> @ classmethod <EOL> def load ( cls , game ) : <EOL> game_store = GameStoreItems ( game ) <EOL> cls . game_stores [ game . slug ] = game_store <EOL> return game_store <EOL> @ classmethod <EOL> def get ( cls , game ) : <EOL> try : <EOL> return cls . game_stores [ game . slug ] <EOL> except KeyError : <EOL> return cls . load ( game ) <EOL> @ classmethod <EOL> def reset ( cls ) : <EOL> cls . game_stores = { } </s>
