I've conducted a test with Flask.

fred.py is the command line program.
hello.py is the Flask program.
default.py is the Welcome controller.
testdata.txt is the test data.
shippackage.py is a required module.

fred.py:
0.024 second
0.067 second

hello.py:
0.029 second
0.073 second

default.py:
0.27 second
0.78 second

The Flask program is slightly slower than the command line. However, the 
Welcome app is about 10x slower!

*Web2py is much, much slower than Flask.*

I conducted the test in a Parallels VM running Ubuntu Server 12.04 (1GB 
memory allocated). I have a 2.5GHz dual-core Mac mini with 8GB.


I can't quite figure out how to use gunicom.


On Saturday, 15 March 2014 23:41:49 UTC-4, horridohobbyist wrote:
>
> I'll see what I can do. It will take time for me to learn how to use 
> another framework.
>
> As for trying a different web server, my (production) Linux server is 
> intimately reliant on Apache. I'd have to learn how to use another web 
> server, and then try it in my Linux VM.
>
>
> On Saturday, 15 March 2014 22:45:27 UTC-4, Anthony wrote:
>>
>> Are you able to replicate the exact task in another web framework, such 
>> as Flask (with the same server setup)?
>>
>> On Saturday, March 15, 2014 10:34:56 PM UTC-4, horridohobbyist wrote:
>>>
>>> Well, putting back all my apps hasn't widened the discrepancy. So I 
>>> don't know why my previous web2py installation was so slow.
>>>
>>> While the Welcome app with the calculations test shows a 2x discrepancy, 
>>> the original app that initiated this thread now shows a 13x discrepancy 
>>> instead of 100x. That's certainly an improvement, but it's still too slow.
>>>
>>> The size of the discrepancy depends on the code that is executed. 
>>> Clearly, what I'm doing in the original app (performing permutations) is 
>>> more demanding than mere arithmetical operations. Hence, 13x vs 2x.
>>>
>>> I anxiously await any resolution to this performance issue, whether it 
>>> be in WSGI or in web2py. I'll check in on this thread periodically...
>>>
>>>
>>> On Saturday, 15 March 2014 16:19:12 UTC-4, horridohobbyist wrote:
>>>>
>>>> Interestingly, now that I've got a fresh install of web2py with only 
>>>> the Welcome app, my Welcome vs command line test shows a consistent 2x 
>>>> discrepancy, just as you had observed.
>>>>
>>>> My next step is to gradually add back all the other apps I had in 
>>>> web2py (I had 8 of them!) and see whether the discrepancy grows with the 
>>>> number of apps. That's the theory I'm working on.
>>>>
>>>> Yes, yes, I know, according to the Book, I shouldn't have so many apps 
>>>> installed in web2py. This apparently affects performance. But the truth 
>>>> is, 
>>>> most of those apps are hardly ever executed, so their existence merely 
>>>> represents a static overhead in web2py. In my mind, this shouldn't widen 
>>>> the discrepancy, but you never know.
>>>>
>>>>
>>>> On Saturday, 15 March 2014 11:19:06 UTC-4, Niphlod wrote:
>>>>>
>>>>> @mcm: you got me worried. Your test function was clocking a hell lower 
>>>>> than the original script. But then I found out why; one order of 
>>>>> magnitude 
>>>>> less (5000 vs 50000). Once that was corrected, you got the exact same 
>>>>> clock 
>>>>> times as "my app" (i.e. function directly in the controller). I also 
>>>>> stripped out the logging part making the app just return the result and 
>>>>> no 
>>>>> visible changes to the timings happened.
>>>>>
>>>>> @hh: glad at least we got some grounds to hold on. 
>>>>> @mariano: compiled or not, it doesn't seem to "change" the mean. a 
>>>>> compiled app has just lower variance. 
>>>>>
>>>>> @all: jlundell definitively hit something. Times are much more lower 
>>>>> when threads are 1.
>>>>>
>>>>> BTW: if I change "originalscript.py" to 
>>>>>
>>>>> # -*- coding: utf-8 -*-
>>>>> import time
>>>>> import threading
>>>>>
>>>>> def test():
>>>>>     start = time.time()
>>>>>     x = 0.0
>>>>>     for i in range(1,50000):
>>>>>         x += (float(i+10)*(i+25)+175.0)/3.14
>>>>>     res = str(time.time()-start)
>>>>>     print "elapsed time: "+ res + '\n'
>>>>>
>>>>> if __name__ == '__main__':
>>>>>     t = threading.Thread(target=test)
>>>>>     t.start()
>>>>>     t.join()
>>>>>
>>>>> I'm getting really close timings to "wsgi environment, 1 thread only" 
>>>>> tests, i.e. 
>>>>> 0.23 min, 0.26 max, ~0.24 mean
>>>>>
>>>>>

