5
\$\begingroup\$

I've been studying Python for a bit now (coming from no prior programming experience with the exception of dabbling in Java years ago) and I've tried putting something together that would affect my day-to-day work.

I work in a warehouse as a freight handler, unloading trucks full of product and building pallets from said product. Before unloading a truck, I get a PO that lists off which products are coming out, how much of each product and how much product goes onto a single pallet. So basically before unloading the truck I do some basic math.

I take the total amount of product for one item and I figure out how many pallets are going to have the full amount of product and whether or not there will be a pallet that is going to have less than full amount of product on it.

def productToPallets(totalProduct,tie,height): 
    """
    Takes 3 arguments from user inputted variables and does the math and outputs the division between pallets
    Good for checking individual product numbers or testing the formula. For multiple product calculations, run and repeat
    the program using startProgram()

    INPUT: 3 arguments: The total product count, the amount of product on a single line and how many lines high on one pallet
    OUTPUT: Total amount of full pallets, total amount of lines and total amount of boxes
    """

    lines = totalProduct / tie
    fullLines = int(lines)

    pallets = lines / height
    fullPallets = int(pallets)

    productPerPallet = tie * height

    leftOverLastPallet = totalProduct % productPerPallet

    linesOnLastPallet = int(leftOverLastPallet / tie)
    productOnLastPallet = leftOverLastPallet % tie


    if pallets >= 1 and productOnLastPallet > 0 and linesOnLastPallet <= 0: #Full, Boxes (TESTED)
        fullPallets+=1
        if productOnLastPallet > 1:
            print '\n%r Full and %r Boxes on one pallet making a total of: %r Pallets' %(int(pallets), productOnLastPallet, fullPallets)
        else:
            print '\n%r Full and %r Box on one pallet making a total of: %r Pallets' %(int(pallets), productOnLastPallet, fullPallets)     


    elif pallets < 1 and productOnLastPallet > 0 and linesOnLastPallet <= 0: #Boxes (TESTED)
        fullPallets+=1
        if productOnLastPallet > 1:
            print '\n%r Boxes on one pallet making a total of: %r Pallets' %(productOnLastPallet, fullPallets)
        else:
            print '\n%r Box on one pallet making a total of: %r Pallets' %(productOnLastPallet, fullPallets)


    elif pallets >= 1 and linesOnLastPallet > 0 and productOnLastPallet <= 0: #Full, Lines (TESTED)
        fullPallets+=1
        if linesOnLastPallet > 1:
            print '\n%r Full, %r Lines on one pallet making a total of: %r Pallets' %(int(pallets), linesOnLastPallet, fullPallets) #Plural Lines
        else:
            print '\n%r Full, %r Line on one pallet making a total of: %r Pallets' %(int(pallets), linesOnLastPallet, fullPallets) #Single Line


    elif pallets < 1 and linesOnLastPallet > 0 and productOnLastPallet <= 0: #Lines (TESTED)
        fullPallets+=1
        if linesOnLastPallet > 1:
            print '\n%r Lines on one pallet making a total of: %r Pallets' %(linesOnLastPallet, fullPallets) #Plural Lines
        else:
            print '\n%r Line on one pallet making a total of: %r Pallets' %(linesOnLastPallet, fullPallets) #Single Line


    elif pallets >= 1 and linesOnLastPallet > 0 and productOnLastPallet > 0: #Full, Lines and Boxes (TESTED)
        fullPallets+=1
        print '\n%r Full, %r Line(s) + %r Boxe(s) making a total of: %r Pallets' %(int(pallets), linesOnLastPallet, productOnLastPallet, fullPallets)

    elif pallets < 1 and linesOnLastPallet > 0 and productOnLastPallet > 0: #Lines and boxes (TESTED)
        fullPallets+=1
        print '\n%r Line(s) + %r Boxe(s) making a total of: %r Pallets' %(linesOnLastPallet, productOnLastPallet, fullPallets)

    else: #Full (TESTED)
        print '\n%r Full making a total of: %r Pallets' %(int(pallets), int(pallets))



