gcube-cms-suite/use-cases/notebooks/gna/MOSI.ipynb

322 lines
19 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"id": "ad36ae4c-6359-41f6-b9c5-971c96a678f0",
"metadata": {},
"source": [
"# MOSI UseCase\n",
"\n",
"This notebooks :\n",
"\n",
"- Creates UCD\n",
"- Creates UCD Schema \n",
"- Insert MOSI Record\n",
"- Publishes"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "ca65023e-ae07-45de-ba75-4d39d66df53b",
"metadata": {
"tags": []
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Requirement already satisfied: rich[jupyter] in /opt/conda/lib/python3.9/site-packages (12.2.0)\n",
"Requirement already satisfied: pygments<3.0.0,>=2.6.0 in /opt/conda/lib/python3.9/site-packages (from rich[jupyter]) (2.11.2)\n",
"Requirement already satisfied: commonmark<0.10.0,>=0.9.0 in /opt/conda/lib/python3.9/site-packages (from rich[jupyter]) (0.9.1)\n",
"Requirement already satisfied: ipywidgets<8.0.0,>=7.5.1 in /opt/conda/lib/python3.9/site-packages (from rich[jupyter]) (7.7.0)\n",
"Requirement already satisfied: widgetsnbextension~=3.6.0 in /opt/conda/lib/python3.9/site-packages (from ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (3.6.0)\n",
"Requirement already satisfied: ipython-genutils~=0.2.0 in /opt/conda/lib/python3.9/site-packages (from ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.2.0)\n",
"Requirement already satisfied: ipykernel>=4.5.1 in /opt/conda/lib/python3.9/site-packages (from ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (6.11.0)\n",
"Requirement already satisfied: jupyterlab-widgets>=1.0.0 in /opt/conda/lib/python3.9/site-packages (from ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (1.1.0)\n",
"Requirement already satisfied: ipython>=4.0.0 in /opt/conda/lib/python3.9/site-packages (from ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (8.2.0)\n",
"Requirement already satisfied: nbformat>=4.2.0 in /opt/conda/lib/python3.9/site-packages (from ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (5.2.0)\n",
"Requirement already satisfied: traitlets>=4.3.1 in /opt/conda/lib/python3.9/site-packages (from ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (5.1.1)\n",
"Requirement already satisfied: matplotlib-inline>=0.1 in /opt/conda/lib/python3.9/site-packages (from ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.1.3)\n",
"Requirement already satisfied: nest-asyncio in /opt/conda/lib/python3.9/site-packages (from ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (1.5.5)\n",
"Requirement already satisfied: jupyter-client>=6.1.12 in /opt/conda/lib/python3.9/site-packages (from ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (7.2.1)\n",
"Requirement already satisfied: tornado>=6.1 in /opt/conda/lib/python3.9/site-packages (from ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (6.1)\n",
"Requirement already satisfied: psutil in /opt/conda/lib/python3.9/site-packages (from ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (5.9.0)\n",
"Requirement already satisfied: setuptools>=60 in /opt/conda/lib/python3.9/site-packages (from ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (61.3.1)\n",
"Requirement already satisfied: debugpy>=1.0 in /opt/conda/lib/python3.9/site-packages (from ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (1.5.1)\n",
"Requirement already satisfied: pexpect>4.3 in /opt/conda/lib/python3.9/site-packages (from ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (4.8.0)\n",
"Requirement already satisfied: pickleshare in /opt/conda/lib/python3.9/site-packages (from ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.7.5)\n",
"Requirement already satisfied: decorator in /opt/conda/lib/python3.9/site-packages (from ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (5.1.1)\n",
"Requirement already satisfied: backcall in /opt/conda/lib/python3.9/site-packages (from ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.2.0)\n",
"Requirement already satisfied: prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0 in /opt/conda/lib/python3.9/site-packages (from ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (3.0.27)\n",
"Requirement already satisfied: stack-data in /opt/conda/lib/python3.9/site-packages (from ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.2.0)\n",
"Requirement already satisfied: jedi>=0.16 in /opt/conda/lib/python3.9/site-packages (from ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.18.1)\n",
"Requirement already satisfied: jsonschema!=2.5.0,>=2.4 in /opt/conda/lib/python3.9/site-packages (from nbformat>=4.2.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (4.4.0)\n",
"Requirement already satisfied: jupyter-core in /opt/conda/lib/python3.9/site-packages (from nbformat>=4.2.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (4.9.2)\n",
"Requirement already satisfied: notebook>=4.4.1 in /opt/conda/lib/python3.9/site-packages (from widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (6.4.10)\n",
"Requirement already satisfied: parso<0.9.0,>=0.8.0 in /opt/conda/lib/python3.9/site-packages (from jedi>=0.16->ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.8.3)\n",
"Requirement already satisfied: pyrsistent!=0.17.0,!=0.17.1,!=0.17.2,>=0.14.0 in /opt/conda/lib/python3.9/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat>=4.2.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.18.1)\n",
"Requirement already satisfied: attrs>=17.4.0 in /opt/conda/lib/python3.9/site-packages (from jsonschema!=2.5.0,>=2.4->nbformat>=4.2.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (21.4.0)\n",
"Requirement already satisfied: pyzmq>=22.3 in /opt/conda/lib/python3.9/site-packages (from jupyter-client>=6.1.12->ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (22.3.0)\n",
"Requirement already satisfied: python-dateutil>=2.8.2 in /opt/conda/lib/python3.9/site-packages (from jupyter-client>=6.1.12->ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (2.8.2)\n",
"Requirement already satisfied: entrypoints in /opt/conda/lib/python3.9/site-packages (from jupyter-client>=6.1.12->ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.4)\n",
"Requirement already satisfied: Send2Trash>=1.8.0 in /opt/conda/lib/python3.9/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (1.8.0)\n",
"Requirement already satisfied: argon2-cffi in /opt/conda/lib/python3.9/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (21.3.0)\n",
"Requirement already satisfied: jinja2 in /opt/conda/lib/python3.9/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (3.1.1)\n",
"Requirement already satisfied: nbconvert>=5 in /opt/conda/lib/python3.9/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (6.4.5)\n",
"Requirement already satisfied: terminado>=0.8.3 in /opt/conda/lib/python3.9/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.13.3)\n",
"Requirement already satisfied: prometheus-client in /opt/conda/lib/python3.9/site-packages (from notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.13.1)\n",
"Requirement already satisfied: ptyprocess>=0.5 in /opt/conda/lib/python3.9/site-packages (from pexpect>4.3->ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.7.0)\n",
"Requirement already satisfied: wcwidth in /opt/conda/lib/python3.9/site-packages (from prompt-toolkit!=3.0.0,!=3.0.1,<3.1.0,>=2.0.0->ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.2.5)\n",
"Requirement already satisfied: pure-eval in /opt/conda/lib/python3.9/site-packages (from stack-data->ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.2.2)\n",
"Requirement already satisfied: asttokens in /opt/conda/lib/python3.9/site-packages (from stack-data->ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (2.0.5)\n",
"Requirement already satisfied: executing in /opt/conda/lib/python3.9/site-packages (from stack-data->ipython>=4.0.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.8.3)\n",
"Requirement already satisfied: testpath in /opt/conda/lib/python3.9/site-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.6.0)\n",
"Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.9/site-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (2.1.1)\n",
"Requirement already satisfied: mistune<2,>=0.8.1 in /opt/conda/lib/python3.9/site-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.8.4)\n",
"Requirement already satisfied: defusedxml in /opt/conda/lib/python3.9/site-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.7.1)\n",
"Requirement already satisfied: nbclient<0.6.0,>=0.5.0 in /opt/conda/lib/python3.9/site-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.5.13)\n",
"Requirement already satisfied: pandocfilters>=1.4.1 in /opt/conda/lib/python3.9/site-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (1.5.0)\n",
"Requirement already satisfied: beautifulsoup4 in /opt/conda/lib/python3.9/site-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (4.10.0)\n",
"Requirement already satisfied: jupyterlab-pygments in /opt/conda/lib/python3.9/site-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.1.2)\n",
"Requirement already satisfied: bleach in /opt/conda/lib/python3.9/site-packages (from nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (4.1.0)\n",
"Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.9/site-packages (from python-dateutil>=2.8.2->jupyter-client>=6.1.12->ipykernel>=4.5.1->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (1.16.0)\n",
"Requirement already satisfied: argon2-cffi-bindings in /opt/conda/lib/python3.9/site-packages (from argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (21.2.0)\n",
"Requirement already satisfied: cffi>=1.0.1 in /opt/conda/lib/python3.9/site-packages (from argon2-cffi-bindings->argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (1.15.0)\n",
"Requirement already satisfied: soupsieve>1.2 in /opt/conda/lib/python3.9/site-packages (from beautifulsoup4->nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (2.3.1)\n",
"Requirement already satisfied: packaging in /opt/conda/lib/python3.9/site-packages (from bleach->nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (21.3)\n",
"Requirement already satisfied: webencodings in /opt/conda/lib/python3.9/site-packages (from bleach->nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (0.5.1)\n",
"Requirement already satisfied: pycparser in /opt/conda/lib/python3.9/site-packages (from cffi>=1.0.1->argon2-cffi-bindings->argon2-cffi->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (2.21)\n",
"Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /opt/conda/lib/python3.9/site-packages (from packaging->bleach->nbconvert>=5->notebook>=4.4.1->widgetsnbextension~=3.6.0->ipywidgets<8.0.0,>=7.5.1->rich[jupyter]) (3.0.7)\n"
]
}
],
"source": [
"import requests\n",
"import json\n",
"\n",
"# These parameters should be discovered / initialized by common gcube libraries. \n",
"gcube_token = \"3aa85bbf-d5f3-4df4-ad03-4f8f788eba3d-98187548\"\n",
"service_base_endpoint =\"https://geoportal.dev.int.d4science.net/geoportal-service/srv\"\n",
"\n",
"# Actual customizable parameters\n",
"ucid = \"GNA-MOSI\"\n",
"gpkgFile = \"templateItalia.gpkg\"\n",
"\n",
"\n",
"#NB The following should not be changed \n",
"projects_endpoint = service_base_endpoint + \"/projects/\"+ucid\n",
"ucd_endpoint = service_base_endpoint + \"/ucd/\"\n",
"plugins_endpoint=service_base_endpoint + \"/plugins/\"\n",
"\n",
"\n",
"## Import Rich text support for pretty json \n",
"!pip install rich[jupyter]\n",
"import rich\n",
"from rich import print_json\n",
"\n",
"## Define utility function for HTTP requests logging\n",
"def send(dest,data=None,method=\"GET\",params={}):\n",
" print(\"GOING TO SEND \"+method+\" REQUEST TO \"+dest)\n",
" headers={\n",
" 'Content-type':'application/json', \n",
" 'Accept':'*/*',\n",
" 'gcube-token':gcube_token\n",
" }\n",
" if data is not None : print(\"Data is \"+str(data))\n",
" if method == \"GET\" : response = requests.get(url=dest,params=params,headers=headers)\n",
" if method == \"PUT\" : response = requests.put(url=dest,data=data,params=params,headers=headers)\n",
" if method == \"POST\" : response = requests.post(url=dest,data=data,params=params,headers=headers)\n",
" if method == \"DELETE\" : response = requests.delete(url=dest,data=data,params=params,headers=headers)\n",
" print(\"RESPONSE STATUS \"+str(response.status_code)) \n",
" if response.status_code<200 or response.status_code>=300 : \n",
" print(\"CAUSE \"+str(response.reason))\n",
" print(response.text)\n",
" return json.loads(response.text)\n",
"\n",
"#print(send(method=\"GET\",dest=\"http://geoportal.dev.int.d4science.net/geoportal-service/srv/mongo-concessioni/\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "047cf782-73d4-4d8c-9275-69eba2573e67",
"metadata": {},
"outputs": [],
"source": [
"# GPKG support\n",
"import fiona\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a3fae252-963e-4a20-9968-e906d3709681",
"metadata": {},
"outputs": [],
"source": [
"# Init parameters\n",
"UCD = {\n",
" \"_id\" : ucid,\n",
" \"_version\" : \"1.0.0\",\n",
" \"_name\" : \"GNA : MOSI\",\n",
"\n",
" \"_description\" : \"Modulistica siti\",\n",
"\n",
"\n",
"\n",
"\n",
"\n",
" \"_handlers\" : [\n",
" ,\n",
" \n",
" ]\n",
"}"
]
},
{
"cell_type": "markdown",
"id": "c43579c9-0c2c-4e30-a1c6-0565f27f9a55",
"metadata": {},
"source": [
"## UCD"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "7c0a9d27-86d3-4a9c-a51a-c47a80f2cadc",
"metadata": {},
"outputs": [],
"source": [
"schema={\n",
" \"CD\" : {\"_min\":1, \"label\" : \"Identificazione\",\"_children\" :[\n",
" {\"TSK\" : {\"_min\":1,\"_type\" : \"STRING-CONSTANT\", \"value\":{\"MOSI\"},\"label\":\"Tipo modulo\"}},\n",
" {\"CMD\" : {\"_min\":1,\"label\":\"Codice modulo\"}}, # ?? Auto generate\n",
" {\"ESC\" : {\"_min\":1,\"label\":\"Ente schedatore\"}}, # ?? Auto generate\n",
" {\"ECP\" : {\"_min\":1,\"label\":\"Ente competente per tutela\"}}, # ?? Auto generate\n",
" {\"CBC\" : {\"label\":\"Identificativo scheda bene culturale\"}},\n",
" {\"ACC\" : {\"_max\":-1,\"label\":\"Altro codice\", \"_children\"}}\n",
" \n",
" ]\n",
" }\n",
"}\n",
"#NB Cannot set schema from gpkg.MOSI if we want it structured\n",
"#with fiona.open(gpkgFile, layer='MOSI_multipolygon') as layer:\n",
"# feature = layer[0]\n",
" #feature is GeoJSON\n",
"\n",
"\n",
"\n",
"\n",
"# SET GEOMETR \n",
"schema.update(\"multipolygonGeometries\":{\"_max\":-1})\n",
"schema.update(\"points\":{\"_max\":-1})\n",
"schema.update(\"multipolygonGeometries\":{\"_max\":-1})\n",
"\n",
"\n",
"\n",
"\n",
"UCD.update({\"_schema\" :schema})"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "44280c2e-e513-4aec-825a-b4f9504ad35b",
"metadata": {},
"outputs": [],
"source": [
"#TODO DEFINE ROLES\n",
"UCD.update({\"_dataAccessPolicies\" : [\n",
" {\"_policy\" : {\"_read\" : \"own\", \"_write\" : \"own\"}, \"_roles\":[]},\n",
" ]})"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "9e3afe4a-a301-4eea-acd3-95cb03dd26b1",
"metadata": {},
"outputs": [],
"source": [
"# UPDATE Lifecycle Settings\n",
"UCD[\"_handlers\"].append({\n",
" \"_id\" : \"DEFAULT-SINGLE-STEP\",\n",
" \"_type\" : \"LifecycleManagement\",\n",
" \"_configuration\" : {\n",
" \"step_access\" : [\n",
" {\"STEP\" : \"PUBLISH\", \"roles\" :[]}\n",
" ]\n",
" }\n",
" })"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "c6ce3205-38e7-4531-9a46-bea2d48a9321",
"metadata": {},
"outputs": [
{
"ename": "SyntaxError",
"evalue": "closing parenthesis '}' does not match opening parenthesis '(' on line 2 (3773975152.py, line 17)",
"output_type": "error",
"traceback": [
"\u001b[0;36m Input \u001b[0;32mIn [6]\u001b[0;36m\u001b[0m\n\u001b[0;31m })\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m closing parenthesis '}' does not match opening parenthesis '(' on line 2\n"
]
}
],
"source": [
"# UPDATE Lifecycle Settings\n",
"UCD[\"_handlers\"].append({\n",
" \"_id\" : \"SDI-Indexer-Plugin\",\n",
" \"_type\" : \"Indexer\",\n",
" \"_configuration\" : {\n",
"# TODO use \n",
"# \"bboxEvaluation\" : [\"$..posizionamentoScavo.._bbox\"],\n",
" \"explicitFieldMapping\" : [\n",
"#TODO READ FROM MOSI LAYER \n",
" ],\n",
" \"additionalLayers\" : [\n",
"# TODO CONFIGURE Additional\n",
" {\"source\" : {\"url\" : \"...\"},\"toSetTitle\":\"\"}\n",
" ]\n",
" }\n",
" }\n",
" })"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "76b0a6a2-c418-450c-bf79-ff82f3f2df6b",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}