diff --git a/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/01_Patrons.ipynb b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/01_Patrons.ipynb new file mode 100644 index 000000000000..5fa433175299 --- /dev/null +++ b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/01_Patrons.ipynb @@ -0,0 +1,605 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The Patron\n", + "## An example of the Digital Twin\n", + "\n", + "This example just includes:\n", + "* Connecting\n", + "* Uploading models\n", + "* Creating twins\n", + "* Querying twins\n", + "\n", + "It's not a lot, but it will all tie together when we start running scenarios. \n", + "\n", + "[This is the SDK repo on Github](https://github.com/Azure/azure-sdk-for-python/tree/4559e19e2f3146a49f1eba1706bb798071f4a1f5/sdk/digitaltwins/azure-digitaltwins-core)\n", + "\n", + "[Here is the doc on the query language](https://docs.microsoft.com/en-us/azure/digital-twins/concepts-query-language)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Connecting\n", + "\n", + "**note**: you will need to replace {`home-test-twin.api.wcus.digitaltwins.azure.net`} with the address of your ADT. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from azure.identity import AzureCliCredential\n", + "from azure.digitaltwins.core import DigitalTwinsClient\n", + "\n", + "# using yaml instead of \n", + "import yaml\n", + "import uuid\n", + "\n", + "# using altair instead of matplotlib for vizuals\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "# you will get this from the ADT resource at portal.azure.com\n", + "your_digital_twin_url = \"home-test-twin.api.wcus.digitaltwins.azure.net\"\n", + "\n", + "azure_cli = AzureCliCredential()\n", + "service_client = DigitalTwinsClient(\n", + " your_digital_twin_url, azure_cli)\n", + "service_client" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "service_client" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Loading the model from a local Json file, I'm defining a model for a `Patron`. That's like a `class` that will be used to make several instances of a customer." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Uploading models" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "patron_model_id = \"dtmi:mymodels:patron;1\"" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# # Delete the model that you don't want. \n", + "# service_client.delete_model(patron_model_id)\n", + "\n", + "# # Create it if you just deleted it.\n", + "# patron_model_json = yaml.safe_load(open(\"models/patron.json\"))\n", + "# service_client.create_models([patron_model_json])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the json must be in a list. This is so that you can deploy several models in a single go." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'display_name': {'en': 'Patron'},\n", + " 'description': {'en': 'As an example, contains all of the properties possible in the DTDL.'},\n", + " 'id': 'dtmi:billmanh:patron;1',\n", + " 'upload_time': '2020-11-19T02:14:27.21094Z',\n", + " 'decommissioned': False}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Get the Patron model\n", + "get_model = service_client.get_model(patron_model_id)\n", + "get_model.as_dict()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In my case I use `customer` as an _instance_ of a model (a _twin_). The only thing to add is the information specific to the twin I want to upload. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating twins" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "digital_twin_id = 'customer-' + str(uuid.uuid4())\n", + "\n", + "customer_json = {\n", + " \"$metadata\": {\n", + " \"$model\": patron_model_id\n", + " },\n", + " \"satisfaction\": 10,\n", + " \"totalWaitTime\": 10\n", + "}\n", + "\n", + "created_twin = service_client.upsert_digital_twin(digital_twin_id, customer_json)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can see that the process with the API is simple:\n", + "* Define a model\n", + "* Build a twin from that model\n", + "* Give that model some attributes.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "dict" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "type(created_twin)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'$dtId': 'customer-3cbd5e60-957d-44ff-944f-9adb42a20a52',\n", + " '$etag': 'W/\"ea33ae65-f47c-4116-8e3d-1c6fa61777d6\"',\n", + " 'satisfaction': 10,\n", + " 'totalWaitTime': 10,\n", + " '$metadata': {'$model': 'dtmi:billmanh:patron;1',\n", + " 'satisfaction': {'lastUpdateTime': '2020-11-19T15:48:13.1057699Z'},\n", + " 'totalWaitTime': {'lastUpdateTime': '2020-11-19T15:48:13.1057699Z'}}}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "created_twin" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So let's create a bunch of people. Let's pretend we have a single room full of a bunch of people that all have similar attributes:\n", + "* `satisfaction` varies from person to person.\n", + "* `totalWaitTime` is the same at the beginning. \n", + "\n", + "the item for the `$metadata` is the model that it will be an instance of. The rest of the items in the dict are the `contents` of the model as you defined it.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('customer-5c454e2f-f70b-4352-b75a-958f1a49beba',\n", + " {'$metadata': {'$model': 'dtmi:billmanh:patron;1'},\n", + " 'satisfaction': 7,\n", + " 'totalWaitTime': 0}),\n", + " ('customer-26196fee-5ffd-457a-86b7-192a998f3cf2',\n", + " {'$metadata': {'$model': 'dtmi:billmanh:patron;1'},\n", + " 'satisfaction': 9,\n", + " 'totalWaitTime': 0}),\n", + " ('customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32c',\n", + " {'$metadata': {'$model': 'dtmi:billmanh:patron;1'},\n", + " 'satisfaction': 7,\n", + " 'totalWaitTime': 0}),\n", + " ('customer-c87adbfa-1c6e-4ea9-9f03-83e3877ef5fc',\n", + " {'$metadata': {'$model': 'dtmi:billmanh:patron;1'},\n", + " 'satisfaction': 8,\n", + " 'totalWaitTime': 0}),\n", + " ('customer-21e17d28-76c3-4c04-8df9-396703692a68',\n", + " {'$metadata': {'$model': 'dtmi:billmanh:patron;1'},\n", + " 'satisfaction': 8,\n", + " 'totalWaitTime': 0})]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def create_new_customer():\n", + " digital_twin_id = 'customer-' + str(uuid.uuid4())\n", + " customer_json = {\n", + " \"$metadata\": {\n", + " \"$model\": patron_model_id\n", + " },\n", + " \"satisfaction\": np.random.randint(5,10),\n", + " \"totalWaitTime\": 0\n", + " }\n", + " return digital_twin_id,customer_json\n", + " \n", + "customer_twin_examples = [create_new_customer() for i in range(40)]\n", + "customer_twin_examples[:5]" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + "" + ], + "text/plain": [ + "alt.Chart(...)" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "customer_df = pd.DataFrame([i[1] for i in customer_twin_examples])\n", + "\n", + "customer_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Of course, those are just local customer data points. You could imagine that this is information collected at the edge. \n", + "\n", + "Now let's upload those people to see them in the cloud. Try to imagine that each customer in the store had an app on their phone that sent this information to the graph. Each `customer` represents a data feed from IoT telemetry. Generally, each device would upload a separate feed of IoT, but we are simulating using a single device (your computer) to upload this data.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "for i in customer_twin_examples:\n", + " service_client.upsert_digital_twin(i[0], i[1])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's query our customers to see how they are feeling. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Querying twins\n", + "This is pretty easy. If you've ever done SQL you'll get this right away.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "query_expression = 'SELECT * FROM digitaltwins'\n", + "query_result = service_client.query_twins(query_expression)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "for i in query_result:\n", + " pass" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'$dtId': 'customer-2e551c55-265a-46e0-a84b-4965bc734e21',\n", + " '$etag': 'W/\"8f1be988-70af-4488-a7c9-5403a4b6ea1d\"',\n", + " 'satisfaction': 9,\n", + " 'totalWaitTime': 0,\n", + " '$metadata': {'$model': 'dtmi:billmanh:patron;1',\n", + " 'satisfaction': {'lastUpdateTime': '2020-11-19T16:13:44.9882083Z'},\n", + " 'totalWaitTime': {'lastUpdateTime': '2020-11-19T16:13:44.9882083Z'}}}" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "i" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's say I want to get all of the customers that have > 7 satisfaction _Happy customers_" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "query_result = service_client.query_twins(\n", + "\"\"\"\n", + "SELECT *\n", + "FROM DigitalTwins T \n", + "WHERE T.satisfaction > 7\n", + "\"\"\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446 10\n", + "customer-3cbd5e60-957d-44ff-944f-9adb42a20a52 10\n", + "customer-26196fee-5ffd-457a-86b7-192a998f3cf2 9\n", + "customer-c87adbfa-1c6e-4ea9-9f03-83e3877ef5fc 8\n", + "customer-21e17d28-76c3-4c04-8df9-396703692a68 8\n", + "customer-8375cece-abf8-400f-be66-459f7f40fe6a 8\n", + "customer-d0483de8-0f9f-41d4-ac0c-84d608f7467a 8\n", + "customer-8ae03d4b-ee2c-4ffd-9aa8-8edbfbf39728 9\n", + "customer-fc568319-d5a9-492a-83ce-fed774257003 8\n", + "customer-d9ca8715-1a6d-43ea-83d2-671fc972ebd7 9\n", + "customer-acd7a91f-1d28-46b3-894d-6f3953fbd85e 8\n", + "customer-a867aacb-68a7-4872-a3bb-330b0b34ef7c 9\n", + "customer-373017d3-d7e2-4407-8c5b-5fc44e20286f 9\n", + "customer-418cbc74-b101-4f50-8cf0-6075a2a8053c 8\n", + "customer-04cc7f9d-5503-4d64-8dbc-373a47a8710a 9\n", + "customer-e5d090d6-ae68-4c31-890e-16ce96c1e462 9\n", + "customer-12a558cf-b15d-41ee-a98b-d07c4b7411ee 8\n", + "customer-1c51c2a0-45bc-442c-b4df-1d193e11f628 8\n", + "customer-ca674938-4d13-463f-8858-e0b9ac5deebc 9\n", + "customer-2139a9c9-ff79-4a63-9576-c34744f0521d 9\n", + "customer-2e551c55-265a-46e0-a84b-4965bc734e21 9\n", + "customer-107c1168-d86b-40e1-a57d-4c500148e172 10\n", + "customer-73299891-e2f2-4d29-98f1-9d2ee0e9f12b 9\n", + "customer-d1de64aa-cfc2-4385-9644-667506f0f474 9\n", + "customer-d4e84d9f-ee9d-45e8-ba9f-d9d90885f578 9\n", + "customer-b7a9fbba-8e06-4b23-ac39-35acfbacfe7c 8\n", + "customer-83f65979-cd94-41da-b248-c6a1a76e2f13 9\n", + "customer-84e9f81b-db4c-40c7-8284-230a357937a7 8\n", + "customer-06c5c57e-aec0-47c4-ac78-b986e589da15 8\n", + "customer-26a50817-189d-48cf-8ecc-efa9828f092a 9\n", + "customer-45e9aa03-733d-4a99-b9d5-94f1c6b04214 9\n", + "customer-0234cb48-1fa2-43e0-b69d-36a6ff269666 9\n", + "customer-048f85a8-173e-4305-92b8-ead2a748b07f 8\n", + "customer-25e19268-3433-4f09-afe3-94f466313368 10\n" + ] + } + ], + "source": [ + "for i in query_result:\n", + " print (i['$dtId'],i['satisfaction'])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Of course, you can just pull everything, but this might be problematic as you build a larger model. " + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "query_result = service_client.query_twins(\n", + "\"\"\"\n", + "SELECT COUNT() \n", + "FROM DigitalTwins \n", + "\"\"\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'COUNT': 88}\n" + ] + } + ], + "source": [ + "for i in query_result:\n", + " print(i)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (azure_test)", + "language": "python", + "name": "azure_test" + }, + "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.7.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/02_Purchasing_Tickets.ipynb b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/02_Purchasing_Tickets.ipynb new file mode 100644 index 000000000000..f5f2199c7661 --- /dev/null +++ b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/02_Purchasing_Tickets.ipynb @@ -0,0 +1,1695 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Purchasing tickets\n", + "## An example use case of the Digital Twin\n", + "\n", + "This example just includes:\n", + "* Connecting\n", + "* Building relationships between models\n", + "* Updating values\n", + "* Modifying the relationships and updating twins\n", + "* Querying twins by relationship\n", + "\n", + "In the previous scenario we made a bunch of customers. In this scenario we built a bunch of individual users \n", + "[This is the SDK repo on Github](https://github.com/Azure/azure-sdk-for-python/tree/4559e19e2f3146a49f1eba1706bb798071f4a1f5/sdk/digitaltwins/azure-digitaltwins-core)\n", + "\n", + "[Here is the doc on the query language](https://docs.microsoft.com/en-us/azure/digital-twins/concepts-query-language)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from azure.identity import AzureCliCredential\n", + "from azure.digitaltwins.core import DigitalTwinsClient\n", + "\n", + "# using yaml instead of \n", + "import yaml\n", + "import uuid\n", + "\n", + "# using altair instead of matplotlib for vizuals\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "# you will get this from the ADT resource at portal.azure.com\n", + "your_digital_twin_url = \"home-test-twin.api.wcus.digitaltwins.azure.net\"\n", + "\n", + "azure_cli = AzureCliCredential()\n", + "service_client = DigitalTwinsClient(\n", + " your_digital_twin_url, azure_cli)\n", + "service_client" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So from the previous notebook we uploaded some models. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'display_name': {'en': 'Patron'},\n", + " 'description': {'en': 'As an example, contains all of the properties possible in the DTDL.'},\n", + " 'id': 'dtmi:billmanh:patron;1',\n", + " 'upload_time': '2020-11-30T00:26:34.514069Z',\n", + " 'decommissioned': False}" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "patron = service_client.get_model(\"dtmi:mymodels:patron;1\")\n", + "patron.as_dict()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And we have several `customers` in our ecosystem that are made from the `patron` model." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "query_expression = \"SELECT * FROM digitaltwins t where IS_OF_MODEL('dtmi:billmanh:patron;1')\"\n", + "query_result = service_client.query_twins(query_expression)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note** the query object loves to drop values. To keep from making multiple queries, save the data somewhere. " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "values = []\n", + "for i in query_result:\n", + " values.append(i)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "df_customers = pd.DataFrame([[i['$dtId'],i['satisfaction']] for i in values],\n", + " columns=['id','satisfaction'])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idsatisfaction
0customer-cc04f3b6-39b0-4cef-bfff-a7d668cce44610
1customer-3cbd5e60-957d-44ff-944f-9adb42a20a5210
2customer-5c454e2f-f70b-4352-b75a-958f1a49beba7
3customer-26196fee-5ffd-457a-86b7-192a998f3cf29
4customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32c7
.........
79customer-45e9aa03-733d-4a99-b9d5-94f1c6b042149
80customer-0234cb48-1fa2-43e0-b69d-36a6ff2696669
81customer-75b2f757-faee-4a85-bc93-e6e9ff7cd8916
82customer-048f85a8-173e-4305-92b8-ead2a748b07f8
83customer-25e19268-3433-4f09-afe3-94f46631336810
\n", + "