-- 
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
--- 
You received this message because you are subscribed to the Google Groups 
"web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to web2py+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
430x300x200 430x300x200 400x370x330 390x285x140 585x285x200
430x300x200 400x370x330 553x261x152 290x210x160 390x285x140
import time
import sys
import os
debug_path = os.path.join(request.folder,'static/debug.out')
def debug(str):
    f = open(debug_path,'a')
    f.write(str+'\n')
    f.close()
    return

#
# pyShipping 1.8a
#
import time
import random
from shippackage import Package

def packstrip(bin, p):
    """Creates a Strip which fits into bin.

    Returns the Packages to be used in the strip, the dimensions of the strip as a 3-tuple
    and a list of "left over" packages.
    """
    # This code is somewhat optimized and somewhat unreadable
    s = []                # strip
    r = []                # rest
    ss = sw = sl = 0      # stripsize
    bs = bin.heigth       # binsize
    sapp = s.append       # speedup
    rapp = r.append       # speedup
    ppop = p.pop          # speedup
    while p and (ss <= bs):
        n = ppop(0)
        nh, nw, nl = n.size
        if ss + nh <= bs:
            ss += nh
            sapp(n)
            if nw > sw:
                sw = nw
            if nl > sl:
                sl = nl
        else:
            rapp(n)
    return s, (ss, sw, sl), r + p


def packlayer(bin, packages):
    strips = []
    layersize = 0
    layerx = 0
    layery = 0
    binsize = bin.width
    while packages:
        strip, (sizex, stripsize, sizez), rest = packstrip(bin, packages)
        if layersize + stripsize <= binsize:
            if not strip:
                # we were not able to pack anything
                break
            layersize += stripsize
            layerx = max([sizex, layerx])
            layery = max([sizez, layery])
            strips.extend(strip)
            packages = rest
        else:
            # Next Layer please
            packages = strip + rest
            break
    return strips, (layerx, layersize, layery), packages


def packbin(bin, packages):
    packages.sort()
    layers = []
    contentheigth = 0
    contentx = 0
    contenty = 0
    binsize = bin.length
    while packages:
        layer, (sizex, sizey, layersize), rest = packlayer(bin, packages)
        if contentheigth + layersize <= binsize:
            if not layer:
                # we were not able to pack anything
                break
            contentheigth += layersize
            contentx = max([contentx, sizex])
            contenty = max([contenty, sizey])
            layers.extend(layer)
            packages = rest
        else:
            # Next Bin please
            packages = layer + rest
            break
    return layers, (contentx, contenty, contentheigth), packages


def packit(bin, originalpackages):
    packedbins = []
    packages = sorted(originalpackages)
    while packages:
        packagesinbin, (binx, biny, binz), rest = packbin(bin, packages)
        if not packagesinbin:
            # we were not able to pack anything
            break
        packedbins.append(packagesinbin)
        packages = rest
    # we now have a result, try to get a better result by rotating some bins

    return packedbins, rest


