Hi Andres,
Apologies for the delay in replying. So I am not sure if you made further progress with this but I did some digging around.
So I went head and used XNATpy 0.5.2 (I think 0.5.1 will also work without issues but I didn't test this) and used Hakim's suggestion to get past the "*** TypeError: 'NoneType' object is not iterable" exception that was thrown when trying to query on an XNAT class. This is a hack and I'm sure that with the next iteration of XNATpy this will be fixed. To make that change:
1. Quick hack for searching on 0.5.2
Find module `search.py` on your python path for example `lib/python3.9/site-packages/xnat/search.py` (there is another search,py at /xnat/client/search.py that you dont want). In the definition for Class Query change fields=None to fields = ()
so before looks like this:
class Query(object):
def __init__(self, queried_class, xnat_session, fields=None, constraints=None):
self.queried_class = queried_class
after looks like this:
class Query(object):
def __init__(self, queried_class, xnat_session, fields=(), constraints=None):
self.queried_class = queried_class
2. What can I search on?
XNATpy's documentation on searching is really comprehensive and explains much
https://xnat.readthedocs.io/en/latest/static/tutorial.html#searching. To inspect what fields are available in a connection class e.g SubjectData - we can use the vars() method as shown below - I have highlighted the Custom Fields that I created using the Custom Form
import xnat
from pprint import pprint
connection = xnat.connect(server="http://127.0.0.1",user='admin',password='admin')
SubjectData = connection.classes.SubjectData pprint(vars(SubjectData))
This is a trunctated output showing custom fields and standard fields:
mappingproxy({'ADD_IDS': <SearchField xnat:subjectData/ADD_IDS>,
'AGE': <SearchField xnat:subjectData/AGE>,
'AGE_BRACKET': <SearchField xnat:subjectData/AGE_BRACKET>,
'DOB': <SearchField xnat:subjectData/DOB>,
'EDUC': <SearchField xnat:subjectData/EDUC>,
'ETHNICITY': <SearchField xnat:subjectData/ETHNICITY>,
'GENDER_TEXT': <SearchField xnat:subjectData/GENDER_TEXT>,
'HANDEDNESS_TEXT': <SearchField xnat:subjectData/HANDEDNESS_TEXT>,
'INSERT_DATE': <SearchField xnat:subjectData/INSERT_DATE>,
'INSERT_USER': <SearchField xnat:subjectData/INSERT_USER>,
'INVEST_CSV': <SearchField xnat:subjectData/INVEST_CSV>,
'PROJECT': <SearchField xnat:subjectData/PROJECT>,
'PROJECTS': <SearchField xnat:subjectData/PROJECTS>,
'RACE': <SearchField xnat:subjectData/RACE>,
'SES': <SearchField xnat:subjectData/SES>,
'SUB_GROUP': <SearchField xnat:subjectData/SUB_GROUP>,
'XNAT:SUBJECTDATA_CUSTOM-FORM_0E51F111-E2AA-4671-A0C8-B5C50981F332_DRUGDOSE': <SearchField xnat:subjectData/XNAT:SUBJECTDATA_CUSTOM-FORM_0E51F111-E2AA-4671-A0C8-B5C50981F332_DRUGDOSE>,
'XNAT:SUBJECTDATA_CUSTOM-FORM_0E51F111-E2AA-4671-A0C8-B5C50981F332_DRUGTYPE': <SearchField xnat:subjectData/XNAT:SUBJECTDATA_CUSTOM-FORM_0E51F111-E2AA-4671-A0C8-B5C50981F332_DRUGTYPE>,
3. Constructing a search using custom fields (from custom form) and standard fields?
because of the format of the custom fields we will access them using the getattr() method instead. So for a combined search on gender and drug dose for example we can do this:
sublist = SubjectData.query().filter((SubjectData.GENDER_TEXT == "M") & (getattr(SubjectData,"XNAT:SUBJECTDATA_CUSTOM-FORM_0E51F111-E2AA-4671-A0C8-B5C50981F332_DRUGDOSE") > 20))
sublist.all()
[<SubjectData SUB001 (XNAT_S00001)>, <SubjectData SUB004 (XNAT_S00004)>]