Commit 7ca8b674 authored by Robin Trioux's avatar Robin Trioux
Browse files

Sync

Buying history DONE
Multi user WIP
parent 1adc9318
from Console import *
class UIProperties:
def __init__(self):
self.text = (
[]
) # A list because there is several things we may want to show from an Atom (name,price,...)
self.text = [] # A list because there is several things we may want to show from an Atom (name,price,...)
self.foreground = None
self.background = None
self.icon = None
......@@ -55,19 +51,20 @@ class UIProperties:
def getToolTip(self):
return self.toolTip
class Atom:
def __init__(self,texts=[], icon=""):
def __init__(self, texts=[], icon=""):
self.ui = UIProperties()
self.setTexts(texts)
self.setIcon(icon)
self.id = None #id used in db
self.id = None # id used in db
def setTexts(self, text):
self.ui.setTexts(text)
return self
def setText(self, text, index):
self.ui.setText(text,index)
self.ui.setText(text, index)
return self
def setForeground(self, foreground):
......@@ -86,6 +83,10 @@ class Atom:
self.ui.setToolTip(toolTip)
return self
def setId(self, id):
self.id = id
return self
def getTexts(self):
return self.ui.getTexts()
......@@ -110,23 +111,18 @@ class Atom:
def getId(self):
return self.id
def setId(self, id):
self.id = id
return self
def __eq__(self, key):
raise NotImplementedError('__eq__ not implemented for this class.')
raise NotImplementedError("__eq__ not implemented for this class.")
class HappyHours(Atom):
def __init__(self):
super().__init__()
self.start = None # QTime
self.end = None # QTime
self.price = None # Eur
def setStart(self,start):
self.start = None # QTime
self.end = None # QTime
self.price = None # Eur
def setStart(self, start):
self.start = start
return self
......@@ -147,6 +143,7 @@ class HappyHours(Atom):
def getPrice(self):
return self.price
class User(Atom):
def __init__(self):
super().__init__()
......@@ -154,7 +151,6 @@ class User(Atom):
self.balance = None
self.canDrink = None
def setBalance(self, balance):
self.balance = balance
return self
......@@ -176,10 +172,10 @@ class Product(Atom):
self.name = None # pretty print
self.code = None # short name
self.price = None # The price is either updated when it's in a the selctor, either retreived from database when it's in history
self.defaultPrice = None # Unit price without any happy hours
self.defaultPrice = None # Unit price without any happy hours
self.quantity = None # When product is used as selector, quantity is 1 and when used in a basket, it may vary
self.happyHours = None # List of happy hours
self.category = None # string e.g "Drinks.Alcool.Wine"
self.happyHours = None # List of happy hours
self.category = None # string e.g "Drinks.Alcool.Wine"
def setName(self, name):
self.name = name
......@@ -187,16 +183,16 @@ class Product(Atom):
def setCode(self, code):
self.code = code
self.setIcon(self.code) # /!\ Maybe this should not hardcoded here
self.setToolTip(self.code) #Same remark
self.setIcon(self.code) # /!\ Maybe this should not hardcoded here
self.setToolTip(self.code) # Same remark
return self
def setPrice(self, price): # price is never a float but something inherited from Decimal (Money.Eur)
def setPrice(self, price): # price is never a float but something inherited from Decimal (Money.Eur)
if isinstance(price, float):
printWW("Price should not be float !")
self.price = price
return self
def setDefaultPrice(self, price):
if isinstance(price, float):
printWW("Price should not be float !")
......@@ -207,7 +203,7 @@ class Product(Atom):
self.quantity = quantity
return self
def setHappyHours(self,happyHours):
def setHappyHours(self, happyHours):
self.happyHours = happyHours
return self
......@@ -236,7 +232,6 @@ class Product(Atom):
def getCategory(self):
return self.category
def __eq__(self, key):
if type(self) == type(key):
return self.getId() == key.getId()
......@@ -244,10 +239,10 @@ class Product(Atom):
return False
def __repr__(self):
return "Product({0}, {1})".format(self.id,self.name)
return "Product({0}, {1})".format(self.id, self.name)
def __str__(self):
return "{0}: {1}".format(self.id,self.name)
return "{0}: {1}".format(self.id, self.name)
class Operation(Atom):
......@@ -292,6 +287,7 @@ class Operation(Atom):
else:
return False
class Buying(Operation):
def __init__(self):
super().__init__()
......@@ -339,14 +335,14 @@ class Refilling(Operation):
def setNewBalance(self, balance):
self.newBalance = balance
return self
def getNewBalance(self):
return self.newBalance
def setCustomerId(self, uid):
self.customerId = uid
return self
def getCustomerId(self):
return self.customerId
......@@ -364,44 +360,45 @@ class Counter(Atom):
return self.name
def __repr__(self):
return "Counter({0}, {1})".format(self.id,self.name)
return "Counter({0}, {1})".format(self.id, self.name)
def __str__(self):
return "{0}: {1}".format(self.id,self.name)
return "{0}: {1}".format(self.id, self.name)
def __eq__(self, key):
if type(self) == type(key):
return key.getId() == self.getId() and key.getName() == self.getName()
else:
return False
class Distribution(Atom):
def __init__(self):
super().__init__()
self.userList = [] # list of user uid
self.userBalance = [] # the current balance of each user before transaction
self.amount = [] # the amount paid by each user
self.totalPrice = None # The total price to pay
def setUserList(self,uidList):
self.userList = [] # list of user uid
self.userBalance = [] # the current balance of each user before transaction
self.amount = [] # the amount paid by each user
self.totalPrice = None # The total price to pay
def setUserList(self, uidList):
self.userList = uidList
return self
def getUserList(self):
return self.userList
def addUser(self,uid):
def addUser(self, uid):
self.userList.append(uid)
return self
def addAmount(self, amount):
self.amount.append(amount)
return self
def removeUser(self,uid):
def removeUser(self, uid):
try:
indexUser = self.userList.index(uid)
del(self.userList[indexUser])
del self.userList[indexUser]
return self
except ValueError:
printE("User {} not found".format(uid))
......@@ -425,7 +422,7 @@ class Distribution(Atom):
def getUserBalance(self, uid):
try:
indexUser = self.userList.index(uid)
return self.userBalance[indexUser]
return self.userBalance[indexUser]
except ValueError:
printE("User {} not found".format(uid))
return None
......@@ -440,7 +437,7 @@ class Distribution(Atom):
self.amount[userIndex] = amount
else:
printWW("Insufficient balance for user {}".format(uid))
self.amount[userIndex] = self.userBalance[userIndex]
self.amount[userIndex] = self.userBalance[userIndex]
except ValueError:
printE("user {} not found".format(uid))
......@@ -454,6 +451,12 @@ class Distribution(Atom):
except ValueError:
printE("user {} not found".format(uid))
def getTotalPrice(self):
return self.totalPrice
def setTotalPrice(self, totalPrice):
self.totalPrice = totalPrice
return self
def setFairDistribution(self):
pass
\ No newline at end of file
pass
......@@ -10,9 +10,10 @@ from Console import *
from convert import *
# OPTINAL FOR PINGING THE SERVER AND ENSURE IT'S AVAILABLE
import platform # For getting the operating system name
import platform # For getting the operating system name
import subprocess # For executing a shell command
def ping(host):
"""
Returns True if host (str) responds to a ping request.
......@@ -20,37 +21,38 @@ def ping(host):
"""
# Option for the number of packets as a function of
param = '-n' if platform.system().lower()=='windows' else '-c'
param = "-n" if platform.system().lower() == "windows" else "-c"
# Building the command. Ex: "ping -c 1 google.com"
command = ['ping', param, '1', host]
command = ["ping", param, "1", host]
return subprocess.call(command) == 0
class ClientSingleton(type):
_instance = {}
def __call__(cls):
if cls not in cls._instance:
cls._instance[cls] = super(ClientSingleton , cls).__call__()
cls._instance[cls] = super(ClientSingleton, cls).__call__()
return cls._instance[cls]
class Client(metaclass=ClientSingleton):
class Client(metaclass=ClientSingleton):
def __init__(self):
self.serverAddress="127.0.0.1:50051"
self.serverAddress = "127.0.0.1:50051"
self.channel = grpc.insecure_channel(self.serverAddress)
self.stub = com_pb2_grpc.PaymentProtocolStub(self.channel)
self.now = None #datetime
self.now = None # datetime
def setServerAddress(self, address):
self.serverAddress = address
#TODO: add ping test here
#TODO: Test address format
# TODO: add ping test here
# TODO: Test address format
self.channel = grpc.insecure_channel(self.serverAddress)
self.stub = com_pb2_grpc.PaymentProtocolStub(self.channel)
def requestBuy(self,**kwargs):
def requestBuy(self, **kwargs) -> Buying:
"""
int64 counter_id
string device_uuid
......@@ -58,8 +60,8 @@ class Client(metaclass=ClientSingleton):
repeated BasketItem basket
"""
try:
payments = kwargs['payments']
basket = kwargs['basket']
payments = kwargs["payments"]
basket = kwargs["basket"]
newPayments = []
newBasket = []
......@@ -67,48 +69,44 @@ class Client(metaclass=ClientSingleton):
product = qProduct.getAtom()
newBasket.append(packProduct(product))
kwargs['basket'] = newBasket
kwargs['payments'] = packDistribution(kwargs['payments'])
kwargs["basket"] = newBasket
kwargs["payments"] = packDistribution(kwargs["payments"])
buyingRequest = com_pb2.BuyingRequest(**kwargs)
buyingReply = self.stub.Buy(buyingRequest)
self.now = unpackTime(buyingReply.now)
if buyingReply.status == com_pb2.BuyingReply.SUCCESS :
if buyingReply.status == com_pb2.BuyingReply.SUCCESS:
transaction = buyingReply.transaction
buying = unpackBuying(transaction)
elif buyingReply.status == com_pb2.BuyingReply.NOT_ENOUGH_MONEY:
printW("Not enough money")
return None
return buying
except RpcError:
pass
def requestRefilling(self, **kwargs):
def requestRefilling(self, **kwargs) -> Refilling:
"""
string customer_id
int64 counter_id
string device_uuid
PaymentMethod payment_method
int payment_method
Eur amount
"""
try:
paymentMethodList = [com_pb2.UNKNOWN,
com_pb2.CASH,
com_pb2.CARD,
com_pb2.CHECK,
com_pb2.AE,
com_pb2.TRANSFER,
com_pb2.OTHER]
kwargs['payment_method'] = paymentMethodList[kwargs['payment_method']]
kwargs['amount'] = packMoney(kwargs['amount'])
paymentMethodList = [
com_pb2.UNKNOWN,
com_pb2.CASH,
com_pb2.CARD,
com_pb2.CHECK,
com_pb2.AE,
com_pb2.TRANSFER,
com_pb2.OTHER,
]
kwargs["payment_method"] = paymentMethodList[kwargs["payment_method"]]
kwargs["amount"] = packMoney(kwargs["amount"])
refillingRequest = com_pb2.RefillingRequest(**kwargs)
refillingReply = self.stub.Refill(refillingRequest)
if refillingReply.status == com_pb2.RefillingReply.SUCCESS:
......@@ -125,14 +123,57 @@ class Client(metaclass=ClientSingleton):
return None
def requestHistory(self, **kwargs):
pass
"""
HistoryType type (enum)
RefoundStatus refounded (enum)
# Optional fields
uint64 counter_id
string customer_id
string device_uuid
uint64 max_history_size (0: no limit)
"""
buyings = []
refillings = []
requestType = [com_pb2.HistoryRequest.BUYINGS, com_pb2.HistoryRequest.REFILLINGS]
refoundStatus = [
com_pb2.HistoryRequest.NOT_SPECIFIED,
com_pb2.HistoryRequest.NOT_REFOUNDED,
com_pb2.HistoryRequest.REFOUNDED,
]
try:
historyRequest = com_pb2.HistoryRequest(**kwargs)
historyReply = self.stub.History(historyRequest)
if historyReply.status == com_pb2.HistoryReply.SUCCESS:
for buying in historyReply.buyings:
buyings.append(unpackBuying(buying))
for refilling in historyReply.refillings:
refillings.append(unpackRefilling(refilling))
return buyings, refillings
else:
return None
except RpcError:
pass
def requestRefund(self, **kwargs):
pass
"""
int64 buying_id
"""
try:
refoundBuyingRequest = com_pb2.RefoundBuyingRequest(**kwargs)
refoundBuyingReply = self.stub.RefoundBuying(refoundBuyingRequest)
if refoundBuyingReply.status == com_pb2.RefoundBuyingReply.SUCCESS:
return True
else:
printE("Unable to refound: {}".format(refoundBuyingReply.status))
return None
except RpcError:
printE("RPC: Unable to get product list")
return None
def requestCounterProduct(self, **kwargs):
def requestCounterProduct(self, **kwargs) -> [Product]:
"""
int64 counter_id
"""
......@@ -142,8 +183,8 @@ class Client(metaclass=ClientSingleton):
productsReply = self.stub.Products(productsRequest)
if productsReply.status == com_pb2.ProductsReply.SUCCESS:
self.now = unpackTime(productsReply.now)
# Fill product List
pbProductList = productsReply.products # get protobuff products
# Fill product List
pbProductList = productsReply.products # get protobuff products
for pb_product in pbProductList:
newProduct = unpackProduct(pb_product)
productList.append(newProduct)
......@@ -157,15 +198,13 @@ class Client(metaclass=ClientSingleton):
return None
return None
def requestUserBalance(self, **kwargs):
def requestUserBalance(self, **kwargs) -> Eur:
"""
string customer_id
"""
try:
balanceRequest = com_pb2.BalanceRequest(customer_id = kwargs['customer_id'])
balanceRequest = com_pb2.BalanceRequest(customer_id=kwargs["customer_id"])
balanceReply = self.stub.Balance(balanceRequest)
self.now = unpackTime(balanceReply.now)
return unpackMoney(balanceReply.balance)
......@@ -175,29 +214,25 @@ class Client(metaclass=ClientSingleton):
return None
def requestCounterList(self, **kwargs):
def requestCounterList(self, **kwargs) -> [Counter]:
"""No parameters requiered"""
counterList = []
try:
counterListRequest = com_pb2.CounterListRequest()
counterListReply = self.stub.CounterList(counterListRequest)
pbCounterList = counterListReply.counters #get the payload
self.now = unpackTime(counterListReply.now) #update the time
pbCounterList = counterListReply.counters # get the payload
self.now = unpackTime(counterListReply.now) # update the time
for pb_counter in pbCounterList:
newCounter = unpackCounter(pb_counter)
counterList.append(newCounter)
return counterList
except RpcError:
printE("Unable to get counter list")
return None
return None
def requestTransfert(self, **kwargs):
pass
......@@ -2,31 +2,39 @@ from termcolor import colored
from babel.numbers import format_currency
# At the moment I assume we only print single strings
def printE(string): #Error
print(colored("ERROR: " + str(string),'red'))
def printE(string): # Error
print(colored("ERROR: " + str(string), "red"))
def printW(string): #Warning
print(colored("WARNING: " + str(string),'yellow'))
def printWW(string): #Super Warning
print(colored("WARNING: " + str(string),'yellow',attrs=['bold']))
def printW(string): # Warning
print(colored("WARNING: " + str(string), "yellow"))
def printN(string): #Nice
print(colored(str(string),'green'))
def printD(string): #Debug
print(colored("DEBUG: "+ str(string),''))
def printWW(string): # Super Warning
print(colored("WARNING: " + str(string), "yellow", attrs=["bold"]))
def printI(string): #Info
print(colored(string, 'cyan'))
def printNFC(string): #NFC message
print(colored(string,'magenta'))
def printN(string): # Nice
print(colored(str(string), "green"))
def printM(eur): #Money
print(colored(eur.format('fr_FR'),'yellow'))
#Suggestion: create printEE, printEEE, printWW, printWWW ... to create a hierarchy among the message
def printD(string): # Debug
print(colored("DEBUG: " + str(string), ""))
def printI(string): # Info
print(colored(string, "cyan"))
def printNFC(string): # NFC message
print(colored(string, "magenta"))
def printM(eur): # Money
print(colored(eur.format("fr_FR"), "yellow"))
# Suggestion: create printEE, printEEE, printWW, printWWW ... to create a hierarchy among the message
# could be usefull to filter the message to print in the future ...
#Suggestion: create a singleton class to handle all of these...
\ No newline at end of file
# Suggestion: create a singleton class to handle all of these...
from money import Money
from babel.numbers import format_currency
from Console import *
# money is based on Decimal, this class handle perfect float representation
# every single amount of money should be generated through this library to avoid float virgule problem
# e.g with regular float 1.1 + 1.2 = 2.30000000000000003
# e.g with regular float 1.1 + 1.2 = 2.30000000000000003
# with money/Decimal class 1.1 + 1.2 = 2.30
#TEST
# TEST
import money
#money could theoricaly handle multi device program, here we just need euro so we create a specialized class
class Eur(Money):
# money could theoricaly handle multi device program, here we just need euro so we create a specialized class
def __init__(self,amount="0",currency='EUR'): #the currency parameter is here just to make this class compatible with money __add__ __sub__...
super().__init__(amount=amount,currency='EUR')
if isinstance(amount,float) is True:
class Eur(Money):
def __init__(
self, amount="0", currency="EUR"
): # the currency parameter is here just to make this class compatible with money __add__ __sub__...
super().__init__(amount=amount, currency="EUR")
if isinstance(amount, float) is True:
printW("Eur should not be instancied with a float number, approximation may occure")
if self.as_tuple().exponent < -2:
printWW("Money with more than two decimals. Unforseen behavior may occure")
def __str__(self): # Overloaded function ... allow me to use XX.YY€ instead of EUR XX.YY
def __str__(self,): # Overloaded function ... allow me to use XX.YY€ instead of EUR XX.YY