如何改进此脚本以使其更具Python风格?
我对Python编程相当陌生,迄今为止一直是以前的开发人员编写的反向工程代码,或者是我自己拼凑的一些函数 剧本本身是有效的;长话短说,其目的是解析CSV,并(a)创建和/或更新CSV中的联系人,以及(b)将联系人正确分配给其关联公司。所有这些都在使用。为了实现这一点,我还引入了和 我有以下问题:如何改进此脚本以使其更具Python风格?,python,python-2.7,csv,hubspot,Python,Python 2.7,Csv,Hubspot,我对Python编程相当陌生,迄今为止一直是以前的开发人员编写的反向工程代码,或者是我自己拼凑的一些函数 剧本本身是有效的;长话短说,其目的是解析CSV,并(a)创建和/或更新CSV中的联系人,以及(b)将联系人正确分配给其关联公司。所有这些都在使用。为了实现这一点,我还引入了和 我有以下问题: 如何改进此脚本以使其更具Python风格 使此脚本在远程服务器上运行的最佳方法是什么, 请记住,请求和CSVMapper可能不是 安装在那个服务器上,而我很可能不会安装 安装它们的权限-最好的“打包”方
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys, os.path, requests, json, csv, csvmapper, glob, shutil
from time import sleep
major, minor, micro, release_level, serial = sys.version_info
# Client Portal ID
portal = "XXXXXX"
# Client API Key
hapikey = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
# This attempts to find any file in the directory that starts with "note" and ends with ".CSV"
# Server Version
# findCSV = glob.glob('/home/accountName/public_html/clientFolder/contact*.CSV')
# Local Testing Version
findCSV = glob.glob('contact*.CSV')
for i in findCSV:
theCSV = i
csvfileexists = os.path.isfile(theCSV)
# Prints a confirmation if file exists, prints instructions if it doesn't.
if csvfileexists:
print ("\nThe \"{csvPath}\" file was found ({csvSize} bytes); proceeding with sync ...\n".format(csvSize=os.path.getsize(theCSV), csvPath=os.path.basename(theCSV)))
else:
print ("File not found; check the file name to make sure it is in the same directory as this script. Exiting ...")
sys.exit()
# Begin the CSVmapper mapping... This creates a virtual "header" row - the CSV therefore does not need a header row.
mapper = csvmapper.DictMapper([
[
{'name':'account'}, #"Org. Code"
{'name':'id'}, #"Hubspot Ref"
{'name':'company'}, #"Company Name"
{'name':'firstname'}, #"Contact First Name"
{'name':'lastname'}, #"Contact Last Name"
{'name':'job_title'}, #"Job Title"
{'name':'address'}, #"Address"
{'name':'city'}, #"City"
{'name':'phone'}, #"Phone"
{'name':'email'}, #"Email"
{'name':'date_added'} #"Last Update"
]
])
# Parse the CSV using the mapper
parser = csvmapper.CSVParser(os.path.basename(theCSV), mapper)
# Build the parsed object
obj = parser.buildObject()
def contactCompanyUpdate():
# Open the CSV, use commas as delimiters, store it in a list called "data", then find the length of that list.
with open(os.path.basename(theCSV),"r") as f:
reader = csv.reader(f, delimiter = ",", quotechar="\"")
data = list(reader)
# For every row in the CSV ...
for row in range(0, len(data)):
# Set up the JSON payload ...
payload = {
"properties": [
{
"name": "account",
"value": obj[row].account
},
{
"name": "id",
"value": obj[row].id
},
{
"name": "company",
"value": obj[row].company
},
{
"property": "firstname",
"value": obj[row].firstname
},
{
"property": "lastname",
"value": obj[row].lastname
},
{
"property": "job_title",
"value": obj[row].job_title
},
{
"property": "address",
"value": obj[row].address
},
{
"property": "city",
"value": obj[row].city
},
{
"property": "phone",
"value": obj[row].phone
},
{
"property": "email",
"value": obj[row].email
},
{
"property": "date_added",
"value": obj[row].date_added
}
]
}
nameQuery = "{first} {last}".format(first=obj[row].firstname, last=obj[row].lastname)
# Get a list of all contacts for a certain company.
contactCheck = "https://api.hubapi.com/contacts/v1/search/query?q={query}&hapikey={hapikey}".format(hapikey=hapikey, query=nameQuery)
# Convert the payload to JSON and assign it to a variable called "data"
data = json.dumps(payload)
# Defined the headers content-type as 'application/json'
headers = {'content-type': 'application/json'}
contactExistCheck = requests.get(contactCheck, headers=headers)
for i in contactExistCheck.json()[u'contacts']:
# ... Get the canonical VIDs
canonicalVid = i[u'canonical-vid']
if canonicalVid:
print ("{theContact} exists! Their VID is \"{vid}\"".format(theContact=obj[row].firstname, vid=canonicalVid))
print ("Attempting to update their company...")
contactCompanyUpdate = "https://api.hubapi.com/companies/v2/companies/{companyID}/contacts/{vid}?hapikey={hapikey}".format(hapikey=hapikey, vid=canonicalVid, companyID=obj[row].id)
doTheUpdate = requests.put(contactCompanyUpdate, headers=headers)
if doTheUpdate.status_code == 200:
print ("Attempt Successful! {theContact}'s has an updated company.\n".format(theContact=obj[row].firstname))
break
else:
print ("Attempt Failed. Status Code: {status}. Company or Contact not found.\n".format(status=doTheUpdate.status_code))
def createOrUpdateClient():
# Open the CSV, use commas as delimiters, store it in a list called "data", then find the length of that list.
with open(os.path.basename(theCSV),"r") as f:
reader = csv.reader(f, delimiter = ",", quotechar="\"")
data = list(reader)
# For every row in the CSV ...
for row in range(0, len(data)):
# Set up the JSON payload ...
payloadTest = {
"properties": [
{
"property": "email",
"value": obj[row].email
},
{
"property": "firstname",
"value": obj[row].firstname
},
{
"property": "lastname",
"value": obj[row].lastname
},
{
"property": "website",
"value": None
},
{
"property": "company",
"value": obj[row].company
},
{
"property": "phone",
"value": obj[row].phone
},
{
"property": "address",
"value": obj[row].address
},
{
"property": "city",
"value": obj[row].city
},
{
"property": "state",
"value": None
},
{
"property": "zip",
"value": None
}
]
}
# Convert the payload to JSON and assign it to a variable called "data"
dataTest = json.dumps(payloadTest)
# Defined the headers content-type as 'application/json'
headers = {'content-type': 'application/json'}
#print ("{theContact} does not exist!".format(theContact=obj[row].firstname))
print ("Attempting to add {theContact} as a contact...".format(theContact=obj[row].firstname))
createOrUpdateURL = 'http://api.hubapi.com/contacts/v1/contact/createOrUpdate/email/{email}/?hapikey={hapikey}'.format(email=obj[row].email,hapikey=hapikey)
r = requests.post(createOrUpdateURL, data=dataTest, headers=headers)
if r.status_code == 409:
print ("This contact already exists.\n")
elif (r.status_code == 200) or (r.status_code == 202):
print ("Success! {firstName} {lastName} has been added.\n".format(firstName=obj[row].firstname,lastName=obj[row].lastname, response=r.status_code))
elif r.status_code == 204:
print ("Success! {firstName} {lastName} has been updated.\n".format(firstName=obj[row].firstname,lastName=obj[row].lastname, response=r.status_code))
elif r.status_code == 400:
print ("Bad request. You might get this response if you pass an invalid email address, if a property in your request doesn't exist, or if you pass an invalid property value.\n")
else:
print ("Contact Marko for assistance.\n")
if __name__ == "__main__":
# Run the Create or Update function
createOrUpdateClient()
# Give the previous function 5 seconds to take effect.
sleep(5.0)
# Run the Company Update function
contactCompanyUpdate()
print("Sync complete.")
print("Moving \"{something}\" to the archive folder...".format(something=theCSV))
# Cron version
#shutil.move( i, "/home/accountName/public_html/clientFolder/archive/" + os.path.basename(i))
# Local version
movePath = "archive/{thefile}".format(thefile=theCSV)
shutil.move( i, movePath )
print("Move successful! Exiting...\n")
sys.exit()
我会从上到下。第一条规则是,做其中的事,这不是最终的风格指南,但它肯定是Python程序员的参考基准,这更重要,尤其是当您刚开始的时候。第二条规则是,使其可维护。几年后,当其他新来的孩子出现时,她应该很容易明白你在做什么。有时候,这意味着要做很多事情,以减少错误。有时,这意味着要缩短时间,减少错误。:-) 两件事:根据政治公众人物8,编码正确。及 编写好的文档字符串(也称为“docstrings”)的约定在 你有一个可以做点什么的程序。但你没有记录什么
from __future__ import print_function
import sys, os.path, requests, json, csv, csvmapper, glob, shutil
from time import sleep
major, minor, micro, release_level, serial = sys.version_info
按照PEP 8:将您的导入模块
语句每行一条
根据奥斯汀:使你的段落有单独的主题。在一些版本信息的旁边有一些导入。插入一个空行。另外,对数据做点什么!或者你不需要它就在这里,是吗
# Client Portal ID
portal = "XXXXXX"
# Client API Key
hapikey = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
你用不止一种方式掩盖了这些。WTF是一个hapikey?我想你的意思是Hubspot\u API\u key
。portal
做什么
一条建议:事物越“全球化”,它就应该越“正式”。如果有for循环,可以调用其中一个变量i
。如果在函数中使用了一段数据,请将其称为obj
或portal
。但是,如果您有一段数据是全局使用的,或者是一个类变量,请将其放在领带和外套上,以便每个人都能识别它:将其设置为Hubspot\u api\u key
,而不是client\u api\u key
。如果存在多个api,甚至可能Hubspot\u client\u api\u key
。对portal
执行相同的操作
# This attempts to find any file in the directory that starts with "note" and ends with ".CSV"
# Server Version
# findCSV = glob.glob('/home/accountName/public_html/clientFolder/contact*.CSV')
没过多久,这些评论就变成了谎言。如果它们不是真的,就删除它们
# Local Testing Version
findCSV = glob.glob('contact*.CSV')
这是您应该为其创建函数的类型。只需创建一个名为“get_csv_files”的简单函数,让它返回一个文件名列表。这使您与glob分离,这意味着您可以使测试代码数据驱动(将文件名列表传递到函数中,或将单个文件传递到函数中,而不是要求它搜索它们)。此外,这些glob模式正是配置文件、全局变量或作为命令行参数传递的类型
for i in findCSV:
我敢打赌一直用大写字母输入CSV是件痛苦的事。那么findCSV
是什么意思?读这一行,找出应该调用的变量。可能是csv\u文件
?或新建联系人文件
?证明事物是一个集合的东西
theCSV = i
csvfileexists = os.path.isfile(theCSV)
现在我做什么?你有一个很好的小变量名,在一个biiig循环中。这是一个错误,因为如果不能在一个页面上看到一个变量的整个作用域,它可能需要一个更长的名称。但是你为它创建了一个别名。i
和csv
都指同一事物。而且。。。我没有看到您再次使用I
。因此,您的循环变量可能应该是theCSV
。或者它应该是的_csv,以便更容易输入。或者只是csvname
# Prints a confirmation if file exists, prints instructions if it doesn't.
这似乎有点不必要。如果您使用glob
获取文件名,那么它们基本上是存在的。(如果没有,那是因为它们是在您调用glob
和尝试打开它们之间被删除的。这是可能的,但很少见。只需继续
或引发异常,具体取决于。)
在这段代码中,您使用csvfileexists
的值。但那是你唯一使用它的地方。在这种情况下,您可能可以将对os.path.isfile()
的调用移动到if语句中,并去掉该变量
else:
print ("File not found; check the file name to make sure it is in the same directory as this script. Exiting ...")
sys.exit()
请注意,在本例中,当出现实际问题时,您没有打印文件名吗?这有多大帮助
另外,还记得你在远程服务器上的那部分吗?您应该考虑使用Python来以有用的方式记录这些消息。
# Begin the CSVmapper mapping... This creates a virtual "header" row - the CSV therefore does not need a header row.
mapper = csvmapper.DictMapper([
[
{'name':'account'}, #"Org. Code"
{'name':'id'}, #"Hubspot Ref"
{'name':'company'}, #"Company Name"
{'name':'firstname'}, #"Contact First Name"
{'name':'lastname'}, #"Contact Last Name"
{'name':'job_title'}, #"Job Title"
{'name':'address'}, #"Address"
{'name':'city'}, #"City"
{'name':'phone'}, #"Phone"
{'name':'email'}, #"Email"
{'name':'date_added'} #"Last Update"
]
])
您正在创建一个包含大量数据的对象。这将是一个举行活动的好地方。定义一个make_csvmapper()
函数来为您执行所有这些操作,并将其移出行外
另外,请注意,该标准具有您正在使用的大部分功能。我认为您实际上不需要csvmapper
# Parse the CSV using the mapper
parser = csvmapper.CSVParser(os.path.basename(theCSV), mapper)
# Build the parsed object
obj = parser.buildObject()
这是另一个功能的机会。也许您可以返回obj
,而不是制作csv映射器
def contactCompanyUpdate():
在这一点上,事情变得可疑。您已经缩进了这些函数定义,但我认为您不需要它们。这是stackoverflow问题,还是您的代码看起来像这样
# Open the CSV, use commas as delimiters, store it in a list called "data", then find the length of that list.
with open(os.path.basename(theCSV),"r") as f:
不,显然是这样的。因为您在这个函数中使用了csv
,而实际上您并不需要这样做。请考虑使用
def contactCompanyUpdate():
# Open the CSV, use commas as delimiters, store it in a list called "data", then find the length of that list.
with open(os.path.basename(theCSV),"r") as f:
reader = csv.reader(f, delimiter = ",", quotechar="\"")
data = list(reader)
# For every row in the CSV ...
for row in range(0, len(data)):
# Set up the JSON payload ...
payload = {
"properties": [
{
"name": "account",
"value": obj[row].account
},
{
"name": "id",
"value": obj[row].id
},
{
"name": "company",
"value": obj[row].company
},
{
"property": "firstname",
"value": obj[row].firstname
},
{
"property": "lastname",
"value": obj[row].lastname
},
{
"property": "job_title",
"value": obj[row].job_title
},
{
"property": "address",
"value": obj[row].address
},
{
"property": "city",
"value": obj[row].city
},
{
"property": "phone",
"value": obj[row].phone
},
{
"property": "email",
"value": obj[row].email
},
{
"property": "date_added",
"value": obj[row].date_added
}
]
}
nameQuery = "{first} {last}".format(first=obj[row].firstname, last=obj[row].lastname)
# Get a list of all contacts for a certain company.
contactCheck = "https://api.hubapi.com/contacts/v1/search/query?q={query}&hapikey={hapikey}".format(hapikey=hapikey, query=nameQuery)
# Convert the payload to JSON and assign it to a variable called "data"
data = json.dumps(payload)
# Defined the headers content-type as 'application/json'
headers = {'content-type': 'application/json'}
contactExistCheck = requests.get(contactCheck, headers=headers)
for i in contactExistCheck.json()[u'contacts']:
# ... Get the canonical VIDs
canonicalVid = i[u'canonical-vid']
if canonicalVid:
print ("{theContact} exists! Their VID is \"{vid}\"".format(theContact=obj[row].firstname, vid=canonicalVid))
print ("Attempting to update their company...")
contactCompanyUpdate = "https://api.hubapi.com/companies/v2/companies/{companyID}/contacts/{vid}?hapikey={hapikey}".format(hapikey=hapikey, vid=canonicalVid, companyID=obj[row].id)
doTheUpdate = requests.put(contactCompanyUpdate, headers=headers)
if doTheUpdate.status_code == 200:
print ("Attempt Successful! {theContact}'s has an updated company.\n".format(theContact=obj[row].firstname))
break
else:
print ("Attempt Failed. Status Code: {status}. Company or Contact not found.\n".format(status=doTheUpdate.status_code))
def createOrUpdateClient():
else:
print ("Contact Marko for assistance.\n")
if __name__ == "__main__":
# Run the Create or Update function
createOrUpdateClient()
# Give the previous function 5 seconds to take effect.
sleep(5.0)
# Run the Company Update function
contactCompanyUpdate()
print("Sync complete.")
print("Moving \"{something}\" to the archive folder...".format(something=theCSV))
# Cron version
#shutil.move( i, "/home/accountName/public_html/clientFolder/archive/" + os.path.basename(i))
# Local version
movePath = "archive/{thefile}".format(thefile=theCSV)
shutil.move( i, movePath )
print("Move successful! Exiting...\n")
sys.exit()