def runProgram():
    """
    Takes no arguments. When this function is called, it will essentially run everything in sequence. Grabs 3 amounts from 
    user, plugs those amounts into the productToPallets function, returning a statement of how the product will be divided,
    and then checking if more calculations need to be done in which this function will be called again.

    INPUT: 3 user inputted variables from user: totalproduct, tie and height
    OUTPUT: How the product will be divided onto pallets and then repeat the process if user specifies there are more numbers
    """

    inputTotalProduct = input("How much product?: ")
    inputTie = input("How much product per line?: ")
    inputHeight = input("How many lines of product per pallet?: ")

    productToPallets(inputTotalProduct,inputTie,inputHeight) #Runs the productToPallets function, given the 3 arguments from the users input values

    tryAgainCheck = raw_input("Would you like to check more numbers? yes/no: ")

    if tryAgainCheck == 'yes':
        startProgram() #restarts this function from the beginning
    else:
        print 'Program Stopped.'

totalProduct is the total amount of product coming out for one single product type

tie is the amount of product that goes onto one layer(line) on the pallet

height is the amount of layers(lines) that are allowed on one pallet

REALWORLD EXAMPLE:

Say you want to store your beer in a fridge, gotta keep your beer cold right? You've got 153 bottles of beer and a minifridge with 3 shelves on it. On each shelf you can store a total of 15 bottles. (Minifridge represents the pallet, shelves represent height and the amount > of bottles per shelf represents tie.) So you can store a total of 45 bottles total in one minifridge. Now if you wanted to keep ALL your beer cold, You would have 3 minifridges in which all would be full with 45 bottles, making a total of 135 bottles stored. Now you're left with 18, we know each shelf(out of 3) can hold 15 bottles, this means you would entirely fill one shelf, and have 3 bottles on another shelf leaving that shelf partially empty and one shelve entirely empty. So in the end, you have 3 filled minifridges, the last fridge will have 1 shelf full of 15 and a second shelf partially full of 3 bottles making a total of 4 minifridges. Now realistically I doubt you'd buy several minifridge as opposed to one big fridge, but that is the concept.

This converted to product and pallets would come out to 3 Full Pallets, 1 Line, and 3 Boxes making 4 Pallets total. So if you were using the code for this, you would enter 150 as the total product, 15 as the tie and 3 as the height.

I'm sorry if I'm way over explaining this, or making it confusing. I'm just > very keen to being as detailed as I can I guess haha.

