Pizza.cpp

// Pizza.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2017

@import "PythonUtilityFunctions.h"
@import "Ceda/cxPython/cxPython.h"
#include "Ceda/cxUtils/TracerUtils.h"

namespace pizza1
{
    void Run()
    {
        ceda::TraceGroup g("pizza example 1");

        PyRun_SimpleString(
            @strx
            (
                dt = rootnamespace.dt
                pizza = rootnamespace.pizza

                def createPizzaOrder():
                    p = pizza.TOrderedPizza()

                    p.Shape.Circle = pizza.TCircle(100)

                    p.BaseType = pizza.EBaseType.DeepPan
                    for i in range(3):
                        p.ToppingIds.insert(0,2*i+1)

                    order = pizza.TPizzaOrder.new(
                        Id=1,
                        CustomerId=3,
                        DeliveryStatus=pizza.EDeliveryStatus.Cooking,
                        DateTimeOrderTaken=dt.TDateTime(
                            dt.TDate(Year=2017,Month=dt.TMonth.Aug,Day=24),
                            dt.TTime(Hour=20,Minute=30,Second=0)),
                        TotalOrderPrice=35.50,
                        Pizzas=[])
                    order.Pizzas.insert(0,p)
                    return order

                def appendSomeOrders(count):
                    for i in range(count):
                        pdd.Orders.append(createPizzaOrder())

                def setEmployee_Smith(e):
                    e.Address.Number = 57
                    e.Address.Street = 'Alfred St'
                    e.Address.City = 'London'
                    e.Address.Country = 'England'
                    e.FirstName = 'John'
                    e.LastName = 'Smith'
                    e.PhoneNumber = '0489 451 644'

                def setEmployee_Jameson(e):
                    e.Address.Number = 45
                    e.Address.Street = 'Mayfair St'
                    e.Address.City = 'London'
                    e.Address.Country = 'England'
                    e.FirstName = 'Pete'
                    e.LastName = 'Jameson'
                    e.PhoneNumber = '0478 123 294'

                def createPizzaDeliveryDatabase():
                    pdd = pizza.TPizzaDeliveryDatabase.new()
                    pdd.Orders.insert(0, createPizzaOrder()) 
                    pdd.Orders.insert(1, createPizzaOrder()) 
                    setEmployee_Smith(pdd.Employees[0])
                    setEmployee_Jameson(pdd.Employees[1])
                    return pdd
                
                def printPizzaDeliveryDatabase(pdd):
                    print 'Pizza delivery database = ' + `pdd`
                    print 'Num orders = ' + `len(pdd.Orders)`
                    for i in range(len(pdd.Orders)):
                        order = pdd.Orders[i]
                        print 'order #' + `i` + ' : ' + `order`
                    print 'Num employees = ' + `len(pdd.Employees)`
                    for id in pdd.Employees:
                        e = pdd.Employees[id]
                        print 'Employee #' + `id` + ' : ' + `e`

                ceda.SetThreadPSpace(None)
                cs = ceda.CreateCSpace(1000)
                ceda.SetThreadCSpace(cs)
                cs.Lock(ceda.ECSpaceLockMode.Exclusive)
                pdd = createPizzaDeliveryDatabase()
                printPizzaDeliveryDatabase(pdd)
                cs.Unlock()
                cs.Destroy()
            ));
    }
}

