github kobotoolbox/kpi 2.025.34

latest release: 2.025.34a
one day ago

What's changed

Features (78)
  • asset: expose last_modified_by field for Asset (#5912)

    Expose last_modified_by value in asset API endpoint response

    Expose the last_modified_by field for Asset serializer.The field is
    now available in the response for the following endpoints:

    • /api/v2/assets/

    • /api/v2/organizations/<org_id>/assets/

    • /api/v2/project-views/<pv_uid>/assets/

  • attachment: create Celery tasks for attachment file deletion (#5662)

    Introduced initial Celery task structure for handling attachment file
    deletion.

    This PR adds tasks for asynchronous deletion of attachment files using
    Celery

  • attachments: create query file for removing attachments and a way to test it (#5577)

  • attachments: add a simple component for a deleted attachment (#5591)

    Adds a text component to display when an attachment has been deleted
    from a submission.

  • attachments: add download and delete buttons (#5599)

  • attachments: handle deleted attachments in repeat groups (#5605)

    In Data Table, for attachment related questions (e.g. audio) inside
    repeat groups, it displays "Deleted" word instead of the response for
    deleted attachments.

  • attachments: add bulk deleting attachments using the row selector in table with mock api (#5626)

    Adds a way to bulk delete attachments for submissions through the table
    view bulk options.

  • attachments: modal displays background audio in player and add remove attachment dropdown (#5673)

    Now single submission modal will display background audio in audio
    player. Background audio player also have dropdown menu to be downloaded
    or deleted.

  • attachments: make bulk delete attachments account for partial permissions (#5695)

    Displays the correct number of attachments/submissions in the UI when
    bulk deleting based on current user permissions.

  • attachments: new endpoint to bulk delete specific attachments on an asset (#5666)

    Add new endpoint which allows users to delete specific attachments on an
    project.

    • Users can only make DELETE requests to this endpoint and must pass a
      list of attachment uids as the payload
    • Users must have either partial or full edit_submission permissions
      in order to delete attachments
  • attachments: new endpoint to bulk delete all attachments from a submission (#5705)

    Add new endpoint which allows users to delete all attachments from a
    submission.

  • attachments: update storage counters when attachments are moved to or from trash (#5686)

    Ensure storage counters are updated when attachments are trashed or
    restored in ongoing projects.

    This change improves storage accounting by updating
    attachment_storage_bytes on both UserProfile and XForm when
    attachments are soft-deleted or restored using
    AttachmentTrash.toggle_statuses().

    • When an attachment is moved to the trash,
      pre_delete_attachment(only_update_counters=True) is invoked to
      decrement storage usage.
    • When an attachment is restored, post_save_attachment(created=True)
      is used to restore the counters.
    • Logic is in place to prevent counters from being decremented twice if
      the submission is deleted after the attachment has already been trashed.
  • attachments: update admin model to support attachments in trash bin (#5738)

    Improved the admin interface to support managing attachments in the
    trash bin.

    Attachments that are moved to the trash can now be viewed and managed
    directly from the admin panel. This includes the ability to permanently
    delete attachments from the trash ("Empty trash") or restore them ("Put
    back"). These actions update relevant storage counters and are logged in
    the AuditLogs. This enhancement provides better visibility and control
    over trashed attachments for admins.

  • attachments: Add tests for move_to_trash and put_back support for AttachmentTrash (#5793)

    Added unit tests for attachment trash functionality, including
    move_to_trash, put_back, and media file deletion behavior.

    Added unit tests to verify the behavior of move_to_trash and
    put_back for attachment objects. These tests confirm that:

    • Attachments are properly marked for deletion and storage counters
      updated
    • Media files are deleted via the empty_attachment task while
      retaining the DB record
    • Attachments can be restored and associated storage values are
      recalculated
    • Task restart logic works correctly based on status and time thresholds
  • attachments: Update mongo documents to expose attachment deleted flag (#5788)

    Added is_deleted flag to attachments in Mongo to reflect deletion
    status after attachment trash or restore actions.

    This update ensures that the _attachments array stored in Mongo instance
    documents accurately reflects the deletion status of each attachment.

    This change ensures that:

    • Each attachment in the _attachments array now includes an
      is_deleted flag.
    • The flag is automatically updated when attachments are moved to trash
      or restored.
    • This is implemented efficiently using Mongo bulk_write() operations.
  • attachments: Update formpack export to handle deleted attachments (#5819)

    Ensure KPI installs the latest formpack version to support new export
    features for deleted media attachments.

    This PR updates the formpack dependency hash in requirements.in,
    requirements.txt, and dev_requirements.txt to use the commit from
    formpack#335. That
    PR introduces support for displaying deleted attachments more clearly in
    exports:

    • Replaces {field}_URL with the text Deleted when the attachment is
      marked as deleted and include_media_url=True.

    These changes ensure that exports generated by KPI reflect deleted
    attachments more transparently for end users.

  • attachments: Add Celery task to process users exceeding storage limit (#5909)

    Adds a new celery task that identifies users who have exceeded their
    storage limits for a configurable number of days and dispatches a
    cleanup task for each. This prepares the foundation for automated
    enforcement of storage quotas via attachment deletion.

    This PR introduces a Celery task
    (schedule_auto_attachment_cleanup_for_users) that:

    • Runs periodically via Celery Beat (every 30 mins, currently commented
      out in CELERY_BEAT_SCHEDULE)
    • Filters users from the ExceededLimitCounter model who have exceeded
      their storage quota for more than
      LIMIT_ATTACHMENT_REMOVAL_GRACE_PERIOD days (default: 90)
    • For each qualifying user, dispatches a cleanup task
      (auto_delete_excess_attachments(user_id)), currently a placeholder.

    Additional Constance settings added:

    • AUTO_DELETE_ATTACHMENTS: Global toggle for enabling/disabling the
      auto deletion (default: False)
    • LIMIT_ATTACHMENT_REMOVAL_GRACE_PERIOD: Grace period before
      triggering cleanup for over-quota users
  • attachments: Add celery task to auto delete excess attachments (#5938)

    Adds auto_delete_excess_attachments task that soft-deletes a user’s
    oldest attachments until their storage usage falls below the configured
    limit. This completes the attachment cleanup workflow for users
    exceeding their storage quotas.

    This PR adds the auto_delete_excess_attachments(user_id) Celery task,
    which:

    • Checks if the user is exceeding their storage quota
    • If so, iterates over the user's oldest attachments (date_created
      order)
    • Soft-deletes attachments using the existing move_to_trash() utility
    • Stops once the user is back within the allowed quota
  • billing: add xform pending_transfer flag () (#5685)

    Adds a new pending_transfer field to the xform model and updates the
    suspend_submissions() method on OpenRosaDeploymentBackend to update
    this field instead of updating the submissions_suspended flag on the
    user model.

  • billing: add exceeded limit counter (#5780)

    Adds a new ExceededLimitCounter model for tracking how long a user has
    been over their quota for a given usage type.

  • billing: add usage balances to usage calculator (#5790)

    Adds a get_usage_balances() method to the UsageCalculator that returns
    a dict of the user's current usage balance relative to their usage
    limits.

  • billing: create useTrackingPeriod hook (#5817)

    This PR:

    • Adds the useTrackingPeriod hook
    • Replaces current uses of the trackingPeriod, previously from the
      useUsage hook the the new hook
  • billing: add balances to service usage api (#5808)

    Adds a field 'balances' on the service usage endpoint that indicates
    whether and by how much a user is over their usage limit for each usage
    type.

  • billing: ensure accurate usage limit reporting (#5860)

    Adds a check_limit_exceeded() util to update ExceededLimitCounters
    after submissions are submitted or nlp counters are updated.

  • billing: Replace useUsage with useServiceUsageQuery (#5873)

    This PR replaces the current useUsage by a react query implementation
    in useServiceUsage hook.
    All uses of the current useUsage and usageContext were removed and
    replaced by the new hook.

  • billing: block asr/mt actions when usage limit exceeded (#5872)

    Blocks ASR/MT actions with a 402 payment required error if asset owner
    has exceeded the relevant ASR/MT usage limit.

  • billing: reset usage/limit data on subscription change (#5882)

    Clears usage calculator cache and updates or remove counters as
    necessary whenever a subscription is saved.

  • billing: block submissions for users over usage limit (#5866)

    Blocks submissions with a 402 payment required error if asset owner has
    exceeded their submission or storage limit.

  • billing: recurring task to update limit counters (#5933)

    Adds a recurring task to check usage and update or remove exceeded limit
    counters that are more than one day old and not modified in the past day
    to ensure that counters remain up-to-date.

  • billing: disable/enable usage limit enforcement on frontend (#6000)

    Disable/enable NLP usage limit enforcement on the frontend depending on
    backend USAGE_LIMIT_ENFORCEMENT configuration.

  • billing: allow disabling limit enforcement (#5997)

    Adds a constance config setting for toggling whether or not usage limits
    should be enforced on stripe-enabled instances. Set to False by default.

  • billing: disable/enable usage limit enforcement on frontend (#6039)

    Disable/enable NLP usage limit enforcement on the frontend depending on
    backend USAGE_LIMIT_ENFORCEMENT configuration.

  • datatable: handle deleted audio attachment (#5619)

    In forms data table, display deleted audiofiles as deleted. The button
    for NLP is still there to access transcripts.

  • datatable: handle deleted file attachment (#5620)

    In forms data table, display deleted files as deleted. Hover on it to
    see the original path.

  • dataview: handle deleted attachments (#5622)

    In forms data view, display deleted attachments as deleted. The button
    for NLP is still there to access transcripts.

  • disclaimers: disable override when disclaimers are hidden (#6092)

    Disable the creation of an asset-specific disclaimer when the default
    disclaimer is hidden.

    In Django admin, clarify that a user can either select 'Hide disclaimer
    for all languages of the form' or create an overriding disclaimer (but
    not both) by disabling the areas for creating an override when 'Hide
    disclaimer for all languages' is checked.

  • enketo: allow user media for preview iframe (#6093)

    This PR adds the allow attribute to the form preview iframe to allow
    the use of user media from thre preview.
    Geolocation was also added, since there are geolocation widgets in
    Enketo.

    1. ℹ️ have an account and a project with an audio question
    2. Setup Enketo to work with KPI
      (docs)
    3. Pull the feat-audio-recording branch from Enketo
    4. Open the form in the Preview (eye icon from the form view)
    5. Try to record audio
    6. 🟢 The microphone should be accessible by the form
  • exports: Add option to filter exports by date (#5934)

    Adds the option to choose the export range in the UI. Helps with an
    error that can occur on the backend because it was OOM.

    Users can select one or more dates, if only one date is selected it will
    be treated as "greater than" or "less than" if it is the start or end
    date respectfully.

  • formLanding: General UI improvements of the deployed form history view (#5795)

    Some UI improvements to help modernize the form version history view.

    • Updating the "deployed" badge to match our other badges
    • Left-align the date in the "Last modified" column instead of center
      right aligned
    • Clone icons should sit under the clone header and not next to the date
      when the form was last modified.
  • gallery: handle deleted attachments (#5611)

    In gallery, display deleted attachments as deleted. Also, show the
    filename on hover.

  • longRunningMigrations: add system check to validate completion of critical long-running migrations (#5655)

    Added a Django system check to ensure important long-running migrations
    have completed

    This PR introduces a Django system check that validates whether specific
    long-running migrations—considered critical to data integrity or system
    functionality—have been completed. If any of these migrations are still
    running or incomplete, the system check will raise an error to prevent
    the application from starting.

    This mechanism not only protects the system from running in an
    inconsistent state, but also serves as a useful signal for sysadmins,
    giving them immediate visibility into whether the database is fully
    up-to-date. Instead of silently failing or relying on logs, this check
    provides a clear and explicit message during startup, making it easier
    to identify and act on pending background data migrations.

  • massEmail: query for identifying inactive users based on login, form activity, and submissions (#5569)

    Detect and return a list of users who have been inactive for a specified
    period based on login activity, project updates, and submissions.

    This update introduces a method to identify inactive users who have not:

    • Logged into their account.
    • Modified their projects or had modifications made by others.
    • Received submissions to their projects.

    By default, the inactivity period is set to 365 days but can be
    configured dynamically.

  • massEmail: add celery task to mark old enqueued mass email record as failed (#5590)

    Automatically marks old enqueued mass email records as failed if they
    exceed a configurable expiry period.

    This PR introduces a Celery task that periodically scans
    MassEmailRecord entries with status enqueued and updates them to
    failed if they were created before the configured expiry threshold
    (MASS_EMAIL_RECORD_EXPIRY_DAYS). This ensures that stale email records
    do not remain indefinitely enqueued.

  • massEmail: add admin action to enqueue mass email records for scheduled sending (#5604)

    Added an admin action to create mass email jobs and records, allowing
    admins to enqueue emails for users based on predefined queries.

    Introduces an "Add to daily send queue" action in the Django admin for
    MassEmailConfig. When selected, it:

    • Checks if emails for the selected configuration are already enqueued
      or being sent. If so, it notifies the user and exits.
    • If not, it creates a MassEmailJob and enqueues email records for
      eligible users asynchronously via a Celery task.
    • Notifies the admin that emails have been successfully scheduled for
      the next day.

    This improves automation and ensures scheduled mass email dispatches run
    efficiently.

  • massEmail: update mass email models (#5608)

    Added support for scheduling mass emails with a configurable frequency
    and introduced a constraint to prevent duplicate email records for the
    same user in a given email job.

    • Added frequency (integer) and live (boolean) fields to
      MassEmailConfig to allow configuring email sending behavior.
    • Ensured frequency defaults to -1 and live defaults to False.
    • These fields are not displayed in the admin panel.
    • Enforced a uniqueness constraint on (email_job, user) in
      MassEmailRecord to prevent duplicate email records for the same user
      in a given email job.
  • massEmail: create/register celery tasks mass emails (#5574)

    Celery task and admin action to send emails for mass email
    configurations asynchronously by user request on the admin screen.

    The mass email configs admin action "Send emails" triggers a celery task
    for each email config that creates or uses an existing mass email job
    that is associated to a set of mass email records. The records are
    linked to a user, they start with enqueued status and when they are
    processed by the celery task, their status is set to sent or failed.
    This is work in progress, future PRs will enable more functionality.

  • massEmails: query for users approaching submission limits (#5572)

    Query for users who are approaching their maximum submission limit.

    Create queries for users who are over 90% and 100%, respectively, of
    their submission limit. These queries will be accessible by mass email
    jobs.

  • massEmails: Query for users reaching nlp usage limits (#5633)

    Create the query for retrieving users who are >90% and >100% of their
    nlp usage limits.

    The query returns a list of user uids. It will be used as part of a
    MassEmailConfig. Emails that use that query will be sent to the list of
    users generated by it. The usage calculated is the same as what is shown
    on the Usage page.

  • massEmails: create celery task for generating daily user lists (#5640)

    Added a new task to generate and enqueue MassEmailRecords while
    tracking processed configurations using caching.

    This update introduces a task that processes active MassEmailConfig
    entries, gathering all configs with date_created < today and
    live==True, ensuring emails are only sent to eligible users based on
    predefined rules. The task:

    • Caches processed email configs to avoid duplicate processing.
    • If a cache object for today's emails doesn't already exist, it creates
      a new cache object <todays_date>_emails as an empty set, representing
      emails for which today's user list has already been generated.
    • Skips configs with existing enqueued or sent records.
    • Filters out users who have received the same email within the
      configured frequency.
    • Handles duplicate entry errors to ensure robustness.

    This prevents redundant emails and improves the reliability of the email
    queuing system.

  • massEmails: condense send interval on dev machines (#5657)

    Adds a test setting that condenses the mass email send job to treat 15
    minutes as a day and run every 5 minutes. Only for use on testing/dev
    machines.

  • massEmails: Celery task for sending emails (#5659)

    Adds a new task to sync with the recurring emails feature. For now this
    task is not enabled but it will replace the old task in the near future.

  • massEmails: add 80% queries (#5743)

    Adds new queries for mass emails for finding users who are between 80%
    and 90% of their storage and submission limits.

  • massEmails: add test query (#5779)

    Add the ability to email a Constance-defined selection of users for
    easier testing of mass emails.

  • massEmails: prioritize recurring sends over one-time (#5760)

    Allocates send capacity to recurring emails first, then allocates
    whatever is left to the one-time emails.

    Previously, send capacity was allotted to each email E by calculating (#
    of enqueued E records/total enqueued records)*total send capacity. In
    order to ensure recurring emails go out regardless of any large one-time
    sends that may be pending, we are dividing emails into "one-time" and
    "recurring" categories. For a recurring email R, the send capacity is
    now (# of enqueued R records/total enqueued recurring records)*total
    send capacity. After the send capacity for each recurring email is
    calculated, any remaining capacity is allocated among the one-time sends
    using the same calculation.

  • massEmails: enable recurring sends (#5796)

    Turn on new process for recurring mass emails.

    Allows the creation of regularly recurring emails for certain
    conditions. Instead of just creating email configs and sending them the
    next day with 'Add to daily send,' superusers specify a frequency for
    how often a given individual should receive the email. One time sends
    are still possible with a frequency of -1. Once a recurring email is
    added to the daily send, beginning the next day, users matching the
    query will be calculated every day and emails sent, filtering out those
    who received the same email too recently.

  • massEmails: adapt timeouts for condensed send mode (#5821)

    Lower the timeouts for mass email sends when in test mode.

  • massEmails: allow multiple sends of one-time emails (#5831)

    Allow one-off emails to be resent if they have completely finished
    sending.

  • massEmails: preview query results (#5841)

    Allow users to download a list of expected recipients of a mass email
    based on the query used.

    Creates ExportJobs for MassEmailConfigs that will have details about the
    expected recipients for the emails. Once requested, the user will
    receive an email when the job is done and be able to download the list
    from Django admin.

  • massEmails: download a CSV of recipients from the last 30 days (#5843)

    Allow superusers to download a report of recipients of all emails for
    the past 30 days.

  • nlp: handle deleted audiofile (#5612)

    In NLP, display deleted audiofiles as deleted.

  • nlp: disable ASR for deleted attachment (#5629)

    Creating automated transcript or translation is no longer possible after
    attachment is deleted.

  • nlp: processing view button conditional rendering (#5631)

    Hide "Open" button in Data Table for audio question responses that now
    have deleted attachment and no NLP features in use.

  • permissions: use delete-all permissions endpoint on frontend (#5543)

    The "Remove shared project" button and the "Delete" button in the
    sharing modal were not fully removing users' access, so they have been
    updated to completely remove all their permissions and ensure they are
    fully removed from the project.

  • projectHistoryLogs: add project owner to submission logs (#5645)

    Add asset owner to project history logs for submission-related actions.

  • projectHistoryLogs: add owner to deployment logs (#5647)

    Add the project owner to all project history logs dealing with
    deployment/archiving.

  • projectHistoryLogs: add owner to permission logs (#5699)

    Add project owner to project history logs that record changes to project
    permissions.

  • projectHistoryLogs: add project owner to related viewsets (#5706)

    Add project owner to project history logs for adding/removing media,
    exporting data, importing fields from other projects, and modifying REST
    services.

  • projectHistoryLogs: add owner to import and export logs (#5710)

    Add project owner to the project history logs for importing new forms
    and using the old export endpoint.

  • projectHistoryLogs: change code to codename for partial perms (#5715)

    Change the key from 'code' to 'codename' for consistency when creating
    PH logs for partial permission changes.

  • projectHistoryLogs: add project owner to remaining logs (#5804)

    Add project owner to transfer, QA data update, name change, and settings
    update project history logs.

  • projectViews: handle last edited column in UI (#6094)

    New column "Last edited" that displays username of user that has last
    edited given project. It will appear by default for My Projects, Custom
    Project Views, and My Org Projects View, allowing filtering and ordering
    by it.

  • releases: auto-generate changelog with git-cliff (eee3b36)

  • removingAttachments: use API instead of mocks (#5765)

    Remove mock code that was operating single attachment deletion button
    and bulk attachment deletion modal. Use ready APIs instead.

  • trashBin: add object owner filters to ProjectTrash and AttachmentTrash admin views (#5884)

    Admins can now filter trashed projects and attachments by the object
    owner, making it easier to locate and manage soft-deleted content.

    This update improves the admin interface for the trash bin by adding
    filters for project and attachment owners. When viewing trashed projects
    or attachments, admins can now narrow down the list based on the user
    who originally owned them.
    This enhancement improves usability, especially on instances with large
    volumes of deleted items, by making it faster and easier to find
    relevant records.

  • usageLimits: Replace useExceedingLimits (#5921)

    This PR

    • Includes usage information in useServiceUsage hook
    • Removes the useExceedingLimits hook and replaces its current usage
      by useServiceUsage's new information.
  • usageLimits: handle project ownership transfer limits (#5931)

    This PR blocks the transfer project ownership button if either
    submission or storage limits are exceeded.

  • usageLimits: Add limit banners to new pages (#5937)

    This PR adds the service limits banner to the following views:

    • Project summary
    • Project form
    • Project data
    • Project settings (’general’ sub-section)
  • user: include uid in /me endpoint (#5922)

    Add user uid to the current user endpoint.

Bug Fixes (62)
  • accessLogs: button spacing (#5609)

  • accountSettings: ensure correct help article URL for non-KoboToolbox instances (#5969)

    The "Learn more about these changes here" link in account settings is
    broken on KoboToolbox instances using a custom support URL

  • activityLogs: export button correctly passes no payload (#6045)

  • attachment: avoid blocking migration (#6133)

    Use SKIP_HEAVY_MIGRATIONS flag to alter logger_attachment table to
    drop NULL on specific fields

  • attachmentStorage: fix UserProfile storage counter to exclude soft-deleted XForms (#5687)

    Updates the management command to ensure that deleted projects (forms in
    the trash) are no longer counted toward the user profile's attachment
    storage usage.

    Previously, when a user soft-deleted a project, the media files
    associated with that form continued to be included in the UserProfile's
    attachment_storage_bytes counter, which is used to track total storage
    usage per account. This led to inconsistencies between the backend
    storage counter and what users saw in the /service_usage/ page, which
    already excluded deleted forms.

    This fix updates the update_attachment_storage_bytes management
    command to exclude soft-deleted forms (pending_delete=True) from the
    total storage calculation at the user profile level. Individual XForm
    storage totals are still updated, even for trashed forms, so they remain
    accurate if restored.

  • attachments: display attachment dropdown additionally to media in single submission modal (#5672)

    Fixes bug where attachment dropdown did not display beside their
    respective attachments.

  • attachments: fix import order (#5694)

  • attachments: fix user profile counter bug on attachment deletion after project transfer (#5756)

    Fixes a bug where storage counter on UserProfile was not updated
    correctly when deleting or restoring attachments after transferring a
    project to a new owner.

    There was a bug affecting attachment storage usage tracking: when a
    project was transferred to a new owner, the storage counter on the new
    owner's UserProfile object was not updated correctly if attachments
    were later trashed or restored.

    This PR updates the attachment trash logic to ensure that ownership is
    correctly reassigned before applying any storage counter updates. It
    also adds a unit test to verify the correctness of counter updates
    during transfer, trash, and restore operations.

  • attachments: hide the "Delete media files only" button from any permission except change_submissions or higher (#5838)

    Those with delete_submissions permission for submissions now cannot
    see the "Delete media files only" button in the bulk options and those
    with change_submissions permission are now able to see the button.

  • attachments: fix attachment deletion for edited submissions (#5844)

    Fixes an issue where attachments could not be deleted from edited
    submissions due to incorrect handling of submission UUIDs.

    This fix updates the attachment deletion logic to correctly filter using
    root_uuid for edited submissions. Previously, the API returned a 400
    error when edited submissions were included, treating their root UUIDs
    as invalid. The updated query ensures attachments from both original and
    edited submissions can be deleted successfully.

  • attachments: clear usage cache and remove limit counter after attachment cleanup (#6139)

    Ensure users are unblocked immediately after excess attachments are
    auto-deleted by clearing cached usage and removing the storage limit
    counter right away.

    Previously, when celery task automatically deleted older attachments for
    users exceeding their storage quota, the system did not immediately
    clear the usage cache or remove the storage limit counter. As a result,
    users could remain blocked for up to 15 minutes (due to caching) or
    appear over quota until the next daily counter update.

    With this fix, the auto_delete_excess_attachments task now clears the
    user’s usage cache and updates or removes the storage limit counter
    immediately after cleanup, ensuring quota status reflects the changes
    right away.

  • avatar: avoid initials shrinking (#5621)

    In some cases avatar initials were shrinking, causing them to not be a
    circle anymore.

  • billing: fix missing interval dropdown for "File Storage" add-on in some languages (#5797)

    Fixes the issue where if Kobo is in some non-english languages, the time
    interval dropdown for choosing month/year doesn't appear.

  • exports: display export language labels instead of code (#6116)

    Fixes issue where the default "language" is being shown as _default
    rather than the dropdown label "Labels". Now we always display the label
    and fall back to the language code if this somehow fails.

  • formBuilder: Question Matrix settings crash (#5646)

    Opening settings for Question Matrix is possible again.

  • formatting: biome formatting (#5968)

    Fixing an error in formatting that somehow got through the process

  • formbuilder: Improve accuracy in language for validation & skip logic description (#5919)

    Changed the wording of

    • "Previous responses should match all of these conditions"
    • "Previous responses should match any of these conditions"

    in the validation criteria and skip logic dropdowns to:

    • All of these conditions must be met
    • At least one of these conditions must be met

    For improved context and accuracy to the actual logic

  • formbuilder: remove double prompt when existing formbuilder with changes (#5800)

    Formbuilder only uses one method of warning user when leaving with
    changes made.

  • frontend: unable to access "Data" and "Settings" pages (#5571)

  • frontend: use kobo default line-height again (#5602)

    Revert broken default line-height for various elements.

  • frontend: don't install playwright in dockerfile (#5776)

    Reverse an accidental increase in docker file by 1.5GB.

  • frontend: printing styles (#5879)

    Fix issues with styles while printing.

    Now all Project related routes should be printable without data being
    cropped out. The only issue still prevaling is printing horizontally
    huge tables from Project → Data. In such case it is advised to change
    the amount of columns being displayed before printing and using
    landscape orientation of paper.

  • frontend: type error in useServiceUsageQuery.ts (#6081)

  • kpi: Support geo+json mime type for form media (#5601)

    Support geojson mime type

  • languageSelector: force refresh languages list after clearing the searchbox (#5681)

  • longRunningMigration: do not block fresh installs with system checks (#5772)

    Prevented system checks from raising errors on fresh installs when
    long-running migrations haven't been executed.

    System checks for long-running migrations were incorrectly raising
    errors even on fresh installs, where no data exists and migrations are
    not required. This caused unnecessary blocking during setup or
    deployment of a new environment.

  • massEmails: Use default plan name in templates (#5628)

    Use the default plan name provided by the stripe product model in the
    mas emails templates. A new utility function was added to the stripe app
    in order to provide this name easily.

  • massEmails: only import product if stripe enabled (#5632)

    Fix a build failure that occurs when Stripe isn't enabled.

  • massEmails: fix plan name (#5639)

    Use the correct plan name in mass emails.

  • massEmails: throttle mass email send (#5641)

    Limit the rate of emails sent per second to avoid hitting SES limits.

  • massEmails: handle organizations with no owners (#5656)

    Fixes an error that was causing 500s when attempting to trigger mass
    emails with usage queries.

    Removes organizations with no owner when determining which organizations
    may be over their limits since there is no one to email.

  • massEmails: read MASS_EMAIL_CONDENSE_SEND from env (#5691)

  • massEmails: handle orgs with no owner (#5725)

    Fix a 500 error that occurs when attempting to add a usage-dependent
    email to the daily send.

  • massEmails: fix 500 from usage queries (#5740)

    Fixes a 500 error when trying to add usage-query-dependent emails to the
    daily send.

  • massEmails: make get_plan_name ordering more deterministic (#5811)

    Ensure get_plan_name() util function returns plans before addons.

  • massEmails: no error on empty test user list (#5830)

    Do not attempt to send emails when the test email configuration is
    empty.

    Fixes an error wherein if the MASS_EMAIL_TEST_EMAILS configuration in
    Constance was empty, any email using the test_users query would throw
    errors, which could kill the whole process and prevent other emails from
    sending.

  • massEmails: make sure sends are not interrupted when configs change (#5810)

    Ensure emails continue to be sent throughout the day even when
    configurations are changed before the send is over.

    Fixes a bug wherein if sending all mass emails took more than an hour
    (either because of volume or because the process was killed) and new
    configurations were added before all emails had been sent, emails would
    stop sending for the rest of the day. The same thing would happen if an
    existing configuration was removed from the daily send list.

  • massEmails: convert dates to strings for xslx (#5847)

    Fix exporting mass email records to XSLX format.

  • massEmails: rename export action (#5849)

    Rename the export action from 'Export with celery' to 'Export recipient
    list' for clarity

  • nlp: handle old qpaths that point at deleted questions (#5754)

    Allow users to access their project data even when older analysis
    features refer to questions that have since been removed from the
    project.

  • organizations: skip stripe-related tests in no-stripe testing (#5732)

    Uses pytest.mark.skipif to skip stripe-related MMO tests when testing
    without Stripe.

  • permissions: hide discover_asset in the UI (#5737)

  • permissions: sharing modal correctly reflects asset type in permissions labels (#5736)

    Fixes a bug where the sharing modals will always display "form" or
    "project" regardless of asset type. Now the modal correctly references
    the asset being shared.

  • permissions: make sharing form request permissions upon opening (#5785)

    Fixes issue where wharing modal did not update permissions until a hard
    refresh.

  • permissions: uncheckable permission checkboxes (#5798)

    After selecting "Manage collection" permission in Library's Sharing
    Form, User can unselect all checkboxes (one at a time) beck into initial
    empty state.

  • permissions: update team members row with changes from sharing modal (#5791)

    When making changes to a project's permissions, if a user is added or
    removed it will reflect immidately in the "Team members" component in
    the "Summary" page.

  • permissions: improve UI in Sharing Form (#6056)

    Fix minor issue of "Act on submissions only from specific users (…)" not
    mentioning all the users from permissions.

  • projectHistoryLogs: remove query count checks from tests (#5674)

  • projects: "fields" selector uses same default display list as my projects route (#5801)

    Fixes issue where the "fields" selector would show "Countries" checked
    but the columns don't display it. Now the "fields" selector leaves
    "Countries" unchecked by default (to match the columns).

  • removingAttachments: add missing trailing slash in attachment bulk url (#5842)

  • removingAttachments: don't disable automatic translation button (#5858)

    Don't disable automatic translation for deleted attachments.

  • storybook: handle the new '#' path alias (#5583)

  • storybook: make dates consistent in UniversalTable tests (#5789)

  • stripe: only import Product when stripe is enabled (#5579)

    Return all limits as infinite when Stripe is not enabled.

    Fixes a bug that was preventing kpi from starting up when Stripe was not
    enabled.

  • stripe: annotate methods requiring stripe functionality (#5649)

    Fixes a bug that was preventing startup when stripe was disabled.

  • submissions: prevent the management command to crash when fixing rootUuid conflicts (#5971)

    Fixes a crash in the clean_duplicated_submissions_root_uuid command
    when processing submissions without a meta/rootUuid.

    This PR resolves an issue where the
    clean_duplicated_submissions_root_uuid management command would crash
    if it encountered a submission that had never been edited and whose XML
    lacked a meta/rootUuid node.

  • tests: Use reverse() instead of hardcoded url in unit tests (#5822)

    Replaced hardcoded URL in unit test with reverse() to ensure better
    maintainability and consistency with URL routing.

    This change updates a test case to use Django's reverse() function
    instead of hardcoding the API URL.

  • linter (3456eb7)

  • fix merge conflicts (4adfd1e)

  • fix wrong merge with #5662 (0dca120)

  • (reports): Fix infinite reloading failed report (#5755)

    This PR fixes a bug where under a certain condition loading a report
    containing a Group By configured fails and the app tries to reload it
    infinitely.

  • fix previous bad merge (8423be8)

Performance (1)
  • attachments: Improve bulk update attachments storage counters (#5835)

    Improved performance of storage counter updates when bulk trashing or
    restoring attachments and projects.

    • Refactors the logic for updating attachment storage counters on user
      profiles and xforms during bulk trash or restore operations.
    • With this PR, when a XForm is trashed or restored, only the
      UserProfile storage counter will be updated. The XForm’s storage value
      remains unchanged because trashed forms are excluded from queries and
      considered deleted along with their attachments.
Continous Integration (47)
  • backend: run biome for static files too (#6084)

  • biome: fix filter syntax (#6114)

  • chromatic: run tests daily (#5781)

    Once a day run Chromatic visual tests to verify we don't have unwanted
    changes.

  • chromatic: fix failing nightly builds (#5846)

  • dependabot: run nighly with automerge (#5929)

    Make dependabot create daily bulk PRs with npm and github-actions
    dependencies updates. PRs for patch and minor versions will get
    auto-merged after successful tests.

  • dependabot: automerge using squash (#5947)

  • dependabot: fix permissions issues for major update review request (#5957)

  • dependabot: disable automerging PRs (#5960)

  • dependabot: drop major version handling and only log automerge (#6002)

  • dependabot: drop dependabot prs limit (#6007)

  • dependabot: dependabot workflow typo fix (#6009)

  • dependabot: auto merge successful minor and patch deps prs (#6010)

  • dependabot: change dependabot PR limit for github-actions (#6011)

  • dependabot: make it group all types of minor/patch updates (#6022)

  • dependabot: fix duplicated wrong file (#6125)

  • frontend: build storybook (#5584)

  • frontend: skip storybook until fixing flaky tests (#6113)

  • gitlab: fix storybook deployment (#5786)

  • gitlab: update node version for Storybook deployment (#5787)

  • pr: remove forgotten debug (#5900)

  • pr: remove forgotten debug (#5900)

  • pr: typo in filters (#6046)

  • releases: use ISO week number (#5898)

  • releases: typo at secrets prop (#5906)

  • releases: typo at secrets prop (#5906)

  • releases: fix bash syntax typo (#5910)

  • releases: fix deploy to beta (#5911)

  • releases: auto-merge release branches (#5908)

  • releases: fix script execution perms (#5923)

  • releases: fix github action typo (#5924)

  • releases: use Github App for cascading merges (#5973)

  • releases: use Github App (#5979)

  • releases: delete previous unreleased branch (#5974)

  • releases: fix debugging leftovers (#5989)

  • releases: fix missing env variables (#6005)

  • releases: simplify and improve !failure() ifs (#6054)

  • storybook: install playwright and run tests (#5723)

    Run Storybook tests on CI

  • format github action alert title (#5594)

  • run ci steps locally (#5593)

  • pytest with stripe disabled (#5650)

  • upgrade to ubuntu-24.04 for all jobs (#5881)

  • extract a reusable zulip workflow (#5890)

  • semi-automatic continuous bi-weekly releases (#5877)

  • use ISO week number everywhere (#5899)

  • use ISO week number everywhere (#5899)

  • update job filters (#5894)

  • share playwright cache across branches (#5895)

Build & Dependencies (3)
  • deps: bump react-hot-toast from 2.4.1 to 2.5.2 (#6008)
  • deps-dev: bump undici from 6.21.0 to 6.21.3 (#5773)
  • storybook: upgrade to v9 (#5750)
Testing (8)
  • backend: Add deployment__uuid to asset API (246a8ab)

  • projectHistory: FormActivity stories (#5720)

  • projectHistoryLogs: check for project owner on submission logs (#5698)

  • projectHistoryLogs: backfill some unit tests (#5803)

  • storybook: ActionIcon click (#5669)

  • storybook: languageSelector search (#5670)

    Add Storybook test to verify that searching LanguageSelector works
    correctly.

  • storybook: enable a11y tests (#5734)

    Run a11y tests on CI

  • add query-counting tests to critical endpoints (#5696)

Security (26)
  • deps: bump mobx-react from 7.6.0 to 9.2.0 (#5949)
  • deps: bump react-textarea-autosize from 8.5.6 to 8.5.9 (#5952)
  • deps: bump @tanstack/react-query from 5.50.1 to 5.83.0 (#5953)
  • deps: bump backbone from 1.6.0 to 1.6.1 (#6021)
  • deps: bump react-select from 5.8.0 to 5.10.2 (#6078)
  • deps-dev: bump webpack-dev-middleware from 6.1.3 to 7.4.2 (#5950)
  • deps-dev: bump @storybook/addon-docs from 9.0.0-beta.11 to 9.0.17 (#5951)
  • deps-dev: bump webpack-cli from 5.1.4 to 6.0.1 (#5942)
  • deps-dev: bump jest-environment-jsdom from 29.7.0 to 30.0.5 (#5954)
  • deps-dev: bump @storybook/react-webpack5 from 9.0.0-beta.11 to 9.0.18 (#5972)
  • deps-dev: bump fork-ts-checker-webpack-plugin from 9.0.2 to 9.1.0 (#5956)
  • deps-dev: bump eslint-plugin-storybook from 9.0.0-beta.11 to 9.0.18 (#6013)
  • deps-dev: bump cheerio from 1.0.0 to 1.1.2 (#6012)
  • deps-dev: bump concurrently from 9.1.2 to 9.2.0 (#6015)
  • deps-dev: bump @types/jquery from 3.5.30 to 3.5.32 (#6019)
  • deps-dev: bump eslint-plugin-import from 2.31.0 to 2.32.0 (#6020)
  • deps-dev: bump @types/leaflet from 1.9.16 to 1.9.20 (#6025)
  • deps-dev: bump brace-expansion from 1.1.11 to 1.1.12 (#6063)
  • deps-dev: bump @storybook/test-runner from 0.23.0-next.2 to 0.24.0-next.1 (#6070)
  • deps-dev: bump typescript-plugin-css-modules from 5.1.0 to 5.2.0 (#6071)
  • deps-dev: bump @testing-library/react from 16.0.1 to 16.3.0 (#6072)
  • deps-dev: bump @vusion/webfonts-generator from 0.7.3 to 0.8.0 (#6075)
  • deps-dev: bump msw from 2.7.3 to 2.10.4 (#6076)
  • deps-dev: bump @testing-library/jest-dom from 6.5.0 to 6.6.4 (#6074)
  • deps-dev: bump @faker-js/faker from 9.8.0 to 9.9.0 (#6079)
  • deps-dev: bump eslint-plugin-react from 7.37.3 to 7.37.5 (#6086)
Refactor (40)
  • UniversalTable: use hardcoded mock data in stories (#5782)

  • attachments: simplify using arrow functions (#5597)

  • attachments: make Attachment inherit from AbstractTimeStampedModel (#5614)

  • attachments: enforce non-nullable uid on Attachment model (#5615)

  • attachments: eliminate null values in the Attachment model , 1681 (#5636)

    Disallow null or blank values for new Attachment model fields.

  • attachments: expose uid instead of pk in the data API endpoint (#5642)

  • attachments: match FE to the updated response for submission attachments (#5671)

  • attachments: bulk and single attachment deletion endpoints (#5733)

  • attachments: use DRF-compliant error format for field-specific validation (#5829)

    Standardizes error responses to return a list of field-specific
    validation errors using DRF format.

    This PR refactors the bulk deletion attachment validation logic to
    comply with Django REST Framework’s error response format.

    Instead of returning a generic error or a flat structure, the API now
    returns a dictionary where each field maps to a list of corresponding
    error messages.

  • billing: remove unused UpdateBadge component (#5586)

    Removes UpdateBadge component file, as it is unused and the necessary UI
    is already implemented.

  • billing: standardize usage naming (#5748)

    Switches to standardized choices for referring to usage types in backend
    code.

  • billing: add organization service usage tests (#5806)

    Adds backend unit tests for the organization service usage endpoint.

  • billing: split stripe utils into several files (#5848)

    Refactors stripe utils to reduce file size and risk of circular
    dependencies.

  • coffeeScript: fix DS0002 in mv.skipLogicHelpers.coffee (#5982)

  • coffeeScript: fix DS0002 in view.row.coffee (#5983)

  • coffeeScript: fix DS0002 in view.widgets.coffee (#5984)

  • coffeeScript: fix DS0002 in view.surveyApp.coffee (#5985)

  • coffeeScript: fix DS0002 in model.surveyFragment.coffee (#5986)

  • coffeeScript: fix DS0002 in view.rowDetail.SkipLogic.coffee (#5987)

  • coffeeScript: fix DS0002 in model.rowDetails.skipLogic.coffee (#5988)

  • formbuilder: bulk-decaffeinate on single file (#5897)

    Part of the decaffeination process of the formbuilder. This is the "base
    case" of converting a single file from .coffee to .js.

  • formbuilder: remove implicit returns for model.row.coffee (#6036)

    Fixes DS102 from decaffeinate process

  • formbuilder: remove implicit returns in some coffeescript files (#6103)

    Part of the decaffeination process. Removes implicit returns in many
    coffeescript files for easier conversion.

  • frontend: rename FormActivity files (#5702)

  • frontend: query client mock decorator (#5713)

  • frontend: update endpoints list file (#5714)

  • frontend: move endpoints mocks to new directory (#5719)

  • frontend: consistent mock files naming (#5792)

  • map: TypeScriptize Project Data Map files (#5558)

  • massEmails: refactor usage queries to make nlp easier (#5610)

    Developer-only changes.

  • removingAttachments: better success and error handling in bulkDeleteAttachments (#5825)

  • select: replace and remove KoboSelect3 (#5679)

  • storybook: migrate to v8 and update obsolete stories (#5567)

  • storybook: reorganize stories (#5661)

  • storybook: migrate button tests (#5676)

  • submissionUtils: migrate code to TypeScript (#5652)

  • submissionUtils: type improvements (#5654)

  • trashBin: standardize status toggling via abstract base method (#5635)

    Refactored toggle_<model>_statuses into an abstract method on the base
    trash bin class and prepares the codebase for managing attachments in
    the trash bin in future work.

    As part of upcoming work to support attachments in the trash bin system,
    the toggle_<model>_statuses method has been moved to the base class as
    an abstract method. This change standardizes the implementation pattern
    for all models handled by the trash bin system.

  • trashBin: group tasks by object type for better organization (#5660)

    Split trash bin tasks into separate files based on the object they
    operate on.

    Previously, all trash bin–related tasks were located in a single
    tasks.py file, regardless of the object type (e.g., projects, accounts).
    This refactor organizes tasks by grouping them into separate files
    according to the object they manage.

    This restructuring sets the stage for integrating attachment-related
    tasks cleanly and consistently.

  • trashBin: unify status toggling inside move_to_trash() and put_back() for all trash models (#5747)

    Simplified the trash process for attachments, projects, and users by
    consolidating the status toggle into a single step.

    Previously, moving items (attachments, projects, or user accounts) to
    the trash required calling both toggle_statuses() and
    move_to_trash() separately. This update refactors move_to_trash() to
    internally handle the status change, unifying the logic across all trash
    models.

Styling (4)
  • biome: remove unused import (1eaa556)
  • frontend: enable passing Biome linter rules (#5573)
  • frontend: enable auto-fixable Biome linter rules (#5578)
  • frontend: align coffeelint among ci_locally and CI (c0a72d9)
Chores (27)
  • cypress: remove code (#5735)

    Remove cypress related code, as we are replacing it with storybook
    tests.

  • deps: bump the minor-and-patch group with 4 updates (#5948)

  • deps: bump the actions-deps group across 1 directory with 4 updates (#6003)

  • deps: bump http-proxy-middleware from 2.0.6 to 2.0.9 (#5680)

  • deps: update @sentry/react and minimum node version (#6030)

  • deps: bum react-cookie from 4.0.1 to 8.0.1 (#6065)

  • deps: bump pretty-bytes from 6.1.1 to 7.0.0 (#6024)

  • deps: bump patch-package from 6.5.1 to 8.0.0 (#6066)

  • deps: update dependabot ignore list (#6068)

  • deps: update autogenerated mockServiceWorker file (#6117)

  • deps-dev: bump webpack-cli from 5.1.4 to 6.0.1 (#6014)

  • deps-dev: bump @jest/create-cache-key-function from 29.7.0 to 30.0.5 (#6018)

  • deps-dev: bump @tanstack/react-query-devtools from 5.50.1 to 5.84.1 (#6073)

  • envStore: remove unused TS interface props (#5682)

  • frontend: remove unused getSubmissionsQuery function (#5603)

  • frontend: put back removed storybook script (#5638)

  • frontend: .git-blame-ignore-revs for Biome (#5764)

  • frontend: change package.json versions to use carets for all deps (#6061)

  • frontend: bump webpack-dev-server to 5.2.2 (#6062)

  • frontend: delete unused source-map-loader (#6064)

  • release: manually trigger deployment to staging (b154791)

  • removingAttachments: remove feature flag (#5824)

  • updateHelmRepo: Updating helm repo to use ghcr (#5853)

  • vscode: exclude compiled js from search (#5648)

  • improve long running migrations system checks message (#5665)

    Enhanced the clarity and usefulness of system check messages for
    long-running migrations.

    This update improves the system check messages related to long-running
    migrations, making them more descriptive and actionable. The goal is to
    help developers and sysadmins quickly understand which migrations are
    incomplete and what steps may be required to resolve them.

  • install playwright deps within ci_locally.sh (#5799)

  • update PR template (#5834)

Revert (7)
  • revert "chore(deps-dev): bump webpack-cli from 5.1.4 to 6.0.1" (#5961)
  • revert "chore(deps): bump mobx-react from 7.6.0 to 9.2.0" (#5962)
  • revert "chore(deps-dev): bump jest-environment-jsdom from 29.7.0 to 30.0.5" (#5963)
  • revert "chore(deps-dev): bump webpack-dev-middleware from 6.1.3 to 7.4.2" (#5964)
  • revert "chore(deps): bump @tanstack/react-query from 5.50.1 to 5.83.0" (#5966)
  • revert "test(backend): Add deployment__uuid to asset API" (e78ce0b)
  • revert "feat(billing): disable/enable usage limit enforcement on frontend " (#6038)
Other (6)
  • Fix lint errors, tests again (6f51655)
  • Fix bad merge (e9ea26b)
  • Bad merge and conflicts (e86b238)
  • [INFRA-144] Updating nonprod envs to use new helm chart pattern (#5878)
  • js (9e2f559)
  • Update image, migrate to rules syntax, and fix public-beta deploy trigger (#6001)

Full Changelog: https://github.com/kobotoolbox/kpi/compare/2.025.29c..2.025.34

Don't miss a new kpi release

NewReleases is sending notifications on new releases.