Error in GET request for Central API

Hi All!!!

Apologies, this is my first time posting here. If I have not gone about it the right way please tell me! I have been following the documentation for setting up ODK central. I have tried to follow all steps exactly using these guidelines. I set up a server on digital ocean and I have my domain name.

I have been following the developer documentation on how to use the API. Below is the python script that I have been using:

import os
import requests
import json
import urllib
username=os.environ["odk_username"]
password=os.environ["odk_password"]

base_url="https://MY_WEBSITE"
values ={"email":username,"password":password}
headers = {
    'Content-Type': 'application/json'
}
request=requests.get(url=base_url,
                    data=values,
                    headers=headers)
print(request.text)

I get this error (along with some other HTML):
"We're sorry but ODK Central doesn't work properly without JavaScript enabled. Please enable it to continue."

I really apologize but I have been trying for days and I cannot understand the problem!

1 Like

Welcome, @lgorman, and thanks for taking the time to introduce yourself!

When you hit your website directly, you are accessing the Central frontend and indeed, it needs JavaScript. What you want to do is to access the backend by adding /v1/ between your base url and the endpoint you want to access.

1 Like

@LN Thank you so so much! Do I also need to add /Session/. I am seeing this in the documentation now!

Apologies for the daft questions!

Yes, every API call you make needs to specify what you will interact with. That’s what I was referring to as an “endpoint”. When you look at the docs, you should be able to use a drop down on the examples to get Python code.

2 Likes

Thanks again @LN , and can I say thank you to everyone who worked on the documentation, it is really fantastic.

This has solved the initial error but I am getting a newer one:

{"message":"Could not parse the given data (56 chars) as json.","code":400.1,"details": 
{"format":"json","rawLength":56}}

I will post the solution if I manage to find one!

Hi @lgorman! My knowledge of Python is limited, but I'm guessing that values is being converted to a string representation rather than to JSON. From a brief look into this, it seems that one option is to specify a json parameter (json=values) rather than a data parameter. Apparently an added benefit of this approach is that you don't need to specify the Content-Type header.

Hope this helps!

2 Likes

Hi @Matthew_White! Thanks for your responses :slight_smile: Unfortunately now I get the following error message:

{"message":"Could not find the resource you were looking for.","code":404.1}

Which I do no understand as my login credentials are correct and I have modified the URL as describes above (i.e https://lgormanodk.co.uk/v1/sessions). Is there something I am missing? I can log into the website no problem and create app-users etc... I just don't seem to be able to connect with the API.

Sorry to bother you with the questions!

One thing that's interesting about Central is that the management website simply uses the API: the frontend has no privileged access to the data. That means that if you're able to log into the website, that's a sign that the API is working correctly. I think the next step is making sure the API request is formulated correctly.

I'm noticing in your first post that you specify requests.get(). However, to create a session, you need to send a POST request. Does it help if you specify POST instead of GET?

3 Likes

@Matthew_White @LN
Thank you so much for this! I am new to all this stuff and you both massively helped me!!! Hopefully one day I'll be able to help with something!

One thing to note is that in the API documentation, the python code uses urllib2. I am told the urllib2 is a "poisoned package" and that it is easier to use the requests library (see here).

For anyone who encounters the same issue here is the code that worked for me:

import os
import requests


# need to ensure that these variables are set in your environment
username=os.environ["odk_username"]
password=os.environ["odk_password"]

base_url="https://MYDOMAIN.co.uk/v1/sessions"
values ={"email":username,"password":password}

headers = {'Content-Type': 'application/json'}
request=requests.post(url=base_url,json=values,headers=headers)
print(request.text)

Once again thanks for the help everyone!

2 Likes

Just to give you something to start from, here's a quick script that lists projects and app_users.

#!/usr/bin/env python3

import requests
import json

central_url = "https://central.example.org"
central_email = "myemail@email.com"
central_password = "mypassword"


def get_email_token():

    email_token_response = requests.post(
        central_url + "/v1/sessions",
        data=json.dumps({"email": central_email, "password": central_password}),
        headers={"Content-Type": "application/json"},
    )

    if email_token_response.status_code == 200:
        return email_token_response.json()["token"]


def list_app_users(email_token, central_project_id):

    app_users_response = requests.get(
        central_url + "/v1/projects/" + str(central_project_id) + "/app-users",
        headers={"Authorization": "Bearer " + email_token},
    )

    app_users = {}
    for app_user in app_users_response.json():
        app_users[app_user["id"]] = app_user["displayName"]
    return app_users


def list_projects(email_token):

    projects_response = requests.get(
        central_url + "/v1/projects/",
        headers={"Authorization": "Bearer " + email_token},
    )

    projects = {}
    for project in projects_response.json():
        projects[project["id"]] = project["name"]

    return projects


email_token = get_email_token()
if email_token:
    projects = list_projects(email_token)
    for project_id, project_name in projects.items():
        print("- Project", project_id, "- " + project_name)
        app_users = list_app_users(email_token, project_id)
        for app_user_id, app_user_name in app_users.items():
            print("  - App user", app_user_id, "- " + app_user_name)
else:
    print("Error getting email token")
6 Likes

Hi @yanokwa! Thank you so so much, this is fantastic! Sorry for the daft questions in the first place!

1 Like

Hi, thank you your code. I try your code which above.It returned emails,project names.I just wondering ,can we get data for "submission" part of odk platform ?

The code just helps you get started. Use the structure and ideas shown in the code to connect with the submissions API described at https://odkcentral.docs.apiary.io/#reference/forms-and-submissions/submissions.

1 Like

Building on the Python structure provided in this thread, here is a quick example to retrieve submission data.

5 Likes

Thank you for reply.actually I tryed this example over and over again but result code error: "JSONDecodeError: Expecting value: line 1 column 1 (char 0)" :(( .Do you have an idea or anthing else,please ?I really don't understand what it happened. Thank you again

Without sharing the code you are using / your context it is not possible for me to tell. The code I shared is slightly adapted from Yaw's and needs to be further adapted as I have stored credentials in an external json file (even better in env. variables). Given the error message you get, I would understand that you are trying to load an empty JSON somewhere in your code

json.loads("")

2 Likes

Hi all!

Just in case anyone is interested. I have also created some R-functions to work with the ODK central API. Hopefully these are useful to someone! Find them here.

2 Likes

That's some nice code, I like the clean and readable variable names! Would you be interested in contributing those functions not ruODK? (No drama if you prefer not to)

3 Likes

Hi Florian,

Thanks! I have been writing code alone for a while so it's good to get feedback from someone else! That's made my day :slight_smile:

I would absolutely love to contribute it! Let me know if you have any preferred way you'd like me to contribute them!

3 Likes

Hey that made my day in return!
I've created issues with checklists for all missing functions. Feel free to pick any one function (maybe a reading function because they're simpler to test) and create a PR. Happy to review your PR, you can submit a very early work in progress so I can review and help.

There's a few pointers in the contrib guide, and of course existing functions and tests are a good starting point re structure (documentation, authentication etc).

Looking forward to collaborating!

1 Like