namespace pizza2
{
    void Run()
    {
        ceda::TraceGroup g("pizza example 2");

        PyRun_SimpleString(
            @strx(mPythonUtilityFunctions)
            "\n"
            @strx
            (
                import random
                import string
                dt = rootnamespace.dt
                pizza = rootnamespace.pizza

                def printMap(name,m):
                    print('  len({}) = {}'.format(name, len(m)))
                    for key in m:
                        print('  {}.has_key({}) = {}'.format(name, key, m.has_key(key)))
                    for key in m:
                        print('  {} in {} = {}'.format(key, name, key in m))
                    for key in m.iterkeys():
                        print('  {}[{}] = {}'.format(name, key, m[key]))
                    for value in m.itervalues():
                        print('  value: {}'.format(value))
                    for item in m.iteritems():
                        print('  item: {}'.format(item))
                    for key,value in m.iteritems():
                        print('  key: {}  value: {}'.format(key,value))

                ####################### DateTime

                def selectDate():
                    y = random.randint(2016,2017)
                    m = random.randint(0,11)
                    d = random.randint(1,28)
                    return dt.TDate(Year=y,Month=m,Day=d)

                def selectTime():
                    h = random.randint(0,23)
                    m = random.randint(0,59)
                    return dt.TTime(Hour=h,Minute=m,Second=0)
                
                def selectDateTime():
                    return dt.TDateTime(selectDate(), selectTime())

                def setDate(d):
                    d.Year = random.randint(2016,2017)
                    d.Month = random.randint(0,11)
                    d.Day = random.randint(1,28)

                def setTime(t):
                    t.Hour = random.randint(0,23)
                    t.Minute = random.randint(0,59)

                def setDateTime(dt):
                    setDate(dt.Date)
                    setTime(dt.Time)

                ####################### TVehicleType

                # Add all vehicle types to the given TPizzaDeliveryDatabase pdd
                def addVehicleTypes(pdd):
                    for s in [ 'Ford Fiesta', 'Mazda3', 'Kia Rio', 'Toyota Yaris', 'Honda Civic', 'Mitsubishi Lancer', 'Subaru Impreza', 'Volkswagen Passat']:
                        id = len(pdd.VehicleTypes)+1
                        pdd.VehicleTypes[id].Description = s 

                ####################### TVehicle

                def selectLicensePlateNumber():
                    s = ''
                    for i in range(3):
                        s += random.choice(string.ascii_uppercase)
                    s += '.'
                    for i in range(3):
                        s += str(random.randint(0,9))
                    return s

                def setVehicle(v):
                    v.VehicleTypeId = random.randint(1,8)
                    v.LicensePlateNumber = selectLicensePlateNumber()

                ####################### Address
                
                def selectStreet():
                    return random.choice([ 'Old Kent Road', 'Mayfair St', 'Oxford St', 'Regent St'])

                def selectCity():
                    return random.choice([ 'London', 'New York', 'Paris', 'Perth', 'Berlin'])

                def selectState():
                    return random.choice([ 'Alaska', 'New South Wales', 'Florida', 'California', 'Texas'])

                def selectCountry():
                    return random.choice([ 'Australia', 'USA', 'England', 'India'])

                def selectZipCode():
                    return random.randint(1000,9999)

                def setAddress(a):
                    a.Number = random.randint(1,50)
                    a.Street = selectStreet()
                    a.City = selectCity()
                    a.ZipPostCode = selectZipCode()
                    a.StateProvinceCounty = selectState()
                    a.Country = selectCountry()

                ####################### Employee

                def selectFirstName():
                    return random.choice([ 'John', 'Janet', 'Kate', 'Rihanna', 'Peter', 'Greg'])

                def selectLastName():
                    return random.choice([ 'Smith', 'Matthews', 'Gates', 'Howard', 'Johnson'])

                def selectPhoneNumber():
                    s = ''
                    for i in range(10):
                        s += str(random.randint(0,9))
                        if i == 3 or i == 6:
                            s += ' '
                    return s

                def setEmployee(e):
                    setAddress(e.Address)
                    e.FirstName = selectFirstName()
                    e.LastName = selectLastName()
                    e.PhoneNumber = selectPhoneNumber()

                # Add an employee to the given TPizzaDeliveryDatabase pdd
                def addEmployee(pdd):
                    i = len(pdd.Employees)
                    setEmployee(pdd.Employees[i])

                ####################### Shape

                def selectCircle():
                    return pizza.TCircle(Radius=random.choice([100, 150, 250, 300]))

                def selectRectangle():
                    return pizza.TRectangle(Width=random.choice([100, 200]), Height=random.choice([200, 300]))

                # Assign to s which is a variable of type TShape
                def setShape(s):
                    if random.randint(0,2) == 0:
                        s.Circle = selectCircle()
                    else:
                        s.Rectangle = selectRectangle()

                ####################### Pizza

                def selectTopping():
                    return random.choice([ pizza.EBaseType.Thin, pizza.EBaseType.DeepPan])

                def selectPizza():
                    p = pizza.TOrderedPizza()
                    setShape(p.Shape)
                    p.BaseType = selectTopping()
                    for i in range(random.randint(0,5)):
                        p.ToppingIds.append(random.randint(0,10))
                    return p

                ####################### TPizzaOrder

                def selectDeliveryStatus():
                    return random.choice([ pizza.EDeliveryStatus.Cooking, pizza.EDeliveryStatus.Delivering,  pizza.EDeliveryStatus.Completed,  pizza.EDeliveryStatus.Returned])

                # Must be called by a thread that has opened a transaction on the database.
                # Create an object of type TPizzaOrder.  
                def createPizzaOrder(id):
                    order = pizza.TPizzaOrder.new(
                        Id = id,
                        CustomerId = random.randint(1,15),
                        TakenByEmployeeId = random.randint(1,9),
                        DeliveredByEmployeeId = random.randint(1,9),
                        DeliveryStatus = selectDeliveryStatus(),
                        VehicleId = random.randint(1,9),
                        DateTimeOrderTaken = selectDateTime(),
                        DateTimeOrderDelivered = selectDateTime(),
                        TotalOrderPrice = random.randint(1000,5000)/100.0,
                        Pizzas = [])
                    for i in range(random.randint(0,3)):
                        order.Pizzas.insert(i,selectPizza())
                    return order

                def insertSomeOrders(pdd, count):
                    for i in range(count):
                        pdd.Orders.insert(0, createPizzaOrder())

                def appendSomeOrders(pdd, count):
                    for i in range(count):
                        pdd.Orders.append(createPizzaOrder())

                # Add an order to the given TPizzaDeliveryDatabase pdd
                def addOrder(pdd):
                    id = len(pdd.Orders)
                    pdd.Orders.append(createPizzaOrder(id))

                # Replace last pizza in the ith order in the given TPizzaDeliveryDatabase pdd
                def ReplaceLastPizzaInOrder(pdd,i):
                    if pdd.Orders[i].Pizzas:
                        pdd.Orders[i].Pizzas.pop()
                        pdd.Orders[i].Pizzas.append(selectPizza())

                # Print all the orders in the given TPizzaDeliveryDatabase pdd
                def printOrders(pdd):
                    print('Num orders = {}'.format(len(pdd.Orders)))
                    for i in range(len(pdd.Orders)):
                        order = pdd.Orders[i]
                        print `order`

                def printOrdersInTable(pdd):
                    print(' Id  CustomerId        Name       Status')
                    print('------------------------------------------')
                    for i in range(len(pdd.Orders)):
                        order = pdd.Orders[i]
                        if order.CustomerId in pdd.Customers:
                            c = pdd.Customers[order.CustomerId]
                            print('{0:>4} {1:>10} {2:>17} {3:>6}'.format(order.Id, order.CustomerId, (str(c.FirstName) + ' ' + str(c.LastName)), order.DeliveryStatus))

                ####################### Customers

                def selectEmail():
                    return selectFirstName() + '.' + selectLastName() + '@gmail.com'

                def selectPaymentMethod():
                    return random.choice([ pizza.EPaymentMethod.Cash, pizza.EPaymentMethod.EftPos,  pizza.EPaymentMethod.CreditCard])

                def setCustomer(c):
                    setAddress(c.Address)
                    c.FirstName = selectFirstName()
                    c.LastName = selectLastName()
                    c.PhoneNumber = selectPhoneNumber()
                    c.Email = selectEmail()
                    setDateTime(c.DateOfFirstOrder)
                    c.PaymentMethod = selectPaymentMethod()

                # Add a customer to the given TPizzaDeliveryDatabase pdd
                def addCustomer(pdd):
                    id = len(pdd.Customers)
                    setCustomer(pdd.Customers[id])

                ####################### Toppings

                def selectToppingPrice():
                    return random.randint(100,200)/100.0

                def selectToppingDescription():
                    return random.choice([ 'Ham', 'Pineapple', 'Chicken', 'Jalapenos', 'Mushrooms', 'Zucchini', 'Garlic', 'Red peppers'])

                def setTopping(t):
                    t.Price = selectToppingPrice()
                    t.Description = selectToppingDescription()

                # Add a topping to the given TPizzaDeliveryDatabase pdd
                def addTopping(pdd):
                    id = len(pdd.Toppings)
                    setTopping(pdd.Toppings[id])

                # Returns a dictionary representation of the toppings
                def getToppings(pdd):
                    d = {}
                    for id,topping in pdd.Toppings.iteritems():
                        # The types of the topping fields are native python value types, so as required we are not
                        # returning references to objects in the database (which can be an issue when done across a
                        # transaction boundary).
                        assert type(topping.Price) is float 
                        assert type(topping.Description) is str 
                        d[id] = (topping.Price, topping.Description)
                    return d

                ####################### TPizzaDeliveryDatabase

                def createPizzaDeliveryDatabase():
                    pdd = pizza.TPizzaDeliveryDatabase.new()
                    addVehicleTypes(pdd)
                    for i in range(10):
                        setVehicle(pdd.Vehicles[i+1])
                    for i in range(10):
                        addCustomer(pdd)
                    return pdd

                def printPizzaDeliveryDatabase(pdd):
                    print(str(pdd))
                    #printOrders(pdd)
                    #printMap('pdd.Employees', pdd.Employees)

                utRootKey = 'pizzadatabaseroot'

                class PizzaDeliveryDatabase(Database):

                    def __init__(self, path, createNew):
                        Database.__init__(self)
                        self.path = path
                        self.createNew = createNew

                    def __enter__(self):
                        Database.open(self, self.path, self.createNew) 
                        self.bootstrapRootObject(utRootKey, lambda : createPizzaDeliveryDatabase())
                        return self

                    def __exit__(self, etype, value, tb):
                        self.close()
                
                def getEmployees(pdd):
                    e = []
                    for k,v in pdd.Employees.iteritems():
                        e.append(v.copy())
                    return e;
                
                ####################### 

                for repeat in range(3):
                    createNew = True if repeat==0 else False
                    with PizzaDeliveryDatabase('mypizzadatabase', createNew) as db:
                        pdd = db.rootObj       # The TPizzaDeliveryDatabase in the database

                        if db.justCreated:
                            print 'Created new database'
                        else:
                            print 'Opened existing database'

                        with Txn(db):
                            addCustomer(pdd)

                        with Txn(db):
                            addEmployee(pdd)

                        with Txn(db):
                            addTopping(pdd)

                        with Txn(db):
                            for i in range(10):
                                addOrder(pdd)

                        with Txn(db):
                            ReplaceLastPizzaInOrder(pdd,0)

                        with Txn(db):
                            printOrdersInTable(pdd)
                            #printPizzaDeliveryDatabase(pdd)

                        with Txn(db):
                            d = getToppings(pdd)
                        print 'toppings: ' + str(d)

                        with Txn(db):
                            copyOfEmployees = getEmployees(pdd)

                        # Cannot assign attribute FirstName of read-only pizza::TEmployee
                        #with Txn(db):
                        #    for id,e in pdd.Employees.iteritems():
                        #        e.FirstName = 'Burt'

                    print 'copyOfEmployees = ' + `copyOfEmployees`

                DataReplicationTest('mypizzadatabase', 'myreplicatedpizzadatabase', utRootKey, printPizzaDeliveryDatabase)
            ));
    }
}

namespace pizza
{
    void Run()
    {
        pizza1::Run();
        pizza2::Run();
    }
}