{"id":23626330,"url":"https://github.com/yetanalytics/project-persephone","last_synced_at":"2025-08-31T02:31:45.258Z","repository":{"id":41331534,"uuid":"195886225","full_name":"yetanalytics/project-persephone","owner":"yetanalytics","description":"Library for validating Statements against Profiles","archived":false,"fork":false,"pushed_at":"2024-01-24T16:58:09.000Z","size":3001,"stargazers_count":7,"open_issues_count":3,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-11-28T11:51:05.279Z","etag":null,"topics":["validation","xapi","xapi-profiles"],"latest_commit_sha":null,"homepage":"https://clojars.org/com.yetanalytics/project-persephone","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yetanalytics.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2019-07-08T21:06:50.000Z","updated_at":"2024-03-12T18:10:42.000Z","dependencies_parsed_at":"2024-01-08T22:44:59.836Z","dependency_job_id":null,"html_url":"https://github.com/yetanalytics/project-persephone","commit_stats":{"total_commits":437,"total_committers":5,"mean_commits":87.4,"dds":"0.020594965675057253","last_synced_commit":"e6856b38fb3c9d9dac88f8e79646f6ef22830457"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yetanalytics%2Fproject-persephone","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yetanalytics%2Fproject-persephone/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yetanalytics%2Fproject-persephone/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yetanalytics%2Fproject-persephone/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yetanalytics","download_url":"https://codeload.github.com/yetanalytics/project-persephone/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231548410,"owners_count":18393559,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["validation","xapi","xapi-profiles"],"created_at":"2024-12-27T22:52:57.754Z","updated_at":"2025-08-31T02:31:45.247Z","avatar_url":"https://github.com/yetanalytics.png","language":"Clojure","funding_links":[],"categories":[],"sub_categories":[],"readme":"# project-persephone\n\n\u003cimg src=\"logo/logo.svg\" alt=\"Persephone Logo\" width=\"325px\" align=\"left\" /\u003e\n\n[![CI](https://github.com/yetanalytics/project-persephone/actions/workflows/main.yml/badge.svg)](https://github.com/yetanalytics/project-persephone/actions/workflows/main.yml)\n\n_Only Persephone, daughter of Zeus and wife of Hades, could travel between the Underworld and the world of the living. Project Persephone is the liaison between our physical world and the world of the Semantic Web._\n\nA Clojure library for validating xAPI Statements against xAPI Profiles, featuring interactive CLI and webserver applications.\n\n## Index\n\n- [Library](doc/library.md): How to use the library/API functions\n- [CLI](doc/cli.md): How to run the `validate` and `match` commands\n- [Webserver](doc/server.md): How to start up and run a webserver\n\n## Installation\n\nAdd the following to the `:deps` map in your `deps.edn` file:\n```clojure\ncom.yetanalytics/project-persephone {:mvn/version \"0.9.1\"}\n```\n\nAlternatively, to run the CLI or server as an application, you can pull a Docker image from [DockerHub](https://hub.docker.com/repository/docker/yetanalytics/persephone):\n```\ndocker pull yetanalytics/persephone:latest\n```\n\n## How It Works \n\nPersephone performs two main tasks on [xAPI Statements](https://github.com/adlnet/xAPI-Spec/blob/master/xAPI-Data.md#20-statements): [Statement Template](https://github.com/adlnet/xapi-profiles/blob/master/xapi-profiles-structure.md#statment-templates) validation and [Pattern](https://github.com/adlnet/xapi-profiles/blob/master/xapi-profiles-structure.md#patterns) matching. The former checks if a Statement follows the specifications of one or more Statement Templates in xAPI Profiles, while the latter performs regex-like matching against Profile Patterns. Persephone does so following the [validation and matching guidelines](https://github.com/adlnet/xapi-profiles/blob/master/xapi-profiles-communication.md#20-algorithms) of the [xAPI Profile specification](https://github.com/adlnet/xapi-profiles).\n\nFor example, suppose that you have the following Statement:\n```json\n{\n  \"id\": \"00000000-4000-8000-0000-000000000000\",\n  \"timestamp\": \"2022-06-27T10:10:10.000Z\",\n  \"actor\": {\n    \"name\": \"My Name\",        \n    \"mbox\": \"mailto:foo@example.org\",\n    \"objectType\": \"Agent\"\n  },\n  \"verb\": {\n    \"id\": \"https://xapinet.org/xapi/yet/calibration/v1/concepts#did\",\n    \"display\": {\"en-US\": \"Did\"}\n  },\n  \"object\": {\n    \"id\": \"https://xapinet.org/xapi/yet/calibration/v1/concepts#activity-1\",\n    \"objectType\": \"Activity\",\n    \"definition\": {\n      \"name\": {\"en-US\": \"Activity 1\"},\n      \"description\": {\"en-US\": \"The first Activity\"}\n    }\n  },\n  \"context\": {\n    \"contextActivities\": {\n      \"category\": [\n        {\n          \"id\": \"https://xapinet.org/xapi/yet/calibration/v1\",\n       \t  \"objectType\": \"Activity\"\n        }\n      ]\n    }\n  }\n}\n```\n\nThe Statement will match against the following Statement Template:\n```json\n{\n  \"id\" : \"https://xapinet.org/xapi/yet/calibration/v1/templates#activity-1\",\n  \"inScheme\" : \"https://xapinet.org/xapi/yet/calibration/v1\",\n  \"prefLabel\" : {\n    \"en\" : \"Activity Template 1\"\n  },\n  \"definition\" : {\n    \"en\" : \"The statement template and rules associated with Activity 1 getting done.\"\n  },\n  \"type\" : \"StatementTemplate\",\n  \"verb\" : \"https://xapinet.org/xapi/yet/calibration/v1/concepts#did\",\n  \"rules\" : [\n    {\n      \"location\" : \"$.id\",\n      \"presence\" : \"included\"\n    },\n    {\n      \"location\" : \"$.timestamp\",\n      \"presence\" : \"included\"\n    },\n    {\n      \"any\" : [\"https://xapinet.org/xapi/yet/calibration/v1/concepts#activity-1\"],\n      \"location\" : \"$.object.id\",\n      \"presence\" : \"included\"\n    },\n    {\n      \"any\" : [\"Activity 1\"],\n      \"location\" : \"$.object.definition.name.en-US\",\n      \"presence\" : \"included\"\n    },\n    {\n      \"any\" : [\"The first Activity\"],\n      \"location\" : \"$.object.definition.description.en-US\",\n      \"presence\" : \"included\"\n    },\n    {\n      \"any\" : [\"https://xapinet.org/xapi/yet/calibration/v1\"],\n      \"location\" : \"$.context.contextActivities.category[0].id\",\n      \"presence\" : \"included\"\n    }\n  ]\n}\n```\nbecause the `verb` in the Statement is the same as the `verb` in the Statement Template, and because the Statement's `id`, `name`, `description`, and `category.id` property values are included in the values specified by the Template's [rules](https://github.com/adlnet/xapi-profiles/blob/master/xapi-profiles-structure.md#statement-template-rules), which are matched using the `location` [JSONPath](https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html) strings.\n\nNow suppose that the Statement is the first in a sequence of Statements. This Statement will match against the following Patterns:\n```json\n{\n  \"definition\" : {\n    \"en\" : \"Pattern 1\"\n  },\n  \"primary\" : true,\n  \"prefLabel\" : {\n    \"en\" : \"Learning Pattern 1\"\n  },\n  \"type\" : \"Pattern\",\n  \"inScheme\" : \"https://xapinet.org/xapi/yet/calibration/v1\",\n  \"id\" : \"https://xapinet.org/xapi/yet/calibration/v1/patterns#pattern-1\",\n  \"sequence\" : [\n    \"https://xapinet.org/xapi/yet/calibration/v1/patterns#pattern-2\",\n    \"https://xapinet.org/xapi/yet/calibration/v1/patterns#pattern-3\"\n  ]\n},\n{\n  \"definition\" : {\n    \"en\" : \"Pattern 2\"\n  },\n  \"primary\" : false,\n  \"prefLabel\" : {\n    \"en\" : \"Learning Pattern 2\"\n  },\n  \"type\" : \"Pattern\",\n  \"inScheme\" : \"https://xapinet.org/xapi/yet/calibration/v1\",\n  \"id\" : \"https://xapinet.org/xapi/yet/calibration/v1/patterns#pattern-2\",\n  \"optional\" : \"https://xapinet.org/xapi/yet/calibration/v1/templates#activity-1\"\n}\n```\nsince Pattern 1 (which is a primary Pattern, indicating that it is the starting point for Pattern matching) specifies a sequence of Patterns 2 and 3, and Pattern 2 indicates that the Statement can optionally match against the aforementioned Statement Template, which the Statement indeed does.\n\n## License\n\nCopyright © 2019-2025 Yet Analytics, Inc.\n\nDistributed under the Apache License version 2.0.\n\nThe Persephone logo is based off of [_Proserpine_](https://commons.wikimedia.org/wiki/File:%27Proserpine%27,_marble_bust_by_Hiram_Powers,_1844,_Cincinnati_Art_Museum.jpg) by [Hiram Powers](https://en.wikipedia.org/wiki/Hiram_Powers).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyetanalytics%2Fproject-persephone","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyetanalytics%2Fproject-persephone","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyetanalytics%2Fproject-persephone/lists"}