84 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " id satisfaction\n", + "0 customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446 10\n", + "1 customer-3cbd5e60-957d-44ff-944f-9adb42a20a52 10\n", + "2 customer-5c454e2f-f70b-4352-b75a-958f1a49beba 7\n", + "3 customer-26196fee-5ffd-457a-86b7-192a998f3cf2 9\n", + "4 customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32c 7\n", + ".. ... ...\n", + "79 customer-45e9aa03-733d-4a99-b9d5-94f1c6b04214 9\n", + "80 customer-0234cb48-1fa2-43e0-b69d-36a6ff269666 9\n", + "81 customer-75b2f757-faee-4a85-bc93-e6e9ff7cd891 6\n", + "82 customer-048f85a8-173e-4305-92b8-ead2a748b07f 8\n", + "83 customer-25e19268-3433-4f09-afe3-94f466313368 10\n", + "\n", + "[84 rows x 2 columns]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_customers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's add another model for `tickets`. In our very simple model, we will assume that `customers` will buy `tickets` to our events." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "# service_client.delete_model(ticket_model_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "# ticket_model_json = yaml.safe_load(open(\"models/ticket.json\"))\n", + "# service_client.create_models([ticket_model_json])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'display_name': {'en': 'ticket'},\n", + " 'description': {'en': 'an abstract ticket'},\n", + " 'id': 'dtmi:billmanh:ticket;1',\n", + " 'upload_time': '2020-11-26T20:12:34.578813Z',\n", + " 'decommissioned': False}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ticket_model_id = \"dtmi:mymodels:ticket;1\"\n", + "get_model = service_client.get_model(ticket_model_id)\n", + "get_model.as_dict()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the dict returned from the service doesn't contain all of the values that you created, but that's ok. " + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def generate_twin(name,model_id):\n", + " digital_twin_id = f'{name}-{str(uuid.uuid4())}'\n", + " dt_json = {\n", + " \"$metadata\": {\n", + " \"$model\": model_id\n", + " }\n", + " }\n", + " return digital_twin_id,dt_json\n", + "\n", + "def updsert_twin():\n", + " created_twin = service_client.upsert_digital_twin(digital_twin_id, dt_json)\n", + " return created_twin" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "ticket_id, ticket_json = generate_twin(\"ticket\",ticket_model_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'$metadata': {'$model': 'dtmi:billmanh:ticket;1'}}" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ticket_json" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So when I create the `tickets` I’m going to create them as unsold (`available`) and for a range of events. I'm just going to introduce some tickets into the system for some shows coming up. These tickets just exist locally at this point, but we will 'go live' when we push them into the ecosystem." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$metadataevent_titlestateticket_locationuid
0{'$model': 'dtmi:billmanh:ticket;1'}Nirvanaopen0ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbe
1{'$model': 'dtmi:billmanh:ticket;1'}Nirvanaopen1ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9
2{'$model': 'dtmi:billmanh:ticket;1'}Nirvanaopen2ticket-260e3349-adc5-41af-9ae5-4f3690d813c7
3{'$model': 'dtmi:billmanh:ticket;1'}Nirvanaopen3ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717
4{'$model': 'dtmi:billmanh:ticket;1'}Nirvanaopen4ticket-cf70a216-0183-4458-8e13-94f432f7b8c0
5{'$model': 'dtmi:billmanh:ticket;1'}Smashing Pumpkinsopen0ticket-57a45e6f-f42a-41b0-a0f5-68e5e0fd67ba
6{'$model': 'dtmi:billmanh:ticket;1'}Smashing Pumpkinsopen1ticket-7f414b3f-fde6-4cbc-ad58-86c7ccfb1109
7{'$model': 'dtmi:billmanh:ticket;1'}Smashing Pumpkinsopen2ticket-2d69b397-3ec3-4588-9eaf-5eb931f1f8c4
8{'$model': 'dtmi:billmanh:ticket;1'}Smashing Pumpkinsopen3ticket-e9fc6ad3-d7ad-4ef1-8a58-0759a0f27a00
9{'$model': 'dtmi:billmanh:ticket;1'}Smashing Pumpkinsopen4ticket-dc48bc69-b1f5-4850-b5af-037c4ea72961
10{'$model': 'dtmi:billmanh:ticket;1'}Foo Fightersopen0ticket-b7a550e8-0fa8-46ff-8b4c-1a290a5d706c
11{'$model': 'dtmi:billmanh:ticket;1'}Foo Fightersopen1ticket-92826a40-b024-4b91-a4d2-7e20dcfddce3
12{'$model': 'dtmi:billmanh:ticket;1'}Foo Fightersopen2ticket-8c483b5c-37d4-4027-8d67-d920bf8ca063
13{'$model': 'dtmi:billmanh:ticket;1'}Foo Fightersopen3ticket-09028447-8e23-4e4f-a100-098a4dc8e919
14{'$model': 'dtmi:billmanh:ticket;1'}Foo Fightersopen4ticket-ccc89397-0d52-42a3-8fe1-39c97158c87c
\n", + "
" + ], + "text/plain": [ + " $metadata event_title state \\\n", + "0 {'$model': 'dtmi:billmanh:ticket;1'} Nirvana open \n", + "1 {'$model': 'dtmi:billmanh:ticket;1'} Nirvana open \n", + "2 {'$model': 'dtmi:billmanh:ticket;1'} Nirvana open \n", + "3 {'$model': 'dtmi:billmanh:ticket;1'} Nirvana open \n", + "4 {'$model': 'dtmi:billmanh:ticket;1'} Nirvana open \n", + "5 {'$model': 'dtmi:billmanh:ticket;1'} Smashing Pumpkins open \n", + "6 {'$model': 'dtmi:billmanh:ticket;1'} Smashing Pumpkins open \n", + "7 {'$model': 'dtmi:billmanh:ticket;1'} Smashing Pumpkins open \n", + "8 {'$model': 'dtmi:billmanh:ticket;1'} Smashing Pumpkins open \n", + "9 {'$model': 'dtmi:billmanh:ticket;1'} Smashing Pumpkins open \n", + "10 {'$model': 'dtmi:billmanh:ticket;1'} Foo Fighters open \n", + "11 {'$model': 'dtmi:billmanh:ticket;1'} Foo Fighters open \n", + "12 {'$model': 'dtmi:billmanh:ticket;1'} Foo Fighters open \n", + "13 {'$model': 'dtmi:billmanh:ticket;1'} Foo Fighters open \n", + "14 {'$model': 'dtmi:billmanh:ticket;1'} Foo Fighters open \n", + "\n", + " ticket_location uid \n", + "0 0 ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbe \n", + "1 1 ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9 \n", + "2 2 ticket-260e3349-adc5-41af-9ae5-4f3690d813c7 \n", + "3 3 ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717 \n", + "4 4 ticket-cf70a216-0183-4458-8e13-94f432f7b8c0 \n", + "5 0 ticket-57a45e6f-f42a-41b0-a0f5-68e5e0fd67ba \n", + "6 1 ticket-7f414b3f-fde6-4cbc-ad58-86c7ccfb1109 \n", + "7 2 ticket-2d69b397-3ec3-4588-9eaf-5eb931f1f8c4 \n", + "8 3 ticket-e9fc6ad3-d7ad-4ef1-8a58-0759a0f27a00 \n", + "9 4 ticket-dc48bc69-b1f5-4850-b5af-037c4ea72961 \n", + "10 0 ticket-b7a550e8-0fa8-46ff-8b4c-1a290a5d706c \n", + "11 1 ticket-92826a40-b024-4b91-a4d2-7e20dcfddce3 \n", + "12 2 ticket-8c483b5c-37d4-4027-8d67-d920bf8ca063 \n", + "13 3 ticket-09028447-8e23-4e4f-a100-098a4dc8e919 \n", + "14 4 ticket-ccc89397-0d52-42a3-8fe1-39c97158c87c " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def generate_tickets(title,n_tickets):\n", + " tickets = []\n", + " for i in range(n_tickets):\n", + " ticket_id, ticket_json = generate_twin(\"ticket\",ticket_model_id)\n", + " ticket_json['event_title'] = title\n", + " ticket_json['state'] = 'open'\n", + " ticket_json['ticket_location'] = i\n", + " ticket_json['uid'] = f'ticket-{str(uuid.uuid4())}'\n", + " tickets.append(ticket_json)\n", + " return tickets\n", + "\n", + "tickets_df = pd.concat([pd.DataFrame(generate_tickets('Nirvana',5)),\n", + " pd.DataFrame(generate_tickets('Smashing Pumpkins',5)),\n", + " pd.DataFrame(generate_tickets('Foo Fighters',5))]).reset_index(drop=True)\n", + "\n", + "tickets_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ok now that we know what tickets we want to sell, let's push them to the digital twin ecosystem. This is exactly the same as what we did with `Customers` in step one. " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "ticket_twins = []\n", + "for i in tickets_df.index:\n", + " ticket_data = tickets_df.loc[i]\n", + " ticket_twin_id = ticket_data['uid']\n", + " ticket_json = ticket_data.drop('uid').to_dict()\n", + " created_twin = service_client.upsert_digital_twin(ticket_twin_id, ticket_json)\n", + " ticket_twins.append(created_twin)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'$dtId': 'ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbe',\n", + " '$etag': 'W/\"172ddd8b-a58c-41da-9081-19b0ff611ffa\"',\n", + " 'event_title': 'Nirvana',\n", + " 'state': 'open',\n", + " 'ticket_location': '0',\n", + " '$metadata': {'$model': 'dtmi:billmanh:ticket;1',\n", + " 'event_title': {'lastUpdateTime': '2020-12-07T01:30:50.2153563Z'},\n", + " 'state': {'lastUpdateTime': '2020-12-07T01:30:50.2153563Z'},\n", + " 'ticket_location': {'lastUpdateTime': '2020-12-07T01:30:50.2153563Z'}}},\n", + " {'$dtId': 'ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9',\n", + " '$etag': 'W/\"f775c7be-176a-401a-b427-bd7797752283\"',\n", + " 'event_title': 'Nirvana',\n", + " 'state': 'open',\n", + " 'ticket_location': '1',\n", + " '$metadata': {'$model': 'dtmi:billmanh:ticket;1',\n", + " 'event_title': {'lastUpdateTime': '2020-12-07T01:30:50.8441094Z'},\n", + " 'state': {'lastUpdateTime': '2020-12-07T01:30:50.8441094Z'},\n", + " 'ticket_location': {'lastUpdateTime': '2020-12-07T01:30:50.8441094Z'}}},\n", + " {'$dtId': 'ticket-260e3349-adc5-41af-9ae5-4f3690d813c7',\n", + " '$etag': 'W/\"8670c70a-b849-4d0c-abbc-5bcf6b77ec21\"',\n", + " 'event_title': 'Nirvana',\n", + " 'state': 'open',\n", + " 'ticket_location': '2',\n", + " '$metadata': {'$model': 'dtmi:billmanh:ticket;1',\n", + " 'event_title': {'lastUpdateTime': '2020-12-07T01:30:51.0152850Z'},\n", + " 'state': {'lastUpdateTime': '2020-12-07T01:30:51.0152850Z'},\n", + " 'ticket_location': {'lastUpdateTime': '2020-12-07T01:30:51.0152850Z'}}}]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ticket_twins[:3]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# # Delete twins if you want\n", + "# for i in tickets_df.index:\n", + "# ticket_data = tickets_df.loc[i]\n", + "# ticket_twin_id = ticket_data['uid']\n", + "# service_client.delete_digital_twin(ticket_twin_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So now I have `customers` and I have `tickets`, but there is no relationship between them. Let's pretend that I have a website that sells tickets. When a `customer` buys a `ticket` on the website, a relationship between the two is established AND the status of the ticket changes to sold. \n", + "\n", + "* website (or mobile app) will tell the user which tickets are still available, and have features that allow them to search by row, or by concert.\n", + "* the website (or mobile app) gets the latitude and longitude of the user when they buy the tickets. \n", + "* once purchased, the status of that ticket changes to `sold`.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# The user experience (mobile app)\n", + "\n", + "Here is where the user will buy tickets. \n", + "\n", + "This cell is a customer experience. The `customer` enters the website and looks up the available tickets for the show they want. This data comes from the website. " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "# selection from a dropdown menu or something\n", + "selection_show = 'Nirvana'\n", + "# just grabbing the first customer. \n", + "user = df_customers.loc[0]['id']\n", + "user_lat = np.random.randint(0,100)\n", + "user_long = np.random.randint(0,100)\n", + "\n", + "query_expression = f\"\"\"\n", + "SELECT * FROM digitaltwins where IS_OF_MODEL('{ticket_model_id}') \n", + "and state = 'open'\n", + "and event_title = '{selection_show}'\n", + "\"\"\"\n", + "query_result = service_client.query_twins(query_expression)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the client dumps records after the query. Try running the cell below a few times. If you need to use the values again and again you need to put them into memory somewhere. " + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$dtIdevent_titlestate
0ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbeNirvanaopen
1ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9Nirvanaopen
2ticket-cf70a216-0183-4458-8e13-94f432f7b8c0Nirvanaopen
3ticket-260e3349-adc5-41af-9ae5-4f3690d813c7Nirvanaopen
4ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717Nirvanaopen
\n", + "
" + ], + "text/plain": [ + " $dtId event_title state\n", + "0 ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbe Nirvana open\n", + "1 ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9 Nirvana open\n", + "2 ticket-cf70a216-0183-4458-8e13-94f432f7b8c0 Nirvana open\n", + "3 ticket-260e3349-adc5-41af-9ae5-4f3690d813c7 Nirvana open\n", + "4 ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717 Nirvana open" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "available_tickets_df = pd.DataFrame([[i['$dtId'],i['event_title'],i['state']] for i in query_result],\n", + " columns = ['$dtId','event_title','state'])\n", + "available_tickets_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now the user chooses to buy a ticket. The ticket state changes to closed." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'$dtId': 'ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbe',\n", + " '$etag': 'W/\"172ddd8b-a58c-41da-9081-19b0ff611ffa\"',\n", + " 'event_title': 'Nirvana',\n", + " 'state': 'open',\n", + " 'ticket_location': '0',\n", + " '$metadata': {'$model': 'dtmi:billmanh:ticket;1',\n", + " 'event_title': {'lastUpdateTime': '2020-12-07T01:30:50.2153563Z'},\n", + " 'state': {'lastUpdateTime': '2020-12-07T01:30:50.2153563Z'},\n", + " 'ticket_location': {'lastUpdateTime': '2020-12-07T01:30:50.2153563Z'}}}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "customer_selection = available_tickets_df.loc[0]['$dtId']\n", + "\n", + "service_client.get_digital_twin(customer_selection)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [], + "source": [ + "customer_selection = available_tickets_df.loc[0]['$dtId']\n", + "\n", + "patch = [\n", + " {\n", + " \"op\": \"replace\",\n", + " \"path\": \"\",\n", + " \"value\": \"sold\"\n", + " }\n", + "]\n", + "service_client.update_component(customer_selection,\"state\", patch)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**NOTE** that the path is `\"\"` when the item is at the root of the state." + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$dtIdevent_titlestate
0ticket-600f2149-1ed8-4cb4-b0b8-bb6d03f575cdNirvanasold
1ticket-b8860b9b-cc54-4591-abb3-2abc3adc0800Nirvanasold
2ticket-2256b8c9-1dd8-4ef8-a00a-b191dc122532Nirvanasold
3ticket-99023c6e-5d97-4e43-ab08-3d893233c8b0Nirvanasold
4ticket-132839a8-a4a3-4c75-ae96-9ff4085a07d4Nirvanasold
5ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbeNirvanaopen
6ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9Nirvanaopen
7ticket-cf70a216-0183-4458-8e13-94f432f7b8c0Nirvanaopen
8ticket-260e3349-adc5-41af-9ae5-4f3690d813c7Nirvanaopen
9ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717Nirvanaopen
\n", + "
" + ], + "text/plain": [ + " $dtId event_title state\n", + "0 ticket-600f2149-1ed8-4cb4-b0b8-bb6d03f575cd Nirvana sold\n", + "1 ticket-b8860b9b-cc54-4591-abb3-2abc3adc0800 Nirvana sold\n", + "2 ticket-2256b8c9-1dd8-4ef8-a00a-b191dc122532 Nirvana sold\n", + "3 ticket-99023c6e-5d97-4e43-ab08-3d893233c8b0 Nirvana sold\n", + "4 ticket-132839a8-a4a3-4c75-ae96-9ff4085a07d4 Nirvana sold\n", + "5 ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbe Nirvana open\n", + "6 ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9 Nirvana open\n", + "7 ticket-cf70a216-0183-4458-8e13-94f432f7b8c0 Nirvana open\n", + "8 ticket-260e3349-adc5-41af-9ae5-4f3690d813c7 Nirvana open\n", + "9 ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717 Nirvana open" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query_expression = f\"\"\"\n", + "SELECT * FROM digitaltwins where IS_OF_MODEL('{ticket_model_id}') \n", + "and event_title = '{selection_show}'\n", + "\"\"\"\n", + "query_result = service_client.query_twins(query_expression)\n", + "available_tickets_df = pd.DataFrame([[i['$dtId'],i['event_title'],i['state']] for i in query_result],\n", + " columns = ['$dtId','event_title','state'])\n", + "available_tickets_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Relationships: \n", + "* the target is the leaf (or the customer)\n", + "* the source is the branch (or the ticket)\n", + "\n", + "you can add extra stuff to the relationship. " + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbe\n", + "customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446\n" + ] + } + ], + "source": [ + "print(customer_selection)\n", + "print(user)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'$relationshipId': 'ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbeownedBycustomer-cc04f3b6-39b0-4cef-bfff-a7d668cce446',\n", + " '$etag': 'W/\"63140a7e-b8e9-4638-909e-9adf67a05d0d\"',\n", + " '$sourceId': 'ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbe',\n", + " '$relationshipName': 'ownedBy',\n", + " '$targetId': 'customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446',\n", + " 'bought_online': True}" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tickethoder_relationship = {\n", + " \"$relationshipId\": f\"{customer_selection}ownedBy{user}\",\n", + " \"$sourceId\": customer_selection,\n", + " \"$relationshipName\": \"ownedBy\",\n", + " \"$targetId\": user,\n", + " \"bought_online\": True\n", + " }\n", + "\n", + "service_client.upsert_relationship(\n", + " tickethoder_relationship[\"$sourceId\"],\n", + " tickethoder_relationship[\"$relationshipId\"],\n", + " tickethoder_relationship\n", + " )" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "14" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[i for i in service_client.list_relationships(customer_selection)]\n", + "i" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Another customer will buy several tickets. " + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$dtIdevent_titlestate
0ticket-52e25a0a-06b1-428f-b6a2-479cafc45c16Smashing Pumpkinsopen
1ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56Foo Fightersopen
2ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3cFoo Fightersopen
3ticket-3f82286d-7e0b-4df5-a491-d9e28f7c94f9Foo Fightersopen
4ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9Nirvanaopen
5ticket-cf70a216-0183-4458-8e13-94f432f7b8c0Nirvanaopen
6ticket-260e3349-adc5-41af-9ae5-4f3690d813c7Nirvanaopen
7ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717Nirvanaopen
8ticket-b7a550e8-0fa8-46ff-8b4c-1a290a5d706cFoo Fightersopen
9ticket-57a45e6f-f42a-41b0-a0f5-68e5e0fd67baSmashing Pumpkinsopen
10ticket-7f414b3f-fde6-4cbc-ad58-86c7ccfb1109Smashing Pumpkinsopen
11ticket-2d69b397-3ec3-4588-9eaf-5eb931f1f8c4Smashing Pumpkinsopen
12ticket-e9fc6ad3-d7ad-4ef1-8a58-0759a0f27a00Smashing Pumpkinsopen
13ticket-dc48bc69-b1f5-4850-b5af-037c4ea72961Smashing Pumpkinsopen
14ticket-92826a40-b024-4b91-a4d2-7e20dcfddce3Foo Fightersopen
15ticket-8c483b5c-37d4-4027-8d67-d920bf8ca063Foo Fightersopen
16ticket-09028447-8e23-4e4f-a100-098a4dc8e919Foo Fightersopen
17ticket-ccc89397-0d52-42a3-8fe1-39c97158c87cFoo Fightersopen
\n", + "
" + ], + "text/plain": [ + " $dtId event_title state\n", + "0 ticket-52e25a0a-06b1-428f-b6a2-479cafc45c16 Smashing Pumpkins open\n", + "1 ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56 Foo Fighters open\n", + "2 ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3c Foo Fighters open\n", + "3 ticket-3f82286d-7e0b-4df5-a491-d9e28f7c94f9 Foo Fighters open\n", + "4 ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9 Nirvana open\n", + "5 ticket-cf70a216-0183-4458-8e13-94f432f7b8c0 Nirvana open\n", + "6 ticket-260e3349-adc5-41af-9ae5-4f3690d813c7 Nirvana open\n", + "7 ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717 Nirvana open\n", + "8 ticket-b7a550e8-0fa8-46ff-8b4c-1a290a5d706c Foo Fighters open\n", + "9 ticket-57a45e6f-f42a-41b0-a0f5-68e5e0fd67ba Smashing Pumpkins open\n", + "10 ticket-7f414b3f-fde6-4cbc-ad58-86c7ccfb1109 Smashing Pumpkins open\n", + "11 ticket-2d69b397-3ec3-4588-9eaf-5eb931f1f8c4 Smashing Pumpkins open\n", + "12 ticket-e9fc6ad3-d7ad-4ef1-8a58-0759a0f27a00 Smashing Pumpkins open\n", + "13 ticket-dc48bc69-b1f5-4850-b5af-037c4ea72961 Smashing Pumpkins open\n", + "14 ticket-92826a40-b024-4b91-a4d2-7e20dcfddce3 Foo Fighters open\n", + "15 ticket-8c483b5c-37d4-4027-8d67-d920bf8ca063 Foo Fighters open\n", + "16 ticket-09028447-8e23-4e4f-a100-098a4dc8e919 Foo Fighters open\n", + "17 ticket-ccc89397-0d52-42a3-8fe1-39c97158c87c Foo Fighters open" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query_expression = f\"\"\"\n", + "SELECT * FROM digitaltwins t where IS_OF_MODEL('{ticket_model_id}') \n", + "and t.state = 'open'\n", + "\"\"\"\n", + "query_result = service_client.query_twins(query_expression)\n", + "available_tickets_df = pd.DataFrame([[i['$dtId'],i['event_title'],i['state']] for i in query_result],\n", + " columns = ['$dtId','event_title','state'])\n", + "available_tickets_df" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'customer-25e19268-3433-4f09-afe3-94f466313368'" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "other_user = df_customers.loc[len(df_customers)-1]['id']\n", + "other_user" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['ticket-52e25a0a-06b1-428f-b6a2-479cafc45c16',\n", + " 'ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56',\n", + " 'ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3c',\n", + " 'ticket-3f82286d-7e0b-4df5-a491-d9e28f7c94f9',\n", + " 'ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9',\n", + " 'ticket-cf70a216-0183-4458-8e13-94f432f7b8c0']" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tickets_bought = available_tickets_df.loc[:5,'$dtId'].tolist()\n", + "tickets_bought" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['ticket-52e25a0a-06b1-428f-b6a2-479cafc45c16',\n", + " 'ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56',\n", + " 'ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3c',\n", + " 'ticket-3f82286d-7e0b-4df5-a491-d9e28f7c94f9',\n", + " 'ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9',\n", + " 'ticket-cf70a216-0183-4458-8e13-94f432f7b8c0']" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tickets_bought" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ticket-52e25a0a-06b1-428f-b6a2-479cafc45c16\n", + "ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56\n", + "ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3c\n", + "ticket-3f82286d-7e0b-4df5-a491-d9e28f7c94f9\n", + "ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9\n", + "ticket-cf70a216-0183-4458-8e13-94f432f7b8c0\n" + ] + } + ], + "source": [ + "for t in tickets_bought:\n", + " print(t)\n", + " tickethoder_relationship = {\n", + " \"$relationshipId\": f\"{customer_selection}ownedBy{other_user}\",\n", + " \"$sourceId\": t,\n", + " \"$relationshipName\": \"ownedBy\",\n", + " \"$targetId\": other_user,\n", + " \"bought_online\": False\n", + " }\n", + "\n", + " service_client.upsert_relationship(\n", + " tickethoder_relationship[\"$sourceId\"],\n", + " tickethoder_relationship[\"$relationshipId\"],\n", + " tickethoder_relationship\n", + " )\n", + " customer_selection = available_tickets_df.loc[0]['$dtId']\n", + "\n", + " patch = [\n", + " {\n", + " \"op\": \"replace\",\n", + " \"path\": \"\",\n", + " \"value\": \"sold\"\n", + " }\n", + " ]\n", + " service_client.update_component(t,\"state\", patch)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's lets look at the tickets. " + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$dtIdevent_titlestate
0ticket-600f2149-1ed8-4cb4-b0b8-bb6d03f575cdNirvanasold
1ticket-b8860b9b-cc54-4591-abb3-2abc3adc0800Nirvanasold
2ticket-2256b8c9-1dd8-4ef8-a00a-b191dc122532Nirvanasold
3ticket-18663c82-67a2-4791-b1c4-05db261af867Smashing Pumpkinssold
4ticket-99023c6e-5d97-4e43-ab08-3d893233c8b0Nirvanasold
5ticket-132839a8-a4a3-4c75-ae96-9ff4085a07d4Nirvanasold
6ticket-79f3f382-bd65-4788-8ad1-0db507b8a3abSmashing Pumpkinssold
7ticket-9f7c0906-9729-4568-a559-89d63b634d09Smashing Pumpkinssold
8ticket-633f4537-50a1-4735-9e70-99bf19be51e4Smashing Pumpkinssold
9ticket-52e25a0a-06b1-428f-b6a2-479cafc45c16Smashing Pumpkinssold
10ticket-ca767da7-0152-4861-9be6-fbe39b5dd9d8Foo Fighterssold
11ticket-0cd531c6-d81c-4c4c-84c9-9592a8f43e70Foo Fighterssold
12ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56Foo Fighterssold
13ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3cFoo Fighterssold
14ticket-3f82286d-7e0b-4df5-a491-d9e28f7c94f9Foo Fighterssold
15ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbeNirvanasold
16ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9Nirvanasold
17ticket-cf70a216-0183-4458-8e13-94f432f7b8c0Nirvanasold
18ticket-260e3349-adc5-41af-9ae5-4f3690d813c7Nirvanaopen
19ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717Nirvanaopen
20ticket-b7a550e8-0fa8-46ff-8b4c-1a290a5d706cFoo Fightersopen
21ticket-57a45e6f-f42a-41b0-a0f5-68e5e0fd67baSmashing Pumpkinsopen
22ticket-7f414b3f-fde6-4cbc-ad58-86c7ccfb1109Smashing Pumpkinsopen
23ticket-2d69b397-3ec3-4588-9eaf-5eb931f1f8c4Smashing Pumpkinsopen
24ticket-e9fc6ad3-d7ad-4ef1-8a58-0759a0f27a00Smashing Pumpkinsopen
25ticket-dc48bc69-b1f5-4850-b5af-037c4ea72961Smashing Pumpkinsopen
26ticket-92826a40-b024-4b91-a4d2-7e20dcfddce3Foo Fightersopen
27ticket-8c483b5c-37d4-4027-8d67-d920bf8ca063Foo Fightersopen
28ticket-09028447-8e23-4e4f-a100-098a4dc8e919Foo Fightersopen
29ticket-ccc89397-0d52-42a3-8fe1-39c97158c87cFoo Fightersopen
\n", + "
" + ], + "text/plain": [ + " $dtId event_title state\n", + "0 ticket-600f2149-1ed8-4cb4-b0b8-bb6d03f575cd Nirvana sold\n", + "1 ticket-b8860b9b-cc54-4591-abb3-2abc3adc0800 Nirvana sold\n", + "2 ticket-2256b8c9-1dd8-4ef8-a00a-b191dc122532 Nirvana sold\n", + "3 ticket-18663c82-67a2-4791-b1c4-05db261af867 Smashing Pumpkins sold\n", + "4 ticket-99023c6e-5d97-4e43-ab08-3d893233c8b0 Nirvana sold\n", + "5 ticket-132839a8-a4a3-4c75-ae96-9ff4085a07d4 Nirvana sold\n", + "6 ticket-79f3f382-bd65-4788-8ad1-0db507b8a3ab Smashing Pumpkins sold\n", + "7 ticket-9f7c0906-9729-4568-a559-89d63b634d09 Smashing Pumpkins sold\n", + "8 ticket-633f4537-50a1-4735-9e70-99bf19be51e4 Smashing Pumpkins sold\n", + "9 ticket-52e25a0a-06b1-428f-b6a2-479cafc45c16 Smashing Pumpkins sold\n", + "10 ticket-ca767da7-0152-4861-9be6-fbe39b5dd9d8 Foo Fighters sold\n", + "11 ticket-0cd531c6-d81c-4c4c-84c9-9592a8f43e70 Foo Fighters sold\n", + "12 ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56 Foo Fighters sold\n", + "13 ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3c Foo Fighters sold\n", + "14 ticket-3f82286d-7e0b-4df5-a491-d9e28f7c94f9 Foo Fighters sold\n", + "15 ticket-b10211a2-1a7a-4f6f-9fc3-d23daefe8cbe Nirvana sold\n", + "16 ticket-d1d2ad08-add8-4f5f-97cf-0b6516914cc9 Nirvana sold\n", + "17 ticket-cf70a216-0183-4458-8e13-94f432f7b8c0 Nirvana sold\n", + "18 ticket-260e3349-adc5-41af-9ae5-4f3690d813c7 Nirvana open\n", + "19 ticket-9f2bc5f4-b7d6-4f86-a1f8-c570cb263717 Nirvana open\n", + "20 ticket-b7a550e8-0fa8-46ff-8b4c-1a290a5d706c Foo Fighters open\n", + "21 ticket-57a45e6f-f42a-41b0-a0f5-68e5e0fd67ba Smashing Pumpkins open\n", + "22 ticket-7f414b3f-fde6-4cbc-ad58-86c7ccfb1109 Smashing Pumpkins open\n", + "23 ticket-2d69b397-3ec3-4588-9eaf-5eb931f1f8c4 Smashing Pumpkins open\n", + "24 ticket-e9fc6ad3-d7ad-4ef1-8a58-0759a0f27a00 Smashing Pumpkins open\n", + "25 ticket-dc48bc69-b1f5-4850-b5af-037c4ea72961 Smashing Pumpkins open\n", + "26 ticket-92826a40-b024-4b91-a4d2-7e20dcfddce3 Foo Fighters open\n", + "27 ticket-8c483b5c-37d4-4027-8d67-d920bf8ca063 Foo Fighters open\n", + "28 ticket-09028447-8e23-4e4f-a100-098a4dc8e919 Foo Fighters open\n", + "29 ticket-ccc89397-0d52-42a3-8fe1-39c97158c87c Foo Fighters open" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query_expression = f\"\"\"\n", + "SELECT * FROM digitaltwins t where IS_OF_MODEL('{ticket_model_id}') \n", + "\"\"\"\n", + "query_result = service_client.query_twins(query_expression)\n", + "available_tickets_df = pd.DataFrame([[i['$dtId'],i['event_title'],i['state']] for i in query_result],\n", + " columns = ['$dtId','event_title','state'])\n", + "available_tickets_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can see that our web app can easily filter on tickets that are open and give a person specific rows. We will get into more complicated analysis in the next steps." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (azure_test)", + "language": "python", + "name": "azure_test" + }, + "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.7.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/03_Adding_a_bunch of_other_components.ipynb b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/03_Adding_a_bunch of_other_components.ipynb new file mode 100644 index 000000000000..578e852889ed --- /dev/null +++ b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/03_Adding_a_bunch of_other_components.ipynb @@ -0,0 +1,649 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Moving through a physical experience\n", + "## Some examples of IoT application that ping a digital twin\n", + "\n", + "This doc does not really teach anything new, but it will give us some exercise with the previous examples. We will need a more complicated model in future steps. \n", + "* Adding more connections.\n", + "* Making connections using ids. \n", + "\n", + "\n", + "[This is the SDK repo on Github](https://github.com/Azure/azure-sdk-for-python/tree/4559e19e2f3146a49f1eba1706bb798071f4a1f5/sdk/digitaltwins/azure-digitaltwins-core)\n", + "\n", + "[Here is the doc on the query language](https://docs.microsoft.com/en-us/azure/digital-twins/concepts-query-language)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from azure.identity import AzureCliCredential\n", + "from azure.digitaltwins.core import DigitalTwinsClient\n", + "\n", + "# using yaml instead of \n", + "import yaml\n", + "import uuid\n", + "\n", + "# using altair instead of matplotlib for vizuals\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "# you will get this from the ADT resource at portal.azure.com\n", + "your_digital_twin_url = \"home-test-twin.api.wcus.digitaltwins.azure.net\"\n", + "\n", + "azure_cli = AzureCliCredential()\n", + "service_client = DigitalTwinsClient(\n", + " your_digital_twin_url, azure_cli)\n", + "service_client" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "query_expression = \"SELECT * FROM digitaltwins t where IS_OF_MODEL('dtmi:billmanh:patron;1')\"\n", + "query_result = service_client.query_twins(query_expression)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**Note** the query object loves to drop values. To keep from making multiple queries, save the data somewhere. " + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "values = []\n", + "for i in query_result:\n", + " values.append(i)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "df_customers = pd.DataFrame([[i['$dtId'],i['satisfaction']] for i in values],\n", + " columns=['id','satisfaction'])" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idsatisfaction
0customer-cc04f3b6-39b0-4cef-bfff-a7d668cce44610
1customer-3cbd5e60-957d-44ff-944f-9adb42a20a5210
2customer-5c454e2f-f70b-4352-b75a-958f1a49beba7
3customer-26196fee-5ffd-457a-86b7-192a998f3cf29
4customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32c7
.........
79customer-45e9aa03-733d-4a99-b9d5-94f1c6b042149
80customer-0234cb48-1fa2-43e0-b69d-36a6ff2696669
81customer-75b2f757-faee-4a85-bc93-e6e9ff7cd8916
82customer-048f85a8-173e-4305-92b8-ead2a748b07f8
83customer-25e19268-3433-4f09-afe3-94f46631336810
\n", + "

84 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " id satisfaction\n", + "0 customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446 10\n", + "1 customer-3cbd5e60-957d-44ff-944f-9adb42a20a52 10\n", + "2 customer-5c454e2f-f70b-4352-b75a-958f1a49beba 7\n", + "3 customer-26196fee-5ffd-457a-86b7-192a998f3cf2 9\n", + "4 customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32c 7\n", + ".. ... ...\n", + "79 customer-45e9aa03-733d-4a99-b9d5-94f1c6b04214 9\n", + "80 customer-0234cb48-1fa2-43e0-b69d-36a6ff269666 9\n", + "81 customer-75b2f757-faee-4a85-bc93-e6e9ff7cd891 6\n", + "82 customer-048f85a8-173e-4305-92b8-ead2a748b07f 8\n", + "83 customer-25e19268-3433-4f09-afe3-94f466313368 10\n", + "\n", + "[84 rows x 2 columns]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df_customers" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "and a `df` of the tickets" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$dtIdevent_titlestate
0ticket-633f4537-50a1-4735-9e70-99bf19be51e4Smashing Pumpkinsopen
1ticket-52e25a0a-06b1-428f-b6a2-479cafc45c16Smashing Pumpkinsopen
2ticket-0cd531c6-d81c-4c4c-84c9-9592a8f43e70Foo Fightersopen
3ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56Foo Fightersopen
4ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3cFoo Fightersopen
5ticket-3f82286d-7e0b-4df5-a491-d9e28f7c94f9Foo Fightersopen
\n", + "
" + ], + "text/plain": [ + " $dtId event_title state\n", + "0 ticket-633f4537-50a1-4735-9e70-99bf19be51e4 Smashing Pumpkins open\n", + "1 ticket-52e25a0a-06b1-428f-b6a2-479cafc45c16 Smashing Pumpkins open\n", + "2 ticket-0cd531c6-d81c-4c4c-84c9-9592a8f43e70 Foo Fighters open\n", + "3 ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56 Foo Fighters open\n", + "4 ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3c Foo Fighters open\n", + "5 ticket-3f82286d-7e0b-4df5-a491-d9e28f7c94f9 Foo Fighters open" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query_expression = f\"\"\"\n", + "SELECT * FROM digitaltwins t where IS_OF_MODEL('dtmi:mymodels:ticket;1') and t.state='open'\n", + "\"\"\"\n", + "query_result = service_client.query_twins(query_expression)\n", + "available_tickets_df = pd.DataFrame([[i['$dtId'],i['event_title'],i['state']] for i in query_result],\n", + " columns = ['$dtId','event_title','state'])\n", + "available_tickets_df" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's sell a couple more of those tickets. " + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['ticket-633f4537-50a1-4735-9e70-99bf19be51e4',\n", + " 'ticket-0cd531c6-d81c-4c4c-84c9-9592a8f43e70']" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "tickets_sold = available_tickets_df.drop_duplicates(subset='event_title')['$dtId'].tolist()\n", + "\n", + "tickets_sold" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['customer-c87adbfa-1c6e-4ea9-9f03-83e3877ef5fc',\n", + " 'customer-21e17d28-76c3-4c04-8df9-396703692a68']" + ] + }, + "execution_count": 34, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "customers_sold = df_customers['id'].tolist()[5:7]\n", + "\n", + "customers_sold" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The purpose is to create a simulation. So I'm just hacking something out real quick to make our model look a little fuller." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "ticket-633f4537-50a1-4735-9e70-99bf19be51e4\n", + "ticket-0cd531c6-d81c-4c4c-84c9-9592a8f43e70\n" + ] + } + ], + "source": [ + "for c,t in enumerate(tickets_sold):\n", + " print(t)\n", + " tickethoder_relationship = {\n", + " \"$relationshipId\": f\"{t}ownedBy{customers_sold[c]}\",\n", + " \"$sourceId\": t,\n", + " \"$relationshipName\": \"ownedBy\",\n", + " \"$targetId\": customers_sold[c],\n", + " \"bought_online\": False\n", + " }\n", + "\n", + " service_client.upsert_relationship(\n", + " tickethoder_relationship[\"$sourceId\"],\n", + " tickethoder_relationship[\"$relationshipId\"],\n", + " tickethoder_relationship\n", + " )\n", + "\n", + " patch = [\n", + " {\n", + " \"op\": \"replace\",\n", + " \"path\": \"\",\n", + " \"value\": \"sold\"\n", + " }\n", + " ]\n", + " service_client.update_component(t,\"state\", patch)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You should be able to load the storage explorer at this point and look at the relationships. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I'm going to add some areas. \n", + "* People go into areas when the concert begins.\n", + "* areas have capacity\n", + "\n", + "Also adding lines:\n", + "* Lines lead to other places, but one line can lead to many places (like the entrance).\n", + "* Lines have a capacity\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 87, + "metadata": {}, + "outputs": [], + "source": [ + "# # # Delete the model that you don't want. \n", + "# # service_client.delete_model(patron_model_id)\n", + "\n", + "# # Create it if you just deleted it.\n", + "# # area_model_json = yaml.safe_load(open(\"models/area.json\"))\n", + "# line_model_json = yaml.safe_load(open(\"models/Patron.json\"))\n", + "# service_client.create_models([line_model_json])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ok now that we know what tickets we want to sell, let's push them to the digital twin ecosystem. This is exactly the same as what we did with `Customers` in step one. " + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "area-0\n", + "{'$dtId': 'area-0', '$etag': 'W/\"7789a5c1-8ab0-473d-9c0e-6c12abe8f20f\"', 'capacity': 3, 'status': 'open', '$metadata': {'$model': 'dtmi:billmanh:area;1', 'capacity': {'lastUpdateTime': '2020-11-29T23:47:16.3097488Z'}, 'status': {'lastUpdateTime': '2020-11-29T23:47:16.3097488Z'}}}\n", + "area-1\n", + "{'$dtId': 'area-1', '$etag': 'W/\"78d4f941-5c4f-4e55-9a24-e1061f35a0f9\"', 'capacity': 3, 'status': 'open', '$metadata': {'$model': 'dtmi:billmanh:area;1', 'capacity': {'lastUpdateTime': '2020-11-29T23:47:16.3994079Z'}, 'status': {'lastUpdateTime': '2020-11-29T23:47:16.3994079Z'}}}\n", + "area-2\n", + "{'$dtId': 'area-2', '$etag': 'W/\"8d9d633f-51e1-4814-bf7f-71ffa5b647e6\"', 'capacity': 3, 'status': 'open', '$metadata': {'$model': 'dtmi:billmanh:area;1', 'capacity': {'lastUpdateTime': '2020-11-29T23:47:16.4841446Z'}, 'status': {'lastUpdateTime': '2020-11-29T23:47:16.4841446Z'}}}\n", + "area-3\n", + "{'$dtId': 'area-3', '$etag': 'W/\"990a5327-b8e5-4051-9ce2-9a078ec3c0aa\"', 'capacity': 3, 'status': 'open', '$metadata': {'$model': 'dtmi:billmanh:area;1', 'capacity': {'lastUpdateTime': '2020-11-29T23:47:16.5562464Z'}, 'status': {'lastUpdateTime': '2020-11-29T23:47:16.5562464Z'}}}\n" + ] + } + ], + "source": [ + "for r in range(4):\n", + " digital_twin_id = f'area-{r}'\n", + " print(digital_twin_id)\n", + " dt_json = {\n", + " \"$metadata\": {\n", + " \"$model\": \"dtmi:mymodels:area;1\"\n", + " },\n", + " \"capacity\": 3,\n", + " \"status\": \"open\"\n", + " }\n", + " created_twin = service_client.upsert_digital_twin(digital_twin_id, dt_json)\n", + " print(created_twin)" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "line-0\n", + "{'$dtId': 'line-0', '$etag': 'W/\"efd0563b-244c-41b1-8a02-7d2f5bcb11cb\"', '$metadata': {'$model': 'dtmi:billmanh:line;1'}}\n", + "line-1\n", + "{'$dtId': 'line-1', '$etag': 'W/\"e96e3bcb-3829-4fd0-a042-6d5f866fbd0a\"', '$metadata': {'$model': 'dtmi:billmanh:line;1'}}\n", + "line-2\n", + "{'$dtId': 'line-2', '$etag': 'W/\"c2f75074-3491-47b4-a703-6ade5e9993ad\"', '$metadata': {'$model': 'dtmi:billmanh:line;1'}}\n", + "line-3\n", + "{'$dtId': 'line-3', '$etag': 'W/\"8d749fc0-c15f-448d-b246-8f2d9212a29d\"', '$metadata': {'$model': 'dtmi:billmanh:line;1'}}\n" + ] + } + ], + "source": [ + "for r in range(4):\n", + " digital_twin_id = f'line-{r}'\n", + " print(digital_twin_id)\n", + " dt_json = {\n", + " \"$metadata\": {\n", + " \"$model\": \"dtmi:mymodels:line;1\"\n", + " }\n", + " }\n", + " created_twin = service_client.upsert_digital_twin(digital_twin_id, dt_json)\n", + " print(created_twin)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now establishing a realtionship between the lines and the areas. If a person leaves one area they have to get into the line of the next area in order to get there. " + ] + }, + { + "cell_type": "code", + "execution_count": 77, + "metadata": {}, + "outputs": [], + "source": [ + "def line_to_room(line,area,walking):\n", + " tickethoder_relationship = {\n", + " \"$relationshipId\": f\"{line}leadsTo{area}\",\n", + " \"$sourceId\": line,\n", + " \"$relationshipName\": \"leadsTo\",\n", + " \"$targetId\": area,\n", + " \"walk_distance\": walking\n", + " }\n", + "\n", + " service_client.upsert_relationship(\n", + " tickethoder_relationship[\"$sourceId\"],\n", + " tickethoder_relationship[\"$relationshipId\"],\n", + " tickethoder_relationship\n", + " )\n", + " \n", + "line_to_room(\"line-0\",\"area-0\",5)\n", + "line_to_room(\"line-1\",\"area-1\",5)\n", + "line_to_room(\"line-2\",\"area-2\",5)\n", + "line_to_room(\"line-3\",\"area-3\",5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now I'm going to add some people into different parts of the experience. This will change from user to user, but you can use this as an example of how this would work on your twin. Use the queries above to get the specific ids of your twins." + ] + }, + { + "cell_type": "code", + "execution_count": 85, + "metadata": {}, + "outputs": [], + "source": [ + "def cust_to_area(cust,area):\n", + " tickethoder_relationship = {\n", + " \"$relationshipId\": f\"{cust}locatedIn{area}\",\n", + " \"$sourceId\": cust,\n", + " \"$relationshipName\": \"locatedIn\",\n", + " \"$targetId\": area,\n", + " }\n", + "\n", + " service_client.upsert_relationship(\n", + " tickethoder_relationship[\"$sourceId\"],\n", + " tickethoder_relationship[\"$relationshipId\"],\n", + " tickethoder_relationship\n", + " )\n", + " \n", + "cust_to_area(\"customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32c\",\"line-1\")\n", + "cust_to_area(\"customer-21e17d28-76c3-4c04-8df9-396703692a68\",\"line-1\")\n", + "# cust_to_area(\"customer-25e19268-3433-4f09-afe3-94f466313368\",\"line-0\")\n", + "# cust_to_area(\"customer-c87adbfa-1c6e-4ea9-9f03-83e3877ef5fc\",\"line-2\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Sell a couple more tickets" + ] + }, + { + "cell_type": "code", + "execution_count": 83, + "metadata": {}, + "outputs": [], + "source": [ + "def ticket_to_cust(ticket,cust):\n", + " tickethoder_relationship = {\n", + " \"$relationshipId\": f\"{ticket}ownedBy{cust}\",\n", + " \"$sourceId\": ticket,\n", + " \"$relationshipName\": \"ownedBy\",\n", + " \"$targetId\": cust,\n", + " }\n", + "\n", + " service_client.upsert_relationship(\n", + " tickethoder_relationship[\"$sourceId\"],\n", + " tickethoder_relationship[\"$relationshipId\"],\n", + " tickethoder_relationship\n", + " )\n", + " \n", + "ticket_to_cust(\"ticket-58bf00bf-d5b8-4d06-9002-f8bf1693cc56\",\"customer-418cbc74-b101-4f50-8cf0-6075a2a8053c\")\n", + "ticket_to_cust(\"ticket-67f3cbfb-4b35-4e10-bf6d-3fd216093c3c\",\"customer-9c9b5c36-69f6-4f48-9362-4aaac4cb1be4\")\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (azure_test)", + "language": "python", + "name": "azure_test" + }, + "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.7.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/04_Lots_on_Queries.ipynb b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/04_Lots_on_Queries.ipynb new file mode 100644 index 000000000000..0a6969916e4b --- /dev/null +++ b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/04_Lots_on_Queries.ipynb @@ -0,0 +1,1197 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Complex queries\n", + "## An example use of the Digital Twin\n", + "\n", + "In this notebook we are going to dive deep into queries:\n", + "* Examining the customer experience through the lens of different aspects of the experience. \n", + "\n", + "In our previous steps we made:\n", + "* Patrons, that have a `customer satisfaction`, a relationship with tickets, and locations.\n", + "* Tickets that are `owned by customers`\n", + "* Lines that lead to areas\n", + "* Areas where people are located. \n", + "\n", + "We will be doing a bunch of different queries on this theme. \n", + "\n", + "\n", + "[This is the SDK repo on Github](https://github.com/Azure/azure-sdk-for-python/tree/4559e19e2f3146a49f1eba1706bb798071f4a1f5/sdk/digitaltwins/azure-digitaltwins-core)\n", + "\n", + "[Here is the doc on the query language](https://docs.microsoft.com/en-us/azure/digital-twins/concepts-query-language)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from azure.identity import AzureCliCredential\n", + "from azure.digitaltwins.core import DigitalTwinsClient\n", + "\n", + "# using yaml instead of \n", + "import yaml\n", + "import uuid\n", + "\n", + "# using altair instead of matplotlib for vizuals\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "# you will get this from the ADT resource at portal.azure.com\n", + "your_digital_twin_url = \"home-test-twin.api.wcus.digitaltwins.azure.net\"\n", + "\n", + "azure_cli = AzureCliCredential()\n", + "service_client = DigitalTwinsClient(\n", + " your_digital_twin_url, azure_cli)\n", + "service_client" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "I'm going to set up a generic function that runs queries and gets the data. This will keep me from doing it over and over. \n", + "\n", + "**Note that with really large models this might perform poorly** I'm only doing this here as this example is very small. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$dtId$etagsatisfactiontotalWaitTime$metadataevent_titlestateticket_locationcapacitystatus
0customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446W/\"158b01df-d555-4c1b-bf44-9c1e8c63eb02\"10.010.0{'$model': 'dtmi:billmanh:patron;1', 'satisfac...NaNNaNNaNNaNNaN
1customer-3cbd5e60-957d-44ff-944f-9adb42a20a52W/\"5d7359d5-e0f6-44e7-80f8-2a3f191295f2\"10.010.0{'$model': 'dtmi:billmanh:patron;1', 'satisfac...NaNNaNNaNNaNNaN
2customer-5c454e2f-f70b-4352-b75a-958f1a49bebaW/\"d7a8af67-7b40-4ed8-94c4-553fb639e197\"7.00.0{'$model': 'dtmi:billmanh:patron;1', 'satisfac...NaNNaNNaNNaNNaN
3customer-26196fee-5ffd-457a-86b7-192a998f3cf2W/\"ee1d4d68-aa14-4d35-a029-7635675f24d7\"9.00.0{'$model': 'dtmi:billmanh:patron;1', 'satisfac...NaNNaNNaNNaNNaN
4customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32cW/\"d1242800-1c21-4bab-909f-8dc49ef0ce92\"7.00.0{'$model': 'dtmi:billmanh:patron;1', 'satisfac...NaNNaNNaNNaNNaN
.................................
117ticket-dc48bc69-b1f5-4850-b5af-037c4ea72961W/\"5ea29c9e-a6be-4c02-979a-df1b2aa8cb5c\"NaNNaN{'$model': 'dtmi:billmanh:ticket;1', 'event_ti...Smashing Pumpkinsopen4NaNNaN
118ticket-92826a40-b024-4b91-a4d2-7e20dcfddce3W/\"002990f3-4ed8-464c-a90e-4b83ae5362fb\"NaNNaN{'$model': 'dtmi:billmanh:ticket;1', 'event_ti...Foo Fightersopen1NaNNaN
119ticket-8c483b5c-37d4-4027-8d67-d920bf8ca063W/\"22d419c0-a6da-4935-82e3-5b1f7236908e\"NaNNaN{'$model': 'dtmi:billmanh:ticket;1', 'event_ti...Foo Fightersopen2NaNNaN
120ticket-09028447-8e23-4e4f-a100-098a4dc8e919W/\"39cc602c-a41e-47bf-8da9-1a401419832a\"NaNNaN{'$model': 'dtmi:billmanh:ticket;1', 'event_ti...Foo Fightersopen3NaNNaN
121ticket-ccc89397-0d52-42a3-8fe1-39c97158c87cW/\"094b5fa9-4443-4923-b8f9-24201fb2f26f\"NaNNaN{'$model': 'dtmi:billmanh:ticket;1', 'event_ti...Foo Fightersopen4NaNNaN
\n", + "

122 rows × 10 columns

\n", + "
" + ], + "text/plain": [ + " $dtId \\\n", + "0 customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446 \n", + "1 customer-3cbd5e60-957d-44ff-944f-9adb42a20a52 \n", + "2 customer-5c454e2f-f70b-4352-b75a-958f1a49beba \n", + "3 customer-26196fee-5ffd-457a-86b7-192a998f3cf2 \n", + "4 customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32c \n", + ".. ... \n", + "117 ticket-dc48bc69-b1f5-4850-b5af-037c4ea72961 \n", + "118 ticket-92826a40-b024-4b91-a4d2-7e20dcfddce3 \n", + "119 ticket-8c483b5c-37d4-4027-8d67-d920bf8ca063 \n", + "120 ticket-09028447-8e23-4e4f-a100-098a4dc8e919 \n", + "121 ticket-ccc89397-0d52-42a3-8fe1-39c97158c87c \n", + "\n", + " $etag satisfaction totalWaitTime \\\n", + "0 W/\"158b01df-d555-4c1b-bf44-9c1e8c63eb02\" 10.0 10.0 \n", + "1 W/\"5d7359d5-e0f6-44e7-80f8-2a3f191295f2\" 10.0 10.0 \n", + "2 W/\"d7a8af67-7b40-4ed8-94c4-553fb639e197\" 7.0 0.0 \n", + "3 W/\"ee1d4d68-aa14-4d35-a029-7635675f24d7\" 9.0 0.0 \n", + "4 W/\"d1242800-1c21-4bab-909f-8dc49ef0ce92\" 7.0 0.0 \n", + ".. ... ... ... \n", + "117 W/\"5ea29c9e-a6be-4c02-979a-df1b2aa8cb5c\" NaN NaN \n", + "118 W/\"002990f3-4ed8-464c-a90e-4b83ae5362fb\" NaN NaN \n", + "119 W/\"22d419c0-a6da-4935-82e3-5b1f7236908e\" NaN NaN \n", + "120 W/\"39cc602c-a41e-47bf-8da9-1a401419832a\" NaN NaN \n", + "121 W/\"094b5fa9-4443-4923-b8f9-24201fb2f26f\" NaN NaN \n", + "\n", + " $metadata event_title \\\n", + "0 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... NaN \n", + "1 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... NaN \n", + "2 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... NaN \n", + "3 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... NaN \n", + "4 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... NaN \n", + ".. ... ... \n", + "117 {'$model': 'dtmi:billmanh:ticket;1', 'event_ti... Smashing Pumpkins \n", + "118 {'$model': 'dtmi:billmanh:ticket;1', 'event_ti... Foo Fighters \n", + "119 {'$model': 'dtmi:billmanh:ticket;1', 'event_ti... Foo Fighters \n", + "120 {'$model': 'dtmi:billmanh:ticket;1', 'event_ti... Foo Fighters \n", + "121 {'$model': 'dtmi:billmanh:ticket;1', 'event_ti... Foo Fighters \n", + "\n", + " state ticket_location capacity status \n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + ".. ... ... ... ... \n", + "117 open 4 NaN NaN \n", + "118 open 1 NaN NaN \n", + "119 open 2 NaN NaN \n", + "120 open 3 NaN NaN \n", + "121 open 4 NaN NaN \n", + "\n", + "[122 rows x 10 columns]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def query_ADT(query):\n", + " query_result = service_client.query_twins(query)\n", + " values = [i for i in query_result]\n", + " return values\n", + "\n", + "def query_to_df(query):\n", + " query_result = query_ADT(query)\n", + " values = pd.DataFrame(query_result)\n", + " return values\n", + "\n", + "query_expression = \"SELECT * FROM digitaltwins\"\n", + "query_to_df(query_expression)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Note that the larger query will give you back all of the values, so you can pop it into a dataframe and filter on the `$metadata` to get the values you want" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## IS_OF_MODEL\n", + "The process for most analysis is to query the items that are relevant into a dataframe and do your analysis on them. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "count 84.000000\n", + "mean 7.142857\n", + "std 1.529965\n", + "min 5.000000\n", + "25% 6.000000\n", + "50% 7.000000\n", + "75% 8.250000\n", + "max 10.000000\n", + "Name: satisfaction, dtype: float64" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query_expression = \"SELECT * FROM digitaltwins where IS_OF_MODEL('dtmi:mymodels:patron;1')\"\n", + "customers = query_to_df(query_expression)\n", + "customers.satisfaction.describe()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TCT
0{'$dtId': 'customer-cc04f3b6-39b0-4cef-bfff-a7...{'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2...
1{'$dtId': 'customer-25e19268-3433-4f09-afe3-94...{'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2...
2{'$dtId': 'customer-c87adbfa-1c6e-4ea9-9f03-83...{'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2...
\n", + "
" + ], + "text/plain": [ + " T \\\n", + "0 {'$dtId': 'customer-cc04f3b6-39b0-4cef-bfff-a7... \n", + "1 {'$dtId': 'customer-25e19268-3433-4f09-afe3-94... \n", + "2 {'$dtId': 'customer-c87adbfa-1c6e-4ea9-9f03-83... \n", + "\n", + " CT \n", + "0 {'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2... \n", + "1 {'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2... \n", + "2 {'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2... " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query_expression =\"\"\"\n", + "SELECT T, CT\n", + "FROM DIGITALTWINS T\n", + "JOIN CT RELATED T.locatedIn\n", + "WHERE CT.$dtId = 'line-2'\n", + "\"\"\"\n", + "\n", + "customers_in_area_2 = query_to_df(query_expression)\n", + "customers_in_area_2" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Ok let's unpack that:\n", + "\n", + "`SELECT T, CT` - means to give short names to all the different classes, so in this case `T` refers to all twins, and CT refers to a related item. The related item comes out in the second column\n", + "\n", + "`RELATED T.locatedIn` - gits all of elements that have a `locatedIn` relationship and stores it in `CT`. \n", + "\n", + "`WHERE CT.$dtId = 'line-2'` - limits the query to items that have that relationship with `line-2`. This is the filter part. \n", + "\n", + "**Note** it seems that all _joined queries_ require a specific twin by name. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "T {'$dtId': 'customer-cc04f3b6-39b0-4cef-bfff-a7...\n", + "CT {'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2...\n", + "Name: 0, dtype: object" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "customers_in_area_2.loc[0]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'$dtId': 'line-2',\n", + " '$etag': 'W/\"dd64cd24-6ec2-452d-b17a-83ebe4bcd73a\"',\n", + " 'capacity': 3,\n", + " '$metadata': {'$model': 'dtmi:billmanh:line;1',\n", + " 'capacity': {'lastUpdateTime': '2020-11-30T00:10:31.5945931Z'}}}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "customers_in_area_2.loc[0,'CT']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So let's look at the customers in `line-2`" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'$dtId': 'customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446',\n", + " '$etag': 'W/\"158b01df-d555-4c1b-bf44-9c1e8c63eb02\"',\n", + " 'satisfaction': 10,\n", + " 'totalWaitTime': 10,\n", + " '$metadata': {'$model': 'dtmi:billmanh:patron;1',\n", + " 'satisfaction': {'lastUpdateTime': '2020-11-19T02:17:30.1401999Z'},\n", + " 'totalWaitTime': {'lastUpdateTime': '2020-11-19T02:17:30.1401999Z'}}}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "customers_in_area_2.loc[0,'T']" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
$dtId$etagsatisfactiontotalWaitTime$metadata
0customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446W/\"158b01df-d555-4c1b-bf44-9c1e8c63eb02\"1010{'$model': 'dtmi:billmanh:patron;1', 'satisfac...
1customer-25e19268-3433-4f09-afe3-94f466313368W/\"91509c0d-7dc9-465b-a735-2fde2f67fe02\"1010{'$model': 'dtmi:billmanh:patron;1', 'satisfac...
2customer-c87adbfa-1c6e-4ea9-9f03-83e3877ef5fcW/\"44f9fd9b-141f-4603-8a0b-ad8a3730ed0d\"80{'$model': 'dtmi:billmanh:patron;1', 'satisfac...
\n", + "
" + ], + "text/plain": [ + " $dtId \\\n", + "0 customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446 \n", + "1 customer-25e19268-3433-4f09-afe3-94f466313368 \n", + "2 customer-c87adbfa-1c6e-4ea9-9f03-83e3877ef5fc \n", + "\n", + " $etag satisfaction totalWaitTime \\\n", + "0 W/\"158b01df-d555-4c1b-bf44-9c1e8c63eb02\" 10 10 \n", + "1 W/\"91509c0d-7dc9-465b-a735-2fde2f67fe02\" 10 10 \n", + "2 W/\"44f9fd9b-141f-4603-8a0b-ad8a3730ed0d\" 8 0 \n", + "\n", + " $metadata \n", + "0 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... \n", + "1 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... \n", + "2 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l2_cust = pd.DataFrame(customers_in_area_2['T'].tolist())\n", + "l2_cust" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "count 3.000000\n", + "mean 9.333333\n", + "std 1.154701\n", + "min 8.000000\n", + "25% 9.000000\n", + "50% 10.000000\n", + "75% 10.000000\n", + "max 10.000000\n", + "Name: satisfaction, dtype: float64" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "l2_cust.satisfaction.describe()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "count 84.000000\n", + "mean 7.142857\n", + "std 1.529965\n", + "min 5.000000\n", + "25% 6.000000\n", + "50% 7.000000\n", + "75% 8.250000\n", + "max 10.000000\n", + "Name: satisfaction, dtype: float64" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "customers.satisfaction.describe()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Customers in line 2 have higher satisfaction than customers in general. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## How many people in each line" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
COUNT
05
\n", + "
" + ], + "text/plain": [ + " COUNT\n", + "0 5" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = \"\"\"\n", + "SELECT COUNT() \n", + "FROM DIGITALTWINS T \n", + "JOIN CT RELATED T.locatedIn \n", + "WHERE CT.$dtId IN ['line-0','line-1','line-2', 'line-3']\n", + "\"\"\"\n", + "\n", + "customers_in_lines = query_to_df(query)\n", + "customers_in_lines" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
COUNT
03
\n", + "
" + ], + "text/plain": [ + " COUNT\n", + "0 3" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = \"\"\"\n", + "SELECT COUNT() \n", + "FROM DIGITALTWINS T \n", + "JOIN CT RELATED T.locatedIn \n", + "WHERE CT.$dtId IN ['line-2']\n", + "\"\"\"\n", + "\n", + "customers_in_lines = query_to_df(query)\n", + "customers_in_lines" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The rough part is that you can only get one count back, not a count per line, like you could with propper SQL. You also have to hard code all of your `$dtID` as they require literal values. Lame.\n", + "\n", + "Here is the way around that. \n", + "\n", + "`SELECT line, customer` <- select the columns that you want the query to return\n", + "\n", + "`AND IS_OF_MODEL(customer, 'dtmi:billmanh:patron;1')` <- specify that `customer` are twins of the `patron` model. \n", + "\n", + "You still have to hard type the names of the lines, or rooms or whatever, but it returns all of the customers in all of the lines. " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
linecustomer
0{'$dtId': 'line-1', '$etag': 'W/\"aa93fee2-cba0...{'$dtId': 'customer-e6f49d8a-711b-41c3-9db8-c7...
1{'$dtId': 'line-1', '$etag': 'W/\"aa93fee2-cba0...{'$dtId': 'customer-21e17d28-76c3-4c04-8df9-39...
2{'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2...{'$dtId': 'customer-cc04f3b6-39b0-4cef-bfff-a7...
3{'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2...{'$dtId': 'customer-25e19268-3433-4f09-afe3-94...
4{'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2...{'$dtId': 'customer-c87adbfa-1c6e-4ea9-9f03-83...
\n", + "
" + ], + "text/plain": [ + " line \\\n", + "0 {'$dtId': 'line-1', '$etag': 'W/\"aa93fee2-cba0... \n", + "1 {'$dtId': 'line-1', '$etag': 'W/\"aa93fee2-cba0... \n", + "2 {'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2... \n", + "3 {'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2... \n", + "4 {'$dtId': 'line-2', '$etag': 'W/\"dd64cd24-6ec2... \n", + "\n", + " customer \n", + "0 {'$dtId': 'customer-e6f49d8a-711b-41c3-9db8-c7... \n", + "1 {'$dtId': 'customer-21e17d28-76c3-4c04-8df9-39... \n", + "2 {'$dtId': 'customer-cc04f3b6-39b0-4cef-bfff-a7... \n", + "3 {'$dtId': 'customer-25e19268-3433-4f09-afe3-94... \n", + "4 {'$dtId': 'customer-c87adbfa-1c6e-4ea9-9f03-83... " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "query = \"\"\"\n", + "SELECT line, customer\n", + "FROM DIGITALTWINS customer\n", + "JOIN line RELATED customer.locatedIn\n", + "WHERE line.$dtId IN ['line-0','line-1','line-2', 'line-3']\n", + "AND IS_OF_MODEL(customer, 'dtmi:mymodels:patron;1')\n", + "\"\"\"\n", + "\n", + "customers_in_lines = query_to_df(query)\n", + "customers_in_lines" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Easy enough to munge it into a dataframe:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
line$etagcapacity$metadatacustomer$etagsatisfactiontotalWaitTime$metadata
0line-1W/\"aa93fee2-cba0-4bd4-a020-114f058b0f01\"3{'$model': 'dtmi:billmanh:line;1', 'capacity':...customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32cW/\"d1242800-1c21-4bab-909f-8dc49ef0ce92\"70{'$model': 'dtmi:billmanh:patron;1', 'satisfac...
1line-1W/\"aa93fee2-cba0-4bd4-a020-114f058b0f01\"3{'$model': 'dtmi:billmanh:line;1', 'capacity':...customer-21e17d28-76c3-4c04-8df9-396703692a68W/\"c62357a9-f4e5-4ec6-9d60-08b0da1125a7\"80{'$model': 'dtmi:billmanh:patron;1', 'satisfac...
2line-2W/\"dd64cd24-6ec2-452d-b17a-83ebe4bcd73a\"3{'$model': 'dtmi:billmanh:line;1', 'capacity':...customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446W/\"158b01df-d555-4c1b-bf44-9c1e8c63eb02\"1010{'$model': 'dtmi:billmanh:patron;1', 'satisfac...
3line-2W/\"dd64cd24-6ec2-452d-b17a-83ebe4bcd73a\"3{'$model': 'dtmi:billmanh:line;1', 'capacity':...customer-25e19268-3433-4f09-afe3-94f466313368W/\"91509c0d-7dc9-465b-a735-2fde2f67fe02\"1010{'$model': 'dtmi:billmanh:patron;1', 'satisfac...
4line-2W/\"dd64cd24-6ec2-452d-b17a-83ebe4bcd73a\"3{'$model': 'dtmi:billmanh:line;1', 'capacity':...customer-c87adbfa-1c6e-4ea9-9f03-83e3877ef5fcW/\"44f9fd9b-141f-4603-8a0b-ad8a3730ed0d\"80{'$model': 'dtmi:billmanh:patron;1', 'satisfac...
\n", + "
" + ], + "text/plain": [ + " line $etag capacity \\\n", + "0 line-1 W/\"aa93fee2-cba0-4bd4-a020-114f058b0f01\" 3 \n", + "1 line-1 W/\"aa93fee2-cba0-4bd4-a020-114f058b0f01\" 3 \n", + "2 line-2 W/\"dd64cd24-6ec2-452d-b17a-83ebe4bcd73a\" 3 \n", + "3 line-2 W/\"dd64cd24-6ec2-452d-b17a-83ebe4bcd73a\" 3 \n", + "4 line-2 W/\"dd64cd24-6ec2-452d-b17a-83ebe4bcd73a\" 3 \n", + "\n", + " $metadata \\\n", + "0 {'$model': 'dtmi:billmanh:line;1', 'capacity':... \n", + "1 {'$model': 'dtmi:billmanh:line;1', 'capacity':... \n", + "2 {'$model': 'dtmi:billmanh:line;1', 'capacity':... \n", + "3 {'$model': 'dtmi:billmanh:line;1', 'capacity':... \n", + "4 {'$model': 'dtmi:billmanh:line;1', 'capacity':... \n", + "\n", + " customer \\\n", + "0 customer-e6f49d8a-711b-41c3-9db8-c7ece3dbc32c \n", + "1 customer-21e17d28-76c3-4c04-8df9-396703692a68 \n", + "2 customer-cc04f3b6-39b0-4cef-bfff-a7d668cce446 \n", + "3 customer-25e19268-3433-4f09-afe3-94f466313368 \n", + "4 customer-c87adbfa-1c6e-4ea9-9f03-83e3877ef5fc \n", + "\n", + " $etag satisfaction totalWaitTime \\\n", + "0 W/\"d1242800-1c21-4bab-909f-8dc49ef0ce92\" 7 0 \n", + "1 W/\"c62357a9-f4e5-4ec6-9d60-08b0da1125a7\" 8 0 \n", + "2 W/\"158b01df-d555-4c1b-bf44-9c1e8c63eb02\" 10 10 \n", + "3 W/\"91509c0d-7dc9-465b-a735-2fde2f67fe02\" 10 10 \n", + "4 W/\"44f9fd9b-141f-4603-8a0b-ad8a3730ed0d\" 8 0 \n", + "\n", + " $metadata \n", + "0 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... \n", + "1 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... \n", + "2 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... \n", + "3 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... \n", + "4 {'$model': 'dtmi:billmanh:patron;1', 'satisfac... " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c_in_line = pd.concat(\n", + " [pd.DataFrame(customers_in_lines['line'].tolist()),\n", + " pd.DataFrame(customers_in_lines['customer'].tolist())],\n", + " axis=1\n", + ")\n", + "\n", + "cols = c_in_line.columns.tolist()\n", + "cols[0] = 'line'\n", + "cols[4] = 'customer'\n", + "c_in_line.columns = cols\n", + "c_in_line" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "How many people are in each line: " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "line\n", + "line-1 2\n", + "line-2 3\n", + "Name: customer, dtype: int64" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c_in_line.groupby('line').count()['customer']" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Which group of people has the highest satisfaction?" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "line\n", + "line-1 7.500000\n", + "line-2 9.333333\n", + "Name: satisfaction, dtype: float64" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "c_in_line.groupby('line').mean()['satisfaction']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (azure_test)", + "language": "python", + "name": "azure_test" + }, + "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.7.9" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} \ No newline at end of file diff --git a/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/Patron.json b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/Patron.json new file mode 100644 index 000000000000..7a34a5945d00 --- /dev/null +++ b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/Patron.json @@ -0,0 +1,43 @@ +{ + "@id": "dtmi:mymodels:patron;1", + "@type": "Interface", + "displayName": "Patron", + "contents": [ + { + "@type": "Command", + "name": "order", + "request": { + "name": "wallet", + "displayName": "available money", + "description": "how much the patron is willing to spend", + "schema": "double" + }, + "response": { + "name": "decision", + "displayName": "consumer decision", + "schema": "string" + } + }, + { + "@type": "Property", + "name": "totalWaitTime", + "displayName": "Total Wait Time", + "schema": "double", + "comment": "time in seconds that the customer is required to wait" + }, + { + "@type": "Property", + "name": "satisfaction", + "displayName": "Customer Satisfaction", + "schema": "integer", + "comment": "1-10 scale of how likely to recommend to a friend (NPS)" + }, + { + "@type": "Relationship", + "name": "locatedIn" + } + ], + "@context": "dtmi:dtdl:context;2", + "comment": "This should represent one restaurant visitor.", + "description": "As an example, contains all of the properties possible in the DTDL." +} \ No newline at end of file diff --git a/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/area.json b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/area.json new file mode 100644 index 000000000000..90f84d04aa35 --- /dev/null +++ b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/area.json @@ -0,0 +1,28 @@ +{ + "@id": "dtmi:mymodels:area;1", + "@type": "Interface", + "displayName": "area", + "contents": [ + { + "@type": "Property", + "name": "capacity", + "displayName": "capacity", + "schema": "double", + "comment": "How many people can safely be in this area." + }, + { + "@type": "Property", + "name": "status", + "displayName": "status", + "schema": "string", + "comment": "open, closed, etc." + }, + { + "@type": "Relationship", + "name": "occupiedBy" + } + ], + "@context": "dtmi:dtdl:context;2", + "comment": "A number of people in a room", + "description": "a room" +} \ No newline at end of file diff --git a/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/line.json b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/line.json new file mode 100644 index 000000000000..20004091a117 --- /dev/null +++ b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/line.json @@ -0,0 +1,35 @@ +{ + "@id": "dtmi:mymodels:line;1", + "@type": "Interface", + "displayName": "line", + "contents": [ + { + "@type": "Property", + "name": "capacity", + "displayName": "capacity", + "schema": "double", + "comment": "How many people can safely wait in this line." + }, + { + "@type": "Property", + "name": "status", + "displayName": "status", + "schema": "string", + "comment": "current status of the line" + }, + { + "@type": "Relationship", + "name": "leadsTo", + "properties": [ + { + "@type": "Property", + "name": "walk_distance", + "schema": "double" + } + ] + } + ], + "@context": "dtmi:dtdl:context;2", + "comment": "lines can hold a number of people.", + "description": "a group of people waiting to get into a place." +} \ No newline at end of file diff --git a/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/ticket.json b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/ticket.json new file mode 100644 index 000000000000..94f4ef3d8a01 --- /dev/null +++ b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/models/ticket.json @@ -0,0 +1,63 @@ +{ + "@id": "dtmi:mymodels:ticket;1", + "@type": "Interface", + "displayName": "ticket", + "contents": [ + { + "@type": "Property", + "name": "purchaselocation_lat", + "displayName": "puchased location (latitude)", + "schema": "double", + "comment": "latitude of the place where the ticket was purchased" + }, + { + "@type": "Property", + "name": "purchaselocation_long", + "displayName": "puchased location", + "schema": "double", + "comment": "longitude of the place where the ticket was purchased" + }, + { + "@type": "Property", + "name": "sold_to", + "displayName": "sold to", + "schema": "string", + "comment": "UID of the purchaser from customer data" + }, + { + "@type": "Property", + "name": "event_title", + "displayName": "event name", + "schema": "string", + "comment": "name or id of the event" + }, + { + "@type": "Property", + "name": "ticket_location", + "displayName": "ticket location", + "schema": "string", + "comment": "location of the seat, for demonstration this is just a number" + }, + { + "@type": "Property", + "name": "state", + "displayName": "state", + "schema": "string", + "comment": "current state of the ticket" + }, + { + "@type": "Relationship", + "name": "ownedBy", + "properties": [ + { + "@type": "Property", + "name": "bought_online", + "schema": "boolean" + } + ] + } + ], + "@context": "dtmi:dtdl:context;2", + "comment": "A number of tickets are placed into ADT before purchase with the initial state: open", + "description": "an abstract ticket" +} \ No newline at end of file diff --git a/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/readme.md b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/readme.md new file mode 100644 index 000000000000..160a69239fdd --- /dev/null +++ b/sdk/digitaltwins/azure-digitaltwins-core/samples/notebooks/readme.md @@ -0,0 +1,20 @@ +## Digital Twin Tutorial + +These notebooks will show you how to create a graph and interact with it through the Python API. It will build a venue and allow you to measure the customer satisfaction of people as the pass through the system. + + +You will need: +* A conda environment with the latest `Azure-Digital-Twin SDK` +* The Azure CLI tools for authentication +* The digital twin explorer +* A digital twin account, where you have sufficient permission. + + +The perspective of these notebooks is from the role of the **Analyst** not an application. The purpose is to use the graph to get insights on customers. This would allow you to query the API in real time, as events are happening. + +| notebook | purpose | +|----------|:-------------:| +| 01 | The basics of connecting, uploading models and creating twins | +| 02 | More details about creating relationships, updating values and basic queries | +| 03 | Some examples of arbitrary model changes that I made to shape the model | +| 04 | More detail about running queries with some examples of how to get insights |