Citric#

Tests Documentation Status Updates codecov FOSSA Status PyPI version Python versions PyPI - Downloads

A client to the LimeSurvey Remote Control API 2, written in modern Python.

Installation#

$ pip install citric

Usage#

For the full JSON-RPC reference, see the RemoteControl 2 API docs.

Get surveys and questions#

from citric import Client

LS_URL = "http://localhost:8001/index.php/admin/remotecontrol"

with Client(LS_URL, "iamadmin", "secret") as client:
    # Get all surveys from user "iamadmin"
    surveys = client.list_surveys("iamadmin")

    for s in surveys:
        print(s["surveyls_title"])

        # Get all questions, regardless of group
        questions = client.list_questions(s["sid"])
        for q in questions:
            print(q["title"], q["question"])

Export responses to a pandas dataframe#

import io
import pandas as pd

survey_id = 123456

df = pd.read_csv(
    io.BytesIO(client.export_responses(survey_id, file_format="csv")),
    delimiter=";",
    parse_dates=["datestamp", "startdate", "submitdate"],
    index_col="id",
)

Custom requests session#

It’s possible to use a custom session object to make requests. For example, to cache the requests and reduce the load on your server in read-intensive applications, you can use requests-cache:

import requests_cache

cached_session = requests_cache.CachedSession(
    expire_after=3600,
    allowable_methods=["POST"],
)

with Client(
    LS_URL,
    "iamadmin",
    "secret",
    requests_session=cached_session,
) as client:

    # Get all surveys from user "iamadmin"
    surveys = client.list_surveys("iamadmin")

    # This should hit the cache. Running the method in a new client context will
    # not hit the cache because the RPC session key would be different.
    surveys = client.list_surveys("iamadmin")

Use a different authentication plugin#

By default, this client uses the internal database for authentication but arbitrary plugins are supported by the auth_plugin argument.

with Client(
    LS_URL,
    "iamadmin",
    "secret",
    auth_plugin="AuthLDAP",
) as client:
    ...

Common plugins are Authdb (default), AuthLDAP and Authwebserver.

Get uploaded files and move them to S3#

import base64
import io

import boto3
from citric import Client

s3 = boto3.client("s3")

with Client(
    "https://mylimeserver.com/index.php/admin/remotecontrol",
    "iamadmin",
    "secret",
) as client:
    survey_id = 12345
    files = client.get_uploaded_files(survey_id)
    for file in files:
        content = base64.b64decode(files[file]["content"])  # Decode content
        question_id = files[file]["meta"]["question"]["qid"]
        s3.upload_fileobj(
            io.BytesIO(content),
            "my-s3-bucket",
            f"uploads/{survey_id}/{question_id}/{file}",
        )

Fall back to citric.Session for low-level interaction#

This library doesn’t (yet) implement all RPC methods, so if you’re in dire need to use a method not currently supported, you can use the session attribute to invoke the underlying RPC interface without having to pass a session key explicitly:

# Call the copy_survey method, not available in Client
with Client(LS_URL, "iamadmin", "secret") as client:
    new_survey_id = client.session.copy_survey(35239, "copied_survey")

Notebook samples#

Development#

Use pyenv to setup default Python versions for this repo:

pyenv local 3.10.0 3.9.7 3.8.11 3.7.11

Install project dependencies

poetry install

Docs#

To generate the documentation site, use the following commands:

poetry install -E docs
poetry run sphinx-build docs build

Docker#

You can setup a local instance of LimeSurvey with Docker Compose:

docker compose -f docker-compose.yml -f docker-compose.test.yml up -d

Now you can access LimeSurvey at port 8001.

Import an existing survey file and start testing with it:

from citric import Client

LS_URL = "http://localhost:8001/index.php/admin/remotecontrol"

with Client(LS_URL, "iamadmin", "secret") as client:
    # Import survey from a file
    with open("examples/limesurvey_survey_432535.lss", "rb") as f:
        survey_id = client.import_survey(f, "lss")
    print("New survey:", survey_id)

Testing#

This project uses nox for running tests and linting on different Python versions:

pip install --user --upgrade nox
nox -r

Run only a linting session

nox -rs lint

pre-commit#

pip install --user --upgrade pre-commit
pre-commit install

Releasing an upgrade#

Bump the package version

poetry version <version>
poetry publish

Credits#

License#

FOSSA Status

Contents#