# In newer Python versions these van be imported:
# from itertools import permutations
def product(*args, **kwds):
    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
    # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
    pools = map(tuple, args) * kwds.get('repeat', 1)
    result = [[]]
    for pool in pools:
        result = [x + [y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)


def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(range(n), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)


class Timeout(Exception):
    pass


def allpermutations_helper(permuted, todo, maxcounter, callback, bin, bestpack, counter):
    if not todo:
        return counter + callback(bin, permuted, bestpack)
    else:
        others = todo[1:]
        thispackage = todo[0]
        for dimensions in set(permutations((thispackage[0], thispackage[1], thispackage[2]))):
            thispackage = Package(dimensions, nosort=True)
            if thispackage in bin:
                counter = allpermutations_helper(permuted + [thispackage], others, maxcounter, callback,
                                                 bin, bestpack, counter)
            if counter > maxcounter:
                raise Timeout('more than %d iterations tries' % counter)
        return counter


def trypack(bin, packages, bestpack):
    bins, rest = packit(bin, packages)
    if len(bins) < bestpack['bincount']:
        bestpack['bincount'] = len(bins)
        bestpack['bins'] = bins
        bestpack['rest'] = rest
    if bestpack['bincount'] < 2:
        raise Timeout('optimal solution found')
    return len(packages)


def allpermutations(todo, bin, iterlimit=5000):
    random.seed(1)
    random.shuffle(todo)
    bestpack = dict(bincount=len(todo) + 1)
    start = time.time()
    try:
        # First try unpermuted
        trypack(bin, todo, bestpack)
        # now try permutations
        allpermutations_helper([], todo, iterlimit, trypack, bin, bestpack, 0)
    except Timeout:
        pass
    debug('Elapsed time: '+str(time.time() - start))
    return bestpack['bins'], bestpack['rest']


def binpack(packages, bin=None, iterlimit=5000):
    """Packs a list of Package() objects into a number of equal-sized bins.

    Returns a list of bins listing the packages within the bins and a list of packages which can't be
    packed because they are to big."""
    if not bin:
        bin = Package("600x400x400")
    return allpermutations(packages, bin, iterlimit)


def test():
    test_path = os.path.join(request.folder,'static/testdata.txt')
    fd = open(test_path)
    vorher = 0
    nachher = 0
    debug('Begin...')
    for line in fd:
        packages = [Package(pack) for pack in line.strip().split()]
        if not packages:
            continue
        bins, rest = binpack(packages)
        if rest:
            debug("invalid data")
        else:
            vorher += len(packages)
            nachher += len(bins)
    debug('Percentage fill: '+str(float(nachher) / vorher * 100))
    return

if __name__ == "__main__":
    test()
from flash import Flask
app = Flash(__name__)

import time
import sys
import os
debug_path = os.path.join(request.folder,'static/debug.out')
def debug(str):
    f = open(debug_path,'a')
    f.write(str+'\n')
    f.close()
    return

#
# pyShipping 1.8a
#
import time
import random
from shippackage import Package

def packstrip(bin, p):
    """Creates a Strip which fits into bin.

    Returns the Packages to be used in the strip, the dimensions of the strip as a 3-tuple
    and a list of "left over" packages.
    """
    # This code is somewhat optimized and somewhat unreadable
    s = []                # strip
    r = []                # rest
    ss = sw = sl = 0      # stripsize
    bs = bin.heigth       # binsize
    sapp = s.append       # speedup
    rapp = r.append       # speedup
    ppop = p.pop          # speedup
    while p and (ss <= bs):
        n = ppop(0)
        nh, nw, nl = n.size
        if ss + nh <= bs:
            ss += nh
            sapp(n)
            if nw > sw:
                sw = nw
            if nl > sl:
                sl = nl
        else:
            rapp(n)
    return s, (ss, sw, sl), r + p


def packlayer(bin, packages):
    strips = []
    layersize = 0
    layerx = 0
    layery = 0
    binsize = bin.width
    while packages:
        strip, (sizex, stripsize, sizez), rest = packstrip(bin, packages)
        if layersize + stripsize <= binsize:
            if not strip:
                # we were not able to pack anything
                break
            layersize += stripsize
            layerx = max([sizex, layerx])
            layery = max([sizez, layery])
            strips.extend(strip)
            packages = rest
        else:
            # Next Layer please
            packages = strip + rest
            break
    return strips, (layerx, layersize, layery), packages


def packbin(bin, packages):
    packages.sort()
    layers = []
    contentheigth = 0
    contentx = 0
    contenty = 0
    binsize = bin.length
    while packages:
        layer, (sizex, sizey, layersize), rest = packlayer(bin, packages)
        if contentheigth + layersize <= binsize:
            if not layer:
                # we were not able to pack anything
                break
            contentheigth += layersize
            contentx = max([contentx, sizex])
            contenty = max([contenty, sizey])
            layers.extend(layer)
            packages = rest
        else:
            # Next Bin please
            packages = layer + rest
            break
    return layers, (contentx, contenty, contentheigth), packages


def packit(bin, originalpackages):
    packedbins = []
    packages = sorted(originalpackages)
    while packages:
        packagesinbin, (binx, biny, binz), rest = packbin(bin, packages)
        if not packagesinbin:
            # we were not able to pack anything
            break
        packedbins.append(packagesinbin)
        packages = rest
    # we now have a result, try to get a better result by rotating some bins

    return packedbins, rest


# In newer Python versions these van be imported:
# from itertools import permutations
def product(*args, **kwds):
    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
    # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
    pools = map(tuple, args) * kwds.get('repeat', 1)
    result = [[]]
    for pool in pools:
        result = [x + [y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)


def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(range(n), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)


class Timeout(Exception):
    pass


def allpermutations_helper(permuted, todo, maxcounter, callback, bin, bestpack, counter):
    if not todo:
        return counter + callback(bin, permuted, bestpack)
    else:
        others = todo[1:]
        thispackage = todo[0]
        for dimensions in set(permutations((thispackage[0], thispackage[1], thispackage[2]))):
            thispackage = Package(dimensions, nosort=True)
            if thispackage in bin:
                counter = allpermutations_helper(permuted + [thispackage], others, maxcounter, callback,
                                                 bin, bestpack, counter)
            if counter > maxcounter:
                raise Timeout('more than %d iterations tries' % counter)
        return counter


def trypack(bin, packages, bestpack):
    bins, rest = packit(bin, packages)
    if len(bins) < bestpack['bincount']:
        bestpack['bincount'] = len(bins)
        bestpack['bins'] = bins
        bestpack['rest'] = rest
    if bestpack['bincount'] < 2:
        raise Timeout('optimal solution found')
    return len(packages)


def allpermutations(todo, bin, iterlimit=5000):
    random.seed(1)
    random.shuffle(todo)
    bestpack = dict(bincount=len(todo) + 1)
    start = time.time()
    try:
        # First try unpermuted
        trypack(bin, todo, bestpack)
        # now try permutations
        allpermutations_helper([], todo, iterlimit, trypack, bin, bestpack, 0)
    except Timeout:
        pass
    debug('Elapsed time: '+str(time.time() - start))
    return bestpack['bins'], bestpack['rest']


def binpack(packages, bin=None, iterlimit=5000):
    """Packs a list of Package() objects into a number of equal-sized bins.

    Returns a list of bins listing the packages within the bins and a list of packages which can't be
    packed because they are to big."""
    if not bin:
        bin = Package("600x400x400")
    return allpermutations(packages, bin, iterlimit)


def test():
    test_path = os.path.join(request.folder,'static/testdata.txt')
    fd = open(test_path)
    vorher = 0
    nachher = 0
    debug('Begin...')
    for line in fd:
        packages = [Package(pack) for pack in line.strip().split()]
        if not packages:
            continue
        bins, rest = binpack(packages)
        if rest:
            debug("invalid data")
        else:
            vorher += len(packages)
            nachher += len(bins)
    debug('Percentage fill: '+str(float(nachher) / vorher * 100))
    return

@app.route("/")
def hello():
    test()
    return "Hello World!"

if __name__ == "__main__":
    app.run(host='0.0.0.0')
# this file is released under public domain and you can use without limitations

#########################################################################
## This is a sample controller
## - index is the default action of any application
## - user is required for authentication and authorization
## - download is for downloading files uploaded in the db (does streaming)
## - call exposes all registered services (none by default)
#########################################################################
import time
import sys
import os
debug_path = os.path.join(request.folder,'static/debug.out')
def debug(str):
    f = open(debug_path,'a')
    f.write(str+'\n')
    f.close()
    return

def index():
    test()
    """
    example action using the internationalization operator T and flash
    rendered by views/default/index.html or views/generic.html

    if you need a simple wiki simply replace the two lines below with:
    return auth.wiki()
    """
    response.flash = T("Welcome to web2py!")
    return dict(message=T('Hello World'))


def user():
    """
    exposes:
    http://..../[app]/default/user/login
    http://..../[app]/default/user/logout
    http://..../[app]/default/user/register
    http://..../[app]/default/user/profile
    http://..../[app]/default/user/retrieve_password
    http://..../[app]/default/user/change_password
    http://..../[app]/default/user/manage_users (requires membership in
    use @auth.requires_login()
        @auth.requires_membership('group name')
        @auth.requires_permission('read','table name',record_id)
    to decorate functions that need access control
    """
    return dict(form=auth())

@cache.action()
def download():
    """
    allows downloading of uploaded files
    http://..../[app]/default/download/[filename]
    """
    return response.download(request, db)


def call():
    """
    exposes services. for example:
    http://..../[app]/default/call/jsonrpc
    decorate with @services.jsonrpc the functions to expose
    supports xml, json, xmlrpc, jsonrpc, amfrpc, rss, csv
    """
    return service()


@auth.requires_signature()
def data():
    """
    http://..../[app]/default/data/tables
    http://..../[app]/default/data/create/[table]
    http://..../[app]/default/data/read/[table]/[id]
    http://..../[app]/default/data/update/[table]/[id]
    http://..../[app]/default/data/delete/[table]/[id]
    http://..../[app]/default/data/select/[table]
    http://..../[app]/default/data/search/[table]
    but URLs must be signed, i.e. linked with
      A('table',_href=URL('data/tables',user_signature=True))
    or with the signed load operator
      LOAD('default','data.load',args='tables',ajax=True,user_signature=True)
    """
    return dict(form=crud())

#
# pyShipping 1.8a
#
import time
import random
from shippackage import Package

def packstrip(bin, p):
    """Creates a Strip which fits into bin.

    Returns the Packages to be used in the strip, the dimensions of the strip as a 3-tuple
    and a list of "left over" packages.
    """
    # This code is somewhat optimized and somewhat unreadable
    s = []                # strip
    r = []                # rest
    ss = sw = sl = 0      # stripsize
    bs = bin.heigth       # binsize
    sapp = s.append       # speedup
    rapp = r.append       # speedup
    ppop = p.pop          # speedup
    while p and (ss <= bs):
        n = ppop(0)
        nh, nw, nl = n.size
        if ss + nh <= bs:
            ss += nh
            sapp(n)
            if nw > sw:
                sw = nw
            if nl > sl:
                sl = nl
        else:
            rapp(n)
    return s, (ss, sw, sl), r + p


def packlayer(bin, packages):
    strips = []
    layersize = 0
    layerx = 0
    layery = 0
    binsize = bin.width
    while packages:
        strip, (sizex, stripsize, sizez), rest = packstrip(bin, packages)
        if layersize + stripsize <= binsize:
            if not strip:
                # we were not able to pack anything
                break
            layersize += stripsize
            layerx = max([sizex, layerx])
            layery = max([sizez, layery])
            strips.extend(strip)
            packages = rest
        else:
            # Next Layer please
            packages = strip + rest
            break
    return strips, (layerx, layersize, layery), packages


def packbin(bin, packages):
    packages.sort()
    layers = []
    contentheigth = 0
    contentx = 0
    contenty = 0
    binsize = bin.length
    while packages:
        layer, (sizex, sizey, layersize), rest = packlayer(bin, packages)
        if contentheigth + layersize <= binsize:
            if not layer:
                # we were not able to pack anything
                break
            contentheigth += layersize
            contentx = max([contentx, sizex])
            contenty = max([contenty, sizey])
            layers.extend(layer)
            packages = rest
        else:
            # Next Bin please
            packages = layer + rest
            break
    return layers, (contentx, contenty, contentheigth), packages


def packit(bin, originalpackages):
    packedbins = []
    packages = sorted(originalpackages)
    while packages:
        packagesinbin, (binx, biny, binz), rest = packbin(bin, packages)
        if not packagesinbin:
            # we were not able to pack anything
            break
        packedbins.append(packagesinbin)
        packages = rest
    # we now have a result, try to get a better result by rotating some bins

    return packedbins, rest


# In newer Python versions these van be imported:
# from itertools import permutations
def product(*args, **kwds):
    # product('ABCD', 'xy') --> Ax Ay Bx By Cx Cy Dx Dy
    # product(range(2), repeat=3) --> 000 001 010 011 100 101 110 111
    pools = map(tuple, args) * kwds.get('repeat', 1)
    result = [[]]
    for pool in pools:
        result = [x + [y] for x in result for y in pool]
    for prod in result:
        yield tuple(prod)


def permutations(iterable, r=None):
    pool = tuple(iterable)
    n = len(pool)
    r = n if r is None else r
    for indices in product(range(n), repeat=r):
        if len(set(indices)) == r:
            yield tuple(pool[i] for i in indices)


class Timeout(Exception):
    pass


def allpermutations_helper(permuted, todo, maxcounter, callback, bin, bestpack, counter):
    if not todo:
        return counter + callback(bin, permuted, bestpack)
    else:
        others = todo[1:]
        thispackage = todo[0]
        for dimensions in set(permutations((thispackage[0], thispackage[1], thispackage[2]))):
            thispackage = Package(dimensions, nosort=True)
            if thispackage in bin:
                counter = allpermutations_helper(permuted + [thispackage], others, maxcounter, callback,
                                                 bin, bestpack, counter)
            if counter > maxcounter:
                raise Timeout('more than %d iterations tries' % counter)
        return counter


def trypack(bin, packages, bestpack):
    bins, rest = packit(bin, packages)
    if len(bins) < bestpack['bincount']:
        bestpack['bincount'] = len(bins)
        bestpack['bins'] = bins
        bestpack['rest'] = rest
    if bestpack['bincount'] < 2:
        raise Timeout('optimal solution found')
    return len(packages)


def allpermutations(todo, bin, iterlimit=5000):
    random.seed(1)
    random.shuffle(todo)
    bestpack = dict(bincount=len(todo) + 1)
    start = time.time()
    try:
        # First try unpermuted
        trypack(bin, todo, bestpack)
        # now try permutations
        allpermutations_helper([], todo, iterlimit, trypack, bin, bestpack, 0)
    except Timeout:
        pass
    debug('Elapsed time: '+str(time.time() - start))
    return bestpack['bins'], bestpack['rest']


def binpack(packages, bin=None, iterlimit=5000):
    """Packs a list of Package() objects into a number of equal-sized bins.

    Returns a list of bins listing the packages within the bins and a list of packages which can't be
    packed because they are to big."""
    if not bin:
        bin = Package("600x400x400")
    return allpermutations(packages, bin, iterlimit)


def test():
    test_path = os.path.join(request.folder,'static/testdata.txt')
    fd = open(test_path)
    vorher = 0
    nachher = 0
    debug('Begin...')
    for line in fd:
        packages = [Package(pack) for pack in line.strip().split()]
        if not packages:
            continue
        bins, rest = binpack(packages)
        if rest:
            debug("invalid data")
        else:
            vorher += len(packages)
            nachher += len(bins)
    debug('Percentage fill: '+str(float(nachher) / vorher * 100))
# encoding: utf-8
"""
package.py - shipping/cargo related calculations based on a unit of shipping (box, crate, package)

Created by Maximillian Dornseif on 2006-12-02.
Copyright HUDORA GmbH 2006, 2007, 2010
You might consider this BSD-Licensed.
"""

import doctest
import unittest

class Package(object):
    """Represents a package as used in cargo/shipping aplications."""

    def __init__(self, size, weight=0, nosort=False):
        """Generates a new Package object.

        The size can be given as an list of integers or an string where the sizes are determined by the letter 'x':
        >>> Package((300, 400, 500))
        <Package 500x400x300>
        >>> Package('300x400x500')
        <Package 500x400x300>
        """
        self.weight = weight
        if "x" in size:
            self.heigth, self.width, self.length = [int(x) for x in size.split('x')]
        else:
            self.heigth, self.width, self.length = size
        if not nosort:
            (self.heigth, self.width, self.length) = sorted((int(self.heigth), int(self.width),
                                                             int(self.length)), reverse=True)
        self.volume = self.heigth * self.width * self.length
        self.size = (self.heigth, self.width, self.length)

    def _get_gurtmass(self):
        """'gurtamss' is the circumference of the box plus the length - which is often used to
            calculate shipping costs.

            >>> Package((100,110,120)).gurtmass
            540
        """

        dimensions = (self.heigth, self.width, self.length)
        maxdimension = max(dimensions)
        otherdimensions = list(dimensions)
        del otherdimensions[otherdimensions.index(maxdimension)]
        return maxdimension + 2 * (sum(otherdimensions))
    gurtmass = property(_get_gurtmass)

    def hat_gleiche_seiten(self, other):
        """Pr?ft, ob other mindestens eine gleich grosse Seite mit self hat."""

        meineseiten = set([(self.heigth, self.width), (self.heigth, self.length), (self.width, self.length)])
        otherseiten = set([(other.heigth, other.width), (other.heigth, other.length),
                           (other.width, other.length)])
        return bool(meineseiten.intersection(otherseiten))

    def __getitem__(self, key):
        """The coordinates can be accessed as if the object is a tuple.
        >>> p = Package((500, 400, 300))
        >>> p[0]
        500
        """
        if key == 0:
            return self.heigth
        if key == 1:
            return self.width
        if key == 2:
            return self.length
        if isinstance(key, tuple):
            return (self.heigth, self.width, self.length)[key[0]:key[1]]
        if isinstance(key, slice):
            return (self.heigth, self.width, self.length)[key]
        raise IndexError

    def __contains__(self, other):
        """Checks if on package fits within an other.

        >>> Package((1600, 250, 480)) in Package((1600, 250, 480))
        True
        >>> Package((1600, 252, 480)) in Package((1600, 250, 480))
        False
        """
        return self[0] >= other[0] and self[1] >= other[1] and self[2] >= other[2]

    def __hash__(self):
        return self.heigth + (self.width << 16) + (self.length << 32)

    def __eq__(self, other):
        """Package objects are equal if they have exactly the same dimensions.

           Permutations of the dimensions are considered equal:

           >>> Package((100,110,120)) == Package((100,110,120))
           True
           >>> Package((120,110,100)) == Package((100,110,120))
           True
        """
        return (self.heigth == other.heigth and self.width == other.width and self.length == other.length)

    def __cmp__(self, other):
        """Enables to sort by Volume."""
        return cmp(self.volume, other.volume)

    def __mul__(self, multiplicand):
        """Package can be multiplied with an integer. This results in the Package beeing
           stacked along the biggest side.

           >>> Package((400,300,600)) * 2
           <Package 600x600x400>
           """
        return Package((self.heigth, self.width, self.length * multiplicand), self.weight * multiplicand)

    def __add__(self, other):
        """
            >>> Package((1600, 250, 480)) + Package((1600, 470, 480))
            <Package 1600x720x480>
            >>> Package((1600, 250, 480)) + Package((1600, 480, 480))
            <Package 1600x730x480>
            >>> Package((1600, 250, 480)) + Package((1600, 490, 480))
            <Package 1600x740x480>
            """
        meineseiten = set([(self.heigth, self.width), (self.heigth, self.length),
                           (self.width, self.length)])
        otherseiten = set([(other.heigth, other.width), (other.heigth, other.length),
                           (other.width, other.length)])
        if not meineseiten.intersection(otherseiten):
            raise ValueError("%s has no fitting sites to %s" % (self, other))
        candidates = sorted(meineseiten.intersection(otherseiten), reverse=True)
        stack_on = candidates[0]
        mysides = [self.heigth, self.width, self.length]
        mysides.remove(stack_on[0])
        mysides.remove(stack_on[1])
        othersides = [other.heigth, other.width, other.length]
        othersides.remove(stack_on[0])
        othersides.remove(stack_on[1])
        return Package((stack_on[0], stack_on[1], mysides[0] + othersides[0]), self.weight + other.weight)

    def __str__(self):
        if self.weight:
            return "%dx%dx%d %dg" % (self.heigth, self.width, self.length, self.weight)
        else:
            return "%dx%dx%d" % (self.heigth, self.width, self.length)

    def __repr__(self):
        if self.weight:
            return "<Package %dx%dx%d %d>" % (self.heigth, self.width, self.length, self.weight)
        else:
            return "<Package %dx%dx%d>" % (self.heigth, self.width, self.length)


def buendelung(kartons, maxweight=31000, maxgurtmass=3000):
    """Versucht Pakete so zu b?ndeln, so dass das Gurtmass nicht ?berschritten wird.

    Gibt die geb?ndelten Pakete und die nicht b?ndelbaren Pakete zur?ck.

    >>> buendelung([Package((800, 310, 250)), Package((800, 310, 250)), Package((800, 310, 250)), Package((800, 310, 250))])
    (1, [<Package 800x750x310>], [<Package 800x310x250>])
    >>> buendelung([Package((800, 310, 250)), Package((800, 310, 250)), Package((800, 310, 250)), Package((800, 310, 250)), Package((450, 290, 250)), Package((450, 290, 250))])
    (2, [<Package 800x750x310>, <Package 500x450x290>], [<Package 800x310x250>])
    """

    MAXKARTOONSIMBUENDEL = 6
    if not kartons:
        return 0, [], kartons
    gebuendelt = []
    rest = []
    lastkarton = kartons.pop(0)
    buendel = False
    buendelcounter = 0
    kartons_im_buendel = 1
    while kartons:
        currentcarton = kartons.pop(0)
        # check if 2 dimensions fit
        if (currentcarton.hat_gleiche_seiten(lastkarton)
            and (lastkarton.weight + currentcarton.weight < maxweight)
            and ((lastkarton + currentcarton).gurtmass < maxgurtmass)
            and (kartons_im_buendel < MAXKARTOONSIMBUENDEL)):
            # new carton has the same size in two dimensions and the sum of both in the third
            # ok, we can bundle
            lastkarton = (lastkarton + currentcarton)
            kartons_im_buendel += 1
            if buendel is False:
                # neues B?ndel
                buendelcounter += 1
            buendel = True
        else:
            # different sizes, or too big
            if buendel:
                gebuendelt.append(lastkarton)
            else:
                rest.append(lastkarton)
            kartons_im_buendel = 1
            lastkarton = currentcarton
            buendel = False
    if buendel:
        gebuendelt.append(lastkarton)
    else:
        rest.append(lastkarton)
    return buendelcounter, gebuendelt, rest


def pack_in_bins(kartons, versandkarton):
    """Implements Bin-Packing.

    You provide it with a bin size and a list of Package Objects to be bined. Returns a list of lists
    representing the bins with the binned Packages and a list of Packages too big for binning.

    >>> pack_in_bins([Package('135x200x250'), Package('170x380x390'), Package('485x280x590'), Package('254x171x368'), Package('201x172x349'), Package('254x171x368')], \
                     Package('600x400x400'))
    ([[<Package 250x200x135>, <Package 349x201x172>, <Package 368x254x171>], [<Package 368x254x171>, <Package 390x380x170>]], [<Package 590x485x280>])
    """

    import pyshipping.binpack
    toobig, packagelist, bins, rest = [], [], [], []
    for box in sorted(kartons, reverse=True):
        if box not in versandkarton:
            # passt eh nicht
            toobig.append(box)
        else:
            packagelist.append(box)
    if packagelist:
        bins, rest = pyshipping.binpack.binpack(packagelist, versandkarton)
    return bins, toobig + rest


### Tests
class PackageTests(unittest.TestCase):
    """Simple tests for Package objects."""

    def test_init(self):
        """Tests object initialisation with different constructors."""
        self.assertEqual(Package((100, 100, 200)), Package(('100', '200', '100')))
        self.assertEqual(Package((100.0, 200.0, 200.0)), Package('200x200x100'))

    def test_eq(self):
        """Tests __eq__() implementation."""
        self.assertEqual(Package((200, 100, 200)), Package(('200', '100', '200')))
        self.assertNotEqual(Package((200, 200, 100)), Package(('100', '100', '200')))

    def test_volume(self):
        """Tests volume calculation"""
        self.assertEqual(4000000, Package((100, 200, 200)).volume)
        self.assertEqual(8000, Package((20, 20, 20)).volume)

    def test_str(self):
        """Test __unicode__ implementation."""
        self.assertEqual('200x200x100', Package((100, 200, 200)).__str__())
        self.assertEqual('200x200x100', Package('100x200x200').__str__())

    def test_repr(self):
        """Test __repr__ implementation."""
        self.assertEqual('<Package 200x200x100 44>', Package((100, 200, 200), 44).__repr__())

    def test_gurtmass(self):
        """Test gurtmass calculation."""
        self.assertEqual(800, Package((100, 200, 200)).gurtmass)
        self.assertEqual(900, Package((100, 200, 300)).gurtmass)
        self.assertEqual(1000, Package((200, 200, 200)).gurtmass)
        self.assertEqual(3060, Package((1600, 250, 480)).gurtmass)

    def test_mul(self):
        """Test multiplication."""
        self.assertEqual(Package((200, 200, 200)), Package((100, 200, 200)) * 2)

    def test_sort(self):
        """Test multiplication."""
        data = [Package((1600, 490, 480)), Package((1600, 470, 480)), Package((1600, 480, 480))]
        data.sort()
        self.assertEqual(data,
                         [Package((1600, 470, 480)), Package((1600, 480, 480)),
                          Package((1600, 490, 480))])


if __name__ == '__main__':

    factor = 0
    while True:
        factor += 1
        single = Package((750, 240, 220), 7400)
        multi = single * factor
        if multi.weight > 31000 or multi.gurtmass > 3000:
            multi = single * (factor - 1)
            #print factor - 1, multi, multi.gurtmass
            break

    doctest.testmod()
    unittest.main()

Reply via email to