I know this is incredibly simple, and the program does actually work just fine (Aside from Jupyter's kernal occasionally crashing?) and I figure there's not a lot that can be said about such a simple program, but I'm excited to be learning and want to know early on if I am developing any bad or good habits! so some feedback would be awesome. Some things I would like to know:

  • Is this an effective way of going about this? Is there a way this could be written so explicitly simple that I just beyond over-complicated it?

  • If not, is there a more efficient way of doing anything particular?

  • How is my format? am I effectively using statements, functions, operators, comments, docstrings, variable naming..etc? is everything sequenced effectively? is the code clean and easily readable? I figure I technically didn't have to use functions for this, but obviously I need to learn them, and I wanted to incorporate a way to do repeated user-defined calculations without having to restart the program in jupyter manually.

  • Considering what the program does, is python really even the language of choice for something like this? considering I would like to turn it into a desktop application.

  • How would I go about turning these cell blocks into a functional desktop application with it's own UI and buttons? unfortunately I would not be able to use it at work anyhow considering I won't be lugging my laptop around with me, as well as not knowing how to convert to an iOS friendly language and uploading to the apple store, but still, it would be cool to have a desktop application.

Any other kind of feedback or tips/advice is much appreciated as well. I'm just eager to learn and desperate to do it the right way!

\$\endgroup\$

migrated from stackoverflow.com Dec 22 '17 at 18:07

This question came from our site for professional and enthusiast programmers.

3
\$\begingroup\$

First, congrats on getting started!

Line width: some of your lines are quite long, meaning you need to scroll left and right to read the whole line. This is inconvenient. PEP8 suggests that code lines should be no more than 79 chars long, and docstrings no more than 70. This is a guideline, not a hard-and-fast rule, but it makes your life easier!

Naming functions: while Java expects camelCase, Python's PEP8 standard suggests that functions should be named all lowercase with words separated by underlines ie product_to_pallets(). It is also a good idea to use a "verb-noun" format, so palletize_product(), and it's usually a good idea to avoid slang that many people won't understand (ie "what's a tie?"), so maybe palletize_boxes() instead?

It's usually not a good idea to have a single function perform calculations AND print output; it makes it difficult to reuse.

There is an integer division operator // which you might find useful. Also many operators have an "assignment operator" variant - for example instead of writing a = a * b you can do a *= b instead.

def palletize_boxes(how_many, boxes_per_row, rows_per_pallet):
    """
    Pack boxes on a shipping pallet

    Given the number of boxes, boxes per row, and rows per pallet,
      return the number of full pallets, left-over rows, and left-over boxes
    """
    boxes_per_pallet = boxes_per_row * rows_per_pallet
    full_pallets = how_many  // boxes_per_pallet
    remainder    = how_many  %  boxes_per_pallet
    extra_rows   = remainder // boxes_per_row
    extra_boxes  = remainder %  boxes_per_row
    return full_pallets, extra_rows, extra_boxes

def show_result(full_pallets, extra_rows, extra_boxes):
    """
    Present nicely formatted output
    """
    output = []

    if full_pallets == 1:
        output.append("1 full pallet")
    elif full_pallets > 1:
        output.append("%d full pallets" % full_pallets)

    if extra_rows == 1:
        output.append("1 full row")
    elif extra_rows > 1:
        output.append("%d full rows" % extra_rows)

    if extra_boxes == 1:
        output.append("1 box")
    elif extra_boxes > 1:
        output.append("%d boxes" % extra_boxes)

    total_pallets = full_pallets + (1 if extra_rows or extra_boxes else 0)

    if total_pallets:
        return ", ".join(output) + " for a total of %d pallets" % total_pallets
    else:
        return "Nothing to ship!"

As far as turning it into a graphical app, you probably want to look at Tk (built into Python but a little bit boring-looking), PyQt (the other "standard" gui toolkit), or possibly Kivy (up-and-coming).

\$\endgroup\$
  • \$\begingroup\$ Awesome! just the feedback I was looking for, thank you. I figured I shouldn't have a function that prints output, but I just couldn't figure out how to separate the two without breaking it. Very nicely written code, I appreciate you taking the time to write it, while I don't entirely grasp all of it, it's perfect to compare and study. Although for some reason when calling show_result() I get a NameError saying 'full_pallets' is not defined? I suppose I should figure that out for myself though! \$\endgroup\$ – Jake Dec 20 '17 at 21:08
  • 1
    \$\begingroup\$ Note that your remarks about division are for Python 3 (or for Python 2 with from __future__ import division). The code being reviewed here is Python 2. \$\endgroup\$ – 200_success Dec 22 '17 at 18:21
2
\$\begingroup\$

Whole books have been written about this question! While I will not answer all of your questions, as Google is a powerful research tool with proper motivation, I will give you one tip:

Input validation

Make sure when you ask for input from your user that you verify it's the datatype you are expecting, e.g. calling

productToPallets("1","1","A red 3 from the game Uno.")

will crash when it tries to convert the third argument to an integer.

\$\endgroup\$
  • \$\begingroup\$ Stay excited for making software solutions, it will make you a valuable asset to any company! \$\endgroup\$ – Austin A Dec 20 '17 at 19:01
  • \$\begingroup\$ This is really bad advice for Python. \$\endgroup\$ – timgeb Dec 20 '17 at 19:10
  • \$\begingroup\$ You mean specifying the users input as an integer? such as instead of inputTotalProduct = input("How much product?: ") i use: inputTotalProduct = int(input("How much product?: ")) and timeb, how is that bad advice? \$\endgroup\$ – Jake Dec 20 '17 at 19:11
  • 1
    \$\begingroup\$ Input validation is a basic technique for programming in general, i'm not sure how this is bad advice; it is one of the very first things taught in programming classes. I think Jake made it clear that he is attempting to get into programming. \$\endgroup\$ – Austin A Dec 20 '17 at 19:26
  • \$\begingroup\$ And @Jake, yes. You can wrap those as well in a try except. Check out this post regarding input validation: stackoverflow.com/questions/23294658/… \$\endgroup\$ – Austin A Dec 20 '17 at 19:42
0
\$\begingroup\$

You use a funcion startProgram that isn't defined anywhere. But I think this is a copy and paste error and I replaced it by runProgram.

Your program is started by calling the function runProgram. And if the user input says the the calculations should be repeated then you call runProgram again from inside runProgram. This is not a good way to implement a repetition because this is a recursive function call. Eventually the python stack will be exhausted and the program will crash. Don't use recursion here but an appropriate loop.

def runProgram():
    ...
    tryAgainCheck = raw_input("Would you like to check more numbers? yes/no: ")

    if tryAgainCheck == 'yes':
        runProgram() #restarts this function from the beginning
    else:
        print 'Program Stopped.'
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.