Files
HP_InHand_IG502/code snippets/plcfreshwater tests 1.ipynb
2023-11-30 12:53:42 -06:00

281 lines
21 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import json\n",
"from datetime import datetime as dt"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"message = {'timestamp': 1698256200, 'group_name': 'default', 'values': {'00:00:02:00:00:01': {'cmd_cloud_control': {'raw_data': 0, 'timestamp': 1698256190, 'status': 0}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256190, 'status': 0}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256190, 'status': 0}, 'lifetime_flow_meter_gal': {'raw_data': 0.0, 'timestamp': 1698256190, 'status': 0}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256190, 'status': 0}, 'raw_run_status': {'raw_data': 0, 'timestamp': 1698256190, 'status': 0}, 'raw_auto_input': {'raw_data': 0, 'timestamp': 1698256190, 'status': 0}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256190, 'status': 0}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256190, 'status': 0}, 'scaled_flow_meter': {'raw_data': 0.0, 'timestamp': 1698256190, 'status': 0}}, '00:00:05:00:00:29': {'cmd_cloud_control': {'raw_data': 0, 'timestamp': 1698256195, 'status': 1}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256195, 'status': 1}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256195, 'status': 1}, 'lifetime_flow_meter_gal': {'raw_data': 15413721.0, 'timestamp': 1698256195, 'status': 1}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256195, 'status': 1}, 'raw_run_status': {'raw_data': 0, 'timestamp': 1698256195, 'status': 1}, 'raw_auto_input': {'raw_data': 1, 'timestamp': 1698256195, 'status': 1}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256195, 'status': 1}, 'scaled_pressure_transducer': {'raw_data': 1817.21, 'timestamp': 1698256195, 'status': 1}, 'scaled_flow_meter': {'raw_data': 0.0, 'timestamp': 1698256195, 'status': 1}}, '00:00:05:00:00:28': {'cmd_cloud_control': {'raw_data': 0, 'timestamp': 1698256196, 'status': 0}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256196, 'status': 0}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256196, 'status': 0}, 'lifetime_flow_meter_gal': {'raw_data': 0.0, 'timestamp': 1698256196, 'status': 0}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256196, 'status': 0}, 'raw_run_status': {'raw_data': 0, 'timestamp': 1698256196, 'status': 0}, 'raw_auto_input': {'raw_data': 0, 'timestamp': 1698256196, 'status': 0}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256196, 'status': 0}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256196, 'status': 0}, 'scaled_flow_meter': {'raw_data': 0.0, 'timestamp': 1698256196, 'status': 0}}, '00:00:05:00:00:12': {'cmd_cloud_control': {'raw_data': 0, 'timestamp': 1698256199, 'status': 0}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256199, 'status': 0}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256199, 'status': 0}, 'lifetime_flow_meter_gal': {'raw_data': 0.0, 'timestamp': 1698256199, 'status': 0}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256199, 'status': 0}, 'raw_run_status': {'raw_data': 0, 'timestamp': 1698256199, 'status': 0}, 'raw_auto_input': {'raw_data': 0, 'timestamp': 1698256199, 'status': 0}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256199, 'status': 0}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256199, 'status': 0}, 'scaled_flow_meter': {'raw_data': 0.0, 'timestamp': 1698256199, 'status': 0}}, '00:00:05:00:00:10': {'cmd_cloud_control': {'raw_data': 1, 'timestamp': 1698256199, 'status': 1}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256199, 'status': 1}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256199, 'status': 1}, 'lifetime_flow_meter_gal': {'raw_data': 3389469.5, 'timestamp': 1698256199, 'status': 1}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256199, 'status': 1}, 'raw_run_status': {'raw_data': 1, 'timestamp': 1698256199, 'status': 1}, 'raw_auto_input': {'raw_data': 1, 'timestamp': 1698256199, 'status': 1}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256199, 'status': 1}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256199, 'status': 1}, 'scaled_flow_meter': {'raw_data': 11.25, 'timestamp': 1698256199, 'status': 1}}, '00:00:05:00:00:07': {'cmd_cloud_control': {'raw_data': 0, 'timestamp': 1698256170, 'status': 1}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256170, 'status': 1}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256170, 'status': 1}, 'lifetime_flow_meter_gal': {'raw_data': 958872.25, 'timestamp': 1698256170, 'status': 1}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256170, 'status': 1}, 'raw_run_status': {'raw_data': 0, 'timestamp': 1698256170, 'status': 1}, 'raw_auto_input': {'raw_data': 1, 'timestamp': 1698256170, 'status': 1}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256170, 'status': 1}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256170, 'status': 1}, 'scaled_flow_meter': {'raw_data': 0.0, 'timestamp': 1698256170, 'status': 1}}, '00:00:05:00:00:06': {'cmd_cloud_control': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'lifetime_flow_meter_gal': {'raw_data': 253666.95000000001, 'timestamp': 1698256171, 'status': 1}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'raw_run_status': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'raw_auto_input': {'raw_data': 1, 'timestamp': 1698256171, 'status': 1}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256171, 'status': 1}, 'scaled_flow_meter': {'raw_data': 0.0, 'timestamp': 1698256171, 'status': 1}}, '00:00:05:00:00:05': {'cmd_cloud_control': {'raw_data': 1, 'timestamp': 1698256171, 'status': 1}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'lifetime_flow_meter_gal': {'raw_data': 3064333.7999999998, 'timestamp': 1698256171, 'status': 1}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'raw_run_status': {'raw_data': 1, 'timestamp': 1698256171, 'status': 1}, 'raw_auto_input': {'raw_data': 1, 'timestamp': 1698256171, 'status': 1}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256171, 'status': 1}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256171, 'status': 1}, 'scaled_flow_meter': {'raw_data': 31.649999999999999, 'timestamp': 1698256171, 'status': 1}}, '00:00:05:00:00:04': {'cmd_cloud_control': {'raw_data': 1, 'timestamp': 1698256172, 'status': 1}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256172, 'status': 1}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256172, 'status': 1}, 'lifetime_flow_meter_gal': {'raw_data': 7037433.0, 'timestamp': 1698256172, 'status': 1}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256172, 'status': 1}, 'raw_run_status': {'raw_data': 1, 'timestamp': 1698256172, 'status': 1}, 'raw_auto_input': {'raw_data': 1, 'timestamp': 1698256172, 'status': 1}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256172, 'status': 1}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256172, 'status': 1}, 'scaled_flow_meter': {'raw_data': 23.079999999999998, 'timestamp': 1698256172, 'status': 1}}, '00:00:05:00:00:03': {'cmd_cloud_control': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'lifetime_flow_meter_gal': {'raw_data': 6527304.0, 'timestamp': 1698256173, 'status': 1}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'raw_run_status': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'raw_auto_input': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'raw_hand_input': {'raw_data': 1, 'timestamp': 1698256173, 'status': 1}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256173, 'status': 1}, 'scaled_flow_meter': {'raw_data': 0.0, 'timestamp': 1698256173, 'status': 1}}, '00:00:05:00:00:01': {'cmd_cloud_control': {'raw_data': 1, 'timestamp': 1698256173, 'status': 1}, 'raw_overload_status': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'spt_flow_meter_unit': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'lifetime_flow_meter_gal': {'raw_data': 966336.80000000005, 'timestamp': 1698256173, 'status': 1}, 'raw_local_start': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'raw_run_status': {'raw_data': 1, 'timestamp': 1698256173, 'status': 1}, 'raw_auto_input': {'raw_data': 1, 'timestamp': 1698256173, 'status': 1}, 'raw_hand_input': {'raw_data': 0, 'timestamp': 1698256173, 'status': 1}, 'scaled_pressure_transducer': {'raw_data': 0.0, 'timestamp': 1698256173, 'status': 1}, 'scaled_flow_meter': {'raw_data': 15.24, 'timestamp': 1698256173, 'status': 1}}}}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(json.dumps(message[\"values\"], indent=4))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for device in message[\"values\"].keys():\n",
" print(device)\n",
" for measure in message[\"values\"][device].keys():\n",
" print(measure, message[\"values\"][device][measure][\"raw_data\"])"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"lwtData = {}\n",
"def sendData(message,wizard_api):\n",
" #logger.debug(message)\n",
" #checkCredentialConfig()\n",
" __topic__ = \"meshify/db/194/_/plcfreshwater/\"\n",
" __qos__ = 1\n",
" for device in message[\"values\"].keys():\n",
" if not device in lwtData.keys():\n",
" print(f\"adding {device}\")\n",
" lwtData[device] = {\n",
" \"init\":False,\n",
" \"client\": f\"client{device}\"#client.Client(client_id=str(uuid.uuid4()), clean_session=True, userdata=None, protocol=client.MQTTv311, transport=\"tcp\")\n",
" }\n",
" #setup LWT and refresh status\n",
" #lwt(device)\n",
" #print(device)\n",
" payload = {\"ts\": (round(dt.timestamp(dt.now())/600)*600)*1000, \"values\": {}}\n",
" resetPayload = {\"ts\": \"\", \"values\": {}}\n",
" dayReset, weekReset, monthReset, yearReset = False, False, False, False\n",
" for measure in message[\"values\"][device].keys():\n",
" try:\n",
" if message[\"values\"][device][measure][\"status\"] == 1:\n",
" if measure in [\"lifetime_flow_meter_gal\"]:\n",
" payload[\"values\"][\"total_fm_day_gal\"], payload[\"values\"][\"total_fm_day_bbls\"],dayReset = totalizeDay(message[\"values\"][device][measure][\"raw_data\"], device)\n",
" #payload[\"values\"][\"week_volume\"], weekReset = totalizeWeek(message[\"values\"][device][measure][\"raw_data\"])\n",
" payload[\"values\"][\"total_fm_month_gal\"],payload[\"values\"][\"total_fm_month_bbls\"], monthReset = totalizeMonth(message[\"values\"][device][measure][\"raw_data\"], device)\n",
" #payload[\"values\"][\"year_volume\"], yearReset = totalizeYear(message[\"values\"][device][measure][\"raw_data\"])\n",
" payload[\"values\"][\"lifetime_flow_meter_gal\"] = message[\"values\"][device][measure][\"raw_data\"]\n",
" payload[\"values\"][\"lifetime_flow_meter_bbls\"] = message[\"values\"][device][measure][\"raw_data\"]/42\n",
" elif measure in [\"raw_hand_input\", \"raw_auto_input\", \"raw_run_status\", \"raw_local_start\",\"raw_overload_status\"]:\n",
" payload[\"values\"][measure] = convert_int(measure, message[\"values\"][device][measure][\"raw_data\"])\n",
" else:\n",
" payload[\"values\"][measure] = message[\"values\"][device][measure][\"raw_data\"]\n",
" except Exception as e:\n",
" print(e)\n",
" #print(json.dumps(payload, indent=4))\n",
" if payload[\"values\"]:\n",
" payload[\"values\"][\"plc_ping\"] = \"OK\"\n",
" else:\n",
" payload[\"values\"][\"plc_ping\"] = \"Comms Error to PLC\"\n",
"\n",
" for measure in payload[\"values\"].keys():\n",
" pass\n",
" #print(__topic__+ device + \":01:99/\" + measure, json.dumps({\"value\": payload[\"values\"][measure]}), __qos__)\n",
" \n",
" if dayReset:\n",
" resetPayload[\"values\"][\"total_fm_yesterday_gal\"] = payload[\"values\"][\"total_fm_day_gal\"]\n",
" resetPayload[\"values\"][\"total_fm_day_gal\"] = 0\n",
" resetPayload[\"values\"][\"total_fm_yesterday_bbls\"] = payload[\"values\"][\"total_fm_day_bbls\"]\n",
" resetPayload[\"values\"][\"total_fm_day_bbls\"] = 0\n",
" if weekReset:\n",
" resetPayload[\"values\"][\"last_week_volume\"] = payload[\"values\"][\"week_volume\"]\n",
" resetPayload[\"values\"][\"week_volume\"] = 0\n",
" if monthReset:\n",
" resetPayload[\"values\"][\"total_fm_last_month_gal\"] = payload[\"values\"][\"total_fm_month_gal\"]\n",
" resetPayload[\"values\"][\"total_fm_month_gal\"] = 0\n",
" resetPayload[\"values\"][\"total_fm_last_month_bbls\"] = payload[\"values\"][\"total_fm_month_bbls\"]\n",
" resetPayload[\"values\"][\"total_fm_month_bbls\"] = 0\n",
" if yearReset:\n",
" resetPayload[\"values\"][\"last_year_volume\"] = payload[\"values\"][\"year_volume\"]\n",
" resetPayload[\"values\"][\"year_volume\"] = 0 \n",
"\n",
" if resetPayload[\"values\"]:\n",
" for measure in resetPayload[\"values\"].keys():\n",
" print(__topic__+ device + \":01:99/\" + measure, json.dumps({\"value\": resetPayload[\"values\"][measure]}), __qos__)\n",
" print(lwtData)\n",
"def convert_int(plc_tag, value):\n",
" input_codes = {\n",
" 0: \"Off\",\n",
" 1: \"On\"\n",
" }\n",
"\n",
" run_status_codes = {\n",
" 0: \"Stopped\",\n",
" 1: \"Running\"\n",
" }\n",
"\n",
" overload_codes = {\n",
" 0: \"Good\",\n",
" 1: \"Down on Overload Tripped\"\n",
" }\n",
" \n",
" plc_tags = {\n",
" \"raw_hand_input\": input_codes.get(value, \"Invalid Code\"),\n",
" \"raw_local_start\": input_codes.get(value, \"Invalid Code\"),\n",
" \"raw_auto_input\": input_codes.get(value, \"Invalid Code\"),\n",
" \"raw_run_status\": run_status_codes.get(value, \"Invalid Code\"),\n",
" \"raw_overload_status\": overload_codes.get(value, \"Invalid Code\")\n",
" }\n",
"\n",
" return plc_tags.get(plc_tag, \"Invalid Tag\")\n",
"\n",
"def saveTotalizers(totalizers, mac):\n",
" try:\n",
" mac = \"\".join(mac.split(\":\"))\n",
" with open(f\"/Users/nico/Documents/test/totalizer_test/totalizers_{mac}.json\", \"w\") as t:\n",
" json.dump(totalizers,t)\n",
" except Exception as e:\n",
" print(e)\n",
"\n",
"\n",
"def totalizeDay(lifetime, mac):\n",
" totalizers = get_totalizers(mac)\n",
" now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)\n",
" reset = False\n",
" value = lifetime - totalizers[\"dayHolding\"]\n",
" if not int(now.strftime(\"%d\")) == int(totalizers[\"day\"]):\n",
" totalizers[\"dayHolding\"] = lifetime\n",
" totalizers[\"day\"] = int(now.strftime(\"%d\"))\n",
" saveTotalizers(totalizers, mac)\n",
" reset = True\n",
" return (value,value / 42,reset)\n",
"\n",
"def totalizeWeek(lifetime, mac):\n",
" totalizers = get_totalizers(mac)\n",
" now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)\n",
" reset = False\n",
" value = lifetime - totalizers[\"weekHolding\"]\n",
" if (not now.strftime(\"%U\") == totalizers[\"week\"] and now.strftime(\"%a\") == \"Sun\") or totalizers[\"week\"] == 0:\n",
" totalizers[\"weekHolding\"] = lifetime\n",
" totalizers[\"week\"] = now.strftime(\"%U\")\n",
" saveTotalizers(totalizers, mac)\n",
" reset = True\n",
" return (value,value / 42, reset)\n",
"\n",
"def totalizeMonth(lifetime, mac):\n",
" totalizers = get_totalizers(mac)\n",
" now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)\n",
" reset = False\n",
" value = lifetime - totalizers[\"monthHolding\"]\n",
" if not int(now.strftime(\"%m\")) == int(totalizers[\"month\"]):\n",
" totalizers[\"monthHolding\"] = lifetime\n",
" totalizers[\"month\"] = now.strftime(\"%m\")\n",
" saveTotalizers(totalizers, mac)\n",
" reset = True\n",
" return (value,value / 42,reset)\n",
"\n",
"def totalizeYear(lifetime, mac):\n",
" totalizers = get_totalizers(mac)\n",
" now = dt.fromtimestamp(round(dt.timestamp(dt.now())/600)*600)\n",
" reset = False\n",
" value = lifetime - totalizers[\"yearHolding\"]\n",
" if not int(now.strftime(\"%Y\")) == int(totalizers[\"year\"]):\n",
" totalizers[\"yearHolding\"] = lifetime\n",
" totalizers[\"year\"] = now.strftime(\"%Y\")\n",
" saveTotalizers(totalizers, mac)\n",
" reset = True\n",
" return (value,value / 42, reset)\n",
"\n",
"def get_totalizers(mac):\n",
" try:\n",
" mac = \"\".join(mac.split(\":\"))\n",
" with open(f\"/Users/nico/Documents/test/totalizer_test/totalizers_{mac}.json\", \"r\") as t:\n",
" totalizers = json.load(t)\n",
" if not totalizers:\n",
" print(\"-----INITIALIZING TOTALIZERS-----\")\n",
" totalizers = {\n",
" \"day\": 0,\n",
" \"week\": 0,\n",
" \"month\": 0,\n",
" \"year\": 0,\n",
" \"lifetime\": 0,\n",
" \"dayHolding\": 0,\n",
" \"weekHolding\": 0,\n",
" \"monthHolding\": 0,\n",
" \"yearHolding\": 0\n",
" }\n",
" except:\n",
" totalizers = {\n",
" \"day\": 0,\n",
" \"week\": 0,\n",
" \"month\": 0,\n",
" \"year\": 0,\n",
" \"lifetime\": 0,\n",
" \"dayHolding\": 0,\n",
" \"weekHolding\": 0,\n",
" \"monthHolding\": 0,\n",
" \"yearHolding\": 0\n",
" }\n",
" return totalizers"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'00:00:02:00:00:01': {'init': False, 'client': 'client00:00:02:00:00:01'}, '00:00:05:00:00:29': {'init': False, 'client': 'client00:00:05:00:00:29'}, '00:00:05:00:00:28': {'init': False, 'client': 'client00:00:05:00:00:28'}, '00:00:05:00:00:12': {'init': False, 'client': 'client00:00:05:00:00:12'}, '00:00:05:00:00:10': {'init': False, 'client': 'client00:00:05:00:00:10'}, '00:00:05:00:00:07': {'init': False, 'client': 'client00:00:05:00:00:07'}, '00:00:05:00:00:06': {'init': False, 'client': 'client00:00:05:00:00:06'}, '00:00:05:00:00:05': {'init': False, 'client': 'client00:00:05:00:00:05'}, '00:00:05:00:00:04': {'init': False, 'client': 'client00:00:05:00:00:04'}, '00:00:05:00:00:03': {'init': False, 'client': 'client00:00:05:00:00:03'}, '00:00:05:00:00:01': {'init': False, 'client': 'client00:00:05:00:00:01'}}\n"
]
}
],
"source": [
"sendData(message, \"test\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"mac = \"00:00:05:00:00:01\"\n",
"print(\"\".join(mac.split(\":\")))"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "tbDataCollector",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}