Package: pykobo
pykoboPykobo is a Python module that fetches data fromKoboforms via theKobo API.The data is returned as apandasDataFrame making it easy to integrate in your workflow for further cleaning, filtering, aggregatation, etc...FunctionalitiesPykobo can:Get the list of forms a user has access toFetch the data of a form (with names or labels for the columns and choices) as a pandas DataFrameDownload a form in XLS or XML formatInstall$pipinstallpykoboExamplesHow to startNB: The example below useskf.kobotoolbox.orgbut pykobo also works withkobo.humanitarianresponse.infoand any other Kobo server.To get your API token, seehere.importpykoboURL_KOBO="https://kf.kobotoolbox.org/"API_VERSION=2MYTOKEN="2bc8e0201d23dac4ec1c334107698147b81513a2"# Initialize the Manager objectkm=pykobo.Manager(url=URL_KOBO,api_version=API_VERSION,token=MYTOKEN)Get the list of forms you have access tomy_forms=km.get_forms()This returns a Python list of KoboForm objetcsprint(my_forms)[KoboForm('tpz2buHAdXxcN0JVrZaSdk'),KoboForm('vyARFbyE8Gv3RUvXNfdTRj'),KoboForm('wogyYJzUu2ZFVnzqGg8K7q'),KoboForm('bQLZapErE3UqqG9Avntkhd')]# Each form contains metadataforfinmy_forms:print(f.metadata){'date_created':'2022-07-14T20:44:11.929901Z','date_modified':'2022-10-02T07:49:19.714891Z','geo':True,'has_deployment':True,'name':'Household survey','owner':'pvernier','version_id':'aqUMoSqANiEgH3j4Nn3Cr7'}{'date_created':'2022-07-14T12:41:14.665314Z','date_modified':'2022-09-28T11:55:15.408542Z','geo':True,'has_deployment':True,'name':'Health facilities monitoring','owner':'pvernier','version_id':'abLugnJGURSyyZ8RZxC0wQ'}{'date_created':'2022-07-14T13:40:32.033446Z','date_modified':'2022-09-28T09:19:20.691620Z','geo':False,'has_deployment':True,'name':'Post emergency evaluation','owner':'pvernier','version_id':'aQQUmPns7xLUL4Ro0amqwS'}{'date_created':'2022-09-14T16:54:06.990672Z','date_modified':'2022-09-20T13:27:52.410261Z','geo':True,'has_deployment':True,'name':'Identification of burnt areas','owner':'pvernier','version_id':'xes8JkQRpbDcbct9sqmCYZ'}Fetch a single form with its uid.uid='tpz2buHAdXxcN0JVrZaSdk'my_form=km.get_form(uid)print(my_form.metadata){'date_created':'2022-07-14T20:44:11.929901Z','date_modified':'2022-10-02T07:49:19.714891Z','geo':True,'has_deployment':True,'name':'Household survey','owner':'pvernier','version_id':'aqUMoSqANiEgH3j4Nn3Cr7'}Fetch the data of a formmy_form.fetch_data()# The data is accessible via the `data` attribute as a pandas DataFrameprint(my_form.data)startendtodayusernamedeviceidphonenumber..._submission_time_tags_notes_validation_status_submitted_by_index02022-09-01T15:47:55.797+02:002022-09-01T15:51:48.302+02:002022-09-01surveyer_1collect:4vUec4gLVJx3GP1DNaN...2022-09-01T13:52:04[][]{}surveyer_1112022-09-01T15:58:08.251+02:002022-09-01T16:08:14.548+02:002022-09-01surveyer_1collect:Xk9Z5f1VTW5nig68NaN...2022-09-01T14:08:46[][]{}surveyer_1222022-09-01T14:05:08.484+02:002022-09-01T16:17:59.305+02:002022-09-01surveyer_1collect:0Y8Cozz5fzI8jczsNaN...2022-09-01T14:18:36[][]{}surveyer_1332022-09-01T16:20:39.699+02:002022-09-01T16:32:03.393+02:002022-09-01surveyer_1collect:MPi52tvGiPY6AuK3NaN...2022-09-01T14:32:27[][]{}surveyer_14......[595rowsx38columns]print(type(my_form.data))<class'pandas.core.frame.DataFrame'># The method `fetch_data` returns the data using the Kobo columns and choices namesprint(my_form.data.columns)Index(['start','end','today','username','deviceid','phonenumber','date','health_area','village_name','team_number','cluster_number','household_number','gps','_gps_latitude','_gps_longitude','_gps_altitude','_gps_precision','hhh_present','age_hhh','consent','number_children','__version__','_id','_uuid','_status','_submission_time','_tags','_notes','_validation_status','_submitted_by','_index'],dtype='object')Display the data using Kobo labels for columns and/or choicesmy_form.display(columns_as='label',choices_as='label')print(my_form.data.columns)Index(['start','end','today','username','deviceid','phonenumber','Date of the survey','Health zone','Name of the village','team number','Cluster number','Household number','GPS Coordinates','_GPS Coordinates_latitude','_GPS Coordinates_longitude','_GPS Coordinates_altitude','_GPS Coordinates_precision','Head of the household present?','Age of the head of the household ','Consent obtained','Number of children in the household','__version__','_id','_uuid','_status','_submission_time','_tags','_notes','_validation_status','_submitted_by','_index'],dtype='object')# You can go back and forth between names and labels as much as you wantmy_form.display(columns_as='label',choices_as='name')my_form.display(columns_as='name',choices_as='label')my_form.display(columns_as='name',choices_as='name')my_form.display(columns_as='label',choices_as='label')NoteFor questions of typeselect_multiplethe different answers are separated by a '|'.If a form containsncolumns with the same label, a suffix(1)to(n)will be added to each of the columns.RepeatsRepeatsare supported (only one level, not repeats inside repeats).
In this case data of the repeat groups are separated from the 'main' data and accessible via the 'repeats' attribute
which returns a Python dictionaryprint(my_form.has_repeats)True# This means that the form has at least 1 repeat groupprint(my_form.repeats.keys())dict_keys(['children_questions'])# The form has 1 repeat group called 'children_questions'print(my_form.repeats['children_questions'])index_repeatSexofthechildAgeofthechild...Goingtoschool?_parent_index01MaleNo212FemaleNo221FemaleNo431FemaleYes542FemaleNo5......[1040rowsx27columns]The column_indexin the main DataFrame (my_form.data) and the column_parent_indexin the DatFrame of the repeat
group can be used to join the 2 DataFrames.df_join=pd.merge(my_form.data,my_form.repeats['groupe_questions_enfants'],how="left",left_on='_index',right_on='_parent_index')Save the data to fileBecause the data is a pandas DataFrame, we can take advantage of themanypandas methods to export it to a file.# CSVdf_join.to_csv('household_survey.csv',index=False)# Exceldf_join.to_excel('household_survey.xlsx',index=False)Download a form in XLS or XML formatmy_form.download_form('xls')This downloads the XLSFormtpz2buHAdXxcN0JVrZaSdk.xlsin the current working directoryAlsoPykobo has a bunch of utility methods that make easy to clean you data (not documented yet).NotePykobo only reads and fetches data from Kobo forms. It doesn't update or delete the forms and their data on the Kobo server.DependenciesrequestspandasnumpyTO DOAdd possibility to display group name as a prefixAdd method to download media filesClean and document utility functionsBe more consistent and robust in case of errorsCalculate stats on forms time duration