- What is the best method/practice to keep, let's say, over 3,000 lines of code organized for readability?
- What kind of NO-NOs in regards to coding habits should I get rid of before they become bad habits?
- Inversely what kind of good coding habits should I be picking up?
- Any glaringly obvious mistakes? Any helpful links for further reading would be appreciated.
I am including code that I have been working on for the past couple weeks. Basically it will help to increase the efficiency of my workflow from pulling inventory off trucks, to getting it to where it needs to go.
The following code navigates to a website and extracts information about an Item.
# -*- coding: utf8 -*-
import csv, time, os, random, difflib, re, json
from itertools import islice
import httplib
import urllib2
from bs4 import BeautifulSoup as BS
from shutil import copyfile
def get_product_info(item_number):
''' Returns a dictionary of item information '''
# offer_id_orig = '#NK420#########'
offer_id_orig = item_number
item_uts_number = offer_id_orig[1:6]
print offer_id_orig
# DOWNLOAD PRODUCT INFO IF WE DON"T HAVE IT
def download_page_info(item_uts_number):
#import time, os, random
#import httplib
#import urllib2
# ITEM_NUMBER MUST BE 4NK4200000100 FORMAT
# assigned_row_titles 'offer_id_orig' : current_row[2]
print item_uts_number
httplib.HTTPConnection.debuglevel = 1
# take multiple arguments?
fp = os.path.join(folder,item_uts_number)
print "Fetching:"+fp+"\n"
try:
request = urllib2.Request('http://www.fingerhut.com/product/'+item_uts_number.upper()+'.uts')
request.add_header('User-Agent','jmunsch_thnx_v2.0 +http://jamesmunsch.com/')
opener = urllib2.build_opener()
data = opener.open(request).read()
with open(fp,'w+') as f:
f.write(data)
time.sleep(1+random.random())
print "Fetched."
except Exception,e:print e
return data
# Get the images
def get_images(fp,item_number):
#### TODO ###
# get the correct schema
# will need to use selenium for this? since the page loads with jquery and javascript
################## 3-16-14
# labeling schema for images itemnumber_letter_999
#### This is not require if I get image url from the original init variables from html/json
# letters = [ 'A','B','C','D','E','F','G','VA','VB','VC','VD','VE','VF','VG' ]
# for letter in letters:
# url_string = item_number+"_"+letter+"_999?"
#+"&scl=1"
#+"$swatch$"
# imgName = item_number+"_"+letter+"_999"
try:
# build the url
image_url = "http://s7d5.scene7.com/is/image/bluestembrands/"+url_string
print image_url
# build request header
user_agent = 'jmunsch_v3 (+http://jamesmunsch.com/)'
headers = { 'User-Agent' : user_agent }
imgRequest = urllib2.Request(image_url, headers=headers)
# get the image data
imgData = urllib2.urlopen(imgRequest).read()
#write teh image data
with open(os.path.join(fp,imgName),'w+') as imgFile:
imgFile.write(imgData)
# except problem i.e. wrong filepath / 403 / incorrect request
except Exception,e:
print "Oops: "+str(e)
# Get Item info ( description, specs, picUrl )
def extract_product_info(data):
''' Extract product info from the data from download_page_info '''
#from bs4 import BeautifulSoup as BS
#id="productTabs_tab1"
#id="productTabs_tab3"
# Grab Description
try:
json_data = data.split("page.init(")[1].split("});")[0]
json_data = json_data + "}"
print "Trying to load json"
json_data = json.loads(json_data.replace('\\uXXXX',''))
description = json_data['product']['description']
d_soup = BS(description)
# Remove tags with information that can lead back to FH
for tag in d_soup.find_all('h3'):
tag.replaceWith('')
for tag in d_soup.find_all('script'):
tag.replaceWith('')
for tag in d_soup.find_all('a'):
tag.replaceWith('')
for tag in d_soup.find_all('li'):
if "Available" in tag.text:
tag.replaceWith('')
description = d_soup
except Exception,e:
print e
print 'unable to get description'
#print description
print json_data
description = None
# Grab Specifications
try:
json_data = data.split("page.init(")[1].split("});")[0]
json_data = json_data + "}"
print "Trying to load json"
json_data = json.loads(json_data)
specifications = json_data['product']['specifications']
specs ="<br>".join(x for x in specifications)
except Exception,e:
print e
print 'unable to get specifications'
print json_data
#print specs
specs = None
# Grab Images
image_list = []
try:
json_data = data.split("page.init(")[1].split("});")[0]
json_data = json_data + "}"
print "Trying to load json"
json_data = json.loads(json_data)
images = json_data['product']['media']['image']
if len(images) is not 0:
for url in images:
tmp_str = url['hiRes'].encode('utf8').split('?')[0].split('/')[-1]
#print tmp_str
image_list.append(tmp_str)
if len(images) is 0:
print " images is empty "
split_data = data.split('\"')
for line in split_data:
if ( offer_id_orig[1:6] and "is/bluestembrands" ) in line:
image_list.append(line.split('/')[-1].split('?')[0])
if len(image_list) is 0:
raise NameError('imgList0')
except NameError,n:
print "\n###"+str(n)
if 'imgList0' in n:
print '#~# Didnt find any images...? Good luck.'
image_list = [offer_id_orig]
pass
except Exception,e:
print e
#print json_data
print " Unable to get image_list "
image_list = None
image_list = list(set(image_list))
if len(image_list) is not 0:
print "Got Images."
results = { 'description' : description ,
'specs' : specs ,
'image_list' : image_list
}
return results
# Check if item page has been downloaded
# Get page data
# Make item folder
try:
folder = 'item_number_pages'
os.mkdir(folder)
except Exception,e:print e
fp = os.path.join(folder,item_uts_number)
# Check if the file exists if not download it
if os.path.isfile(fp) is True:
data = open(fp,'r').read()
elif os.path.isfile(fp) is False:
data = download_page_info(item_uts_number)
else:
print "what?"
exit()
# extract product info returns
# 'description','specs','image_list'
#
########################################
results = extract_product_info(data)
print results
return results
And the wxPython code that I am hacking together will display the Item Picture and allow me to make changes to the item information. I also plan to create a dialogue that shows all the images for a particular SKU, some features for inventorying/receiving these items, and eventually write this out to a csv the code below is unfinished:
import wx, urllib2, os
from PIL import Image
class PhotoCtrl(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title)
#self.frame = wx.Frame(None,'Sky Group Scanner')
#self.frame = wx.Frame(None, title='Photo Control')
self.panel = wx.Panel(self)
self.PhotoMaxSize = 240
self.createWidgets()
self.Show()
def createWidgets(self):
filepath = fp
if fp is not None:
img = wx.Image(fp, wx.BITMAP_TYPE_ANY)
if fp is None:
img = wx.EmptyImage(240,240)
self.imageCtrl = wx.StaticBitmap(self.panel, wx.ID_ANY,
wx.BitmapFromImage(img))
instructions = 'Is this image correct?'
instructLbl = wx.StaticText(self.panel, label=instructions)
# menubar
#####################
file = wx.Menu()
edit = wx.Menu()
help = wx.Menu()
file.Append(101,'&Open', 'Open a file')
file.Append(102,'&Save', 'Save a file')
file.AppendSeparator()
quit = wx.MenuItem(file, 105, '&Quit\tCtrl+Q', 'Quit the Application')
file.AppendItem(quit)
menubar = wx.MenuBar()
menubar.Append(file, '&File')
menubar.Append(edit,'&Edit')
menubar.Append(help,'&Help')
self.SetMenuBar(menubar)
self.CreateStatusBar()
#photo txt and browse Button
##################
self.photoTxt = wx.TextCtrl(self.panel, size=(200,-1))
browseBtn = wx.Button(self.panel, label='Browse')
browseBtn.Bind(wx.EVT_BUTTON, self.onBrowse)
SelectImageBtn = wx.Button(self.panel, label="Select Image Choices")
SelectImageBtn.Bind(wx.EVT_BUTTON, self.onBrowse)
# Description Text
##########################
descriptionTextField = wx.TextCtrl(self.panel,size=(300,500), style=wx.TE_MULTILINE | wx.TE_RICH2)
descriptionTextField.AppendText(str(itemInfo['specs'].decode('utf8')+itemInfo['description'].decode('utf8')))
# Add Widgets
#########################
self.mainSizer = wx.BoxSizer(wx.HORIZONTAL)
self.lSizer = wx.BoxSizer(wx.VERTICAL)
self.rSizer = wx.BoxSizer(wx.HORIZONTAL)
self.mainSizer.Add(wx.StaticLine(self.panel, wx.ID_ANY),
0, wx.ALL|wx.EXPAND, 5)
self.lSizer.Add(instructLbl, 0, wx.ALL, 5)
self.lSizer.Add(self.imageCtrl, 0, wx.ALL, 5)
self.lSizer.Add(self.photoTxt, 0, wx.ALL, 5)
self.lSizer.Add(browseBtn, 0, wx.ALL, 5)
self.lSizer.Add(SelectImageBtn, 0, wx.ALL, 5)
self.rSizer.Add(descriptionTextField, 0, wx.ALL, 5)
self.mainSizer.Add(self.lSizer, flag=wx.ALIGN_LEFT)
self.mainSizer.Add(self.rSizer, flag=wx.ALIGN_RIGHT)
self.panel.SetSizer(self.mainSizer)
self.mainSizer.Fit(self)
self.panel.Layout()
def onBrowse(self, event):
"""
Browse for file
"""
wildcard = "JPEG files (*.jpg)|*.jpg"
dialog = wx.FileDialog(None, "Choose a file",
wildcard=wildcard,
style=wx.OPEN)
if dialog.ShowModal() == wx.ID_OK:
self.photoTxt.SetValue(dialog.GetPath())
dialog.Destroy()
self.onView()
def onView(self):
#filepath = self.photoTxt.GetValue()
#img = wx.Image(filepath, wx.BITMAP_TYPE_ANY)
filepath = fp
img = wx.Image(fp, wx.BITMAP_TYPE_ANY)
# scale the image, preserving the aspect ratio
W = img.GetWidth()
H = img.GetHeight()
if W > H:
NewW = self.PhotoMaxSize
NewH = self.PhotoMaxSize * H / W
else:
NewH = self.PhotoMaxSize
NewW = self.PhotoMaxSize * W / H
img = img.Scale(NewW,NewH)
self.imageCtrl.SetBitmap(wx.BitmapFromImage(img))
self.panel.Refresh()
def get_image(item_info,item_number):
global fp
global itemInfo
global itemNumber
itemInfo = item_info
itemNumber = item_number
try:
uts_number = item_info['image_list'][0]
uts_ = uts_number + '?'
folder = 'item_number_pages'
imgName = item_number+'.JPEG'
fp = os.path.join(folder,imgName)
if os.path.isfile(fp) is False:
print uts_number
# build the url
image_url = "http://s7d5.scene7.com/is/image/bluestembrands/"+uts_
print image_url
# build request header
user_agent = 'jmunsch_v3 (+http://jamesmunsch.com/)'
headers = { 'User-Agent' : user_agent }
imgRequest = urllib2.Request(image_url, headers=headers)
# get the image data
imgData = urllib2.urlopen(imgRequest).read()
#write teh image data
# uh... windows requires wb ...
with open(fp,'wb') as imgFile:
print('Writing Image:' + fp)
imgFile.write(imgData)
# except problem i.e. wrong filepath / 403 / incorrect request
except Exception,e:
print "display_image: Oops1: "+str(e)
app = wx.App(False)
frame = PhotoCtrl(None,'Sky Group Scanner')
app.MainLoop()
return
if __name__ == '__main__':
global fp
global itemInfo
global itemNumber
fp = None
itemInfo = {'description':'Empty','specs':'Empty'}
itemNumber = "Empty"
app = wx.App(False)
frame = PhotoCtrl(None,'A Simple Scanner')
app.MainLoop()
Update:
Based off of the recommendations of @alexwlchan I have begun using a dedicated IDE for python called Ninja-IDE. It has been pretty handy in comparison to using NotePad++. It is a little slow in regards to searching for multiple instances of a search term and the implemented terminal doesn't remember history, but overall it has been a good fit. The IDE I think is written in python so could be coded and possibly merged if the Ninja team is interested, but I haven't dug into that code.
I also began using github a little more extensively for peace of mind and also to have backups of backups and to keep a basepoint for a working version in case I really mess my code somewhere. http://github.com/jmunsch/Sky_Group_Inventory_Scanner-wxpython
And also instead of littering my code with a bunch of print statements for debugging, I have been using import pdb; pdb.set_trace()
right before the code that I want to step through and debug.
Also, Modularizing the code has been really helpful.