### Install dependencies Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Install the required packages including boto3 and citric. ```python # Copyright (c) 2026 Edgar Ramírez-Mondragón !pip install --upgrade pip boto3 citric --quiet ``` -------------------------------- ### Install dependencies Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Install or upgrade the required packages for the citric workflow. ```python # Copyright (c) 2026 Edgar Ramírez-Mondragón !pip install --upgrade citric pip pandas faker sqlalchemy --quiet ``` -------------------------------- ### Install dependencies Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Install the required packages for citric, DuckDB, and data generation. ```python !pip install --upgrade pip citric duckdb-engine faker jupysql sqlalchemy --quiet ``` -------------------------------- ### Run style checks Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/environment.md Install and execute project style checks using prek. ```shell prek install prek run --all-files ``` -------------------------------- ### Install Citric Source: https://github.com/edgarrmondragon/citric/blob/main/docs/index.md Installation commands for Citric using pip or conda. ```sh # PyPI pip install citric ``` ```sh # or conda conda install -c conda-forge citric ``` -------------------------------- ### Configure integration test environment variables Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Examples of setting environment variables in a .env file or shell session to customize integration test parameters. ```shell LS_DATABASE_TYPE=mysql LS_IMAGE_TAG=6.6.4-240923-apache ``` ```shell LS_REF=refs/heads/master ``` ```shell LS_REF=f148781ec57fd1a02e5faa26a7465d78c9ab5dfe ``` -------------------------------- ### Export survey responses to DuckDB Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Writes the exported CSV to a local file before loading it into a DuckDB table for SQL analysis. Requires the duckdb package installed. ```python from pathlib import Path import citric import duckdb client = citric.Client( "https://mylimeserver.com/index.php/admin/remotecontrol", "iamadmin", "secret", ) Path("responses.csv").write_bytes(client.export_responses(12345, file_format="csv")) duckdb.execute("CREATE TABLE responses AS SELECT * FROM 'responses.csv'") duckdb.sql(""" SELECT token, submitdate - startdate AS duration FROM responses ORDER BY 2 DESC LIMIT 10 """).show() ``` -------------------------------- ### Build and serve documentation Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/docs.md Use this command to build the documentation site and watch for file changes during development. ```shell nox -s docs-serve ``` -------------------------------- ### Initialize SQL Environment Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Load the SQL extension and establish a connection to an in-memory DuckDB instance. ```python %load_ext sql ``` ```python %sql duckdb:// ``` -------------------------------- ### Upload survey to S3 Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Initialize the S3 client, create a bucket, and upload the survey file. ```python s3 = boto3.client("s3") # use your own bucket name here s3.create_bucket(Bucket="testing") s3.upload_file("../../examples/survey.lss", "testing", "survey.lss") ``` -------------------------------- ### Initialize citric client Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Configure the client with your LimeSurvey remote control URL and credentials. ```python # Use your own server's parameters here client = citric.Client( "http://localhost:8001/index.php/admin/remotecontrol", "iamadmin", "secret", ) ``` -------------------------------- ### Clone the repository Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/environment.md Download the project source code to your local machine. ```shell git clone https://github.com/edgarrmondragon/citric.git cd citric ``` -------------------------------- ### Create a changelog entry Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/environment.md Generate a new changelog entry using the changie CLI. ```shell changie new ``` -------------------------------- ### Connect to LimeSurvey and List Surveys Source: https://github.com/edgarrmondragon/citric/blob/main/docs/index.md Initialize the Citric client with credentials and retrieve survey information. ```python from citric import Client # Connect to your LimeSurvey instance client = Client( "https://mylimesite.limequery.com/admin/remotecontrol", "myusername", "mypassword", ) # Print the LimeSurvey version print(client.get_server_version()) # Print every survey's title for survey in client.list_surveys(): print(survey["surveyls_title"]) ``` -------------------------------- ### Create a development branch Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/environment.md Initialize a new branch for your feature or bugfix. ```shell git switch -c name-of-your-bugfix-or-feature ``` -------------------------------- ### Run doctests Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Executes documentation tests using xdoctest. ```shell nox -s xdoctest ``` -------------------------------- ### List local files Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Verify the presence of survey files in the local directory. ```python !ls ../../examples ``` -------------------------------- ### Create and Import Questions Programmatically Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Compose single-choice and multiple-choice questions using Question objects and import them into a survey. ```python from citric import Client from citric.objects import AnswerOption, Question, QuestionL10n LS_URL = "http://localhost:8001/index.php/admin/remotecontrol" client = Client(LS_URL, "iamadmin", "secret") survey_id = client.list_surveys()[0]["sid"] group_id = client.list_groups(survey_id)[0]["id"] # A simple single-choice (list) question with answer options list_question = Question( title="Q01", type="L", l10ns={ "en": QuestionL10n(question="What is your favourite colour?"), "es": QuestionL10n(question="Cual es tu color favorito?"), }, answer_options=[ AnswerOption(code="R", l10ns={"en": "Red", "es": "Rojo"}), AnswerOption(code="G", l10ns={"en": "Green", "es": "Verde"}), AnswerOption(code="B", l10ns={"en": "Blue", "es": "Azul"}), ], ) client.import_question(list_question.to_lsq(), survey_id, group_id) # A multiple-choice question with subquestions mc_question = Question( title="Q02", type="M", l10ns={"en": QuestionL10n(question="Which of the following do you use?")}, subquestions=[ Question( title="SQ001", type="T", l10ns={"en": QuestionL10n(question="Python")}, ), Question( title="SQ002", type="T", l10ns={"en": QuestionL10n(question="R")}, ), Question( title="SQ003", type="T", l10ns={"en": QuestionL10n(question="Julia")}, ), ], ) client.import_question(mc_question.to_lsq(), survey_id, group_id) ``` -------------------------------- ### Configure logging Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Set up a stream handler to display debug information for the citric library. ```python formatter = logging.Formatter("{asctime} {levelname} {message}", style="{") handler = logging.StreamHandler() handler.setFormatter(formatter) logger = logging.getLogger("citric") logger.addHandler(handler) logger.setLevel(logging.DEBUG) ``` -------------------------------- ### Parse LSQ file with ElementTree Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/parse_files.ipynb Initializes the XML tree from a local LSQ file. ```python # Copyright (c) 2026 Edgar Ramírez-Mondragón import xml.etree.ElementTree as ET # ruff: ignore[suspicious-xml-etree-import] tree = ET.parse("../../examples/free_text.lsq") # ruff: ignore[suspicious-xml-element-tree-usage] ``` -------------------------------- ### Preview DataFrame Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Display the first few rows of the loaded responses. ```python responses_df.head() ``` -------------------------------- ### Run dependency checks Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Validates project dependencies. ```shell nox -s deps ``` -------------------------------- ### Import necessary libraries Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Import the required modules for data handling, file operations, and the citric client. ```python import io from pathlib import Path import pandas as pd from faker import Faker from sqlalchemy import create_engine import citric ``` -------------------------------- ### Import required modules Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Import necessary libraries for handling file streams, logging, and S3 interactions. ```python import io import logging import boto3 from IPython.display import HTML import citric ``` -------------------------------- ### List Nox sessions Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/environment.md Display all available Nox development sessions. ```shell ./noxfile.py -l ``` -------------------------------- ### Run integration tests with nox Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Requires a running Docker daemon and the Docker CLI to be available. ```shell nox -s integration ``` -------------------------------- ### Import survey Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Import a survey structure from a local .lss file. ```python with Path("../../examples/survey.lss").open("rb") as f: survey_id = client.import_survey(f) ``` -------------------------------- ### Commit and push changes Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/environment.md Stage, commit, and push your local changes to the remote repository. ```shell git add . git commit -m "Your detailed description of your changes." git push origin name-of-your-bugfix-or-feature ``` -------------------------------- ### Import survey from S3 Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Download the survey file from S3 into a memory buffer and import it into LimeSurvey. ```python # Use your own server's parameters here with citric.Client( "http://localhost:8001/index.php/admin/remotecontrol", "iamadmin", "secret", ) as client: file_object = io.BytesIO() s3.download_fileobj("testing", "survey.lss", file_object) file_object.seek(0) survey_id = client.import_survey(file_object) questions = client.list_questions(survey_id) for question in questions: display(HTML(question["question"] + question["help"] + "
")) ``` -------------------------------- ### Activate survey and add responses Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Activate the survey and populate it with generated fake data. ```python faker = Faker() client.activate_survey( survey_id, user_activation_settings={ "datestamp": True, }, ) client.activate_tokens(survey_id) data = [ { "G01Q01": faker.text(max_nb_chars=100), "G01Q02": faker.random_int(1, 5), "token": faker.sha1()[:5], "ipaddr": faker.ipv4(), } for _ in range(100) ] result = client.add_responses(survey_id, data) ``` -------------------------------- ### Export and load responses into pandas Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Export survey responses as CSV and load them into a pandas DataFrame, then initialize a SQLite engine. ```python with io.BytesIO() as file: file.write( client.export_responses( survey_id, file_format="csv", additional_options={ "convertY": True, "yValue": 1, "convertN": True, "nValue": 0, }, ), ) file.seek(0) responses_df = pd.read_csv( file, delimiter=";", parse_dates=["datestamp", "startdate", "submitdate"], index_col="id", ) engine = create_engine("sqlite:///responses.db") ``` -------------------------------- ### Run unit tests with nox Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Executes the standard test suite across available Python interpreters. ```shell nox -s test ``` -------------------------------- ### Manage Client Session with Context Manager Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Use the context manager to ensure the session is automatically closed after operations are complete. ```python from citric import Client LS_URL = "http://localhost:8001/index.php/admin/remotecontrol" with Client(LS_URL, "iamadmin", "secret") as client: # Do stuff with the client ... ``` -------------------------------- ### Retrieve Surveys and Questions Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Fetch all surveys for a user and iterate through their associated questions. ```python from citric import Client LS_URL = "http://localhost:8001/index.php/admin/remotecontrol" client = Client(LS_URL, "iamadmin", "secret") # 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"]) ``` -------------------------------- ### Import CSV into DuckDB Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Create a new table named 'responses' by selecting all data from a local CSV file. ```python %sql CREATE TABLE responses AS SELECT * FROM 'responses.csv' ``` -------------------------------- ### Specify an authentication plugin Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Override the default database authentication by providing a specific plugin name via the auth_plugin argument. ```python from citric import Client client = Client( "https://example.com/index.php/admin/remotecontrol", "iamadmin", "secret", auth_plugin="AuthLDAP", ) ``` -------------------------------- ### Run type checking Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Performs static type analysis using mypy. ```shell nox -s mypy ``` -------------------------------- ### Upload and manage survey response files Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Upload files to a specific survey question and associate them with a participant response. Requires valid survey, group, and question IDs to construct the field name. ```python import json from citric import Client PARTICIPANT_TOKEN = "T00000" survey_id = 12 group_id = 34 question_id = 56 # You can also find the field name by using client.get_fieldmap(survey_id) field_name = f"{survey_id}X{group_id}X{question_id}" # Connection parameters URL = "http://localhost:8001/index.php/admin/remotecontrol" USERNAME = "iamadmin" PASSWORD = "secret" with Client(URL, USERNAME, PASSWORD) as client: # Upload files to the question using the field name with open("image_1.png", "rb") as file: content1 = file.read() result1 = client.upload_file_object(survey_id, field_name, "image_1.png", file) with open("image_2.png", "rb") as file: content2 = file.read() result2 = client.upload_file_object(survey_id, field_name, "image_2.png", file) # Add a response response_files = [result1, result2] responses = [ { "token": PARTICIPANT_TOKEN, field_name: json.dumps(response_files), f"{field_name}_filecount": len(response_files), }, ] # Download files paths = client.download_files("./downloads", survey_id, PARTICIPANT_TOKEN) ``` -------------------------------- ### Move survey files to S3 Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Iterates through survey responses to retrieve uploaded files and upload them to an S3 bucket. ```python import json import boto3 from citric import Client s3 = boto3.client("s3") client = Client( "https://mylimeserver.com/index.php/admin/remotecontrol", "iamadmin", "secret", ) survey_id = 12345 exported = json.loads(client.export_responses(survey_id, file_format="json")) # Get all uploaded files for all responses and upload them to S3 for response in exported["responses"]: for file in client.get_uploaded_file_objects(survey_id, response_id=response["id"]): filename = ( f"uploads/sid={survey_id}/qid={file['meta']['question']['qid']}" f"/{response['id']}/{file['meta']['filename']}" ) s3.upload_fileobj(file["content"], "my-s3-bucket", filename) ``` -------------------------------- ### Trigger release workflow with GitHub CLI Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/release.md Use the GitHub CLI to manually trigger the release pull request generation workflow. ```console $ gh workflow run gen-release-pr.yml ✓ Created workflow_dispatch event for gen-release-pr.yml at main To see runs for this workflow, try: gh run list --workflow=gen-release-pr.yml ``` -------------------------------- ### Cleanup resources Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Remove the uploaded file from S3, delete the bucket, and remove the imported survey from LimeSurvey. ```python s3.delete_object(Bucket="testing", Key="survey.lss") s3.delete_bucket(Bucket="testing") with citric.Client( "http://localhost:8001/index.php/admin/remotecontrol", "iamadmin", "secret", ) as client: client.delete_survey(survey_id) ``` -------------------------------- ### Configure a custom requests session Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Use a cached session to reduce server load in read-intensive applications. Requires the requests-cache library. ```python import requests_cache from citric import Client cached_session = requests_cache.CachedSession( expire_after=60, allowable_methods=["POST"], ) client = Client( "https://example.com/index.php/admin/remotecontrol", "iamadmin", "secret", requests_session=cached_session, ) # Get all surveys from user "iamadmin". # All responses will be cached for 1 minute. surveys = client.list_surveys("iamadmin") ``` -------------------------------- ### Invoke low-level RPC methods Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Uses the session attribute to call RPC methods not explicitly implemented by the library. ```python client = Client( "https://mylimeserver.com/index.php/admin/remotecontrol", "iamadmin", "secret", ) # Get the raw response from mail_registered_participants result = client.session.call("mail_registered_participants", 35239) # Get the raw response from remind_participants result = client.session.call("remind_participants", 35239) ``` -------------------------------- ### Query Data Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Perform basic SQL queries to inspect or aggregate data stored in the DuckDB table. ```python %sql SELECT * FROM responses LIMIT 5 ``` ```python %sql SELECT G01Q02, count(1) as TOTAL FROM responses GROUP BY G01Q02 ``` ```python %%sql SELECT token, submitdate - startdate AS duration FROM responses ORDER BY 2 DESC LIMIT 10 ``` -------------------------------- ### Iterate over question field names Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/parse_files.ipynb Extracts and displays the text content of all fieldname elements within the questions section. ```python for fieldname in tree.find("questions").find("fields").findall("fieldname"): display(fieldname.text) ``` -------------------------------- ### Generate fake response data Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Create a list of fake survey responses using Faker. ```python fake = Faker() fake_responses = [] for _ in range(100): start_date = fake.date_time_this_month() submit_date = fake.date_time_between_dates(start_date) response = { "G01Q01": fake.text(max_nb_chars=100), "G01Q02": fake.random_int(1, 5), "token": fake.sha1()[:5], "ipaddr": fake.ipv4(), "startdate": start_date.isoformat(), "submitdate": submit_date.isoformat(), } fake_responses.append(response) len(fake_responses) ``` -------------------------------- ### Iterate over question row entries Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/parse_files.ipynb Iterates through the row elements to display tag names and their corresponding text values. ```python for entry in tree.find("questions").find("rows").find("row"): display(f"{entry.tag} = {entry.text}") ``` -------------------------------- ### Export responses to CSV Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Export the survey responses to a CSV file. ```python Path("responses.csv").write_bytes( client.export_responses( survey_id, file_format="csv", additional_options={ "convertY": True, "yValue": 1, "convertN": True, "nValue": 0, }, ) ) ``` -------------------------------- ### Save DataFrame to SQLite Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Persist the DataFrame into a SQLite table named 'responses'. ```python responses_df.to_sql("responses", engine, if_exists="replace") ``` -------------------------------- ### Export survey responses to a pandas DataFrame Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Uses io.BytesIO to convert the CSV export into a file-like object for pandas to read. Ensure the survey ID and credentials are correct before execution. ```python import io import pandas as pd from citric import Client survey_id = 123456 client = Client( "https://mylimeserver.com/index.php/admin/remotecontrol", "iamadmin", "secret", ) # Export responses to CSV and read into a Pandas DataFrame df = pd.read_csv( io.BytesIO(client.export_responses(survey_id, file_format="csv")), delimiter=";", parse_dates=["datestamp", "startdate", "submitdate"], index_col="id", ) ``` -------------------------------- ### Update survey question answers via REST API Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Uses the RESTClient to fetch survey structure and apply patch operations to specific answer entities. ```python from citric.rest import RESTClient client = RESTClient( "https://example.com/rest/v1", "your_username", "your_password", ) survey_id = 142423 # Get survey details survey = client.get_survey(survey_id) print(survey) # Inspect question 247 to find answer IDs for question in survey["questionGroups"][0]["questions"]: if answers := question.get("answers"): print("Answers for question", question["qid"]) for answer in answers: print(f" Answer ID: {answer['aid']}, Code: {answer['code']}") # Define patch operations operations = [ { "entity": "answer", "op": "update", # Question ID "id": 247, "props": [ { # Answer ID "aid": 31, "code": "AO01", "sortOrder": 1, "assessmentValue": 0, "scaleId": 1, "l10ns": { "de": {"answer": "ANTW1 scale 1", "language": "de"}, "en": {"answer": "ANS1 scale 1", "language": "en"}, }, }, { # Answer ID "aid": 32, "code": "AO02", "sortOrder": 1, "assessmentValue": 0, "scaleId": 1, "l10ns": { "de": {"answer": "ANTW2 scale 1", "language": "de"}, "en": {"answer": "ANS2 scale 1", "language": "en"}, }, }, { # Answer ID "aid": 33, "code": "AO03", "sortOrder": 1, "assessmentValue": 0, "scaleId": 1, "l10ns": { "de": {"answer": "ANTW3 scale 1", "language": "de"}, "en": {"answer": "ANS3 scale 1", "language": "en"}, }, }, ], } ] result = client.patch_survey(survey_id, operations) print(f"Operations applied: {result['operationsApplied']}") # Operations applied: 1 ``` -------------------------------- ### Integrate with R via reticulate Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Exports survey data using Python and accesses the resulting dataframe within an R environment. ```python # export_ls_responses.py import io import citric import pandas as pd client = citric.Client( "http://localhost:8001/index.php/admin/remotecontrol", "iamadmin", "secret", ) data = client.export_responses(123456, file_format="csv") survey_data = pd.read_csv( io.BytesIO(data), delimiter=";", parse_dates=["datestamp", "startdate", "submitdate"], index_col="id", ) ``` ```r > library(reticulate) > reticulate::use_virtualenv("venv") > reticulate::source_python("export_ls_responses.py") > py$survey_data submitdate lastpage startlanguage seed G01Q01 G01Q02 G02Q03 G02Q03[filecount] 1 [nan] [1] ['en'] [245240561] ['lalala'] [5] [nan] [nan] ``` -------------------------------- ### Customize HTTP session attributes Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Injects a custom requests.Session object into the Client to modify headers or other session-level configurations. Useful for authentication proxies or custom API headers. ```python import requests from citric import Client session = requests.Session() # Set custom headers to be sent on each request # https://requests.readthedocs.io/en/latest/api/#requests.Session.headers session.headers["My-Custom-Header"] = "My-Custom-Value" client = Client( "https://mylimeserver.com/index.php/admin/remotecontrol", "iamadmin", "secret", requests_session=session, ) ``` -------------------------------- ### Delete survey Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Remove the survey from the LimeSurvey instance. ```python client.delete_survey(survey_id) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.