I am attempting to fill out a word document form with Python 2.7's docx module. I can modify text elements just fine but I am having difficulty figuring out how to check a yes or no checkbox.
How do I go about checking one the the checkboxes in the form. I have tried a few different ways but I think it all comes do to me not know how the docx xml is structured when it comes to check boxes.
Am I able to use the Bookmark property to find a specific checkbox and check it as seen in the picture below?
I have uploaded a copy of the test form to Google Drive here.
Ok, so after much frustration I finally figured out how to check a checkbox. There is a element within a checkbox element that signifies if the box is checked. I am essenially able to create that element with the following function.
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
def checkedElement():
elm = OxmlElement('w:checked')
elm.set(qn('w:val'),"true")
return elm
I can find all checkboxes within a table cell with the following function. Since the yes is always the first checkbox in each cell I can set the index for a yes check to 0 and a no check to index 1 and then I can append the checked element within the checkbox element:
def yesNoCheck(yes_no,tableIdx,coords):
print coords, yes_no
if yes_no == 'y':
index = 0
x = doc.tables[tableIdx].cell(coords[0],coords[1])._element.xpath('.//w:checkBox')
x[index].append(checkedElement())
elif yes_no == 'n':
index = 1
x = doc.tables[tableIdx].cell(coords[0],coords[1])._element.xpath('.//w:checkBox')
x[index].append(checkedElement())
else:
print "value was neither yes or no"
pass
here is my full code that I have written so far. I have a bunch of refactoring to do but it works great as of now. There are two tables in my .docx template and dictionary table1 and table2 contain the cell row and column coordinates. This script is used to fill out a required form using data published from ESRI's Survey123.
from docx import Document
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.shared import Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH
import arcpy
import datetime
import os
table1 = {
'BusinessName':[2,3],
'LicenseNumber':[2,14],
'OwnerName':[3,3],
'PhoneNumber':[3,14],
'BusinessAddress':[4,5],
'County':[4,14],
'City':[5,1],
'St':[5,8],
'Zip':[5,15],
'LicenceExpired':[6,1], #CheckBox
'DateExpired':[6,15],
'LicenceRenewal':[7,1], #CheckBox
'NumberDisplayed':[8,1], #CheckBox
'NameAddDisplayed':[10,1], #CheckBox
'VehicleInfoMatches':[12,1], #CheckBox
'DischargeValveCapped':[14,1], #CheckBox
'DischargeValveCapChained':[15,1], #CheckBox
'HoseDisinfectCarried':[16,1], #CheckBox
'VehicleAndTankClean':[17,1], #CheckBox
'FreeOfLeaks':[18,1] #CheckBox
}
table2 = {
'LandApplyWaste':[1,1], #Yes/No CheckBox
'LocationDescriptionAccurate':[6,1], #Yes/No CheckBox
'LocationDescriptionAccDesc':[6,5], #text
'Slope':[7,1], #Yes/No CheckBox
'DistanceNearestResidence':[8,1], #Yes/No CheckBox
'DistanceNearestWell':[9,1], #Yes/No CheckBox
'DistanceNearestStreamLakeEtc':[10,1], #Yes/No CheckBox
'SeptageIncorporated':[11,1], #Yes/No CheckBox
'InjectedIncorporated':[12,3], #Yes/No CheckBox, dependent on the septage incorporated being yes
'SeptageStabilized':[13,1], #Yes/No CheckBox
'HowIsLimeMixed':[14,3], #text dependent on if lime was used
'ConfiningLayerOrGroundwater':[15,1], #Yes/No CheckBox
'ConfiningLayerOrGroundwaterDesc':[16,3], #text
'CropGrown':[17,1], #Yes/No CheckBox
'CropGrownHowVerified':[19,3], #text
'LandAppCompliance':[20,1], #Yes/No CheckBox
'AdditionalComments':[22,3],
'SignDate':[22,13]
}
def checkedElement():
elm = OxmlElement('w:checked')
elm.set(qn('w:val'),"true")
return elm
def yesNoCheck(yes_no,tableIdx,coords):
print coords, yes_no
if yes_no == 'y':
index = 0
x = doc.tables[tableIdx].cell(coords[0],coords[1])._element.xpath('.//w:checkBox')
x[index].append(checkedElement())
elif yes_no == 'n':
index = 1
x = doc.tables[tableIdx].cell(coords[0],coords[1])._element.xpath('.//w:checkBox')
x[index].append(checkedElement())
else:
print "value was neither yes or no"
pass
def disposalMethodCheck(method, locationDec):
vals = {
'WastewaterTreatmentFacility':[20,1],
'LandApplication':[22,1],
'SanitaryLandfill':[24,1],
'SeptageLagoonOrDryingBed':[26,1]
}
if method != None:
row,col = vals[method]
checkBoxElm = doc.tables[0].cell(row,col)._element.xpath('.//w:checkBox')[0]
print "{0} Checked!".format(method)
checkBoxElm.append(checkedElement())
editTxt(locationDec,0,[row,6])
def editTxt(text, tblIdx, coords, alignment = WD_ALIGN_PARAGRAPH.LEFT, bold=True):
print text, coords
field = doc.tables[tblIdx].cell(coords[0],coords[1]).paragraphs[0]
field.text = text
field.alignment = alignment
field.runs[0].font.bold = bold
def addSig(sigJpgPath):
para = doc.tables[1].row_cells(23)[0].paragraphs[0]
para.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = para.add_run()
run.add_picture(sigJpgPath,width=Inches(1.34),height=Inches(.35))
fc = r"E:\PumperTruckInspectionFeatureClass"
arcpy.MakeFeatureLayer_management (fc, "PumperTruckInspections")
attach = r"PumperTruckInspection__ATTACH" #Where signatures are stored
def rows_as_dicts(cursor):
colnames = cursor.fields
for row in cursor:
yield dict(zip(colnames, row))
def dateString(date):
if date != None:
d = date.strftime('%m/%d/%Y')
return d
else:
print "no date"
return ''
def checkBusName(name):
if name != None:
return name
else:
return 'unknown'
with arcpy.da.SearchCursor(fc, '*') as sc:
for row in rows_as_dicts(sc):
doc = Document(r"path\to\TEMPLATE.docx")
t = datetime.datetime.now().strftime('%Y-%m-%d')
newDocName = checkBusName(row['BusinessName']) + t + '.docx'
editTxt(row['BusinessName'],0,table1['BusinessName'])
editTxt(row['LicenseNumber'],0,table1['LicenseNumber'])
editTxt(row['OwnerName'],0,table1['OwnerName'])
editTxt(row['PhoneNumber'],0,table1['PhoneNumber'])
editTxt(row['BusinessAddress'],0,table1['BusinessAddress'])
editTxt(row['County'],0,table1['County'])
editTxt(row['City'],0,table1['City'])
editTxt(row['St'],0,table1['St'])
editTxt(row['Zip'],0,table1['Zip'])
editTxt(dateString(row['DateExpired']),0,table1['DateExpired'])
yesNoCheck(row['LicenceExpired'],0, table1['LicenceExpired'])
yesNoCheck(row['LicenceRenewal'],0, table1['LicenceRenewal'])
yesNoCheck(row['NumberDisplayed'],0, table1['NumberDisplayed'])
yesNoCheck(row['NameAddDisplayed'],0, table1['NameAddDisplayed'])
yesNoCheck(row['VehicleInfoMatches'],0, table1['VehicleInfoMatches'])
yesNoCheck(row['DischargeValveCapped'],0, table1['DischargeValveCapped'])
yesNoCheck(row['DischargeValveCapChained'],0, table1['DischargeValveCapChained'])
yesNoCheck(row['HoseDisinfectCarried'],0, table1['HoseDisinfectCarried'])
yesNoCheck(row['VehicleAndTankClean'],0, table1['VehicleAndTankClean'])
yesNoCheck(row['FreeOfLeaks'],0, table1['FreeOfLeaks'])
disposalMethodCheck(row['DisposalMethod'],row['DisposalLocation'])
if row['DisposalMethod'] == 'LandApplication':
yesNoCheck(row['LandApplyWaste'],1,table2['LandApplyWaste'])
yesNoCheck(row['LocationDescriptionAccurate'],1,table2['LocationDescriptionAccurate'])
editTxt(row['LocationDescriptionAccDesc'],1,table2['LocationDescriptionAccDesc'])
yesNoCheck(row['Slope'],1,table2['Slope'])
yesNoCheck(row['DistanceNearestResidence'],1,table2['DistanceNearestResidence'])
yesNoCheck(row['DistanceNearestWell'],1,table2['DistanceNearestWell'])
yesNoCheck(row['DistanceNearestStreamLakeEtc'],1,table2['DistanceNearestStreamLakeEtc'])
yesNoCheck(row['SeptageIncorporated'],1,table2['SeptageIncorporated'])
yesNoCheck(row['InjectedIncorporated'],1,table2['InjectedIncorporated']) #might need a new method since its not yes/no
yesNoCheck(row['SeptageStabilized'],1,table2['SeptageStabilized'])
editTxt(row['HowIsLimeMixed'],1,table2['HowIsLimeMixed'])
yesNoCheck(row['ConfiningLayerOrGroundwater'],1,table2['ConfiningLayerOrGroundwater'])
editTxt(row['ConfiningLayerOrGroundwaterDescript'],1,table2['ConfiningLayerOrGroundwaterDescript'])
yesNoCheck(row['CropGrown'],1,table2['CropGrown'])
editTxt(row['CropGrownHowVerified'],1,table2['CropGrownHowVerified'])
yesNoCheck(row['LandAppCompliance'],1,table2['LandAppCompliance'])
editTxt(row['AdditionalComments'],1,table2['AdditionalComments'],bold=False)
where = "REL_GLOBALID = '{0}'".format(row['GlobalID'])
from pprint import pprint
with arcpy.da.SearchCursor(attach,['DATA', 'ATT_NAME', 'ATTACHMENTID'],where_clause=where) as cursor:
for r in rows_as_dicts(cursor):
pprint(r)
name = r['ATT_NAME']
attachment = r['DATA']
if name.split('_')[0] == 'InspectorSignature':
imagePath = os.path.join(name.split('_')[0] + "_" + )
open(("sig.jpeg"), 'wb').write(attachment.tobytes())
addSig("sig.jpeg")
break
editTxt(dateString(row['SignDate']),1,table2['SignDate'],alignment = WD_ALIGN_PARAGRAPH.CENTER,bold=False)
doc.save(newDocName)
del doc