{ "cells": [ { "cell_type": "markdown", "id": "continent-director", "metadata": {}, "source": [ "# LENABI: Prototype of new metadata API for serlo.org" ] }, { "cell_type": "markdown", "id": "unsigned-spiritual", "metadata": {}, "source": [ "## Table of contents\n", "\n", "* [The metadata format](#format)\n", "* [Accessing the metadata via GraphQL](#graphql)\n", "* [Code examples with Python](#python)\n", " * [Fetching metadata about Serlo Education e.V. (i.e. the publisher)](#python-publisher)\n", " * [Fetching the first page of metadata for entities](#python-entities-first-page)\n", " * [Fetching the first and the second page](#python-entities-second-page)\n", " * [Fetching metadata of all learning resources](#python-entites-fetch-all)\n", " * [Fetching all metadata objects with filters `instance` and `modifiedAfter`](#python-entities-filter)\n", "* [Code examples with curl](#curl)\n", " * [Fetching the publisher](#curl-publisher)\n", " * [Fetching metadata of learning resources](#curl-entities)\n", " * [Fetching entities with arguments](#curl-entities-arguments)\n", "* [Roadmap / Planned Features](#roadmap)\n", "* [Behind the scenes](#source-code)\n", "* [License and disclaimer](#license)" ] }, { "cell_type": "markdown", "id": "dying-bracelet", "metadata": {}, "source": [ "## The metadata format \n", "\n", "We use the specification [Allgemeines Metadatenprofil für Bildungsressourcen](https://github.com/dini-ag-kim/amb) to describe our learning resources. It uses [JSON-LD](https://json-ld.org/) as a format with [schema.org](https://schema.org/) as the main vocabulary. For example, the metadata for the article [\"Addition\"](https://de.serlo.org/mathe/1495/addition) is:\n", "\n", "```json\n", "{\n", " \"@context\": [\n", " \"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\",\n", " {\n", " \"@language\": \"de\"\n", " }\n", " ],\n", " \"id\": \"https://serlo.org/1495\",\n", " \"type\": [\n", " \"LearningResource\",\n", " \"Article\"\n", " ],\n", " \"dateCreated\": \"2014-03-01T20:36:44+00:00\",\n", " \"dateModified\": \"2021-03-08T20:51:17+00:00\",\n", " \"description\": \"Addition, auch Plusrechnen genannt, geh\\\\u00f6rt zu den Grundrechenarten der Mathematik. Lerne, was ein Summand ist. \\\\u21d2 Hier lernst du, dass das Assoziativgesetz und Kommutativgesetz gelten. \\\\u21d2 Veranschaulichung durch Merktabellen und Zahlengeraden. F\\\\u00fcr den Anfang kannst du auch schriftlich addieren! Viele \\\\u00dcbungsaufgaben sind verf\\\\u00fcgbar.\\\\u2713 Lernen mit Serlo!\",\n", " \"headline\": \"Addition\",\n", " \"identifier\": {\n", " \"type\": \"PropertyValue\",\n", " \"propertyID\": \"UUID\",\n", " \"value\": 1495\n", " },\n", " \"inLanguage\": [\n", " \"de\"\n", " ],\n", " \"isAccessibleForFree\": true,\n", " \"isFamilyFriendly\": true,\n", " \"learningResourceType\": \"Article\",\n", " \"license\": {\n", " \"id\": \"https://creativecommons.org/licenses/by-sa/4.0/deed.de\"\n", " },\n", " \"maintainer\": \"https://serlo.org/\",\n", " \"name\": \"Addition\",\n", " \"publisher\": [\n", " {\n", " \"id\": \"https://serlo.org/\"\n", " }\n", " ],\n", " \"version\": \"https://serlo.org/197588\"\n", "}\n", "```\n", "\n", "Each property is a [schema.org](http://schema.org) property. For example, `identifier` is the same as the property https://schema.org/identifier. The site https://json-ld.org/#developers lists tools for working with JSON-LD.\n", "\n", "### Further reading\n", "\n", "* See https://dini-ag-kim.github.io/amb/draft/schemas/schema.json for a detailed description of the used specification.\n", "* The section [Basic concepts](https://www.w3.org/TR/json-ld/#basic-concepts) of the JSON-LD specification gives a good explanation of how JSON-LD extends JSON so that properties have a clear definition." ] }, { "cell_type": "markdown", "id": "median-survey", "metadata": {}, "source": [ "## Accessing the metadata via GraphQL \n", "\n", "The metadata can be accessed via our GraphQL endpoint at https://api.serlo-staging.dev/graphql (`serlo-staging.dev` is our testing environment). In the query namespace `metadata`, we have two properties: `publisher` and `entities`. `publisher` points to the metadata about Serlo Education e.V. (i.e. the publisher). Via `entities` you can access the metadata about our learning resources. `entities` supports pagination via the properties `first` and `after`, filtering by language via `instance` and filtering by modification date via `modifiedAfter`:\n", "\n", "```\n", "extend type Query {\n", " metadata: MetadataQueryNamespace!\n", "}\n", "\n", "type MetadataQueryNamespace {\n", " # Returns metadata about Serlo Education e.V.\n", " publisher: JSONObject!\n", " \n", " # Returns metadata about learning resources with pagination and filter options\n", " entities(\n", " first: Int, # Number of metadata objects which shall\n", " # be returned (default is 100)\n", " after: String, # Cursor to the entity after which the\n", " # new metadata shall be returned (see `endCursor` in `HasNextPageInfo`)\n", " instance: Instance, # Filter for the subdomain / language\n", " modifiedAfter: String # Filters entities which have been modified\n", " # after the given date (format YYYY-MM-DDTHH:MM:SSZ)\n", " ): EntitiesMetadataConnection!\n", "}\n", "\n", "type EntitiesMetadataConnection {\n", " # Array of metadata objects\n", " nodes: [JSONObject!]!\n", " \n", " # Information whether there are more resources to query\n", " pageInfo: HasNextPageInfo!\n", "}\n", "\n", "type HasNextPageInfo {\n", " # Is true when more metadata objects can be queried\n", " hasNextPage: Boolean!\n", " \n", " # Cursor which needs to be passed to `after` in order to fetch more metadata objects\n", " endCursor: String\n", "}\n", "```\n", "\n", "The site https://graphql.org/code/ lists client tools for GraphQL. In the following sections you will also find code examples for accessing our metadata via the GraphQL API.\n", "\n", "In case you program a crawler which shall run regularly you might find the `modifiedAfter` argument helpful. By setting it to the date of the last execution you can fetch the metadata of all learning resources which have been modified since then. Note that the returned metadata objects are always ordered by `id` even if `modifiedAfter` is set. \n", "\n", "### Further reading\n", "\n", "* Use [https://api.serlo-staging.dev/___graphql](https://api.serlo-staging.dev/___graphql) to test your GraphQL queries. There, you can also find an in-depth documentation by clicking on the button `DOCS` on the right side.\n", "* See https://graphql.org/learn/serving-over-http/#post-request for a description of how GraphQL requests can be made.\n", "* https://graphql.org/learn/ gives a good introduction into GraphQL." ] }, { "cell_type": "markdown", "id": "dangerous-driving", "metadata": {}, "source": [ "## Code examples with Python \n", "\n", "For the following examples you need to have [requests](https://docs.python-requests.org/en/latest/) installed (for example via `pip install requests`)." ] }, { "cell_type": "markdown", "id": "treated-storage", "metadata": {}, "source": [ "### Helper functions for displaying the results" ] }, { "cell_type": "code", "execution_count": 1, "id": "orange-evans", "metadata": {}, "outputs": [], "source": [ "import json\n", "\n", "from IPython.display import display, Markdown, HTML\n", "\n", "def display_json(value, title=\"The result\"): \n", " json_formated = json.dumps(value, indent=2)\n", " \n", " display_markdown(f\"#### {title}\")\n", " display_markdown(f\"```json\\n{json_formated}\\n```\")\n", " \n", "def display_len(list_object, explanation=\"elements were fetched\"):\n", " display_markdown(\"**Result:** %s %s\" % (len(list_object), explanation))\n", "\n", "def display_markdown(text):\n", " display(Markdown(text))" ] }, { "cell_type": "markdown", "id": "gothic-secretary", "metadata": { "tags": [] }, "source": [ "### Fetching metadata about Serlo Education e.V. (i.e. the publisher) " ] }, { "cell_type": "code", "execution_count": 2, "id": "loose-combination", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "#### The result" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```json\n", "{\n", " \"data\": {\n", " \"metadata\": {\n", " \"publisher\": {\n", " \"@context\": [\n", " \"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\",\n", " {\n", " \"@language\": \"de\"\n", " }\n", " ],\n", " \"id\": \"https://serlo.org/\",\n", " \"type\": [\n", " \"EducationalOrganization\",\n", " \"NGO\"\n", " ],\n", " \"name\": \"Serlo Education e.V.\",\n", " \"alternateName\": \"Serlo\",\n", " \"url\": \"https://de.serlo.org/\",\n", " \"description\": \"Serlo.org bietet einfache Erkl\\u00e4rungen, Kurse, Lernvideos, \\u00dcbungen und Musterl\\u00f6sungen mit denen Sch\\u00fcler*innen und Studierende nach ihrem eigenen Bedarf und in ihrem eigenen Tempo lernen k\\u00f6nnen. Die Lernplattform ist komplett kostenlos und werbefrei.\",\n", " \"image\": \"https://assets.serlo.org/5ce4082185f5d_5df93b32a2e2cb8a0363e2e2ab3ce4f79d444d11.jpg\",\n", " \"logo\": \"https://de.serlo.org/_assets/img/serlo-logo.svg\",\n", " \"address\": {\n", " \"type\": \"PostalAddress\",\n", " \"streetAddress\": \"Daiserstra\\u00dfe 15 (RGB)\",\n", " \"postalCode\": \"81371\",\n", " \"addressLocality\": \"M\\u00fcnchen\",\n", " \"addressRegion\": \"Bayern\",\n", " \"addressCountry\": \"Germany\"\n", " },\n", " \"email\": \"de@serlo.org\"\n", " }\n", " }\n", " }\n", "}\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import requests\n", "\n", "def get_publisher():\n", " req = requests.post(\n", " \"https://api.serlo-staging.dev/graphql\",\n", " headers = {\n", " \"Content-Type\": \"application/json\",\n", " },\n", " json = {\n", " \"query\": \"\"\"\n", " query {\n", " metadata {\n", " publisher\n", " }\n", " }\n", " \"\"\"\n", " }\n", " )\n", " \n", " return req.json()\n", "\n", "display_json(get_publisher())" ] }, { "cell_type": "markdown", "id": "pacific-mountain", "metadata": {}, "source": [ "### Fetching the first page of metadata for entities " ] }, { "cell_type": "code", "execution_count": 3, "id": "occasional-leadership", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "#### The result" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```json\n", "{\n", " \"data\": {\n", " \"metadata\": {\n", " \"entities\": {\n", " \"nodes\": [\n", " {\n", " \"@context\": [\n", " \"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\",\n", " {\n", " \"@language\": \"de\"\n", " }\n", " ],\n", " \"id\": \"https://serlo.org/1495\",\n", " \"type\": [\n", " \"LearningResource\",\n", " \"Article\"\n", " ],\n", " \"dateCreated\": \"2014-03-01T20:36:44+00:00\",\n", " \"dateModified\": \"2021-03-08T20:51:17+00:00\",\n", " \"description\": \"Addition, auch Plusrechnen genannt, geh\\u00f6rt zu den Grundrechenarten der Mathematik. Lerne, was ein Summand ist. \\u21d2 Hier lernst du, dass das Assoziativgesetz und Kommutativgesetz gelten. \\u21d2 Veranschaulichung durch Merktabellen und Zahlengeraden. F\\u00fcr den Anfang kannst du auch schriftlich addieren! Viele \\u00dcbungsaufgaben sind verf\\u00fcgbar.\\u2713 Lernen mit Serlo!\",\n", " \"headline\": \"Addition\",\n", " \"identifier\": {\n", " \"type\": \"PropertyValue\",\n", " \"propertyID\": \"UUID\",\n", " \"value\": 1495\n", " },\n", " \"inLanguage\": [\n", " \"de\"\n", " ],\n", " \"isAccessibleForFree\": true,\n", " \"isFamilyFriendly\": true,\n", " \"learningResourceType\": \"Article\",\n", " \"license\": {\n", " \"id\": \"https://creativecommons.org/licenses/by-sa/4.0/deed.de\"\n", " },\n", " \"maintainer\": \"https://serlo.org/\",\n", " \"name\": \"Addition\",\n", " \"publisher\": [\n", " {\n", " \"id\": \"https://serlo.org/\"\n", " }\n", " ],\n", " \"version\": \"https://serlo.org/197588\"\n", " },\n", " {\n", " \"@context\": [\n", " \"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\",\n", " {\n", " \"@language\": \"de\"\n", " }\n", " ],\n", " \"id\": \"https://serlo.org/1497\",\n", " \"type\": [\n", " \"LearningResource\",\n", " \"Article\"\n", " ],\n", " \"dateCreated\": \"2014-03-01T20:36:51+00:00\",\n", " \"dateModified\": \"2021-09-06T11:11:40+00:00\",\n", " \"description\": \"\",\n", " \"headline\": \"Kleinstes gemeinsames Vielfaches\",\n", " \"identifier\": {\n", " \"type\": \"PropertyValue\",\n", " \"propertyID\": \"UUID\",\n", " \"value\": 1497\n", " },\n", " \"inLanguage\": [\n", " \"de\"\n", " ],\n", " \"isAccessibleForFree\": true,\n", " \"isFamilyFriendly\": true,\n", " \"learningResourceType\": \"Article\",\n", " \"license\": {\n", " \"id\": \"https://creativecommons.org/licenses/by-sa/4.0/deed.de\"\n", " },\n", " \"maintainer\": \"https://serlo.org/\",\n", " \"name\": \"Kleinstes gemeinsames Vielfaches\",\n", " \"publisher\": [\n", " {\n", " \"id\": \"https://serlo.org/\"\n", " }\n", " ],\n", " \"version\": \"https://serlo.org/224107\"\n", " }\n", " ]\n", " }\n", " }\n", " }\n", "}\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import requests\n", "\n", "req = requests.post(\n", " \"https://api.serlo-staging.dev/graphql\",\n", " headers = {\n", " \"Content-Type\": \"application/json\",\n", " },\n", " json = {\n", " \"query\": \"\"\"\n", " query {\n", " metadata {\n", " entities(first: 2) {\n", " nodes\n", " }\n", " }\n", " }\n", " \"\"\"\n", " }\n", ")\n", "\n", "display_json(req.json())" ] }, { "cell_type": "markdown", "id": "seasonal-gateway", "metadata": {}, "source": [ "### Fetching the first and the second page " ] }, { "cell_type": "code", "execution_count": 4, "id": "professional-rouge", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "#### The PageInfo object of the first request" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```json\n", "{\n", " \"hasNextPage\": true,\n", " \"endCursor\": \"MTQ5Nw==\"\n", "}\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "#### Metadata of the second page" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "```json\n", "{\n", " \"data\": {\n", " \"metadata\": {\n", " \"entities\": {\n", " \"nodes\": [\n", " {\n", " \"@context\": [\n", " \"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\",\n", " {\n", " \"@language\": \"de\"\n", " }\n", " ],\n", " \"id\": \"https://serlo.org/1499\",\n", " \"type\": [\n", " \"LearningResource\",\n", " \"Article\"\n", " ],\n", " \"dateCreated\": \"2014-03-01T20:37:01+00:00\",\n", " \"dateModified\": \"2021-09-06T11:41:11+00:00\",\n", " \"description\": \"Binomische Formeln einfach erkl\\u00e4rt. Verwendung der binomischen Formel zum Aufl\\u00f6sen von Klammern und Faktorisieren. Mit vielen Beispielen und \\u00dcbungen! Erfahre mehr zu leichten Beweisen der binomischen Formel mithilfe des Quadrats. \\u21d2 Ein Kochrezept zur allgemeinen Vorhergehensweise. Video\\u2713\",\n", " \"headline\": \"Binomische Formeln\",\n", " \"identifier\": {\n", " \"type\": \"PropertyValue\",\n", " \"propertyID\": \"UUID\",\n", " \"value\": 1499\n", " },\n", " \"inLanguage\": [\n", " \"de\"\n", " ],\n", " \"isAccessibleForFree\": true,\n", " \"isFamilyFriendly\": true,\n", " \"learningResourceType\": \"Article\",\n", " \"license\": {\n", " \"id\": \"https://creativecommons.org/licenses/by-sa/4.0/deed.de\"\n", " },\n", " \"maintainer\": \"https://serlo.org/\",\n", " \"name\": \"Binomische Formeln\",\n", " \"publisher\": [\n", " {\n", " \"id\": \"https://serlo.org/\"\n", " }\n", " ],\n", " \"version\": \"https://serlo.org/224109\"\n", " },\n", " {\n", " \"@context\": [\n", " \"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\",\n", " {\n", " \"@language\": \"de\"\n", " }\n", " ],\n", " \"id\": \"https://serlo.org/1501\",\n", " \"type\": [\n", " \"LearningResource\",\n", " \"Article\"\n", " ],\n", " \"dateCreated\": \"2014-03-01T20:37:01+00:00\",\n", " \"dateModified\": \"2021-09-08T10:24:28+00:00\",\n", " \"description\": \"\",\n", " \"headline\": \"Ergebnismenge\",\n", " \"identifier\": {\n", " \"type\": \"PropertyValue\",\n", " \"propertyID\": \"UUID\",\n", " \"value\": 1501\n", " },\n", " \"inLanguage\": [\n", " \"de\"\n", " ],\n", " \"isAccessibleForFree\": true,\n", " \"isFamilyFriendly\": true,\n", " \"learningResourceType\": \"Article\",\n", " \"license\": {\n", " \"id\": \"https://creativecommons.org/licenses/by-sa/4.0/deed.de\"\n", " },\n", " \"maintainer\": \"https://serlo.org/\",\n", " \"name\": \"Ergebnismenge\",\n", " \"publisher\": [\n", " {\n", " \"id\": \"https://serlo.org/\"\n", " }\n", " ],\n", " \"version\": \"https://serlo.org/224215\"\n", " }\n", " ],\n", " \"pageInfo\": {\n", " \"hasNextPage\": true,\n", " \"endCursor\": \"MTUwMQ==\"\n", " }\n", " }\n", " }\n", " }\n", "}\n", "```" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import requests\n", "\n", "def fetch_entities(first, after=None):\n", " req = requests.post(\n", " \"https://api.serlo-staging.dev/graphql\",\n", " headers = {\n", " \"Content-Type\": \"application/json\",\n", " },\n", " json = {\n", " \"query\": \"\"\"\n", " query($first: Int, $after: String) {\n", " metadata {\n", " entities(first: $first, after: $after) {\n", " nodes\n", " pageInfo {\n", " hasNextPage\n", " endCursor\n", " }\n", " }\n", " }\n", " }\n", " \"\"\",\n", " \"variables\": { \"first\": first, \"after\": after }\n", " }\n", " )\n", " \n", " return req.json()\n", "\n", "# Fetching the first page with the first two metadata elements\n", "first_result = fetch_entities(first=2)\n", "\n", "# Display the `PageInfo` object of the first request\n", "# Note that `hastNextPage` is true, so there are more objects which can be fetched\n", "first_result_page_info = first_result[\"data\"][\"metadata\"][\"entities\"][\"pageInfo\"]\n", "display_json(first_result_page_info, title=\"The PageInfo object of the first request\")\n", "\n", "# Fetching the second page with the next two elements\n", "# Note that `endCursor` of the first request is passed as the `after` argument in order to get the next elements \n", "second_result = fetch_entities(first=2, after=first_result_page_info[\"endCursor\"])\n", "\n", "display_json(second_result, title=\"Metadata of the second page\")" ] }, { "cell_type": "markdown", "id": "designed-deficit", "metadata": {}, "source": [ "### Fetching metadata of all learning resources \n", "\n", "The following example shows how the `PageInfo` object can be used to fetch all entities in a loop:" ] }, { "cell_type": "code", "execution_count": 5, "id": "weighted-model", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "**Result:** 8371 elements were fetched" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import requests \n", "\n", "def fetch_all_entities(first=500):\n", " result = []\n", " endCursor = None\n", " \n", " while True:\n", " current_page = fetch_entities(first, after=endCursor)[\"data\"][\"metadata\"][\"entities\"]\n", " \n", " result += current_page[\"nodes\"]\n", " \n", " if current_page[\"pageInfo\"][\"hasNextPage\"]:\n", " endCursor = current_page[\"pageInfo\"][\"endCursor\"]\n", " else:\n", " break\n", " \n", " return result\n", "\n", "def fetch_entities(first, after=None):\n", " req = requests.post(\n", " \"https://api.serlo-staging.dev/graphql\",\n", " headers = {\n", " \"Content-Type\": \"application/json\",\n", " },\n", " json = {\n", " \"query\": \"\"\"\n", " query($first: Int, $after: String) {\n", " metadata {\n", " entities(first: $first, after: $after) {\n", " nodes\n", " pageInfo {\n", " hasNextPage\n", " endCursor\n", " }\n", " }\n", " }\n", " }\n", " \"\"\",\n", " \"variables\": { \"first\": first, \"after\": after }\n", " }\n", " )\n", " \n", " return req.json()\n", "\n", "all_entities = fetch_all_entities()\n", "\n", "display_len(all_entities)" ] }, { "cell_type": "markdown", "id": "happy-librarian", "metadata": {}, "source": [ "### Fetching all metadata objects with filters `instance` and `modifiedAfter` " ] }, { "cell_type": "code", "execution_count": 6, "id": "temporal-movement", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "**Result:** 7458 entities fetched from de.serlo.org" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/markdown": [ "**Result:** 3179 entities fetched which are modified in 2021" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import requests \n", "\n", "def fetch_all_entities(first=500, instance=None, modifiedAfter=None):\n", " result = []\n", " endCursor = None\n", " \n", " while True:\n", " current_page = fetch_entities(first, after=endCursor, instance=instance, modifiedAfter=modifiedAfter)\n", " current_page = current_page[\"data\"][\"metadata\"][\"entities\"]\n", " \n", " result += current_page[\"nodes\"]\n", " \n", " if current_page[\"pageInfo\"][\"hasNextPage\"]:\n", " endCursor = current_page[\"pageInfo\"][\"endCursor\"]\n", " else:\n", " break\n", " \n", " return result\n", "\n", "def fetch_entities(first, after=None, instance=None, modifiedAfter=None):\n", " req = requests.post(\n", " \"https://api.serlo-staging.dev/graphql\",\n", " headers = {\n", " \"Content-Type\": \"application/json\",\n", " },\n", " json = {\n", " \"query\": \"\"\"\n", " query($first: Int, $after: String, $instance: Instance, $modifiedAfter: String) {\n", " metadata {\n", " entities(first: $first, after: $after, instance: $instance, modifiedAfter: $modifiedAfter) {\n", " nodes\n", " pageInfo {\n", " hasNextPage\n", " endCursor\n", " }\n", " }\n", " }\n", " }\n", " \"\"\",\n", " \"variables\": {\n", " \"first\": first,\n", " \"after\": after,\n", " \"instance\": instance,\n", " \"modifiedAfter\": modifiedAfter\n", " }\n", " }\n", " )\n", " \n", " return req.json()\n", "\n", "# == Fetch elements by language / subdomain ==\n", "german_entities = fetch_all_entities(instance=\"de\")\n", "\n", "display_len(german_entities, explanation=\"entities fetched from de.serlo.org\")\n", "\n", "# == Fetch elements which are modified in 2021 ==\n", "# Format for modifiedAfter is YYYY-MM-DDTHH:MM:SSZ\n", "entities2021 = fetch_all_entities(modifiedAfter=\"2021-01-01T00:00:00Z\")\n", "\n", "display_len(entities2021, explanation=\"entities fetched which are modified in 2021\")" ] }, { "cell_type": "markdown", "id": "falling-budget", "metadata": {}, "source": [ "## Code examples with curl \n", "\n", "The following examples use [curl](https://curl.se/) for making the requests and [jq](https://stedolan.github.io/jq/) for pretty-printing the JSON responses.\n", "\n", "### Fetching the publisher " ] }, { "cell_type": "code", "execution_count": 7, "id": "excessive-webster", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"data\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"metadata\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"publisher\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@context\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\"\u001b[0m\u001b[1;39m,\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@language\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"EducationalOrganization\"\u001b[0m\u001b[1;39m,\n", " \u001b[0;32m\"NGO\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"name\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Serlo Education e.V.\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"alternateName\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Serlo\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"url\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://de.serlo.org/\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"description\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Serlo.org bietet einfache Erklärungen, Kurse, Lernvideos, Übungen und Musterlösungen mit denen Schüler*innen und Studierende nach ihrem eigenen Bedarf und in ihrem eigenen Tempo lernen können. Die Lernplattform ist komplett kostenlos und werbefrei.\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"image\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://assets.serlo.org/5ce4082185f5d_5df93b32a2e2cb8a0363e2e2ab3ce4f79d444d11.jpg\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"logo\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://de.serlo.org/_assets/img/serlo-logo.svg\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"address\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"PostalAddress\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"streetAddress\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Daiserstraße 15 (RGB)\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"postalCode\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"81371\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"addressLocality\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"München\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"addressRegion\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Bayern\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"addressCountry\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Germany\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"email\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"de@serlo.org\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", "\u001b[1;39m}\u001b[0m\n" ] } ], "source": [ "%%bash\n", "\n", "curl https://api.serlo-staging.dev/graphql \\\n", " --silent -X POST \\\n", " -H \"Content-Type: application/json\" \\\n", " --data '{\n", " \"query\": \"query { metadata { publisher } }\"\n", " }' | jq -C" ] }, { "cell_type": "markdown", "id": "dietary-token", "metadata": {}, "source": [ "### Fetching metadata of learning resources " ] }, { "cell_type": "code", "execution_count": 8, "id": "amber-combine", "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"data\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"metadata\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"entities\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"nodes\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@context\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\"\u001b[0m\u001b[1;39m,\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@language\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/1495\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"LearningResource\"\u001b[0m\u001b[1;39m,\n", " \u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateCreated\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2014-03-01T20:36:44+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateModified\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2021-03-08T20:51:17+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"description\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Addition, auch Plusrechnen genannt, gehört zu den Grundrechenarten der Mathematik. Lerne, was ein Summand ist. ⇒ Hier lernst du, dass das Assoziativgesetz und Kommutativgesetz gelten. ⇒ Veranschaulichung durch Merktabellen und Zahlengeraden. Für den Anfang kannst du auch schriftlich addieren! Viele Übungsaufgaben sind verfügbar.✓ Lernen mit Serlo!\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"headline\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Addition\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"identifier\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"PropertyValue\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"propertyID\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"UUID\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"value\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39m1495\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"inLanguage\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isAccessibleForFree\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isFamilyFriendly\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"learningResourceType\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"license\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://creativecommons.org/licenses/by-sa/4.0/deed.de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"maintainer\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"name\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Addition\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"publisher\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"version\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/197588\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@context\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\"\u001b[0m\u001b[1;39m,\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@language\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/1497\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"LearningResource\"\u001b[0m\u001b[1;39m,\n", " \u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateCreated\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2014-03-01T20:36:51+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateModified\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2021-09-06T11:11:40+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"description\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"headline\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Kleinstes gemeinsames Vielfaches\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"identifier\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"PropertyValue\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"propertyID\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"UUID\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"value\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39m1497\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"inLanguage\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isAccessibleForFree\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isFamilyFriendly\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"learningResourceType\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"license\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://creativecommons.org/licenses/by-sa/4.0/deed.de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"maintainer\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"name\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Kleinstes gemeinsames Vielfaches\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"publisher\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"version\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/224107\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", "\u001b[1;39m}\u001b[0m\n" ] } ], "source": [ "%%bash\n", "\n", "curl https://api.serlo-staging.dev/graphql \\\n", " --silent -X POST \\\n", " -H \"Content-Type: application/json\" \\\n", " --data '{\n", " \"query\": \"query { metadata { entities(first: 2) { nodes } } }\"\n", " }' | jq -C" ] }, { "cell_type": "markdown", "id": "brilliant-berkeley", "metadata": {}, "source": [ "### Fetching entities with arguments \n", "\n", "The following example provides a bash function which you can use to test requests against the `entities` property with any combination of arguments." ] }, { "cell_type": "code", "execution_count": 9, "id": "warming-village", "metadata": { "scrolled": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "== Get the first English entity == \n", "\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"data\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"metadata\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"entities\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"nodes\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@context\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\"\u001b[0m\u001b[1;39m,\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@language\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"en\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/32996\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"LearningResource\"\u001b[0m\u001b[1;39m,\n", " \u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateCreated\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2014-11-16T15:02:57+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateModified\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2021-10-21T19:48:02+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"description\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"headline\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Gaussian elimination\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"identifier\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"PropertyValue\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"propertyID\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"UUID\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"value\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39m32996\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"inLanguage\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"en\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isAccessibleForFree\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isFamilyFriendly\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"learningResourceType\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"license\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"http://creativecommons.org/licenses/by/4.0/\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"maintainer\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"name\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Gaussian elimination\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"publisher\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"version\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/227897\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"pageInfo\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"hasNextPage\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"endCursor\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"MzI5OTY=\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", "\u001b[1;39m}\u001b[0m\n", "\n", "== Get the following entity with the endCursor of the first request ==\n", "\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"data\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"metadata\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"entities\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"nodes\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@context\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\"\u001b[0m\u001b[1;39m,\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@language\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"en\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/33401\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"LearningResource\"\u001b[0m\u001b[1;39m,\n", " \u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateCreated\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2014-11-30T11:27:40+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateModified\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2021-08-19T20:29:05+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"description\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"headline\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"System of linear equations\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"identifier\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"PropertyValue\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"propertyID\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"UUID\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"value\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39m33401\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"inLanguage\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"en\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isAccessibleForFree\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isFamilyFriendly\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"learningResourceType\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"license\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"http://creativecommons.org/licenses/by/4.0/\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"maintainer\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"name\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"System of linear equations\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"publisher\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"version\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/222974\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"pageInfo\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"hasNextPage\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"endCursor\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"MzM0MDE=\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", "\u001b[1;39m}\u001b[0m\n", "\n", "== Get an entity which was modified after 2021-11-01 ==\n", "\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"data\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"metadata\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"entities\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"nodes\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@context\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"https://w3id.org/kim/lrmi-profile/draft/context.jsonld\"\u001b[0m\u001b[1;39m,\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"@language\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/1525\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"LearningResource\"\u001b[0m\u001b[1;39m,\n", " \u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateCreated\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2014-03-01T20:37:31+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"dateModified\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"2022-01-07T23:05:21+00:00\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"description\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Hier lernst du Schritt für Schritt in verschiedenen Situation (Klammer mal Klammer, Faktor mal Klammer und Produkt in der Klammer) das richtige Ausmultiplizieren mit der Klammer.\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"headline\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Klammern ausmultiplizieren\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"identifier\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"type\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"PropertyValue\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"propertyID\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"UUID\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"value\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39m1525\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"inLanguage\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[0;32m\"de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isAccessibleForFree\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"isFamilyFriendly\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"learningResourceType\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Article\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"license\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://creativecommons.org/licenses/by-sa/4.0/deed.de\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"maintainer\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"name\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"Klammern ausmultiplizieren\"\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"publisher\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m[\n", " \u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"id\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"version\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"https://serlo.org/234900\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m]\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"pageInfo\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[1;39m{\n", " \u001b[0m\u001b[34;1m\"hasNextPage\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;39mtrue\u001b[0m\u001b[1;39m,\n", " \u001b[0m\u001b[34;1m\"endCursor\"\u001b[0m\u001b[1;39m: \u001b[0m\u001b[0;32m\"MTUyNQ==\"\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", " \u001b[1;39m}\u001b[0m\u001b[1;39m\n", "\u001b[1;39m}\u001b[0m\n" ] } ], "source": [ "%%bash\n", "\n", "function get_entities_with {\n", " QUERY='\n", " query(\n", " $first: Int,\n", " $after: String,\n", " $instance: Instance,\n", " $modifiedAfter: String\n", " ) {\n", " metadata {\n", " entities(\n", " first: $first,\n", " after: $after,\n", " instance: $instance,\n", " modifiedAfter: $modifiedAfter\n", " ) {\n", " nodes\n", " pageInfo {\n", " hasNextPage\n", " endCursor\n", " }\n", " }\n", " }\n", " }\n", " '\n", " REQUEST_BODY=\"{\n", " \\\"query\\\": \\\"$(echo \"$QUERY\" | tr -d \"\\n\")\\\",\n", " \\\"variables\\\": \"$1\"\n", " }\"\n", "\n", " curl https://api.serlo-staging.dev/graphql \\\n", " --silent -X POST \\\n", " -H \"Content-Type: application/json\" \\\n", " --data \"$REQUEST_BODY\" | jq -C\n", "}\n", "\n", "echo \"== Get the first English entity == \"\n", "\n", "get_entities_with '{ \"first\": 1, \"instance\": \"en\" }'\n", "\n", "\n", "echo\n", "echo \"== Get the following entity with the endCursor of the first request ==\"\n", "\n", "get_entities_with '{ \"first\": 1, \"instance\": \"en\", \"after\": \"MzI5OTY=\" }'\n", "\n", "\n", "echo\n", "echo \"== Get an entity which was modified after 2021-11-01 ==\"\n", "\n", "get_entities_with '{ \"first\": 1, \"modifiedAfter\": \"2021-11-01T00:00:00Z\" }'" ] }, { "cell_type": "markdown", "id": "theoretical-registrar", "metadata": {}, "source": [ "## Roadmap / Planned Features \n", "\n", "This metadata API is a prototype. Before deploying the metadata API in production we plan to integrate at least the following features:\n", "\n", "* support of all useful properties of [schema.org](http://schema.org) / [AMB](https://github.com/dini-ag-kim/amb) for learning resources\n", "* support of all types of learning resources we have at serlo.org (course pages and subexercises are excluded in the prototype)\n", "* possibility to fetch metadata about our taxonomy elements (like [\"Ableitung von Funktionen\"](https://de.serlo.org/mathe/1304/ableitung-von-funktionen))\n", "* filter out community related sites like [\"Editor Anleitung\"](https://de.serlo.org/community/159135/editor-anleitung) which do not represent learning resources\n", "\n", "We look forward to hearing from you if you get in touch with us in case you need a particular feature. You can either open an issue at https://github.com/serlo/api.serlo.org/issues or reach us via [de@serlo.org](mailto:de@serlo.org)." ] }, { "cell_type": "markdown", "id": "creative-facility", "metadata": {}, "source": [ "## Behind the scenes \n", "\n", "In case you are interested in how we generate the metadata you can find the most relevant source code in this [pull request](https://github.com/serlo/serlo.org-database-layer/pull/146/files). It includes the code to generate the metadata from the data in our database. The metadata is passed from our database layer (see repository [serlo.org-database-layer](https://github.com/serlo/serlo.org-database-layer)) to our GraphQL server (see repository [api.serlo.org](https://github.com/serlo/api.serlo.org)) from which it can be fetched. You can find a brief description of our infrastructure on [this page](https://github.com/serlo/api.serlo.org/wiki/Serlo-Infrastructure).\n", "\n", "All of our source code is licensed under the open source license [Apache License 2.0](https://choosealicense.com/licenses/apache-2.0/) and can be used freely. The full license notice is available [here](https://github.com/serlo/serlo.org-database-layer/blob/main/LICENSE)." ] }, { "cell_type": "markdown", "id": "honey-breath", "metadata": {}, "source": [ "## License and disclaimer \n", "\n", "This documentation with all code examples are licensed under the [CC0](https://creativecommons.org/publicdomain/zero/1.0/) license.\n", "\n", "This metadata API is a prototype. It might change in the future depending on the feedback we will receive. You can find the current documentation at https://lenabi.serlo.org/metadata-api If you want to use our metadata API we are happy if get in contact with us. You can reach us at [de@serlo.org](mailto:de@serlo.org)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.1" } }, "nbformat": 4, "nbformat_minor": 5 }