### Mail Merge Script Overview and Setup Source: https://getodk.github.io/pyodk/examples/mail_merge/mail_merge Provides a high-level description of the mail merge script, its purpose, dependencies, and execution instructions. It guides users on setting up requirements and running the script. ```python """ Mail Merge This script will use mail merge to create personalized Word documents with data from Central. In this example, only data from approved submissions are included. For a tutorial on how to populate Word templates with Python, see: https://pbpython.com/python-word-template.html For more examples, see: https://github.com/iulica/docx-mailmerge/tree/master/tests Install requirements for this script in `requirements.txt`. The specified versions are those that were current when the script was last updated, though it should work with more recent versions. Install with `pip install -r requirements.txt`. To run the script, use `python mail_merge.py`. """ ``` -------------------------------- ### Install pyodk from source Source: https://getodk.github.io/pyodk/index Installs pyodk from its source repository. This involves cloning the repository, setting up a virtual environment, and installing the package in editable mode. ```shell # Get a copy of the repository. mkdir -P ~/repos/pyodk cd ~/repos/pyodk git clone https://github.com/getodk/pyodk.git repo # Create and activate a virtual environment for the install. python -m venv venv source venv/bin/activate # Install pyodk and its production dependencies. cd ~/repos/pyodk/repo pip install -e . # Leave the virtualenv. deactivate ``` -------------------------------- ### PyODK Configuration File Example Source: https://getodk.github.io/pyodk/index An example of the `.pyodk_config.toml` file used for configuring pyodk, particularly for end-to-end testing. It stores user credentials and project IDs. ```toml # Example .pyodk_config.toml # [default] # username = "your_username" # password = "your_password" # url = "https://your_central_instance.com" # project_id = "your_project_id" ``` -------------------------------- ### Initialize and List Projects Source: https://getodk.github.io/pyodk/projects Demonstrates how to initialize the pyODK client and fetch a list of all projects. This is a common starting point for interacting with project data. ```python from pyodk.client import Client client = Client() projects = client.projects.list() ``` -------------------------------- ### Install pyodk via pip Source: https://getodk.github.io/pyodk/index Installs the pyODK library and its dependencies using pip. This is the recommended method for most users. ```shell pip install pyodk ``` -------------------------------- ### Basic Python Hello World Example Source: https://getodk.github.io/pyodk/examples/2022-10-pyodk-webinar A fundamental Python example demonstrating the 'print' function. This snippet is often used as a first step in learning a new programming language or environment like Jupyter Lab. ```Python print("Hello, world!") ``` -------------------------------- ### pyODK Client Examples Source: https://getodk.github.io/pyodk/index Provides a collection of common pyODK operations, including listing projects and forms, retrieving submissions, downloading form data, and updating forms with attachments. ```python from pyodk.client import Client client = Client() projects = client.projects.list() forms = client.forms.list() submissions = client.submissions.list(form_id=next(forms).xmlFormId) form_data = client.submissions.get_table(form_id="birds", project_id=8) comments = client.submissions.list_comments(form_id=next(forms).xmlFormId, instance_id="uuid:...") client.forms.update( form_id="my_xlsform", definition="my_xlsform.xlsx", attachments=["fruits.csv", "vegetables.png"], ) client.close() ``` -------------------------------- ### App User Provisioner Script Overview Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner This section provides a high-level description of the App User Provisioner script. It explains the script's purpose: to create ODK Central App Users from a list of usernames in a CSV file. It also outlines the expected input file (`users.csv`), the generated outputs (PNGs for each user, a `users.pdf` compilation), and installation instructions using `pip install -r requirements.txt`. ```python """ App User Provisioner Put a series of user names (one on each line) in a file named `users.csv` in the same directory as this script. The script will create App Users for each user, using the project, forms, and other configurations set below. The outputs are one PNG for each provisioned App User, and a `users.pdf` file with all the App User PNGs in the folder. Install requirements for this script in `requirements.txt`. The specified versions are those that were current when the script was last updated, though it should work with more recent versions. Install these with `pip install -r requirements.txt`. To run the script, use `python app_user_provisioner.py`. """ ``` -------------------------------- ### Install Development Dependencies Source: https://getodk.github.io/pyodk/index Installs the pyodk library in editable mode, including development-specific dependencies required for testing and building. ```shell pip install -e .[dev] ``` -------------------------------- ### Initialize Client and List Forms (Python) Source: https://getodk.github.io/pyodk/forms Demonstrates how to initialize the pyODK client and use the forms service to list all available forms. This is a common starting point for interacting with form data. ```python from pyodk.client import Client client = Client() forms = client.forms.list() ``` -------------------------------- ### Initialize pyODK Client and List Entities Source: https://getodk.github.io/pyodk/entities Demonstrates how to initialize the pyODK client and retrieve a list of entities. This is a common starting point for interacting with the Entities API. ```python from pyodk.client import Client client = Client() data = client.entities.list() ``` -------------------------------- ### Python GET Request for Form Version XLSForm Source: https://getodk.github.io/pyodk/examples/beyond-library-methods Shows a Python example of making a GET request using the pyodk client to fetch an XLSForm for a specific form version. It includes filling in actual project and form IDs and accessing the response content. ```Python response = client.get("/projects/7/forms/favorite_color/versions/2012060405.xlsx") response.content[:150] ``` -------------------------------- ### Get Project Information using Raw HTTP Request Source: https://getodk.github.io/pyodk/examples/beyond-library-methods Illustrates fetching project details via a raw HTTP GET request using the pyODK client's `get` method. This approach returns a standard `requests.Response` object, allowing access to the JSON payload. ```python response = client.get("projects/7") response.json() ``` -------------------------------- ### Initialize and List Entity Lists - pyODK Source: https://getodk.github.io/pyodk/entity_lists Demonstrates how to initialize the pyODK client and retrieve a list of all Entity Lists within a project. This is a common starting point for interacting with Entity List data. ```Python from pyodk.client import Client client = Client() data = client.entity_lists.list() ``` -------------------------------- ### Python Imports and Configuration for Mail Merge Source: https://getodk.github.io/pyodk/examples/mail_merge/mail_merge Imports necessary libraries like `os`, `datetime`, `MailMerge`, and `pyodk.client`. Defines configuration variables for project ID, form ID, template document, and output folder. ```python import os from datetime import datetime from mailmerge import MailMerge from pyodk.client import Client # customize these settings to your environment PROJECT_ID = 1 FORM_ID = "my_form" TEMPLATE_DOCUMENT = "template.docx" OUTPUT_FOLDER = "merged" ``` -------------------------------- ### Get Project Information using Library Method Source: https://getodk.github.io/pyodk/examples/beyond-library-methods Shows how to retrieve project details using pyODK's high-level library methods. The result is a typed Project object with fields like 'id', 'name', and 'updatedAt'. ```python client.projects.get() ``` -------------------------------- ### Configure pyODK Connection Source: https://getodk.github.io/pyodk/examples/basic-analysis-pandas Example TOML configuration file for pyODK. This file, typically named `.pyodk_config.toml` and placed in the home directory, stores connection details for ODK Central, such as the base URL, username, password, and default project ID, to avoid hardcoding credentials in scripts. ```toml [central] base_url = "https://www.example.com" username = "my_user" password = "my_password" default_project_id = 123 ``` -------------------------------- ### Retrieve Form Version XLSForm via API Source: https://getodk.github.io/pyodk/examples/beyond-library-methods Demonstrates how to construct a GET request URL based on API documentation to retrieve a specific form version's XLSForm. It highlights the use of placeholder parameters like projectId, xmlFormId, and version. ```APIDOC GET /projects/{projectId}/forms/{xmlFormId}/versions/{version}.xlsx ``` -------------------------------- ### PyODK Mail Merge Document Generation Logic Source: https://getodk.github.io/pyodk/examples/mail_merge/mail_merge Connects to the pyODK client, fetches submissions for a specified form, filters for approved ones, and uses mailmerge to create personalized Word documents based on a template. It extracts location data and submission details to populate the document. ```python with Client(project_id=PROJECT_ID) as client: submissions = client.submissions.get_table(form_id=FORM_ID) for submission in submissions["value"]: # only include approved submissions if submission["__system"]["reviewState"] == "approved": with MailMerge(TEMPLATE_DOCUMENT) as document: coordinates = submission["age_location"]["location"]["coordinates"] location = f"{coordinates[1]}, {coordinates[0]}" generation_date = datetime.now().strftime("%m-%d-%Y %H:%M:%S.%f") document.merge( first_name=submission["first_name"], age=submission["age_location"]["age"], location=location, instance_id=submission["meta"]["instanceID"], submission_date=submission["__system"]["submissionDate"], generation_date=generation_date, ) # warning: choose variables with unique values to prevent overwritten files output_path = os.path.join( OUTPUT_FOLDER, f"{submission['first_name']}.docx" ) document.write(output_path) ``` -------------------------------- ### pyodk Submissions API: Create, Edit, Get Source: https://getodk.github.io/pyodk/submissions Provides methods to interact with ODK Central submissions. Supports creating new submissions with optional attachments, editing existing submissions with comments, and retrieving submission metadata. Parameters like `form_id`, `project_id`, and `device_id` help contextualize submissions within a project and device. ```APIDOC SubmissionService: create(xml, form_id=None, project_id=None, device_id=None, encoding='utf-8', attachments=None) Description: Creates a new submission. Parameters: xml (str): The submission XML content. form_id (str | None): The xmlFormId of the Form being referenced. project_id (int | None): The id of the project this form belongs to. device_id (str | None): An optional deviceID associated with the submission. encoding (str): The encoding of the submission XML (default: 'utf-8'). attachments (Iterable[PathLike | str] | None): File paths of attachments to upload. File names in XML must match attachment file names. Example XML Structure: uuid:85cb9aff-005e-4edd-9739-dc9c1a829c44 Alice 36 profile.jpg edit(instance_id, xml, form_id=None, project_id=None, comment=None, encoding='utf-8') Description: Edits an existing submission and optionally adds a comment. Parameters: instance_id (str): The instanceId of the Submission being referenced. This ID represents the submission as a whole, not necessarily the instanceID within the XML. xml (str): The updated submission XML content. form_id (str | None): The xmlFormId of the Form being referenced. project_id (int | None): The id of the project this form belongs to. comment (str | None): Text for the comment to be added to the submission. encoding (str): The encoding of the submission XML (default: 'utf-8'). Example Edited XML Structure: uuid:85cb9aff-005e-4edd-9739-dc9c1a829c44 uuid:315c2f74-c8fc-4606-ae3f-22f8983e441e Alice 36 get(instance_id, form_id=None, project_id=None) Description: Retrieves metadata for a specific submission. Parameters: instance_id (str): The instanceId of the Submission being referenced. form_id (str | None): The xmlFormId of the Form being referenced. project_id (int | None): The id of the project this form belongs to. Returns: Submission: An object representation of the Submission's metadata. ``` -------------------------------- ### Initialize pyODK Client Source: https://getodk.github.io/pyodk/index Demonstrates the basic initialization of the pyODK Client. Authentication is handled automatically on the first API call or explicitly via Client.open(). ```python from pyodk.client import Client client = Client() ``` -------------------------------- ### Initialize pyODK Client Source: https://getodk.github.io/pyodk/examples/beyond-library-methods Demonstrates how to initialize the pyODK client by opening a connection to a configured ODK Central server. It relies on a `.pyodk_config.toml` file for credentials. ```python from pyodk.client import Client client = Client().open() ``` -------------------------------- ### Initialize pyodk Client with Config and List Projects Source: https://getodk.github.io/pyodk/examples/2022-10-pyodk-webinar Initializes the pyodk client with specified configuration and cache file paths. This allows for managing multiple client configurations or using specific credentials. It then lists projects accessible via this specific client configuration. ```Python viewer_client = Client(config_path="config.toml", cache_path="cache.toml") viewer_client.open() viewer_client.projects.list() ``` -------------------------------- ### Initialize pyodk Client and List Projects Source: https://getodk.github.io/pyodk/examples/2022-10-pyodk-webinar Initializes the pyodk client using default configuration and opens a connection to the ODK Central backend. It then lists all accessible projects. This method relies on a pre-configured `.pyodk_config.toml` file in the user's home directory. ```Python from pyodk.client import Client client = Client() client.open() client.projects.list() ``` -------------------------------- ### pyODK Submissions API Source: https://getodk.github.io/pyodk/submissions API reference for the pyODK SubmissionService, enabling operations like getting, creating, updating, and reviewing submissions. ```APIDOC pyODK SubmissionService API **Overview**: The `SubmissionService` within `pyODK` allows interaction with ODK Central submissions. It provides methods to retrieve, create, update, and manage the review state of submissions. **Example Usage**: ```python from pyodk.client import Client client = Client() data = client.submissions.get_table(form_id="my-form")["value"] ``` **Methods**: 1. **`get_table(form_id, project_id=None)`** * **Description**: Retrieves submissions for a specific form, potentially filtered by project. * **Parameters**: * `form_id` (str): The xmlFormId of the Form. *required*. * `project_id` (int | None): The id of the project this form belongs to. Defaults to `None`. * **Returns**: A dictionary containing submission data, typically under a "value" key. 2. **`get(instance_id, form_id=None, project_id=None)`** * **Description**: Retrieves a single submission by its instance ID. * **Parameters**: * `instance_id` (str): The instanceId of the Submission being referenced. *required*. * `form_id` (str | None): The xmlFormId of the Form being referenced. Defaults to `None`. * `project_id` (int | None): The id of the project this form belongs to. Defaults to `None`. * **Returns**: A dictionary representing the submission. 3. **`create(xml, form_id=None, project_id=None, encoding='utf-8')`** * **Description**: Creates a new submission with the provided XML data. * **Parameters**: * `xml` (str): The submission XML. *required*. * `form_id` (str | None): The xmlFormId of the Form being referenced. Defaults to `None`. * `project_id` (int | None): The id of the project this form belongs to. Defaults to `None`. * `encoding` (str): The encoding of the submission XML. Defaults to `'utf-8'`. * **Returns**: A dictionary representing the newly created submission. 4. **`edit(instance_id, xml, form_id=None, project_id=None, encoding='utf-8')`** * **Description**: Updates an existing submission with new XML data. * **Parameters**: * `instance_id` (str): The instanceId of the Submission being referenced. *required*. * `xml` (str): The submission XML. *required*. * `form_id` (str | None): The xmlFormId of the Form being referenced. Defaults to `None`. * `project_id` (int | None): The id of the project this form belongs to. Defaults to `None`. * `encoding` (str): The encoding of the submission XML. Defaults to `'utf-8'`. * **Returns**: A dictionary representing the updated submission. 5. **`_patch(instance_id, review_state, form_id=None, project_id=None)`** * **Description**: Updates the review state metadata of a submission. * **Parameters**: * `instance_id` (str): The instanceId of the Submission being referenced. *required*. * `review_state` (str): The current review state of the submission (e.g., 'approved', 'rejected'). *required*. * `form_id` (str | None): The xmlFormId of the Form being referenced. Defaults to `None`. * `project_id` (int | None): The id of the project this form belongs to. Defaults to `None`. * **Returns**: A dictionary representing the updated submission's metadata. 6. **`_put(instance_id, xml, form_id=None, project_id=None, encoding='utf-8')`** * **Description**: Updates submission data using the provided XML. This is an alias for `edit`. * **Parameters**: * `instance_id` (str): The instanceId of the Submission being referenced. *required*. * `xml` (str): The submission XML. *required*. * `form_id` (str | None): The xmlFormId of the Form being referenced. Defaults to `None`. * `project_id` (int | None): The id of the project this form belongs to. Defaults to `None`. * `encoding` (str): The encoding of the submission XML. Defaults to `'utf-8'`. * **Returns**: A dictionary representing the updated submission. 7. **`add_comment(instance_id, comment, project_id=None, form_id=None)`** * **Description**: Creates a new comment for a specific submission. * **Parameters**: * `instance_id` (str): The instanceId of the Submission being referenced. *required*. * `comment` (str): The text of the comment. *required*. * `project_id` (int | None): The id of the project this form belongs to. Defaults to `None`. * `form_id` (str | None): The xmlFormId of the Form being referenced. Defaults to `None`. * **Returns**: A `Comment` object representation of the newly-created Comment. 8. **`list_comments(instance_id, project_id=None, form_id=None)`** * **Description**: Retrieves all comments associated with a specific submission. * **Parameters**: * `instance_id` (str): The instanceId of the Submission being referenced. *required*. * `project_id` (int | None): The id of the project this form belongs to. Defaults to `None`. * `form_id` (str | None): The xmlFormId of the Form being referenced. Defaults to `None`. * **Returns**: A list of comment objects. 9. **`review(instance_id, review_state, form_id=None, project_id=None)`** * **Description**: Sets the review state for a submission. This is an alias for `_patch`. * **Parameters**: * `instance_id` (str): The instanceId of the Submission being referenced. *required*. * `review_state` (str): The current review state of the submission. *required*. * `form_id` (str | None): The xmlFormId of the Form being referenced. Defaults to `None`. * `project_id` (int | None): The id of the project this form belongs to. Defaults to `None`. * **Returns**: A dictionary representing the updated submission's metadata. **Related Methods**: * `client.forms`: For managing forms. * `client.entity_lists`: For managing entity lists. * `client.projects`: For managing projects. ``` -------------------------------- ### Read and Prepare User List Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner Reads a list of desired users from a CSV file, cleans up whitespace, and prepares it for provisioning. It opens the 'users.csv' file and processes each line. ```python # Provision the App Users. with open("users.csv", newline="") as f: desired_users = f.readlines() desired_users = [user.rstrip() for user in desired_users] ``` -------------------------------- ### Python POST Request to Create App User Source: https://getodk.github.io/pyodk/examples/beyond-library-methods Demonstrates how to create a new App User using a POST request with the pyodk client. It shows how to specify the request path and include a JSON payload with the required 'displayName' attribute. ```Python r = client.post("/projects/7/app-users", json={"displayName": "Lab Tech"}) r.json() ``` -------------------------------- ### External Library Imports for QR and Image Generation Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner This section imports libraries crucial for generating QR codes and manipulating images. `segno` is used for creating QR codes, while `PIL` (Pillow) is used for image processing tasks like drawing text, creating images, and applying image operations. `pyodk.client.Client` is imported for interacting with the ODK Central API. ```python import segno from PIL import Image, ImageDraw, ImageFont, ImageOps from pyodk.client import Client ``` -------------------------------- ### QR Code Settings Generation Function Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner The `get_settings` function constructs a dictionary containing configuration parameters for ODK Collect, which will be encoded into a QR image. It takes server URL, project name, and username as input. The returned dictionary includes sections for general settings (like server URL, username, form update mode), admin settings (like admin password), and project details (name, color, icon). ```python def get_settings(server_url: str, project_name: str, username: str) -> dict[str, Any]: """Template for the settings to encode in the QR image. Customise as needed.""" return { "general": { "form_update_mode": "match_exactly", "autosend": "wifi_and_cellular", "delete_send": True, "server_url": server_url, "username": username, }, "admin": { "admin_pw": ADMIN_PASSWORD, "change_server": False, "automatic_update": False, "change_autosend": False, }, "project": {"name": project_name, "color": "#ffeb3b", "icon": "💥"}, } ``` -------------------------------- ### Font Availability Check Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner This code block checks if the 'Roboto-Regular.ttf' font file is accessible. This font is required for rendering text within the generated QR code images. If the font is not found, it prints an informative message guiding the user on how to obtain it, including a link to Google Fonts. ```python # Check that the Roboto font used for the QR images is available (e.g. on Linux / Win). try: ImageFont.truetype("Roboto-Regular.ttf", 24) except OSError: print( "Font file 'Roboto-Regular.ttf' not found. This can be downloaded " "from Google, or copied from the Examples directory. " "Source: https://fonts.google.com/specimen/Roboto/about" ) ``` -------------------------------- ### Core Python Imports Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner This snippet lists essential Python modules imported for the script's functionality. It includes `base64` for encoding data, `glob` for finding files, `json` for handling data structures, `zlib` for compression, and `typing` for type hinting, which improves code readability and maintainability. ```python import base64 import glob import json import zlib from typing import Any ``` -------------------------------- ### Direct HTTP Requests with pyODK Client Source: https://getodk.github.io/pyodk/http-methods Demonstrates making direct GET and POST requests to the ODK Central API using the pyODK client. These methods are useful for interacting with API endpoints not yet implemented in the pyODK library. They utilize the client's session object, which is a customized requests.Session. ```Python client.get("projects/8") client.post("projects/7/app-users", json={"displayName": "Lab Tech"}) ``` -------------------------------- ### Generate QR Codes for User Settings Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner Iterates through provisioned users, generates unique settings for each, compresses and encodes them, creates QR codes using the 'segno' library, and saves them as PNG files. It also embeds the user's display name below the QR code. ```python # Generate the QR codes. for user in provisioned_users: collect_settings = get_settings( server_url=f"{client.session.base_url}key/{user.token}/projects/{PROJECT_ID}", project_name=f"{PROJECT_NAME}: {user.displayName}", username=user.displayName, ) qr_data = base64.b64encode( zlib.compress(json.dumps(collect_settings).encode("utf-8")) ) code = segno.make(qr_data, micro=False) code.save("settings.png", scale=4) png = Image.open("settings.png") png = png.convert("RGB") text_anchor = png.height png = ImageOps.expand(png, border=(0, 0, 0, 30), fill=(255, 255, 255)) draw = ImageDraw.Draw(png) font = ImageFont.truetype("Roboto-Regular.ttf", 24) draw.text((20, text_anchor - 10), user.displayName, font=font, fill=(0, 0, 0)) png.save(f"settings-{user.displayName}.png", format="PNG") ``` -------------------------------- ### Create App User via API Source: https://getodk.github.io/pyodk/examples/beyond-library-methods Illustrates the API endpoint and method for creating a new App User. It specifies the POST method and the base path, indicating that parameters like projectId are required. ```APIDOC POST /projects/{projectId}/app-users ``` -------------------------------- ### Client Context Manager Usage Source: https://getodk.github.io/pyodk/index Shows how to use the pyODK Client as a context manager, which automatically handles authentication and cleanup for session management. ```python from pyodk.client import Client with Client() as client: print(client.projects.list()) ``` -------------------------------- ### Concatenate QR Code Images into PDF Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner Collects all generated QR code PNG images (prefixed with 'settings-') from the current directory, sorts them, and then concatenates them into a single multi-page PDF document named 'users.pdf'. Handles cases where no images are found. ```python # Concatenate the user images into a PDF. images = [Image.open(f) for f in sorted(glob.glob("./settings-*.png"))] if 0 < len(images): img = iter(images) next(img).save( "users.pdf", format="PDF", resolution=100, save_all=True, append_images=img ) ``` -------------------------------- ### pyODK Projects API Documentation Source: https://getodk.github.io/pyodk/projects Comprehensive API documentation for pyODK's project endpoints, covering methods for creating app users, retrieving specific project details, and listing all projects. ```APIDOC pyodk.client.projects: Access project-related functionality via `client.projects`. create_app_users(display_names, forms=None, project_id=None) - Creates new project app users and optionally assigns forms to them. - Parameters: - display_names: Iterable[str] - The friendly nicknames of the app users to be created. (required) - forms: Iterable[str] | None - The xmlFormIds of the forms to assign the app users to. (default: None) - project_id: int | None - The id of the project this form belongs to. (default: None) - Returns: Status or confirmation of user creation. get(project_id=None) - Reads all Project details. - Parameters: - project_id: int | None - The id of the project to read. (default: None) - Returns: - Project: An object representation of the Project's metadata. list() - Reads Project details. - Parameters: None - Returns: - [Project]: A list of object representations of the Projects' metadata. ``` -------------------------------- ### Python: Execution Block Source: https://getodk.github.io/pyodk/examples/create_entities_from_submissions/create_entities_from_submissions This Python code block demonstrates how to execute the entity creation functions. It includes a standard `if __name__ == "__main__":` guard and shows how to call either the `create_one_at_a_time` or `create_with_merge` function. ```python if __name__ == "__main__": # create_one_at_a_time() create_with_merge() ``` -------------------------------- ### pyODK Client Class Source: https://getodk.github.io/pyodk/client The pyODK Client class manages connections to ODK Central servers. It handles authentication and provides methods organized by related Central resources. Initialization requires configuration paths, project ID, an optional session object, and the API version. ```APIDOC class Client: """A connection to a specific ODK Central server. Manages authentication and provides access to Central functionality through methods organized by the Central resource they are most related to.""" __init__(config_path: str | None = None, cache_path: str | None = None, project_id: int | None = None, session: Session | None = None, api_version: str = 'v1') """Initialize the pyODK Client.""" # Parameters: # config_path: Where to read the pyodk_config.toml. Defaults to the path in PYODK_CONFIG_FILE, then the user home directory. # cache_path: Where to read/write pyodk_cache.toml. Defaults to the path in PYODK_CACHE_FILE, then the user home directory. # project_id: The project ID to use for all client calls. Defaults to the "default_project_id" in pyodk_config.toml, or can be specified per call. # session: A prepared pyodk.session.Session class instance, or an instance of a customised subclass. # api_version: The ODK Central API version, which is used in the URL path e.g. 'v1' in 'https://www.example.com/v1/projects'. Defaults to 'v1'. close(*args) """Close the session.""" open() """Enter the session, and authenticate.""" ``` -------------------------------- ### Customizing pyODK Configuration and Cache Paths Source: https://getodk.github.io/pyodk/index Demonstrates how to specify custom file paths for pyODK configuration and session cache. This can be done via environment variables or during client initialization. ```python # Using environment variables: # export PYODK_CONFIG_FILE="my_config.toml" # export PYODK_CACHE_FILE="my_cache.toml" # Using init arguments: # from pyodk import Client # client = Client(config_path="my_config.toml", cache_path="my_cache.toml") ``` -------------------------------- ### Fetch ODK Submissions into Pandas DataFrame Source: https://getodk.github.io/pyodk/examples/2022-10-pyodk-webinar Retrieves submission data from ODK Central using the `get_table` method and processes it into a pandas DataFrame using `json_normalize`. This enables data analysis with pandas. Requires the `pyodk` library and a configured client object. ```Python import pandas as pd # Assuming 'client' is an initialized pyodk client object json_data = client.submissions.get_table(form_id="participants")["value"] df = pd.json_normalize(json_data, sep="-") print(df.head(3)) ``` -------------------------------- ### Project Configuration Constants Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner These constants define the project-specific settings required by the script. Users should customize `PROJECT_ID`, `PROJECT_NAME`, `FORMS_TO_ACCESS`, and `ADMIN_PASSWORD` to match their ODK Central environment and desired configurations. The `ADMIN_PASSWORD` is marked with `# noqa: S105` to suppress a security warning for hardcoded passwords, as this is a common practice in configuration scripts. ```python # Customise these settings to your environment. PROJECT_ID = 149 PROJECT_NAME = "My Cool Project" FORMS_TO_ACCESS = ["all-widgets", "afp-knowledge"] ADMIN_PASSWORD = "s00p3rs3cr3t" # noqa: S105 ``` -------------------------------- ### ODK Central API - Projects Endpoint Source: https://getodk.github.io/pyodk/examples/beyond-library-methods This section outlines how to interact with the ODK Central API for project-related operations. It covers retrieving project details, which can be done via library methods or direct HTTP requests. ```APIDOC GET /projects/{id} Retrieves details for a specific project. Parameters: id: The unique identifier of the project. Returns: A JSON object containing project details, including id, name, description, createdAt, and updatedAt. GET /projects Retrieves a list of all projects accessible to the authenticated user. Returns: A JSON array of project objects. Note: pyODK's `client.projects.get()` maps to GET /projects/{id} (using default project ID if none specified), and `client.get("projects/{id}")` allows direct access to this endpoint. ``` -------------------------------- ### Script Overview: Create or Update Form Source: https://getodk.github.io/pyodk/examples/create_or_update_form/create_or_update_form Provides a high-level description of the Python script for creating or updating forms. It outlines the script's purpose, its dual use as a CLI tool or importable module, and its capability to upload attachments. ```python """ A script to create or update a form, optionally with attachments. Either use as a CLI script, or import create_or_update into another module. If provided, all files in the [attachments_dir] path will be uploaded with the form. """ ``` -------------------------------- ### FormService API Documentation Source: https://getodk.github.io/pyodk/forms Comprehensive API documentation for the pyODK FormService, detailing methods for managing forms. Includes parameters, return types, and descriptions for each operation. ```APIDOC FormService: # Access forms via client.forms create(definition, attachments=None, ignore_warnings=True, form_id=None, project_id=None) # Creates a new form. # Parameters: # definition (PathLike | str | bytes): Path to the file to upload, or form definition in memory (XML string or XLS/XLSX bytes). # attachments (Iterable[PathLike | str] | None): Paths of form attachment files to upload. # ignore_warnings (bool | None): If True, create the form even if there are XLSForm warnings. Defaults to True. # form_id (str | None): The xmlFormId of the Form being referenced. # project_id (int | None): The id of the project this form belongs to. # Returns: # Form: An object representation of the Form's metadata. get(form_id, project_id=None) # Reads details of a specific form. # Parameters: # form_id (str): The id of this form as given in its XForms XML definition. # project_id (int | None): The id of the project this form belongs to. # Returns: # Form: An object representation of the Form's metadata. list(project_id=None) # Reads details of all forms within a project. # Parameters: # project_id (int | None): The id of the project the forms belong to. # Returns: # [list[Form]]: A list of object representations of all Forms' metadata. update(form_id, definition, attachments=None, ignore_warnings=True, project_id=None) # Updates an existing form. # Parameters: # form_id (str): The id of the form to update. # definition (PathLike | str | bytes): Path to the file to upload, or form definition in memory (XML string or XLS/XLSX bytes). # attachments (Iterable[PathLike | str] | None): Paths of form attachment files to upload. # ignore_warnings (bool | None): If True, update the form even if there are XLSForm warnings. Defaults to True. # project_id (int | None): The id of the project this form belongs to. # Returns: # Form: An object representation of the updated Form's metadata. ``` -------------------------------- ### Provision App Users with pyodk Source: https://getodk.github.io/pyodk/examples/app_user_provisioner/app_user_provisioner Uses the pyodk Client to provision app users. It takes a list of display names, specifies forms to access, and targets a specific project ID. This function returns a list of provisioned user objects. ```python client = Client() provisioned_users = client.projects.create_app_users( display_names=desired_users, forms=FORMS_TO_ACCESS, project_id=PROJECT_ID ) ``` -------------------------------- ### List pyodk Forms Source: https://getodk.github.io/pyodk/examples/2022-10-pyodk-webinar Lists all available forms associated with the currently configured project. This method retrieves metadata for each form, including its ID, name, and version. It assumes a client has already been initialized and opened. ```Python client.forms.list() ``` -------------------------------- ### Custom Session Configuration Source: https://getodk.github.io/pyodk/index Explains how to customize pyODK's session behavior, such as setting alternative timeouts or retry strategies, by subclassing the `pyodk.session.Session` class and passing an instance to the Client constructor. ```python from pyodk.client import Client from pyodk.session import Session class MySession(Session): # Custom session logic here pass my_session = MySession() client = Client(session=my_session) ``` -------------------------------- ### Visualize Aggregated ODK Data with Bar Chart Source: https://getodk.github.io/pyodk/examples/working-with-repeats Visualizes the aggregated ODK data using a bar chart. The data is unstacked to facilitate plotting, and axis labels and legend titles are set for clarity. ```Python ax = grouped.unstack(level=0).plot(kind="bar") ax.set_xlabel("Sighting") ax.set_ylabel("Total count") ax.legend().set_title("Data collector") ``` -------------------------------- ### pyODK Logging Configuration Source: https://getodk.github.io/pyodk/index Demonstrates how to configure and manage logging for pyODK using Python's standard `logging` library. This includes setting log levels and controlling propagation for the 'pyodk' logger. ```python import logging # Initialise an example basic logging config (writes to stdout/stderr). logging.basicConfig() logging.getLogger().setLevel(logging.DEBUG) # Get a reference to the pyodk logger. pyodk_log = logging.getLogger("pyodk") # Receive everything DEBUG level and higher. pyodk_log.setLevel(logging.DEBUG) pyodk_log.propagate = True # Ignore everything below FATAL level. pyodk_log.setLevel(logging.FATAL) pyodk_log.propagate = False ``` -------------------------------- ### Create or Update Form with Attachments Source: https://getodk.github.io/pyodk/examples/create_or_update_form/create_or_update_form Implements the main logic to create or update a form. It initializes a `Client` instance, calls `create_ignore_duplicate_error` for initial creation, and then updates the form, optionally uploading attachments from a specified directory. ```python def create_or_update(form_id: str, definition: str, attachments: str | None): """Create (and publish) the form, optionally with attachments.""" with Client() as client: create_ignore_duplicate_error(client=client, definition=definition) attach = None if attachments is not None: attach = Path(attachments).iterdir() client.forms.update( definition=definition, form_id=form_id, attachments=attach, ) ```