### Get Surveys and Questions Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Example of retrieving a list of surveys and their associated questions using the Citric client. ```python surveys = client.get_surveys() for survey in surveys: questions = client.get_questions(survey_id=survey["sid"]) print(f"Survey: {survey['survey_name']}, Questions: {questions}") ``` -------------------------------- ### Install Dependencies Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Installs necessary libraries including boto3 and citric. Use this at the beginning of your notebook. ```python !pip install --upgrade pip boto3 citric --quiet ``` -------------------------------- ### Install Libraries Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Installs or upgrades the required Python packages: citric, pip, pandas, faker, and sqlalchemy. The --quiet flag suppresses verbose output. ```python !pip install --upgrade citric pip pandas faker sqlalchemy --quiet ``` -------------------------------- ### Setup S3 Client and Upload File Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Initializes an S3 client, creates a bucket (if it doesn't exist), and uploads a survey file from the local path to S3. Replace 'testing' with your actual bucket name. ```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") ``` -------------------------------- ### Install Citric using pip Source: https://github.com/edgarrmondragon/citric/blob/main/README.md Install the Citric library from PyPI using pip. This is the standard method for installing Python packages. ```sh pip install citric ``` -------------------------------- ### Install Dependencies Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Installs necessary Python packages including citric, duckdb-engine, faker, jupysql, and sqlalchemy. This is a prerequisite for running the subsequent code. ```python !pip install --upgrade pip citric duckdb-engine faker jupysql sqlalchemy --quiet ``` -------------------------------- ### Install Citric using Conda Source: https://github.com/edgarrmondragon/citric/blob/main/README.md Install the Citric library from the conda-forge channel using Conda. This is an alternative installation method for users who prefer Conda environments. ```sh conda install -c conda-forge citric ``` -------------------------------- ### Get Uploaded Files and Move to S3 Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Retrieves files uploaded to a survey in LimeSurvey and demonstrates how to move them to an S3 bucket. ```python from io import BytesIO file_data = client.get_uploaded_file(survey_id=survey_id, file_id=file_id) # Example: Upload to S3 using boto3 (not included) # s3_client.upload_fileobj(BytesIO(file_data["content"]), "my-bucket", "path/to/file.txt") ``` -------------------------------- ### Get Survey Response Statistics with Citric Client Source: https://context7.com/edgarrmondragon/citric/llms.txt Use `get_summary` to retrieve all aggregate token and response statistics for a survey, or `get_summary_stat` to fetch a single specific metric. ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 123456 summary = client.get_summary(survey_id) if summary: print(f"Completed: {summary['completed_responses']}") print(f"Incomplete: {summary['incomplete_responses']}") print(f"Tokens sent: {summary['token_sent']}") # Fetch a single statistic completed = client.get_summary_stat(survey_id, "completed_responses") print(f"Total completed responses: {completed}") ``` -------------------------------- ### Get and Set Survey Properties with Citric Client Source: https://context7.com/edgarrmondragon/citric/llms.txt Use `get_survey_properties` to read survey settings and `set_survey_properties` to update them. Specify properties to read specific settings. ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 123456 # Read all properties props = client.get_survey_properties(survey_id) print(props["active"], props["anonymized"], props["language"]) # Read specific properties only props = client.get_survey_properties(survey_id, properties=["active", "expires", "startdate"]) # Update properties updated = client.set_survey_properties( survey_id, anonymized="Y", datestamp="Y", ipaddr="N", alloweditaftercompletion="N", ) print(updated) # {"anonymized": True, "datestamp": True, ...} ``` -------------------------------- ### Find Longest Response Durations in DuckDB Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Use SQL within a notebook to calculate and display the top 10 longest response durations by subtracting the start date from the submission date. ```sql SELECT token, submitdate - startdate AS duration FROM responses ORDER BY 2 DESC LIMIT 10 ``` -------------------------------- ### Initialize Client and Basic Usage Source: https://context7.com/edgarrmondragon/citric/llms.txt Demonstrates basic client initialization using a context manager for automatic session cleanup. Shows how to retrieve server version, site name, and default language. Supports custom authentication plugins and integration with `requests.Session` for features like caching. ```python from citric import Client from citric.exceptions import LimeSurveyStatusError # Basic usage — context manager ensures session key is released on exit with Client( "https://mylimesite.example.com/index.php/admin/remotecontrol", "iamadmin", "secret", ) as client: print(client.get_server_version()) # e.g. "6.3.7" print(client.get_site_name()) # e.g. "My LimeSurvey" print(client.get_default_language()) # e.g. "en" # LDAP / custom auth plugin client = Client( "https://example.com/index.php/admin/remotecontrol", "iamadmin", "secret", auth_plugin="AuthLDAP", ) # Custom requests.Session (e.g. with caching) import requests_cache 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, ) surveys = client.list_surveys("iamadmin") # responses cached for 60 s ``` -------------------------------- ### Connect to DuckDB Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Establishes a connection to a DuckDB in-memory database. This command prepares the environment for subsequent SQL queries. ```python %sql duckdb:// ``` -------------------------------- ### Create a New Survey with Client.add_survey Source: https://context7.com/edgarrmondragon/citric/llms.txt Creates a new survey with a specified title and language. Requires a Client instance. The survey can be configured with different formats like GROUP_BY_GROUP. ```python from citric import Client from citric.enums import NewSurveyType with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: # Group-by-group format (default) survey_id = client.add_survey(0, "Employee Feedback 2025", "en", NewSurveyType.GROUP_BY_GROUP) print(f"Created survey with ID: {survey_id}") # Add a question group to the new survey group_id = client.add_group(survey_id, "Section 1: General", "Please answer the following questions.") print(f"Created group with ID: {group_id}") # Activate the survey result = client.activate_survey(survey_id) print(result["status"]) ``` -------------------------------- ### Initialize Citric Client Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Creates an instance of the Citric Client to interact with the LimeSurvey API. Replace placeholder URL and credentials with your actual server details. ```python # Use your own server's parameters here client = citric.Client( "http://localhost:8001/index.php/admin/remotecontrol", "iamadmin", "secret", ) ``` -------------------------------- ### Build and Import Questions Programmatically with Dataclasses Source: https://context7.com/edgarrmondragon/citric/llms.txt Use the `Question`, `QuestionL10n`, and `AnswerOption` dataclasses to construct questions in Python and export them as LSQ XML. This method supports multilingual text, subquestions, and answer options. The `to_lsq()` method converts the dataclass instance to the required XML format for `client.import_question()`. ```python from citric import Client from citric.objects import AnswerOption, Question, QuestionL10n with Client("http://localhost:8001/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = client.list_surveys()[0]["sid"] group_id = client.list_groups(survey_id)[0]["id"] # Single-choice list question with multilingual text and 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"}), ], ) qid = client.import_question(list_question.to_lsq(), survey_id, group_id) print(f"Imported list question: {qid}") # Multiple-choice question with subquestions mc_question = Question( title="Q02", type="M", l10ns={"en": QuestionL10n(question="Which languages 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")}), ], ) qid = client.import_question(mc_question.to_lsq(), survey_id, group_id) print(f"Imported multiple-choice question: {qid}") ``` -------------------------------- ### Run All Tests with Nox Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Execute all defined test suites using the nox command. Ensure your environment is set up correctly before running. ```shell nox -s tests ``` -------------------------------- ### Import Libraries Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Imports essential libraries for S3 interaction, logging, and citric client. ```python import io import logging import boto3 from IPython.display import HTML import citric ``` -------------------------------- ### Run Dependency Checks with Nox Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Execute dependency checks using the nox command. ```shell nox -s deps ``` -------------------------------- ### Build Questions Programmatically Source: https://context7.com/edgarrmondragon/citric/llms.txt Dataclasses for constructing LimeSurvey questions in Python and exporting them as LSQ-format XML streams for use with `client.import_question()`. Supports multilingual questions, subquestions, and answer options. ```APIDOC ## `Question`, `QuestionL10n`, `AnswerOption` — Build questions programmatically Dataclasses for constructing LimeSurvey questions in Python and exporting them as LSQ-format XML streams for use with `client.import_question()`. Supports multilingual questions, subquestions, and answer options. ```python from citric import Client from citric.objects import AnswerOption, Question, QuestionL10n with Client("http://localhost:8001/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = client.list_surveys()[0]["sid"] group_id = client.list_groups(survey_id)[0]["id"] # Single-choice list question with multilingual text and 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"}), ], ) qid = client.import_question(list_question.to_lsq(), survey_id, group_id) print(f"Imported list question: {qid}") # Multiple-choice question with subquestions mc_question = Question( title="Q02", type="M", l10ns={"en": QuestionL10n(question="Which languages 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")}), ], ) qid = client.import_question(mc_question.to_lsq(), survey_id, group_id) print(f"Imported multiple-choice question: {qid}") ``` ``` -------------------------------- ### Configure Logging Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Sets up a stream handler for logging citric events. Useful for debugging. ```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) ``` -------------------------------- ### Import Survey Structures with Client Source: https://context7.com/edgarrmondragon/citric/llms.txt Use this to import surveys, question groups, and questions from exported LimeSurvey files (LSS, LSA, LSG, LSQ). Ensure the file paths are correct and the client is initialized with valid credentials and URL. ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: # Import a survey from an LSS file with open("survey_backup.lss", "rb") as f: new_survey_id = client.import_survey(f, file_type="lss", survey_name="Imported Survey") print(f"New survey ID: {new_survey_id}") # Import a group into the new survey with open("group.lsg", "rb") as f: group_id = client.import_group(f, new_survey_id, name="Demographics") print(f"New group ID: {group_id}") # Import a question into the group with open("question.lsq", "rb") as f: question_id = client.import_question( f, new_survey_id, group_id, mandatory=True, new_question_title="Q_AGE", ) print(f"New question ID: {question_id}") ``` -------------------------------- ### List Local Files Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Lists files in the local directory. Useful for verifying file existence before upload. ```python !ls ../../examples ``` -------------------------------- ### Activate a Survey with Client.activate_survey Source: https://context7.com/edgarrmondragon/citric/llms.txt Activates a survey to make it accessible to respondents. Allows configuration of activation settings like IP recording and anonymization. Also initializes the participant token table. ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 123456 result = client.activate_survey( survey_id, user_activation_settings={ "anonymized": True, "datestamp": True, "ipaddr": False, "ipanonymize": False, "refurl": False, "savetimings": True, }, ) print(result["status"]) # Initialize participant (token) table so participants can be added token_result = client.activate_tokens(survey_id, attributes=[1, 2]) print(token_result["status"]) ``` -------------------------------- ### Import Libraries Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Imports necessary modules from pathlib, pandas, faker, sqlalchemy, and citric for file path manipulation, data handling, fake data generation, database interaction, and survey management. ```python import io from pathlib import Path import pandas as pd from faker import Faker from sqlalchemy import create_engine import citric ``` -------------------------------- ### Create and Import Questions Programmatically Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Composes questions using `citric.objects.Question` and imports them into LimeSurvey using `client.import_question()`. ```python from citric.objects import Question question_data = { "question": { "survey_id": survey_id, "question_code": "my_new_question", "question_type": "T", "question": "What is your favorite color?", "answers": { "1": {"code": "R", "answer": "Red"}, "2": {"code": "G", "answer": "Green"}, "3": {"code": "B", "answer": "Blue"} } } } question = Question.from_dict(question_data) client.import_question(question) ``` -------------------------------- ### Connect to LimeSurvey and list surveys Source: https://github.com/edgarrmondragon/citric/blob/main/README.md Connect to a LimeSurvey instance using the Client class and retrieve the titles of all available surveys. Ensure you have the correct URL, username, and password for your LimeSurvey instance. ```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"]) ``` -------------------------------- ### Configure Integration Tests with .env File Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Set environment variables in a .env file to customize integration test configurations, such as database type and Docker image tag. ```shell LS_DATABASE_TYPE=mysql LS_IMAGE_TAG=6.6.4-240923-apache ``` -------------------------------- ### Manage Survey Participants with Client.add_participants and Client.list_participants Source: https://context7.com/edgarrmondragon/citric/llms.txt Adds participants to a survey's token table and retrieves them with optional filtering. Requires participant data including email and optionally other attributes. Can create tokens during addition. ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 123456 # Add participants new_participants = client.add_participants( survey_id, participant_data=[ {"firstname": "Alice", "lastname": "Smith", "email": "alice@example.com"}, {"firstname": "Bob", "lastname": "Jones", "email": "bob@example.com"}, ], create_tokens=True, ) for p in new_participants: print(p["token"], p["tid"]) # Expected: T00001 1 / T00002 2 # List participants with extra attributes participants = client.list_participants( survey_id, start=0, limit=50, attributes=["email", "completed"], conditions={"completed": "N"}, ) for p in participants: print(p["token"], p["participant_info"]["email"]) ``` -------------------------------- ### Activate Survey and Load Responses Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Activates the survey using the obtained 'survey_id', enables datestamping, activates participant tokens, and then adds the generated fake responses to the survey. Returns the number of responses added. ```python client.activate_survey( survey_id, user_activation_settings={ "datestamp": True, }, ) client.activate_tokens(survey_id) result = client.add_responses(survey_id, fake_responses) len(result) ``` -------------------------------- ### Manually Close Client Session Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Demonstrates how to manually close the client session using the `close()` method when not using a context manager. ```python client = Client() # Use client here client.close() ``` -------------------------------- ### Use a Different Authentication Plugin Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Configures the client to use an alternative authentication plugin, such as LDAP or web server authentication, instead of the default database authentication. ```python from citric.auth import AuthLDAP client = Client(auth_plugin=AuthLDAP(ldap_url="ldap://localhost:389")) ``` -------------------------------- ### Manage Quotas with Citric Client Source: https://context7.com/edgarrmondragon/citric/llms.txt Use `add_quota` to create quotas, `list_quotas` to retrieve them, `get_quota_properties` and `set_quota_properties` to manage their settings, and `delete_quota` to remove them. Requires LimeSurvey 6.0+. ```python from citric import Client from citric.enums import QuotaAction with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 123456 # Create a quota that terminates when 100 responses are collected quota_id = client.add_quota( survey_id, name="Max 100 Respondents", limit=100, active=True, action=QuotaAction.TERMINATE, message="Thank you, we have enough responses.", url="https://example.com/thank-you", ) print(f"Created quota ID: {quota_id}") # List all quotas for the survey for quota in client.list_quotas(survey_id): print(quota["id"], quota["name"], quota["limit"]) # Get and update quota properties props = client.get_quota_properties(quota_id) print(props["qlimit"], props["active"]) client.set_quota_properties(quota_id, qlimit=200, active=1) # Delete a quota client.delete_quota(quota_id) ``` -------------------------------- ### Run Integration Tests with Nox Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Execute integration tests. This requires the Docker daemon to be running and the Docker CLI to be available. ```shell nox -s integration ``` -------------------------------- ### Import Libraries Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Imports essential Python libraries: Path for file operations, Faker for generating fake data, and the citric library for interacting with the survey client. ```python from pathlib import Path from faker import Faker import citric ``` -------------------------------- ### Load SQL Extension Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Loads the SQL extension for Jupyter, enabling the use of SQL magic commands within the notebook. ```python %load_ext sql ``` -------------------------------- ### Run Doctests with Nox Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Execute doctests using the nox command with the xdoctest session. ```shell nox -s xdoctest ``` -------------------------------- ### Automatically Close Session with Context Manager Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Use a context manager to ensure the client session is automatically closed. This is the recommended way to manage sessions. ```python with Client() as client: # Use client here pass ``` -------------------------------- ### Client.get_uploaded_file_objects / Client.download_files Source: https://context7.com/edgarrmondragon/citric/llms.txt Handle files uploaded by respondents to a survey. `get_uploaded_file_objects` iterates over file objects, each containing metadata and a content stream. `download_files` downloads all files to a specified directory, with options to filter by token. ```APIDOC ## `Client.get_uploaded_file_objects` / `Client.download_files` — Handle uploaded files Iterates over or downloads files that respondents uploaded to a survey. Each file object contains metadata and a `BytesIO` content stream. ```python import boto3 from citric import Client # Stream uploaded files directly to S3 s3 = boto3.client("s3") with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 12345 for file in client.get_uploaded_file_objects(survey_id): s3.upload_fileobj( file["content"], "my-s3-bucket", f"uploads/sid={survey_id}/qid={file['meta']['question']['qid']}/{file['meta']['filename']}", ) # Or download all files to a local directory paths = client.download_files("./downloads", survey_id) print(f"Downloaded: {[str(p) for p in paths]}") # Filter by participant token paths = client.download_files("./downloads", survey_id, token="T00001") ``` ``` -------------------------------- ### Handle Uploaded Files with Citric Client Source: https://context7.com/edgarrmondragon/citric/llms.txt Iterate over or download files uploaded by respondents. File objects contain metadata and a BytesIO content stream. Supports streaming to S3 or downloading to a local directory. ```python import boto3 from citric import Client # Stream uploaded files directly to S3 s3 = boto3.client("s3") with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 12345 for file in client.get_uploaded_file_objects(survey_id): s3.upload_fileobj( file["content"], "my-s3-bucket", f"uploads/sid={survey_id}/qid={file['meta']['question']['qid']}/{file['meta']['filename']}", ) # Or download all files to a local directory paths = client.download_files("./downloads", survey_id) print(f"Downloaded: {[str(p) for p in paths]}") # Filter by participant token paths = client.download_files("./downloads", survey_id, token="T00001") ``` -------------------------------- ### Download from S3 and Import Survey Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/import_s3.ipynb Downloads a survey file from S3 using a BytesIO object, then imports it into LimeSurvey using the citric client. Ensure your LimeSurvey server details and credentials are correct. ```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"] + "
")) ``` -------------------------------- ### Create Table from CSV Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Creates a DuckDB table named 'responses' by importing data from the 'responses.csv' file. This makes the survey data queryable via SQL. ```python %sql CREATE TABLE responses AS SELECT * FROM 'responses.csv' ``` -------------------------------- ### Parse LSQ File Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/parse_files.ipynb Imports the ElementTree library and parses an LSQ file. Ensure the file path is correct. ```python import xml.etree.ElementTree as ET # noqa: S405 tree = ET.parse("../../examples/free_text.lsq") # noqa: S314 ``` -------------------------------- ### Activate Survey and Load Fake Responses Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Activates the survey, enabling data collection with timestamps, and then activates survey tokens. Fake response data is generated using Faker and added to the survey. ```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) ``` -------------------------------- ### Import CPDB Participants with Client Source: https://context7.com/edgarrmondragon/citric/llms.txt Use this to import participants into the Central Participant Database (CPDB). Accepts a list of `Participant` dataclass instances. The `update=True` parameter can be used to update existing records if a `participant_id` matches. ```python from uuid import uuid4 from citric import Client from citric.objects import Participant with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: result = client.import_cpdb_participants( [ Participant(firstname="Alice", lastname="Smith", email="alice@example.com", language="en"), Participant( firstname="Bob", lastname="Jones", email="bob@example.com", participant_id=uuid4(), blacklisted=False, attributes={"attribute_1": "VIP"}, ), ], update=True, # update existing records if participant_id matches ) print(f"Imported: {result['ImportCount']}, Updated: {result['UpdateCount']}") ``` -------------------------------- ### Handling Citric Exceptions Source: https://context7.com/edgarrmondragon/citric/llms.txt Demonstrates how to catch specific exceptions raised by the Citric library for robust error handling in production code. ```python from citric import Client from citric.exceptions import ( LimeSurveyStatusError, LimeSurveyApiError, RPCInterfaceNotEnabledError, InvalidJSONResponseError, ResponseMismatchError, ) try: with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: client.activate_survey(999999) # non-existent survey except LimeSurveyStatusError as e: # Raised when the RPC response status field is not "OK" print(f"LimeSurvey status error: {e}") except LimeSurveyApiError as e: # Raised when the RPC response error field is non-null print(f"LimeSurvey API error: {e}") except RPCInterfaceNotEnabledError: # Empty response — JSON-RPC not enabled in LimeSurvey global settings print("Enable the JSON-RPC interface in LimeSurvey Admin → Global Settings → Interfaces") except InvalidJSONResponseError: # Non-JSON response received print("Check that the RemoteControl URL is correct and JSON RPC is enabled") except ResponseMismatchError as e: # Request/response ID mismatch (should be rare) print(f"ID mismatch: {e}") ``` -------------------------------- ### Query First 5 Responses Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/duckdb.ipynb Selects and displays the first 5 rows from the 'responses' table in DuckDB. This provides a quick preview of the imported data. ```python %sql SELECT * FROM responses LIMIT 5 ``` -------------------------------- ### Using Citric Enums for Exporting Responses Source: https://context7.com/edgarrmondragon/citric/llms.txt Demonstrates how to use Citric's StrEnum-based enumerations for typed parameter values when exporting survey responses. Ensure the Client is properly initialized with the correct URL and credentials. ```python from citric.enums import ( ResponsesExportFormat, StatisticsExportFormat, HeadingType, ResponseType, SurveyCompletionStatus, NewSurveyType, ImportSurveyType, ImportGroupType, QuotaAction, TimelineAggregationPeriod, EmailSendStrategy, UserStatus, ) from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: # Use enums directly or pass their string values data = client.export_responses( 123456, file_format=ResponsesExportFormat.CSV, heading_type=HeadingType.FULL, response_type=ResponseType.LONG, completion_status=SurveyCompletionStatus.COMPLETE, ) ``` -------------------------------- ### Configure Integration Tests for Master Branch Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Set the LS_REF environment variable to test against the master branch of LimeSurvey. ```shell LS_REF=refs/heads/master ``` -------------------------------- ### Upload Files and Update Response Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Demonstrates the process of uploading a file to LimeSurvey and then associating it with a specific survey response. ```python with open("my_file.txt", "rb") as f: file_info = client.upload_file(survey_id=survey_id, file_content=f.read(), file_name="my_file.txt") client.update_response( survey_id=survey_id, response_id=response_id, token=token, # Example: update a file upload question update_fields={"MY_UPLOAD_Q": file_info["fid"]} ) ``` -------------------------------- ### Configure Integration Tests for Specific Commit Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Set the LS_REF environment variable to a specific commit hash to test against a particular version of LimeSurvey. ```shell LS_REF=f148781ec57fd1a02e5faa26a7465d78c9ab5dfe ``` -------------------------------- ### Client.get_survey_properties / Client.set_survey_properties Source: https://context7.com/edgarrmondragon/citric/llms.txt Read and write survey settings such as anonymization, tokens, email, and date configurations. ```APIDOC ## `Client.get_survey_properties` / `Client.set_survey_properties` — Read and write survey settings Gets or sets any combination of survey properties (anonymization, tokens, email, date settings, etc.) on an existing survey. ### Usage ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 123456 # Read all properties props = client.get_survey_properties(survey_id) print(props["active"], props["anonymized"], props["language"]) # Read specific properties only props = client.get_survey_properties(survey_id, properties=["active", "expires", "startdate"]) # Update properties updated = client.set_survey_properties( survey_id, anonymized="Y", datestamp="Y", ipaddr="N", alloweditaftercompletion="N", ) print(updated) # {"anonymized": True, "datestamp": True, ...} ``` ``` -------------------------------- ### Run Type Checking with Nox Source: https://github.com/edgarrmondragon/citric/blob/main/docs/contributing/testing.md Perform type checking using mypy via the nox command. ```shell nox -s mypy ``` -------------------------------- ### Export Responses to DuckDB and Analyze with SQL Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Exports survey responses to a DuckDB database, enabling SQL-based analysis of the survey data. ```python import duckdb con = duckdb.connect(database=':memory:', read_only=False) client.duckdb_sql(con, survey_id=survey_id) results = con.execute("SELECT COUNT(T.id) FROM ""responses"" AS T").fetchall() print(results) ``` -------------------------------- ### Import Survey Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Imports a survey definition from a local '.lss' file into the LimeSurvey system using the Citric client. The file is opened in binary read mode. ```python with Path("../../examples/survey.lss").open("rb") as f: survey_id = client.import_survey(f) ``` -------------------------------- ### Import Survey Structures Source: https://context7.com/edgarrmondragon/citric/llms.txt Imports surveys, question groups, and questions by importing exported LimeSurvey format files (LSS, LSA, LSG, LSQ). Returns the new entity ID. ```APIDOC ## `Client.import_survey` / `Client.import_group` / `Client.import_question` — Import survey structures Creates surveys, question groups, and questions by importing exported LimeSurvey format files (LSS, LSA, LSG, LSQ). Returns the new entity ID. ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: # Import a survey from an LSS file with open("survey_backup.lss", "rb") as f: new_survey_id = client.import_survey(f, file_type="lss", survey_name="Imported Survey") print(f"New survey ID: {new_survey_id}") # Import a group into the new survey with open("group.lsg", "rb") as f: group_id = client.import_group(f, new_survey_id, name="Demographics") print(f"New group ID: {group_id}") # Import a question into the group with open("question.lsq", "rb") as f: question_id = client.import_question( f, new_survey_id, group_id, mandatory=True, new_question_title="Q_AGE", ) print(f"New question ID: {question_id}") ``` ``` -------------------------------- ### Client.add_survey Source: https://context7.com/edgarrmondragon/citric/llms.txt Creates a new survey with the specified title and language. It returns the unique identifier of the newly created survey. ```APIDOC ## `Client.add_survey` — Create a new empty survey Creates a new survey with the given title and language. Returns the ID of the newly created survey. ### Method Signature `add_survey(surveyid, survey_title, language, type)` ### Parameters - **surveyid** (int) - The ID of the survey to add. - **survey_title** (str) - The title of the new survey. - **language** (str) - The language code for the survey. - **type** (NewSurveyType) - The type of survey to create (e.g., GROUP_BY_GROUP). ### Returns - **survey_id** (int) - The ID of the newly created survey. ### Example ```python from citric import Client from citric.enums import NewSurveyType with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = client.add_survey(0, "Employee Feedback 2025", "en", NewSurveyType.GROUP_BY_GROUP) print(f"Created survey with ID: {survey_id}") ``` ``` -------------------------------- ### Send Invitation Emails with Client.invite_participants Source: https://context7.com/edgarrmondragon/citric/llms.txt Sends invitation emails to survey participants. Supports sending to pending participants or resending to specific token IDs. Returns the count of emails remaining to be sent. ```python from citric import Client from citric.enums import EmailSendStrategy with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 123456 # Send to all pending participants left = client.invite_participants(survey_id, strategy=EmailSendStrategy.PENDING) print(f"Emails left to send: {left}") # Resend to specific token IDs left = client.invite_participants( survey_id, token_ids=[1, 3, 7], strategy=EmailSendStrategy.RESEND, ) print(f"Emails left: {left}") ``` -------------------------------- ### Import CPDB Participants Source: https://context7.com/edgarrmondragon/citric/llms.txt Imports participants into the Central Participant Database (CPDB). Accepts a list of `Participant` dataclass instances. Optionally updates existing records. ```APIDOC ## `Client.import_cpdb_participants` — Import CPDB participants Imports participants into the Central Participant Database (CPDB). Accepts a list of `Participant` dataclass instances. Optionally updates existing records. ```python from uuid import uuid4 from citric import Client from citric.objects import Participant with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: result = client.import_cpdb_participants( [ Participant(firstname="Alice", lastname="Smith", email="alice@example.com", language="en"), Participant( firstname="Bob", lastname="Jones", email="bob@example.com", participant_id=uuid4(), blacklisted=False, attributes={"attribute_1": "VIP"}, ), ], update=True, # update existing records if participant_id matches ) print(f"Imported: {result['ImportCount']}, Updated: {result['UpdateCount']}") ``` ``` -------------------------------- ### Client.list_questions Source: https://context7.com/edgarrmondragon/citric/llms.txt Retrieves all questions (or questions within a specific group) from a survey, with optional language selection. Returns a list of QuestionsListElement typed dicts. ```APIDOC ## Client.list_questions — List questions in a survey ### Description Retrieves all questions (or questions within a specific group) from a survey, with optional language selection. Returns a list of `QuestionsListElement` typed dicts. ### Method `list_questions` ### Parameters #### Path Parameters - **survey_id** (integer) - Required - The ID of the survey from which to retrieve questions. #### Query Parameters - **group_id** (integer) - Optional - The ID of the question group to filter by. - **language** (string) - Optional - The language code for which to retrieve questions (e.g., 'en', 'fr'). ### Request Example ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: surveys = client.list_surveys("iamadmin") for survey in surveys: print(survey["surveyls_title"]) questions = client.list_questions(survey["sid"]) for q in questions: print(f" [{q['title']}] {q['question']}") # Only questions from group 7, in French questions = client.list_questions(123456, group_id=7, language="fr") ``` ### Response #### Success Response (200) - **questions** (list) - A list of question objects, each containing details like `title`, `question`, and `type`. ``` -------------------------------- ### Copy Survey with Client Source: https://context7.com/edgarrmondragon/citric/llms.txt Duplicates an existing survey using the Client class. Optionally specify a new survey ID for versions 6.4.0 and above. ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: result = client.copy_survey(123456, "Customer Survey - Copy 2025") print(result["status"]) print(result["newsid"]) # Specify desired ID (LimeSurvey >= 6.4.0) result = client.copy_survey(123456, "Quarterly Survey", destination_survey_id=654321) ``` -------------------------------- ### Client.get_summary / Client.get_summary_stat Source: https://context7.com/edgarrmondragon/citric/llms.txt Retrieves aggregate token and response statistics for a survey. `get_summary` returns all stats; `get_summary_stat` fetches one specific metric. ```APIDOC ## `Client.get_summary` / `Client.get_summary_stat` — Survey response statistics Retrieves aggregate token and response statistics for a survey. `get_summary` returns all stats; `get_summary_stat` fetches one specific metric. ### Usage ```python from citric import Client with Client("https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret") as client: survey_id = 123456 summary = client.get_summary(survey_id) if summary: print(f"Completed: {summary['completed_responses']}") print(f"Incomplete: {summary['incomplete_responses']}") print(f"Tokens sent: {summary['token_sent']}") # Fetch a single statistic completed = client.get_summary_stat(survey_id, "completed_responses") print(f"Total completed responses: {completed}") ``` ``` -------------------------------- ### Session Source: https://context7.com/edgarrmondragon/citric/llms.txt Low-level JSON-RPC session handler for authentication, request ID matching, and error parsing. Dispatches all RPC methods not explicitly implemented. ```APIDOC ## `Session` — Low-level JSON-RPC session `Session` is the low-level RPC transport layer. It handles authentication (`get_session_key`), request ID matching, and error parsing. All RPC methods not explicitly implemented are dispatched via `__getattr__` as `Method` callables. ```python from citric.session import Session # Direct RPC call with Session( "https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret", ) as session: # Explicit method call surveys = session.list_surveys("admin", None) # Call any RPC method not explicitly wrapped result = session.get_fieldmap(123456) # Low-level: get raw RPC response with ID and error fields raw = session.call("list_surveys", "admin", None) print(raw["result"], raw["error"], raw["id"]) ``` ``` -------------------------------- ### Low-level Session for RPC Calls Source: https://context7.com/edgarrmondragon/citric/llms.txt Utilizes the Session class for direct JSON-RPC communication with LimeSurvey. Handles authentication and allows explicit or dynamic RPC method calls. ```python from citric.session import Session # Direct RPC call with Session( "https://myls.example.com/index.php/admin/remotecontrol", "admin", "secret", ) as session: # Explicit method call surveys = session.list_surveys("admin", None) # Call any RPC method not explicitly wrapped result = session.get_fieldmap(123456) # Low-level: get raw RPC response with ID and error fields raw = session.call("list_surveys", "admin", None) print(raw["result"], raw["error"], raw["id"]) ``` -------------------------------- ### Export Responses to CSV and Create SQLite Engine Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/pandas_sqlite.ipynb Exports survey responses in CSV format with specific options, reads the CSV data into a Pandas DataFrame, and creates a SQLAlchemy engine for an SQLite database. ```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") ``` -------------------------------- ### Display Field Values Source: https://github.com/edgarrmondragon/citric/blob/main/docs/notebooks/parse_files.ipynb Shows the extracted text content for each 'fieldname' element. This helps in understanding the available metadata for each question. ```python Result: 'qid' ``` ```python Result: 'parent_qid' ``` ```python Result: 'sid' ``` ```python Result: 'gid' ``` ```python Result: 'type' ``` ```python Result: 'title' ``` ```python Result: 'preg' ``` ```python Result: 'other' ``` ```python Result: 'mandatory' ``` ```python Result: 'encrypted' ``` ```python Result: 'question_order' ``` ```python Result: 'scale_id' ``` ```python Result: 'same_default' ``` ```python Result: 'relevance' ``` ```python Result: 'modulename' ``` -------------------------------- ### Use a Custom Requests Session Source: https://github.com/edgarrmondragon/citric/blob/main/docs/how-to.md Integrates a custom `requests.Session` object, such as one with caching enabled via `requests-cache`, for advanced request handling. ```python import requests_cache requests_cache.install_cache('citric_cache') custom_session = requests.Session() client = Client(session=custom_session) ``` -------------------------------- ### Upload Files to a Survey with Citric Client Source: https://context7.com/edgarrmondragon/citric/llms.txt Upload files to a specific survey field, either from a file path or a file object. The returned metadata can be used to attach files to responses. ```python import json from citric import Client survey_id = 12 group_id = 34 question_id = 56 field_name = f"{survey_id}X{group_id}X{question_id}" with Client("http://localhost:8001/index.php/admin/remotecontrol", "admin", "secret") as client: # Upload from a file path result = client.upload_file(survey_id, field_name, "report.pdf") print(result["filename"]) # e.g. "survey_files/12/image_1.png" # Upload from a file object with a custom filename with open("image_1.png", "rb") as f: result1 = client.upload_file_object(survey_id, field_name, "image_1.png", f) with open("image_2.png", "rb") as f: result2 = client.upload_file_object(survey_id, field_name, "image_2.png", f) # Attach uploaded files to a response client.add_responses(survey_id, [{ "token": "T00001", field_name: json.dumps([result1, result2]), f"{field_name}_filecount": 2, }]) ```