{"id":41342983,"url":"https://github.com/rodekruis/qfa-api","last_synced_at":"2026-01-23T06:53:08.071Z","repository":{"id":277564815,"uuid":"932813331","full_name":"rodekruis/qfa-api","owner":"rodekruis","description":"Qualitative Feedback Analysis API","archived":false,"fork":false,"pushed_at":"2025-07-14T14:04:42.000Z","size":310,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-07-14T16:05:31.245Z","etag":null,"topics":["cea"],"latest_commit_sha":null,"homepage":"https://qfa-api.azurewebsites.net","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rodekruis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-02-14T15:11:59.000Z","updated_at":"2025-07-14T14:04:45.000Z","dependencies_parsed_at":"2025-03-12T11:24:16.675Z","dependency_job_id":"a3a8e808-465e-489e-b9a5-303f8dbfb1fa","html_url":"https://github.com/rodekruis/qfa-api","commit_stats":null,"previous_names":["rodekruis/qfa-api"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rodekruis/qfa-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodekruis%2Fqfa-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodekruis%2Fqfa-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodekruis%2Fqfa-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodekruis%2Fqfa-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rodekruis","download_url":"https://codeload.github.com/rodekruis/qfa-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rodekruis%2Fqfa-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28682263,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T05:48:07.525Z","status":"ssl_error","status_checked_at":"2026-01-23T05:48:07.129Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cea"],"created_at":"2026-01-23T06:53:07.473Z","updated_at":"2026-01-23T06:53:08.061Z","avatar_url":"https://github.com/rodekruis.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# qfa-api\n\nQualitative Feedback Analysis (QFA) API.\n\n## Description\n\nSynopsis: a [dockerized](https://www.docker.com/) [python](https://www.python.org/) API to analyze qualitative feedback.\n\nPowered by [open-source language models](https://huggingface.co/). Uses [Poetry](https://python-poetry.org/) for dependency management.\n\n## Setup classification with Kobo\n\n1. Prepare a kobo form as follows:\n   * add one question of type `text`, whose content will be classified. Example: `feedback`.\n   * add up to three cascading select questions of type `select_one` which will determine how the text will be classified. Example: `type`, `category`, `code`.\n   * fill in the possible choices in the `choices` sheet of the form exactly as explained [here](https://support.kobotoolbox.org/cascading_select.html#adding-cascading-question-sets-in-xlsform-option-1); these choices will be used as labels for the classification.\n   * upload and deploy the form.\n\n\u003e [!TIP]\n\u003e The text will be classified according to the `labels` of the choices. A few tips to improve the accuracy of the classification:\n\u003e  * Phrase the label as if explaining to a 10-year-old. This helps the classification model grasp the core idea without unnecessary complexity.\n\u003e  * The model can**not** possibly know all humanitarian acronyms, so make sure to spell them out. Example: use `Water, Sanitation and Hygiene` instead of `WASH`, or `Red Crescent` instead of `RC`.\n\u003e * Avoid using ambiguous labels and be specific. Example: use `distribution of non-food items` and `distribution of cash` instead of `relief` and `cash`.\n\u003e * The more choices you provide, the less accurate the classification will be. Keep the number of choices as low as possible.\n\u003e * Try to use three synonyms when defining a label, for extra clarity. Example: use `misinformation, fake news and made-up stories` instead of only `misinformation`.\n\n2. [Register a new Kobo REST Service](https://support.kobotoolbox.org/rest_services.html) and configure it as follows:\n   * insert as `Endpoint URL`\n    ```\n    https://qfa-api.azurewebsites.net/classify-text\n    ```\n   * add the following headers under `Custom HTTP Headers`:\n       * under `Name` insert `API-KEY` and under `Value` insert the QFA API key (see Bitwarden).\n       * under `Name` insert `source-text` and under `Value` insert the name of the text question to be classified. Example: `feedback`.\n       * under `Name` insert `source-name` and under `Value` insert `kobo`.\n       * under `Name` insert `source-origin` and under `Value` insert the ID of the form (see [where to find it](https://im.unhcr.org/kobosupport/)).\n       * under `Name` insert `source-authorization` and under `Value` insert your Kobo token (see [how to get one](https://support.kobotoolbox.org/api.html#getting-your-api-token)).\n       * under `Name` insert `source-level1` and under `Value` insert the name of the first of the cascading select questions. Example: `type`.\n       * under `Name` insert `source-level2` and under `Value` insert the name of the second of the cascading select questions. Example: `category`.\n       * under `Name` insert `source-level3` and under `Value` insert the name of the third of the cascading select questions. Example: `code`.\n       * [OPTIONAL] under `Name` insert `translate` and under `Value` insert `true`, if you are using a language other than English; this will translate the text to English before classifying it (the results will still be in the original language, as specified in the classification schema).\n\n_That's it_. Your submissions will be automatically classified in a few seconds. Happy qualitative feedback analysis!\n\n\n\u003cimg width=\"763\" alt=\"image\" src=\"https://github.com/user-attachments/assets/919ebe53-17f4-4c5a-8937-baeae32dc4ff\" /\u003e\n\n\n### Current limitations using QFA with Kobo forms\n1. It is not possible to use [repeating groups](https://support.kobotoolbox.org/group_repeat.html) in Kobo forms and repeatedly submit the classification request to QFA. If it is needed to copy/paste data from a previous form into another multiple forms, one could look at how to use [dynamic data attachments](https://support.kobotoolbox.org/dynamic_data_attachment.html) instead. \n2. It is not possible to edit an already submitted Kobo form and then re-submit with the goal of classifying again. Kobo does not allow the REST service to be triggered twice for the same submission.\n\n## Setup classification with EspoCRM\n\nPrerequisite: EspoCRM with [Advanced Pack](https://www.espocrm.com/extensions/advanced-pack/) installed.\n\n1. Prepare EspoCRM as follows:\n   * Create or select one entity which will be classified, e.g. `Feedback`.\n   * Create or select one field of type `Text`, whose content will be classified, e.g. `feedbackText`. Tip: enable `Audited` so that changes can be traced.\n   * Create or select up to three entities which will determine how the text will be classified, e.g. `Type`, `Category`, `Code`. The records' names will be used as labels for the classification.\n   * Link these three entities to the entity that will be classified, by creating a relationship of type `Many-to-One` for each of them.\n   * Create a new role with `Read` permissions over `Type`, `Category`, `Code`, and assign this role to a new API user.\n\n\u003e [!IMPORTANT]\n\u003e The classification schema is strictly hierarchical: each record of a low-level entity must be related to a record of\n\u003e a high-level entity, e.g. each record of `Category` must be related to a record of `Type`, and each record of\n\u003e `Code` must be related to a record of `Category`.\n\n2. Classification can then be performed automatically via Flowchart, using a Task with two Actions: \n   * `Send HTTP Request` to the QFA API\n```\nRequest Type\nPOST\n\nURL\nhttps://qfa-api.azurewebsites.net/classify-text\n\nHeaders\nAPI-KEY: \u003cQFA API key\u003e\nsource-name: espocrm\nsource-origin: \u003cyour EspoCRM instance URL\u003e\nsource-authorization: \u003cyour EspoCRM API key\u003e\nsource-level1: Type\nsource-level2: Category\nsource-level3: Code\n[OPTIONAL] translate: true\n\nPayload\n{\n    \"text\": \"{$feedbackText}\",\n}\n```\n\n   * `Execute Formula Script` to save results to \n```\ntypeId = json\\retrieve($_lastHttpResponseBody, 'TypeId');\ncategoryId = json\\retrieve($_lastHttpResponseBody, 'CategoryId');\ncodeId = json\\retrieve($_lastHttpResponseBody, 'CodeId');\n```\n\n\n\n\n## API Usage\n\nSee [the docs](https://qfa-api.azurewebsites.net/docs).\n\n## Configuration\n\n```sh\ncp example.env .env\n```\n\nand edit the provided [ENV-variables](./example.env) accordingly.\n\n### Run locally\n\n```sh\npip install poetry\npoetry install --no-root\nuvicorn main:app --reload\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodekruis%2Fqfa-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frodekruis%2Fqfa-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frodekruis%2Fqfa-api/lists"}