xlwt can't parse IF statement in formula - python

i'm trying to write a formula to a series of cells by building the string. for instance i want to display the word "true" in a cell if the corresponding cell in column G contains the word "monkey" in it.
for rowi in range(totalRows):
thisRow = str(rowi)
functionString = r'IF(ISNUMBER(SEARCH("monkey",G'+thisRow+')),("true")
this will produce the string "IF(ISNUMBER(SEARCH("monkey",G1)),("true")" -- if i then pass that to xlwt.Formula...
ws.write(rowi+1, 0, xlwt.Formula(functionString))
i get this error...
File "C:\Python27\Lib\site-packages\xlwt\ExcelFormula.py", line 22, in __init__
raise ExcelFormulaParser.FormulaParseException, "can't parse formula " + s
xlwt.ExcelFormulaParser.FormulaParseException: can't parse formula IF(ISNUMBER(SEARCH("bitmap",G2)),("true")
is it possible to use this type of function with xlwt?

Ignoring the impossibility that you typed in monkey and G1 whereas the error message mentions bitmap and G2 ...
I suggest that you fire up Excel, select a cell, type = and paste your formula ... what happens? This check is always advisable before going public, and is available even if your internet connection is broken.
Notes:
(1) the parentheses around "true" are redundant
(2) simpler: ISNUMBER(SEARCH("bitmap",G2))
(3) Are you really expecting that a user will open the resulting XLS file and type text into column G?

Related

Dynamo Revit set formula for a parameter in a family

