{ "cells": [ { "cell_type": "code", "execution_count": 4, "id": "a287fa45", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Initializing Couchbase connection...\n", "Awaiting Couchbase connection...\n", "Couchbase connection ready\n" ] } ], "source": [ "from orchestrator.activities.couchbase import Couchbase\n", "from unittest.mock import MagicMock\n", "logger = MagicMock(info=MagicMock(side_effect=print), debug=MagicMock(side_effect=print))\n", "couchbase = Couchbase(\n", " connection_string=\"couchbase://localhost\",\n", " username=\"sientia\",\n", " password=\"sientia\",\n", " logger=logger,\n", " notification_handler=MagicMock()\n", ")\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "d9a0f9c4", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Executing couchbase query: %s \n", "SELECT\n", " pipelines.*,\n", " models as model\n", "FROM\n", " `pipelines`\n", "JOIN\n", " `models` ON KEYS pipelines.model_id;\n", "\n", "Fetched %d rows from couchbase 1\n", "Rows: \n", " %s [\n", " {\n", " \"filters\": [\n", " {\n", " \"filter_name\": \"OUT_OF_BOUNDS_FILTER\",\n", " \"policy\": \"DISCARD\"\n", " },\n", " {\n", " \"filter_name\": \"NULL_VALUES_FILTER\",\n", " \"policy\": \"DISCARD\"\n", " }\n", " ],\n", " \"frequency\": \"5s\",\n", " \"max_retry_policy\": 1,\n", " \"model\": {\n", " \"name\": \"Demo Model-Demo2\"\n", " },\n", " \"model_id\": \"1\",\n", " \"name\": \"scouter-opcua-pipeline\",\n", " \"read_tags\": [\n", " {\n", " \"aggr_func\": \"avg\",\n", " \"data_range\": [\n", " -100,\n", " 100\n", " ],\n", " \"tag_name\": \"Counter\"\n", " }\n", " ],\n", " \"tag_retention_minutes\": 60,\n", " \"workflow_type\": \"scouter\"\n", " }\n", "]\n", "[{'filters': [{'filter_name': 'OUT_OF_BOUNDS_FILTER', 'policy': 'DISCARD'}, {'filter_name': 'NULL_VALUES_FILTER', 'policy': 'DISCARD'}], 'frequency': '5s', 'max_retry_policy': 1, 'model': {'name': 'Demo Model-Demo2'}, 'model_id': '1', 'name': 'scouter-opcua-pipeline', 'read_tags': [{'aggr_func': 'avg', 'data_range': [-100, 100], 'tag_name': 'Counter'}], 'tag_retention_minutes': 60, 'workflow_type': 'scouter'}]\n" ] } ], "source": [ "query = \"\"\"\n", "SELECT\n", " pipelines.*,\n", " models as model\n", "FROM\n", " `pipelines`\n", "JOIN\n", " `models` ON KEYS pipelines.model_id;\n", "\"\"\"\n", "if __name__ == \"__main__\":\n", "\n", "\n", " result = await couchbase.load_query_from_couchbase({\n", " \"query\": query\n", " })\n", "\n", " print(result)\n", " " ] }, { "cell_type": "code", "execution_count": 1, "id": "7d01f160", "metadata": {}, "outputs": [], "source": [ "from temporalio import client\n", "from orchestrator.activities.temporal_manager import TemporalManager\n", "import os\n", "from unittest.mock import MagicMock\n", "\n", "host = \"localhost:7233\"\n", "logger = MagicMock(info=MagicMock(side_effect=print), debug=MagicMock(side_effect=print))\n", "\n", "temporal_client = await client.Client.connect(\n", " target_host=host,\n", " namespace=os.getenv('TEMPORAL_NAMESPACE', 'default')\n", ")\n", "\n", "manager = TemporalManager(temporal_client=temporal_client,\n", " logger=logger,\n", " notification_handler=MagicMock())\n", " " ] }, { "cell_type": "code", "execution_count": 2, "id": "bb750ae6", "metadata": {}, "outputs": [ { "ename": "ScheduleAlreadyRunningError", "evalue": "Schedule already running", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mRPCError\u001b[39m Traceback (most recent call last)", "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/sientia/sientia-dataops-orchestrator_temporal/venv/lib/python3.11/site-packages/temporalio/service.py:1243\u001b[39m, in \u001b[36m_BridgeServiceClient._rpc_call\u001b[39m\u001b[34m(self, rpc, req, resp_type, service, retry, metadata, timeout)\u001b[39m\n\u001b[32m 1242\u001b[39m client = \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m._connected_client()\n\u001b[32m-> \u001b[39m\u001b[32m1243\u001b[39m resp = \u001b[38;5;28;01mawait\u001b[39;00m client.call(\n\u001b[32m 1244\u001b[39m service=service,\n\u001b[32m 1245\u001b[39m rpc=rpc,\n\u001b[32m 1246\u001b[39m req=req,\n\u001b[32m 1247\u001b[39m resp_type=resp_type,\n\u001b[32m 1248\u001b[39m retry=retry,\n\u001b[32m 1249\u001b[39m metadata=metadata,\n\u001b[32m 1250\u001b[39m timeout=timeout,\n\u001b[32m 1251\u001b[39m )\n\u001b[32m 1252\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m LOG_PROTOS:\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/sientia/sientia-dataops-orchestrator_temporal/venv/lib/python3.11/site-packages/temporalio/bridge/client.py:151\u001b[39m, in \u001b[36mClient.call\u001b[39m\u001b[34m(self, service, rpc, req, resp_type, retry, metadata, timeout)\u001b[39m\n\u001b[32m 150\u001b[39m resp = resp_type()\n\u001b[32m--> \u001b[39m\u001b[32m151\u001b[39m resp.ParseFromString(\u001b[38;5;28;01mawait\u001b[39;00m resp_fut)\n\u001b[32m 152\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m resp\n", "\u001b[31mRPCError\u001b[39m: (6, 'Workflow execution is already running. WorkflowId: temporal-sys-scheduler:meu-schedule-id5, RunId: 01971d9e-b19b-7e25-8f60-ba4ed95e12e4.', b'\\x08\\x06\\x12\\x88\\x01Workflow execution is already running. WorkflowId: temporal-sys-scheduler:meu-schedule-id5, RunId: 01971d9e-b19b-7e25-8f60-ba4ed95e12e4.\\x1a\\xa7\\x01\\nWtype.googleapis.com/temporal.api.errordetails.v1.WorkflowExecutionAlreadyStartedFailure\\x12L\\n$6ae80236-ccbf-45b5-8282-1ef14a559b59\\x12$01971d9e-b19b-7e25-8f60-ba4ed95e12e4')", "\nDuring handling of the above exception, another exception occurred:\n", "\u001b[31mRPCError\u001b[39m Traceback (most recent call last)", "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/sientia/sientia-dataops-orchestrator_temporal/venv/lib/python3.11/site-packages/temporalio/client.py:6430\u001b[39m, in \u001b[36m_ClientImpl.create_schedule\u001b[39m\u001b[34m(self, input)\u001b[39m\n\u001b[32m 6427\u001b[39m temporalio.converter.encode_search_attributes(\n\u001b[32m 6428\u001b[39m \u001b[38;5;28minput\u001b[39m.search_attributes, request.search_attributes\n\u001b[32m 6429\u001b[39m )\n\u001b[32m-> \u001b[39m\u001b[32m6430\u001b[39m \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m._client.workflow_service.create_schedule(\n\u001b[32m 6431\u001b[39m request,\n\u001b[32m 6432\u001b[39m retry=\u001b[38;5;28;01mTrue\u001b[39;00m,\n\u001b[32m 6433\u001b[39m metadata=\u001b[38;5;28minput\u001b[39m.rpc_metadata,\n\u001b[32m 6434\u001b[39m timeout=\u001b[38;5;28minput\u001b[39m.rpc_timeout,\n\u001b[32m 6435\u001b[39m )\n\u001b[32m 6436\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m RPCError \u001b[38;5;28;01mas\u001b[39;00m err:\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/sientia/sientia-dataops-orchestrator_temporal/venv/lib/python3.11/site-packages/temporalio/service.py:1170\u001b[39m, in \u001b[36mServiceCall.__call__\u001b[39m\u001b[34m(self, req, retry, metadata, timeout)\u001b[39m\n\u001b[32m 1155\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Invoke underlying client with the given request.\u001b[39;00m\n\u001b[32m 1156\u001b[39m \n\u001b[32m 1157\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1168\u001b[39m \u001b[33;03m RPCError: Any RPC error that occurs during the call.\u001b[39;00m\n\u001b[32m 1169\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1170\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m.service_client._rpc_call(\n\u001b[32m 1171\u001b[39m \u001b[38;5;28mself\u001b[39m.name,\n\u001b[32m 1172\u001b[39m req,\n\u001b[32m 1173\u001b[39m \u001b[38;5;28mself\u001b[39m.resp_type,\n\u001b[32m 1174\u001b[39m service=\u001b[38;5;28mself\u001b[39m.service,\n\u001b[32m 1175\u001b[39m retry=retry,\n\u001b[32m 1176\u001b[39m metadata=metadata,\n\u001b[32m 1177\u001b[39m timeout=timeout,\n\u001b[32m 1178\u001b[39m )\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/sientia/sientia-dataops-orchestrator_temporal/venv/lib/python3.11/site-packages/temporalio/service.py:1258\u001b[39m, in \u001b[36m_BridgeServiceClient._rpc_call\u001b[39m\u001b[34m(self, rpc, req, resp_type, service, retry, metadata, timeout)\u001b[39m\n\u001b[32m 1257\u001b[39m status, message, details = err.args\n\u001b[32m-> \u001b[39m\u001b[32m1258\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m RPCError(message, RPCStatusCode(status), details)\n", "\u001b[31mRPCError\u001b[39m: Workflow execution is already running. WorkflowId: temporal-sys-scheduler:meu-schedule-id5, RunId: 01971d9e-b19b-7e25-8f60-ba4ed95e12e4.", "\nDuring handling of the above exception, another exception occurred:\n", "\u001b[31mScheduleAlreadyRunningError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 17\u001b[39m\n\u001b[32m 13\u001b[39m customer_id_key = SearchAttributeKey.for_keyword(\u001b[33m\"\u001b[39m\u001b[33mOrchestrated\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 14\u001b[39m search_attributes = TypedSearchAttributes([\n\u001b[32m 15\u001b[39m SearchAttributePair(customer_id_key, \u001b[33m\"\u001b[39m\u001b[33mtrue\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 16\u001b[39m ])\n\u001b[32m---> \u001b[39m\u001b[32m17\u001b[39m \u001b[38;5;28;01mawait\u001b[39;00m temporal_client.create_schedule(\n\u001b[32m 18\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mmeu-schedule-id5\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 19\u001b[39m Schedule(\n\u001b[32m 20\u001b[39m action=ScheduleActionStartWorkflow(\n\u001b[32m 21\u001b[39m \u001b[33m'\u001b[39m\u001b[33mscouter-test2\u001b[39m\u001b[33m'\u001b[39m,\n\u001b[32m 22\u001b[39m {\n\u001b[32m 23\u001b[39m \u001b[33m'\u001b[39m\u001b[33margs\u001b[39m\u001b[33m'\u001b[39m: {\n\u001b[32m 24\u001b[39m \u001b[33m'\u001b[39m\u001b[33marg1\u001b[39m\u001b[33m'\u001b[39m: \u001b[33m'\u001b[39m\u001b[33mvalue1\u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m 25\u001b[39m }\n\u001b[32m 26\u001b[39m },\n\u001b[32m 27\u001b[39m \u001b[38;5;28mid\u001b[39m=\u001b[33m\"\u001b[39m\u001b[33mworkflow-id-unico\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 28\u001b[39m task_queue=\u001b[33m\"\u001b[39m\u001b[33mnome-da-task-queue\u001b[39m\u001b[33m\"\u001b[39m,\n\u001b[32m 29\u001b[39m ),\n\u001b[32m 30\u001b[39m spec=ScheduleSpec(\n\u001b[32m 31\u001b[39m intervals=[ScheduleIntervalSpec(every=timedelta(minutes=\u001b[32m10\u001b[39m))]\n\u001b[32m 32\u001b[39m )\n\u001b[32m 33\u001b[39m ),\n\u001b[32m 34\u001b[39m search_attributes=search_attributes,\n\u001b[32m 35\u001b[39m )\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/sientia/sientia-dataops-orchestrator_temporal/venv/lib/python3.11/site-packages/temporalio/client.py:1308\u001b[39m, in \u001b[36mClient.create_schedule\u001b[39m\u001b[34m(self, id, schedule, trigger_immediately, backfill, memo, search_attributes, static_summary, static_details, rpc_metadata, rpc_timeout)\u001b[39m\n\u001b[32m 1275\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Create a schedule and return its handle.\u001b[39;00m\n\u001b[32m 1276\u001b[39m \n\u001b[32m 1277\u001b[39m \u001b[33;03mArgs:\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 1305\u001b[39m \u001b[33;03m running.\u001b[39;00m\n\u001b[32m 1306\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 1307\u001b[39m temporalio.common._warn_on_deprecated_search_attributes(search_attributes)\n\u001b[32m-> \u001b[39m\u001b[32m1308\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;01mawait\u001b[39;00m \u001b[38;5;28mself\u001b[39m._impl.create_schedule(\n\u001b[32m 1309\u001b[39m CreateScheduleInput(\n\u001b[32m 1310\u001b[39m \u001b[38;5;28mid\u001b[39m=\u001b[38;5;28mid\u001b[39m,\n\u001b[32m 1311\u001b[39m schedule=schedule,\n\u001b[32m 1312\u001b[39m trigger_immediately=trigger_immediately,\n\u001b[32m 1313\u001b[39m backfill=backfill,\n\u001b[32m 1314\u001b[39m memo=memo,\n\u001b[32m 1315\u001b[39m search_attributes=search_attributes,\n\u001b[32m 1316\u001b[39m rpc_metadata=rpc_metadata,\n\u001b[32m 1317\u001b[39m rpc_timeout=rpc_timeout,\n\u001b[32m 1318\u001b[39m )\n\u001b[32m 1319\u001b[39m )\n", "\u001b[36mFile \u001b[39m\u001b[32m~/Documents/projects/sientia/sientia-dataops-orchestrator_temporal/venv/lib/python3.11/site-packages/temporalio/client.py:6445\u001b[39m, in \u001b[36m_ClientImpl.create_schedule\u001b[39m\u001b[34m(self, input)\u001b[39m\n\u001b[32m 6437\u001b[39m already_started = (\n\u001b[32m 6438\u001b[39m err.status == RPCStatusCode.ALREADY_EXISTS\n\u001b[32m 6439\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m err.grpc_status.details\n\u001b[32m (...)\u001b[39m\u001b[32m 6442\u001b[39m )\n\u001b[32m 6443\u001b[39m )\n\u001b[32m 6444\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m already_started:\n\u001b[32m-> \u001b[39m\u001b[32m6445\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m ScheduleAlreadyRunningError()\n\u001b[32m 6446\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m\n\u001b[32m 6447\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m ScheduleHandle(\u001b[38;5;28mself\u001b[39m._client, \u001b[38;5;28minput\u001b[39m.id)\n", "\u001b[31mScheduleAlreadyRunningError\u001b[39m: Schedule already running" ] } ], "source": [ "import asyncio\n", "from datetime import timedelta\n", "from temporalio.client import (\n", " Client,\n", " Schedule,\n", " ScheduleActionStartWorkflow,\n", " ScheduleIntervalSpec,\n", " ScheduleSpec,\n", ")\n", "from temporalio.common import TypedSearchAttributes, SearchAttributeKey, SearchAttributePair\n", "\n", "\n", "customer_id_key = SearchAttributeKey.for_keyword(\"Orchestrated\")\n", "search_attributes = TypedSearchAttributes([\n", " SearchAttributePair(customer_id_key, \"true\")\n", "])\n", "await temporal_client.create_schedule(\n", " \"meu-schedule-id5\",\n", " Schedule(\n", " action=ScheduleActionStartWorkflow(\n", " 'scouter-test2',\n", " {\n", " 'args': {\n", " 'arg1': 'value1'\n", " }\n", " },\n", " id=\"workflow-id-unico\",\n", " task_queue=\"nome-da-task-queue\",\n", " ),\n", " spec=ScheduleSpec(\n", " intervals=[ScheduleIntervalSpec(every=timedelta(minutes=10))]\n", " )\n", " ),\n", " search_attributes=search_attributes,\n", ")\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "1bd82225", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Getting orchestrated schedules...\n", "Found %d orchestrated schedules 5\n", "Orchestrated schedules: %s {'meu-schedule-id5': {'frequency': 600, 'data': {'args': {'arg1': 'value1'}}, 'handle': }, 'meu-schedule-id4': {'frequency': 600, 'data': {}, 'handle': }, 'meu-schedule-id3': {'frequency': 600, 'data': {}, 'handle': }, 'meu-schedule-id2': {'frequency': 600, 'data': {}, 'handle': }, 'meu-schedule-id': {'frequency': 600, 'data': {}, 'handle': }}\n" ] } ], "source": [ "schedules = await manager.load_schedule()" ] }, { "cell_type": "code", "execution_count": 87, "id": "a4c777dd", "metadata": {}, "outputs": [], "source": [ "from google.protobuf.json_format import MessageToDict\n", "import base64\n", "import json\n", "\n", "\n", "schedules_config = {}\n", "for schedule in schedules:\n", " id = schedule.id\n", "\n", " handle = temporal_client.get_schedule_handle(id)\n", "\n", " desc = await handle.describe()\n", "\n", " for arg in desc.schedule.action.args:\n", " data = MessageToDict(arg)['data']\n", " data = base64.b64decode(data).decode('utf-8')\n", "\n", " frequency = desc.schedule.spec.intervals[0].every.seconds\n", "\n", " schedules_config[id] = {\n", " 'frequency': frequency,\n", " 'data': json.loads(data),\n", " 'handle': handle\n", " }\n", " \n", " \n", " \n", " " ] }, { "cell_type": "code", "execution_count": 88, "id": "988d1718", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'meu-schedule-id5': {'frequency': 600,\n", " 'data': {'args': {'arg1': 'value1'}},\n", " 'handle': },\n", " 'meu-schedule-id4': {'frequency': 600,\n", " 'data': {},\n", " 'handle': },\n", " 'meu-schedule-id3': {'frequency': 600,\n", " 'data': {},\n", " 'handle': },\n", " 'meu-schedule-id2': {'frequency': 600,\n", " 'data': {},\n", " 'handle': },\n", " 'meu-schedule-id': {'frequency': 600,\n", " 'data': {},\n", " 'handle': }}" ] }, "execution_count": 88, "metadata": {}, "output_type": "execute_result" } ], "source": [ "schedules_config" ] }, { "cell_type": "code", "execution_count": 85, "id": "c12f5e75", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "meu-schedule-id5 {'frequency': 600, 'data': {'args': {'arg1': 'value1'}}, 'handle': }\n", "meu-schedule-id4 {'frequency': 1200, 'data': {'args': {'arg1': 'value1'}}, 'handle': }\n", "meu-schedule-id4\n", "meu-schedule-id3 {'frequency': 600, 'data': {}, 'handle': }\n", "meu-schedule-id2 {'frequency': 600, 'data': {}, 'handle': }\n", "meu-schedule-id {'frequency': 600, 'data': {}, 'handle': }\n" ] } ], "source": [ "from temporalio.client import Client, ScheduleUpdateInput, ScheduleUpdate, ScheduleSpec\n", "\n", "\n", "for id, schedule in schedules_config.items():\n", " print(id, schedule)\n", " \n", " if schedule['frequency'] != 600:\n", " print(id)\n", "\n", " handle = schedule['handle']\n", " \n", " async def update_schedule(input: ScheduleUpdateInput) -> ScheduleUpdate:\n", " schedule_action = input.description.schedule.action\n", " \n", " if hasattr(schedule_action, 'args'):\n", " schedule_action.args = [{}]\n", " \n", " # Atualiza o intervalo de execução\n", " input.description.schedule.spec.intervals = [\n", " ScheduleIntervalSpec(every=timedelta(minutes=10))\n", " ]\n", " \n", " return ScheduleUpdate(schedule=input.description.schedule)\n", "\n", " await handle.update(update_schedule)\n", " \n" ] }, { "cell_type": "code", "execution_count": 1, "id": "87eb10c8", "metadata": {}, "outputs": [], "source": [ "from redis import Redis\n", "\n", "redis = Redis(host='localhost', port=6379)" ] }, { "cell_type": "code", "execution_count": 6, "id": "fe4ad9dc", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Chave: slot:opc_tags:2, Valor: {\n", "\"slot2\": \"value\"\n", "}\n", "Chave: slot:opc_tags:1, Valor: {\n", "\"slot1\": \"value\"\n", "}\n" ] } ], "source": [ "matching_keys = redis.keys(\"slot:opc_tags:*\")\n", "\n", "if matching_keys:\n", " decoded_keys = [key.decode('utf-8') for key in matching_keys]\n", " values = redis.mget(decoded_keys)\n", "\n", " items = {}\n", " for i, key in enumerate(decoded_keys):\n", " value = values[i]\n", " if value is not None:\n", " try:\n", " items[key] = value.decode('utf-8')\n", " except (UnicodeDecodeError, AttributeError):\n", " items[key] = value\n", " else:\n", " items[key] = None\n", "\n", " for key, value in items.items():\n", " print(f\"Chave: {key}, Valor: {value}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "87a9dc89", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "venv", "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.11.12" } }, "nbformat": 4, "nbformat_minor": 5 }