I am trying to add a formula to a parameter within a Revit Family.
Currently I have multiple families in a project. I run Dynamo from within that project then I extract the families that I want to modify using Dynamo standard nodes.
Then I use a python script node that goes through every selected family and find the parameter I am interested in, and assign a formula for it.
That seemed fine until I noticed that it is not assigning the formula, but it is entering it as a string — as in it is in quotes. And sure enough, the code i am using will only work with Text type parameters.
Can someone shed the light on how to assign a formula to a parameter using dynamo?
see line 32 in code below
Thanks
for family in families:
TransactionManager.Instance.ForceCloseTransaction()
famdoc = doc.EditFamily(family)
FamilyMan = famdoc.FamilyManager
found.append(family.Name)
TransactionManager.Instance.EnsureInTransaction(famdoc)
check = 0
# Loop thru the list of parameters to assign formula values to them... these are given as imput
for r in range(len(param_name_lst)):
# Loop thru the list of parameters in the current family per the families outter loop above.
for param in FamilyMan.Parameters:
#for param in FamilyMan.get_Parameter(param_name_lst[r]):
# for each of the parameters get their name and store in paramName.
paramName = param.Definition.Name
# Check if we have a match in parameter name.
if param_name_lst[r] in paramName:
if param.CanAssignFormula:
canassignformula.append(param_name_lst[r])
else:
cannotassignformula.append(param_name_lst[r])
try:
# Make sure that the parameter is not locked.
if FamilyMan.IsParameterLocked(param):
FamilyMan.SetParameterLocked(param,False)
locked.append(paraName)
# Enter formula value to parameter.
FamilyMan.SetFormula(param, param_value_lst[r])
check += 1
except:
failed.append(paramName)
else:
continue
Actually, you can access the family from the main project, and you can assign a formula automatically.... That's what i currently do, i load all the families i want in one project and run the script.
After a lot of work, i was able to figure out what i was doing wrong, and in it is not in my code... my code was fine.
The main problem is that i need to have all of my formula's dependencies lined up.... just like in manual mode.
so if my formula is:
size_lookup(MY_ID_tbl, "MY_VAR", "MY_DefaultValue", ND1,ND2)
then i need to have the following:
MY_ID_tbl should exist and be assigned a valid value, in this case it should have a csv filename. Moreover, that file should be also loaded. This is important for the next steps.
MY_VAR should be defined in that csv file, so Does ND1, ND2
The default value (My_Default_Value) should match what that csv file says about that variable...in this case, it is a text.
Needless to say, i did not have all of the above lined up as it should be, once i fixed that, my setFormula code did its job. And i had to change my process altogether, cause i have to first create the MY_ID_tbl and load the csv file which i also do using dynamo, then i go and enter the formulas using dynamo.
Revit parameters can only be assigned to a formula inside the family editor only, that is the first point, so you should run your dynamo script inside the family editor for each family which will be a waste of time and you just edit the parameter's formula manually inside each family.
and the second point, I don't even think that it is possible to set a certain parameter's formula automatically, it must be done manually ( I haven't seen anything for it in the Revit API docs).

Get code from module in Excel

I have a number of workbooks that have Macros which point to a particular SQL server using a connection string embedded in the code. We've migrated to a new SQL server so I need to go through these and alter the connection string to look at the new server in each of the Macros that explicitly mentions it.
Currently I'm able to list all of the modules in the workbook, however I'm unable to get the code from each module, just the name and type number.
for vbc in wb.VBProject.VBComponents:
print(vbc.Name + ": " + str(vbc.Type) + "\n" + str(vbc.CodeModule))
What property stores the code so that I can find and replace the server name? I've had a look through the VBA and pywin32 docs but can't find anything.
Got it- there's a Lines method in the CodeModule object that allows you to take a selection based on a starting and ending line. Using this in conjunction with the CountOfLines property allows you to get the whole thing.
for vbc in wb.VBProject.VBComponents:
print(vbc.Name + ":\n" + vbc.CodeModule.Lines(1, vbc.CodeModule.CountOfLines))
It's worth noting as well that the first line is line 1, not line 0 as that caught me out. The following will error vbc.CodeModule.Lines(0, vbc.CodeModule.CountOfLines - 1) because the index 0 is out of range.
Method Lines of property CodeModule has the signature
Function Lines(ByVal first as Integer, ByVal count as Integer) as String
first is in range 1...CodeModule.CountOfLines,
index of the first row of the code section, you want to
retrieve
count is in range 1...CodeModule.CountOfLines-first+1,
number of lines of the section
The return value is a concatenation of the code lines of the section with separator vbNewLine.

Extracting specific variables of a line with linecache

I'm currently using the python linecache module to grab specific lines from a given text document and create a new file with said line. For example, part of the code looks like:
cs = linecache.getline('variables.txt', 7)
cs_1 = open("lo_cs", "w")
cs_1.write(str(cs))
cs_1.close()
The problem is that within variables.txt, line 7 is given by:
variable7 = 3423
for instance. I want the new file, lo_cs, however, to contain only the actual value '3423' and not the entire line of text. Further, I want to insert the whole 'getline' command into an if loop so that if variable7 is left blank, another action is taken. Is there a way to use linecache to check the space following 'variable7 = ' to see if there is anything entered there, and if so, to grab only that particular value or string?
I know (but don't really understand) that bash scripts seem to use '$' as sort of a placeholder for inserting or calling a given file. I think I need to implement something similar to that...
I thought about having instructions in the text file indicating that the value should be specified in the line below -- to avoid selecting out only segments of a line -- but that allows for one to accidentally enter in superfluous breaks, which would mess up all subsequent 'getline' commands, in terms of which line needs to be selected.
Any help in the right direction would be greatly appreciated!
You can use the following method to wrap the functionality you need:
def parseline(l):
sp = l.split('=')
return sp[0], int(sp[1]) if len(sp) > 1 else None
or if you don't need the variable name:
def parseline(l):
sp = l.split('=')
return int(sp[1]) if len(sp) > 1 and sp[1].strip() != '' else None
and then use:
csval = parseline(linecache.getline('variables.txt', 7))
You can later place conditions on csval to see if it's None, and if it is, take another action.

Does xlwt module support INDIRECT?

I used the following codes, but it doesn't work. I checked the xls. The formula is filled in correctly, but remains as a text entry. If to press ENTER to active this cell, it works.
sheet1.write(1, 1, xlwt.Formula('INDIRECT(\"\'sheet1\'!B1\")'))
I find INDIRECT is declared in xlwt\ExcelMagic.py:
all_funcs_by_name = {
# Includes Analysis ToolPak aka ATP aka add-in aka xcall functions,
# distinguished by -ve opcode.
# name: (opcode, min # args, max # args, func return type, func arg types)
# + in func arg types means more of the same.
...
'INDIRECT' : (148, 1, 2, 'R', 'VV'),
...
Can anyone suggest how to use INDIRECT formula?
xlwt has issues with compiling formulas that contain some functions that should return references. This doesn't matter in OpenOffice Calc and Gnumeric, which appear to operate on a "do what I mean" philosophy. In Excel 2003, you need to select the cell then hit F2 (forces it to decompile the formula from xlwt-generated bytecode to text) followed by Enter (makes it compile the text into bytecode that it's happy with). Working on this is very low on my list of priorities.

Python program to search for specific strings in hash values (coding help)

Trying to write a code that searches hash values for specific string's (input by user) and returns the hash if searchquery is present in that line.
Doing this to kind of just learn python a bit more, but it could be a real world application used by an HR department to search a .csv resume database for specific words in each resume.
I'd like this program to look through a .csv file that has three entries per line (id#;applicant name;resume text)
I set it up so that it creates a hash, then created a string for the resume text hash entry, and am trying to use the .find() function to return the entire hash for each instance.
What i'd like is if the word "gpa" is used as a search query and it is found in s['resumetext'] for three applicants(rows in .csv file), it prints the id, name, and resume for every row that has it.(All three applicants)
As it is right now, my program prints the first row in the .csv file(print resume['id'], resume['name'], resume['resumetext']) no matter what the searchquery is, whether it's in the resumetext or not.
lastly, are there better ways to doing this, by searching word documents, pdf's and .txt files in a folder for specific words using python (i've just started reading about the re module and am wondering if this may be the route, rather than putting everything in a .csv file.)
def find_details(id2find):
resumes_f=open("resume_data.csv")
for each_line in resumes_f:
s={}
(s['id'], s['name'], s['resumetext']) = each_line.split(";")
resumetext = str(s['resumetext'])
if resumetext.find(id2find):
return(s)
else:
print "No data matches your search query. Please try again"
searchquery = raw_input("please enter your search term")
resume = find_details(searchquery)
if resume:
print resume['id'], resume['name'], resume['resumetext']
The line
resumetext = str(s['resumetext'])
is redundant, because s['resumetext'] is already a string (since it comes as one of the results from a .split call). So, you can merge this line and the next into
if id2find in s['resumetext']: ...
Your following else is misaligned -- with it placed like that, you'll print the message over and over again. You want to place it after the for loop (and the else isn't needed, though it would work), so I'd suggest:
for each_line in resumes_f:
s = dict(zip('id name resumetext'.split(), each_line.split(";"))
if id2find in s['resumetext']:
return(s)
print "No data matches your search query. Please try again"
I've also shown an alternative way to build dict s, although yours is fine too.
What #Justin Peel said. Also to be more pythonic I would say change
if resumetext.find(id2find) != -1: to if id2find in resumetext:
A few more changes: you might want to lower case the comparison and user input so it matches GPA, gpa, Gpa, etc. You can do this by doing searchquery = raw_input("please enter your search term").lower() and resumetext = s['resumetext'].lower(). You'll note I removed the explicit cast around s['resumetext'] as it's not needed.
One change that I recommend for your code is changing
if resumetext.find(id2find):
to
if resumetext.find(id2find) != -1:
because find() returns -1 if id2find wasn't in resumetext. Otherwise, it returns the index where id2find is first found in resumetext, which could be 0. As #Personman commented, this would give you the false positive because -1 is interpreted as True in Python.
I think that problem has something to do with the fact that find_details() only returns the first entry for which the search string is found in resumetext. It might be good to make find_details() into a generator instead and then you could iterate over it and print the found records out one by one.

Categories