diff --git a/fiscaliza/main.py b/fiscaliza/main.py index 3dca5e9..66c68e2 100644 --- a/fiscaliza/main.py +++ b/fiscaliza/main.py @@ -133,7 +133,6 @@ def extract_value(field: str | int | dict | list) -> str | int | list: elif isinstance(field, (int, float)) or field is None: return field - else: raise TypeError( f"O tipo de campo {type(field)} não é suportado. " diff --git a/nbs/00_main.ipynb b/nbs/00_main.ipynb index 4ff44f4..e41af76 100644 --- a/nbs/00_main.ipynb +++ b/nbs/00_main.ipynb @@ -64,7 +64,11 @@ "#| export\n", "load_dotenv(override=True)\n", "\n", - "UTFCHARS = re.compile(r\"[!\\\"#$%&'\\(\\)*+\\,\\-\\.\\/:;<=>\\?@\\[\\\\\\]\\^`_\\{\\|\\}~]\")\n" + "UTFCHARS = re.compile(r\"[!\\\"#$%&'\\(\\)*+\\,\\-\\.\\/:;<=>\\?@\\[\\\\\\]\\^`_\\{\\|\\}~]\")\n", + "\n", + "CONDITIONAL_FIELDS = {\n", + " k: v.reset(v) for k, v in FIELDS.items() if getattr(v, \"mapping\", False)\n", + "}\n" ] }, { @@ -112,8 +116,8 @@ " ) from e\n", " return fiscaliza\n", "\n", - " def get_issue(self, issue: str) -> dict:\n", - " return Issue(self.client, issue)\n" + " def get_issue(self, issue_id: str | int):\n", + " return Issue(self.client, issue_id)\n" ] }, { @@ -138,12 +142,23 @@ " )\n", " self._ascii2utf = {}\n", "\n", + " @property\n", + " def current_user(self):\n", + " user = dict(list(self.client.user.get(\"current\")))\n", + " return f'{user.get(\"firstname\", \"\")} {user.get(\"lastname\", \"\")}'\n", + "\n", " @staticmethod\n", " def __format_json_string(field: str) -> str:\n", " \"\"\"Recebe uma string formatada como json e retorna a mesma string formatada como json\"\"\"\n", " string = field.replace(\"'\", '\"').replace(\"=>\", \": \")\n", + "\n", + " def force_strings(obj):\n", + " if isinstance(obj, dict):\n", + " return {k: str(v) for k, v in obj.items()}\n", + " return obj\n", + "\n", " try:\n", - " return json.loads(string)\n", + " return json.loads(string, object_hook=force_strings)\n", " except (json.JSONDecodeError, TypeError):\n", " return string\n", "\n", @@ -152,7 +167,9 @@ " \"\"\"Recebe uma string formatada como json e extrai os valores das chaves de acordo com o tipo de campo\"\"\"\n", " if isinstance(field, str):\n", " json_obj = Issue.__format_json_string(field)\n", - " if isinstance(json_obj, (str, int, float)):\n", + " if isinstance(json_obj, (int, float)):\n", + " return str(json_obj)\n", + " elif isinstance(json_obj, str):\n", " return json_obj\n", " return Issue.extract_value(json_obj)\n", "\n", @@ -166,7 +183,6 @@ "\n", " elif isinstance(field, (int, float)) or field is None:\n", " return field\n", - "\n", " else:\n", " raise TypeError(\n", " f\"O tipo de campo {type(field)} não é suportado. \"\n", @@ -285,8 +301,8 @@ " except ValueError:\n", " pass\n", " attrs |= {\n", - " \"latitude_coordenadas\": lat,\n", - " \"longitude_coordenadas\": long,\n", + " \"latitude_coordenadas\": str(lat),\n", + " \"longitude_coordenadas\": str(long),\n", " }\n", " if coords := attrs.pop(\"coordenadas_estacao\", None):\n", " coords = Issue.__format_json_string(coords)\n", @@ -296,8 +312,8 @@ " except ValueError:\n", " pass\n", " attrs |= {\n", - " \"latitude_da_estacao\": lat,\n", - " \"longitude_da_estacao\": long,\n", + " \"latitude_da_estacao\": str(lat),\n", + " \"longitude_da_estacao\": str(long),\n", " }\n", " return attrs\n", "\n", @@ -349,14 +365,14 @@ " return {k: attrs[k] for k in sorted(attrs)}\n", "\n", " @staticmethod\n", - " def _append_irregularity_options(\n", - " tipo_de_inspecao: str, editable_fields: dict\n", - " ) -> dict:\n", + " def _append_irregularity_options(editable_fields: dict) -> dict:\n", " \"\"\"\n", " Appends the options of the 'irregularidade' field to the editable_fields dictionary.\n", " \"\"\"\n", - " if editable_fields.get(\"irregularidade\"):\n", - " match tipo_de_inspecao:\n", + " if (\n", + " \"irregularidade\" in editable_fields\n", + " ): # checking only the existence of the key, because it can be an empty list\n", + " match editable_fields[\"tipo_de_inspecao\"].value:\n", " case \"Certificação\":\n", " options = [\n", " \"Comercialização de produtos\",\n", @@ -375,20 +391,43 @@ " \"Outras irregularidades técnicas (especificar)\",\n", " \"Potência diversa da autorizada\",\n", " ]\n", + " case \"Uso do Espectro - Não Outorgado\":\n", + " options = [\"Entidade não outorgada\"]\n", " case __:\n", " options = []\n", "\n", " editable_fields[\"irregularidade\"].options = options\n", " return editable_fields\n", "\n", + " # @staticmethod\n", + " # def _cast_and_set(field, value):\n", + " # try:\n", + " # match field.dtype:\n", + " # case \"string\":\n", + " # value = str(value)\n", + " # case \"int\":\n", + " # value = int(value)\n", + " # case \"float\":\n", + " # value = float(value)\n", + " # case \"list\":\n", + " # value = listify(value)\n", + " # case _:\n", + " # print(f\"Unknown dtype {field.dtype}, casting skipped...\")\n", + " # except ValueError as e:\n", + " # print(\n", + " # f\"Error casting {field.name} value {value} to dtype {field.dtype}: {e}\"\n", + " # )\n", + " # setattr(field, \"value\", value)\n", + " # return field\n", + "\n", " @cached_property\n", " def editable_fields(self) -> dict:\n", " \"\"\"Retrieves the editable fields of an issue as a dictionary.\"\"\"\n", " editable_fields = {}\n", " keys_by_id = sorted(FIELDS.keys(), key=lambda x: getattr(FIELDS[x], \"id\", 0))\n", - " fields = {k: FIELDS[k] for k in keys_by_id}\n", - " for key, field in fields.items():\n", - " if key in self.attrs:\n", + " fields = {k: FIELDS[k].reset(FIELDS[k]) for k in keys_by_id}\n", + " for key in self.attrs:\n", + " if field := fields.get(key):\n", " if hasattr(field, \"options\"):\n", " if field.multiple:\n", " self.attrs[key] = [str(k) for k in self.attrs[key]]\n", @@ -400,11 +439,8 @@ " elif key == \"fiscal_responsavel\":\n", " setattr(field, \"options\", [\"\"] + self.attrs[\"membros\"])\n", " editable_fields[key] = field\n", - " if tipo_de_inspecao := self.attrs.get(\"tipo_de_inspecao\"):\n", - " editable_fields = self._append_irregularity_options(\n", - " tipo_de_inspecao, editable_fields\n", - " )\n", - " return editable_fields\n", + " editable_fields = self._append_irregularity_options(editable_fields)\n", + " return self._update_fields(self.attrs, editable_fields)\n", "\n", " def mandatory_fields(self) -> dict:\n", " return {\n", @@ -420,43 +456,89 @@ " if getattr(v, \"mapping\", False)\n", " }\n", "\n", - " def update_fields(self, dados: dict) -> dict:\n", + " @staticmethod\n", + " def _fields_derived_from_select_conditional(key, value) -> dict:\n", " \"\"\"\n", - " Check if the data to be submitted to the Fiscaliza server is complete and valid.\n", + " Fill in the fields resulted from values on other fields.\n", " \"\"\"\n", + " dependent_fields = {}\n", + " if field := CONDITIONAL_FIELDS.get(key):\n", + " for val in listify(\n", + " value\n", + " ): # abusing use of empty defaults from dict.get to avoid if clauses\n", + " val = str(val)\n", + " if field.options:\n", + " assert (\n", + " val in field.options\n", + " ), f\"Opção inválida para o campo {key}: {val}\"\n", + " for new_key in field.mapping.get(val, []):\n", + " if new_key not in dependent_fields:\n", + " dependent_fields[new_key] = FIELDS[new_key].reset(\n", + " FIELDS[new_key]\n", + " )\n", "\n", - " for key, field in self.conditional_fields().items():\n", - " if key in dados:\n", - " if hasattr(self, \"editable_fields\"):\n", - " del self.editable_fields\n", - " new_fields = set()\n", - " all_fields = {\n", - " item for values in field.mapping.values() for item in values\n", - " }\n", - " for option in listify(dados[key]):\n", - " if field.options:\n", - " assert (\n", - " option in field.options\n", - " ), f\"Opção inválida para o campo {key}: {option}\"\n", - "\n", - " if fields := field.mapping.get(option):\n", - " new_fields.update(fields)\n", - "\n", - " self.editable_fields = {\n", - " k: v\n", - " for k, v in self.editable_fields.items()\n", - " if k not in all_fields.difference(new_fields)\n", - " }\n", + " return dependent_fields\n", "\n", - " self.editable_fields |= {k: FIELDS[k] for k in new_fields}\n", + " @staticmethod\n", + " def _keys_unrelated_from_select_conditional(key, value) -> dict:\n", + " \"\"\"\n", + " Fill in the fields resulted from values on other fields.\n", + " \"\"\"\n", + " unrelated_fields = set()\n", + " if field := CONDITIONAL_FIELDS.get(key):\n", + " for val in listify(\n", + " value\n", + " ): # abusing use of empty defaults from dict.get to avoid if clauses\n", + " val = str(val)\n", + " if field.options:\n", + " assert (\n", + " val in field.options\n", + " ), f\"Opção inválida para o campo {key}: {val}\"\n", + " for options in field.mapping.values():\n", + " for option in options:\n", + " if option not in field.mapping.get(val, []):\n", + " unrelated_fields.add(option)\n", + " return unrelated_fields\n", + "\n", + " @staticmethod\n", + " def _update_options_for_each_conditional(dados: dict) -> tuple[dict, set]:\n", + " dependent_fields = {}\n", + " unrelated_keys = set()\n", + " for key, value in dados.items():\n", + " dependent_fields |= Issue._fields_derived_from_select_conditional(\n", + " key, value\n", + " )\n", + " unrelated_keys.difference_update(dependent_fields)\n", + " unrelated_keys.update(\n", + " Issue._keys_unrelated_from_select_conditional(key, value)\n", + " )\n", + " return dependent_fields, unrelated_keys\n", + "\n", + " @staticmethod\n", + " def _update_fields(dados: dict, editable_fields: dict) -> dict:\n", + " \"\"\"\n", + " Check if the data to be submitted to the Fiscaliza server is complete and valid.\n", + " \"\"\"\n", + " insert, delete = Issue._update_options_for_each_conditional(dados)\n", + " for key, value in insert.items():\n", + " if key not in editable_fields:\n", + " editable_fields[key] = value\n", + " for key in delete:\n", + " if key in editable_fields:\n", + " del editable_fields[key]\n", + " return editable_fields\n", + "\n", + " def update_fields(self, dados: dict):\n", + " \"\"\"\n", + " Check if the data to be submitted to the Fiscaliza server is complete and valid.\n", + " \"\"\"\n", + " self.editable_fields = self._update_fields(dados, self.editable_fields)\n", "\n", " for key, value in dados.items():\n", " if key in self.editable_fields:\n", " self.editable_fields[key](value)\n", - " if key == \"tipo_de_inspecao\":\n", - " self.editable_fields = self._append_irregularity_options(\n", - " value, self.editable_fields\n", - " )\n", + "\n", + " self.editable_fields = self._append_irregularity_options(self.editable_fields)\n", "\n", " def _get_id_only_fields(self, data: dict) -> dict:\n", " if status := data.get(\"status\"):\n", @@ -474,7 +556,9 @@ " (\"latitude_coordenadas\" in data) and (\"longitude_coordenadas\" in data)\n", " ): # Don't use numeric data that could be zero in clauses, that why the 'in' is here and not := dados.get(...)\n", " newkey = \"coordenadas_geograficas\"\n", - " self.editable_fields[newkey] = SPECIAL_FIELDS[newkey]\n", + " self.editable_fields[newkey] = SPECIAL_FIELDS[newkey].reset(\n", + " SPECIAL_FIELDS[newkey]\n", + " )\n", " self.editable_fields.pop(\"latitude_coordenadas\", None)\n", " self.editable_fields.pop(\"longitude_coordenadas\", None)\n", " data[newkey] = (\n", @@ -488,7 +572,9 @@ " )\n", " if (\"latitude_da_estacao\" in data) and (\"longitude_da_estacao\" in data):\n", " newkey = \"coordenadas_estacao\"\n", - " self.editable_fields[newkey] = SPECIAL_FIELDS[newkey]\n", + " self.editable_fields[newkey] = SPECIAL_FIELDS[newkey].reset(\n", + " SPECIAL_FIELDS[newkey]\n", + " )\n", " self.editable_fields.pop(\"latitude_da_estacao\", None)\n", " self.editable_fields.pop(\"longitude_da_estacao\", None)\n", " data[newkey] = (\n", @@ -512,7 +598,9 @@ " raise ValueError(\n", " \"Para gerar o PLAI é necessário fornecer o tipo do processo e as coordenação da FI\"\n", " )\n", - " self.editable_fields[newkey] = SPECIAL_FIELDS[newkey]\n", + " self.editable_fields[newkey] = SPECIAL_FIELDS[newkey].reset(\n", + " SPECIAL_FIELDS[newkey]\n", + " )\n", " data[newkey] = (tipo_processo_plai, coords_fi_plai)\n", "\n", " return data\n", @@ -608,10 +696,11 @@ " ],\n", " )\n", "\n", - " def update(self, dados: dict):\n", + " def update(self, dados: dict) -> str:\n", " \"\"\"Updates an issue with the given data.\"\"\"\n", " self.refresh()\n", " status = self.editable_fields[\"status\"].value\n", + " message = \"\"\n", " for new_status in FLOW[status]:\n", " status_id = STATUS[new_status]\n", " if subset := STATES.get(new_status):\n", @@ -621,8 +710,10 @@ " else:\n", " data = self._parse_value_dict(dados)\n", " self.client.issue.update(self.id, status_id=status_id, **data)\n", - " print(f\"Atualizado para o status {new_status}\")\n", - " self.refresh()" + " message = f'A Inspeção nº {self.id} foi atualizada. O seu estado atual é \"{new_status}\".'\n", + " self.refresh()\n", + "\n", + " return message" ] }, { diff --git a/nbs/02_datatypes.ipynb b/nbs/02_datatypes.ipynb index 9718401..06c29f4 100644 --- a/nbs/02_datatypes.ipynb +++ b/nbs/02_datatypes.ipynb @@ -44,10 +44,21 @@ " keyword: str\n", " mandatory: bool = True\n", "\n", + " @classmethod\n", + " def reset(cls, instance):\n", + " new_instance = cls(\n", + " **{k: v for k, v in instance.__dict__.items() if k != \"value\"}\n", + " )\n", + " return new_instance\n", + "\n", " @property\n", " def dtype(self):\n", " return \"string\"\n", "\n", + " @cached_property\n", + " def value(self):\n", + " return \"\"\n", + "\n", " def __call__(self, value):\n", " self.value = value\n", " return value\n", @@ -77,6 +88,13 @@ " format_value: bool = False\n", " _dtype: str = \"string\"\n", "\n", + " @classmethod\n", + " def reset(cls, instance):\n", + " new_instance = cls(\n", + " **{k: v for k, v in instance.__dict__.items() if k != \"value\"}\n", + " )\n", + " return new_instance\n", + "\n", " @property\n", " def dtype(self):\n", " return self._dtype\n", @@ -93,7 +111,7 @@ " case \"int\":\n", " return 0\n", " case \"float\":\n", - " return \"\"\n", + " return 0.0\n", " case \"list\":\n", " return []\n", " case _:\n", @@ -122,9 +140,7 @@ " return {\"id\": self.id, \"value\": self.validate_value(value)}\n", "\n", " def __repr__(self) -> str:\n", - " string = \"\"\n", - " if hasattr(self, \"value\"):\n", - " string = f\"(value: {self.value})\"\n", + " string = f'(value: {getattr(self, \"value\", \"\")})'\n", " if self.mandatory:\n", " string += \" | \"\n", " if self.multiple:\n", @@ -149,14 +165,7 @@ " return \"{\" + '\"numero\"=>\"{0}\"'.format(value) + \"}\"\n", "\n", " def __repr__(self) -> str:\n", - " string = \"\"\n", - " if hasattr(self, \"value\"):\n", - " string = f\"({self.value})\"\n", - " if self.mandatory:\n", - " string += \" | \"\n", - " if self.multiple:\n", - " string += \", \"\n", - " return string\n" + " return super().__repr__()\n" ] }, { @@ -181,7 +190,7 @@ " if self.multiple:\n", " value = [str(v) for v in listify(value)]\n", " for v in value:\n", - " if self.options is not None and v not in self.options:\n", + " if self.options and v not in self.options:\n", " raise ValueError(\n", " f\"The value {v} must be one of the valid options: {self.options} for field {self.name}\"\n", " )\n", @@ -189,7 +198,7 @@ " value = [self.options[v] for v in value]\n", " else:\n", " value = str(value)\n", - " if self.options is not None and value not in self.options:\n", + " if self.options and value not in self.options:\n", " raise ValueError(\n", " f\"The value {value} must be one of the valid options: {self.options} for field {self.name}\"\n", " )\n", @@ -225,7 +234,17 @@ " id: int\n", " name: str\n", " mandatory: bool = False\n", - " value: tuple[str | float] | None = None\n", + "\n", + " @classmethod\n", + " def reset(cls, instance):\n", + " new_instance = cls(\n", + " **{k: v for k, v in instance.__dict__.items() if k != \"value\"}\n", + " )\n", + " return new_instance\n", + "\n", + " @cached_property\n", + " def value(self):\n", + " return None\n", "\n", " def format_value_string(self, latitude: str, longitude: str) -> str:\n", " return (\n", @@ -263,15 +282,25 @@ " id: int\n", " name: str\n", " mandatory: bool = False\n", - " value: tuple | None = None\n", " TIPO_DE_PROCESSO = [\n", " \"Gestão da Fiscalização: Lacração, Apreensão e Interrupção\",\n", " \"Gestão da Fiscalização: Processo de Guarda\",\n", " ]\n", - " COORD_FI = [\"FI1\", \"FI2\"]\n", + " COORD_FI = [\"FI\", \"FI1\", \"FI2\", \"FI3\"]\n", " CODES = [\"100000539\", \"100000618\"]\n", " options = list(product(TIPO_DE_PROCESSO, COORD_FI))\n", "\n", + " @classmethod\n", + " def reset(cls, instance):\n", + " new_instance = cls(\n", + " **{k: v for k, v in instance.__dict__.items() if k != \"value\"}\n", + " )\n", + " return new_instance\n", + "\n", + " @cached_property\n", + " def value(self):\n", + " return \"0\"\n", + "\n", " def validate_tipo_processo(self, value: str) -> str:\n", " options = dict(zip(self.TIPO_DE_PROCESSO, self.CODES))\n", " if value not in options:\n", diff --git a/nbs/03_attrs.ipynb b/nbs/03_attrs.ipynb index c02f15a..69dc3e2 100644 --- a/nbs/03_attrs.ipynb +++ b/nbs/03_attrs.ipynb @@ -45,7 +45,10 @@ "SPECIAL_FIELDS = {\n", " \"coordenadas_estacao\": Coordenadas(718, \"Coordenadas Estação\"),\n", " \"coordenadas_geograficas\": Coordenadas(717, \"Coordenadas Geográficas\", True),\n", - " \"gerar_plai\": GerarPlai(426, \"Gerar PLAI\"),\n", + " \"gerar_plai\": GerarPlai(426, \"Gerar PLAI?\"),\n", + " \"outras_fontes_interferentes\": EncodedString( # NotImplemented\n", + " 2076, \"Outras fontes interferentes:\", True\n", + " ),\n", "}\n", "\n", "FIELDS = {\n", @@ -53,7 +56,15 @@ " \"description\": AtomicField(\"Descrição\", \"description\"),\n", " \"start_date\": AtomicField(\"Data de início\", \"start_date\"),\n", " \"due_date\": AtomicField(\"Data limite\", \"due_date\"),\n", - " \"acao_de_risco_a_vida_criada\": SimpleField(154, \"Ação de risco à vida criada?\"),\n", + " \"acao_de_risco_a_vida_criada\": FieldWithOptions(\n", + " 154,\n", + " \"Ação de risco à vida criada?\",\n", + " options=[\"\", \"Não\", \"Sim\"],\n", + " mapping={\"Sim\": [\"acao_de_risco_a_vida\"]},\n", + " ),\n", + " \"acao_de_risco_a_vida\": FieldWithOptions(\n", + " 155, \"Ação de risco à vida\", multiple=True, format_value=True, options=[]\n", + " ),\n", " \"agrupamento\": SimpleField(213, \"Agrupamento:\"),\n", " \"altura_do_sistema_irradiante\": SimpleField(131, \"Altura do sistema irradiante:\"),\n", " \"area_do_pacp\": FieldWithOptions(\n", @@ -68,7 +79,6 @@ " \"5-Feiras e Eventos\",\n", " \"6-Supervisão de Mercados\",\n", " ],\n", - " format_value=True,\n", " ),\n", " \"campo_eletrico__pico_vm\": SimpleField(195, \"Campo elétrico pico (V/m):\", True),\n", " \"campo_eletrico_rms_vm\": SimpleField(194, \"Campo elétrico RMS (V/m):\", True),\n", @@ -85,10 +95,17 @@ " \"documento_instaurador_do_pado\": SimpleField(134, \"Documento instaurador do PADO:\"),\n", " \"endereco_da_inspecao\": SimpleField(142, \"Endereço da Inspeção:\", True),\n", " \"entidade_com_cadastro_stel\": FieldWithOptions(\n", - " 189, \"Entidade com cadastro STEL?\", mandatory=True, options=[\"\", \"Sim\", \"Não\"]\n", + " 189,\n", + " \"Entidade com cadastro STEL?\",\n", + " mandatory=True,\n", + " options=[\"\", \"Não\", \"Sim\"],\n", + " mapping={\n", + " \"Sim\": [\"entidade_da_inspecao\"],\n", + " \"Não\": [\"nome_da_entidade\", \"cnpjcpf_da_entidade\"],\n", + " },\n", " ),\n", " \"entidade_da_inspecao\": FieldWithOptions(\n", - " 30, \"Entidade da Inspeção:\", multiple=True, options=[]\n", + " 30, \"Entidade da Inspeção:\", multiple=True, format_value=True, options=[]\n", " ),\n", " \"entidade_outorgada\": FieldWithOptions(\n", " 138,\n", @@ -103,14 +120,38 @@ " \"fiscais\": FieldWithOptions(26, \"Fiscais:\", mandatory=True, multiple=True),\n", " \"fiscal_responsavel\": FieldWithOptions(25, \"Fiscal responsável:\", mandatory=True),\n", " \"foi_constatada_interferencia\": FieldWithOptions(\n", - " 1967, \"Foi constatada interferência?\", mandatory=True, options=[\"\", \"0\", \"1\"]\n", + " 1967,\n", + " \"Foi constatada interferência?\",\n", + " mandatory=True,\n", + " options=[\"\", \"0\", \"1\"],\n", + " mapping={\n", + " \"0\": [\"justificativa_da_improcedencia\"],\n", + " \"1\": [\n", + " \"interferencia_sanada\",\n", + " \"local_interf_confere_indicado\",\n", + " \"tipo_de_fonte_interferente\",\n", + " \"fonte_e_modelo\",\n", + " \"frequencia_mhz\",\n", + " \"potencia_de_operacao_w\",\n", + " \"homologada\",\n", + " \"ha_outras_fontes_interfer\",\n", + " ],\n", + " },\n", + " ),\n", + " \"fonte_e_modelo\": SimpleField(\n", + " 2069,\n", + " \"Fonte e modelo:\",\n", + " ),\n", + " \"frequencia_mhz\": SimpleField(\n", + " 2071,\n", + " \"Frequência (MHz):\",\n", " ),\n", - " \"frequencia_inicial\": SimpleField(156, \"Frequência inicial:\", True),\n", - " \"frequencia_final\": SimpleField(158, \"Frequência final:\", True),\n", + " \"frequencia_inicial\": SimpleField(156, \"Frequência inicial:\", True, _dtype=\"float\"),\n", + " \"frequencia_final\": SimpleField(158, \"Frequência final:\", True, _dtype=\"float\"),\n", " \"frequencias\": SimpleField(180, \"Frequência(s):\"),\n", " \"gerar_plai\": FieldWithOptions(\n", " 426,\n", - " \"Gerar Plai\",\n", + " \"Gerar PLAI?\",\n", " options=[\"\", \"0\", \"1\"],\n", " mapping={\"1\": [\"tipo_do_processo_plai\", \"coord_fi_plai\"]},\n", " ),\n", @@ -120,17 +161,33 @@ " options=[\"\", \"0\", \"1\"],\n", " mapping={\"1\": [\"html\"]},\n", " ),\n", - " \"horas_de_conclusao\": SimpleField(94, \"Horas de conclusão\", True, _dtype=\"int\"),\n", + " \"ha_outras_fontes_interfer\": FieldWithOptions(\n", + " 2075,\n", + " \"Há outras fontes interferentes?\",\n", + " mandatory=True,\n", + " options=[\"\", \"Não\", \"Sim\"],\n", + " ),\n", + " \"homologada\": FieldWithOptions(\n", + " 2074,\n", + " \"Homologada?\",\n", + " mandatory=True,\n", + " options=[\"\", \"Não\", \"Sim\", \"Indeterminado\"],\n", + " ),\n", + " \"horas_de_conclusao\": SimpleField(94, \"Horas de conclusão\", True, _dtype=\"float\"),\n", " \"horas_de_deslocamento\": SimpleField(\n", - " 92, \"Horas de deslocamento\", True, _dtype=\"int\"\n", + " 92, \"Horas de deslocamento\", True, _dtype=\"float\"\n", " ),\n", - " \"horas_de_execucao\": SimpleField(93, \"Horas de Execução\", True, _dtype=\"int\"),\n", - " \"horas_de_preparacao\": SimpleField(91, \"Horas de Preparação\", True, _dtype=\"int\"),\n", + " \"horas_de_execucao\": SimpleField(93, \"Horas de Execução\", True, _dtype=\"float\"),\n", + " \"horas_de_preparacao\": SimpleField(91, \"Horas de Preparação\", True, _dtype=\"float\"),\n", " \"houve_interferencia\": FieldWithOptions(\n", " 149,\n", " \"Houve interferência?\",\n", " mandatory=True,\n", - " options=[\"\", \"Sim\", \"Não\"],\n", + " options=[\n", + " \"\",\n", + " \"Não\",\n", + " \"Sim\",\n", + " ],\n", " mapping={\"Sim\": [\"identificada_a_origem\"]},\n", " ),\n", " \"houve_obice\": FieldWithOptions(\n", @@ -150,6 +207,21 @@ " \"instrumentos_utilizados\": FieldWithOptions(\n", " 599, \"Instrumentos Utilizados\", True, True, options=[]\n", " ),\n", + " \"interferencia_sanada\": FieldWithOptions(\n", + " 1969,\n", + " \"Interferência sanada?\",\n", + " mandatory=True,\n", + " options=[\n", + " \"\",\n", + " \"Sim, confirmado pelo denunciante\",\n", + " \"Sim, sem confirmação pelo denunciante\",\n", + " \"Não\",\n", + " ],\n", + " mapping={\n", + " \"Sim, sem confirmação pelo denunciante\": [\"justificativa_nao_confirmacao\"],\n", + " \"Não\": [\"justificativa_nao_resolucao\"],\n", + " },\n", + " ),\n", " \"irregularidade\": FieldWithOptions(\n", " 73,\n", " \"Irregularidade:\",\n", @@ -157,11 +229,35 @@ " options=[], # Preechido dinamicamente dentro da classe Issue\n", " format_value=True,\n", " ),\n", - " \"lai_vinculadas\": SimpleField(481, \"LAI vinculadas\"),\n", + " \"justificativa_da_improcedencia\": FieldWithOptions(\n", + " 1968,\n", + " \"Justificativa da Improcedência:\",\n", + " options=[\n", + " \"\",\n", + " \"Reclamante informou que interferência cessou\",\n", + " \"Dados obtidos indicam que interferência não procede\",\n", + " \"Não foi constatada portadora interferente em campo\",\n", + " ],\n", + " ),\n", + " \"justificativa_nao_confirmacao\": SimpleField(\n", + " 1970,\n", + " \"Justificativa não confirmação:\",\n", + " mandatory=True,\n", + " ),\n", + " \"justificativa_nao_resolucao\": SimpleField(\n", + " 1971, \"Justificativa não resolução:\", mandatory=True\n", + " ),\n", + " # \"lai_vinculadas\": SimpleField(481, \"LAI vinculadas\"),\n", " \"latitude_coordenadas\": SimpleField(170, \"Latitude (º):\", True),\n", - " \"latitude_da_estacao\": SimpleField(191, \"Latitude da estação:\", True),\n", + " \"latitude_da_estacao\": SimpleField(191, \"Latitude da estação (º):\", True),\n", + " \"local_interf_confere_indicado\": FieldWithOptions(\n", + " 1972,\n", + " \"Local interferência confere indicado?\",\n", + " mandatory=True,\n", + " options=[\"\", \"Sim\", \"Não\", \"Parcialmente\", \"Não se aplica\"],\n", + " ),\n", " \"longitude_coordenadas\": SimpleField(171, \"Longitude (º):\", True),\n", - " \"longitude_da_estacao\": SimpleField(192, \"Longitude da estação:\", True),\n", + " \"longitude_da_estacao\": SimpleField(192, \"Longitude da estação (º):\", True),\n", " \"motivo_de_lai\": FieldWithOptions(\n", " 164,\n", " \"Motivo de LAI:\",\n", @@ -177,15 +273,16 @@ " ],\n", " ),\n", " \"numero_da_estacao\": SimpleField(137, \"Número da estação:\", True),\n", - " \"numero_do_pai\": SimpleField(211, \"Número do PAI:\"),\n", + " \"numero_do_pai\": SimpleField(211, \"Nº SEI PAI:\"),\n", " \"no_de_homologacao\": SimpleField(161, \"Nº de Homologação:\"),\n", - " \"no_do_lacre\": SimpleField(165, \"Nº do lacre\", True),\n", + " \"no_do_lacre\": SimpleField(165, \"Nº do lacre:\", True),\n", " \"no_pcdp\": SimpleField(112, \"Nº PCDP:\"),\n", " \"no_sav\": SimpleField(111, \"Nº SAV:\"),\n", " \"no_sei_do_aviso_lai\": EncodedString(427, \"Nº SEI Referendo (Aviso LAI):\", True),\n", + " \"no_sei_do_oficio_ao_mctic\": EncodedString(428, \"Nº SEI do Ofício ao MCTIC:\"),\n", " \"no_sei_do_plaiguarda\": EncodedString(426, \"Nº SEI PLAI:\"),\n", " \"no_sei_processo_fiscalizacao\": EncodedString(422, \"Nº SEI PFIS:\"),\n", - " \"no_sei_relatorio_monitoramento\": SimpleField(544, \"Nº SEI Relatório:\"),\n", + " \"no_sei_relatorio_de_atividades\": SimpleField(544, \"Nº SEI Relatório:\"),\n", " \"nome_da_entidade\": SimpleField(140, \"Nome da Entidade:\", True),\n", " \"observacao_tecnica_amostral\": SimpleField(\n", " 693, \"Observação (técnica amostral):\", True\n", @@ -198,6 +295,7 @@ " options=[\"\", \"Não\", \"Sim\"],\n", " mapping={\"Sim\": [\"numero_do_pai\"]},\n", " ),\n", + " \"potencia_de_operacao_w\": SimpleField(2072, \"Potência de Operação (W):\", True),\n", " \"potencia_medida\": SimpleField(81, \"Potência:\"),\n", " \"precisa_reservar_instrumentos\": FieldWithOptions(\n", " 596,\n", @@ -248,21 +346,21 @@ " \"no_do_lacre\",\n", " \"motivo_de_lai\",\n", " \"no_sei_do_aviso_lai\",\n", - " \"lai_vinculadas\",\n", + " # \"lai_vinculadas\",\n", " \"no_sei_do_plaiguarda\",\n", " \"gerar_plai\",\n", " ],\n", " \"Apreensão\": [\n", " \"motivo_de_lai\",\n", " \"no_sei_do_aviso_lai\",\n", - " \"lai_vinculadas\",\n", + " # \"lai_vinculadas\",\n", " \"no_sei_do_plaiguarda\",\n", " \"gerar_plai\",\n", " ],\n", " \"Interrupção\": [\n", " \"motivo_de_lai\",\n", " \"no_sei_do_aviso_lai\",\n", - " \"lai_vinculadas\",\n", + " # \"lai_vinculadas\",\n", " \"no_sei_do_plaiguarda\",\n", " \"gerar_plai\",\n", " ],\n", @@ -279,7 +377,7 @@ " 597, \"Reserva de instrumentos:\", True, True, True\n", " ),\n", " \"sanada_ou_mitigada\": FieldWithOptions(\n", - " 163, \"Sanada ou mitigada?\", mandatory=True, options=[\"\", \"1\", \"0\"]\n", + " 163, \"Sanada ou mitigada?\", mandatory=True, options=[\"\", \"0\", \"1\"]\n", " ),\n", " \"servicos_da_inspecao\": FieldWithOptions(\n", " 57,\n", @@ -299,7 +397,7 @@ " 150,\n", " \"Situação de risco à vida?\",\n", " mandatory=True,\n", - " options=[\"\", \"Sim\", \"Não\"],\n", + " options=[\"\", \"Não\", \"Sim\"],\n", " ),\n", " \"tipificacao_da_infracao\": FieldWithOptions(\n", " 148,\n", @@ -311,6 +409,26 @@ " \"Outro\",\n", " ],\n", " ),\n", + " \"tipo_de_fonte_interferente\": FieldWithOptions(\n", + " 2068,\n", + " \"Tipo de fonte interferente:\",\n", + " mandatory=True,\n", + " options=[\n", + " \"\",\n", + " \"BSR\",\n", + " \"Estação de Radiodifusão\",\n", + " \"Estação de Telecomunicações\",\n", + " \"Estação Clandestina\",\n", + " \"Estação Estrangeira\",\n", + " \"Reforçador de Celular\",\n", + " \"Câmera sem fio\",\n", + " \"Microfone sem fio\",\n", + " \"Equipamento de Radiação Não Intencional\",\n", + " \"Vazamento de TV a Cabo\",\n", + " \"Rede Elétrica\",\n", + " \"Outros\",\n", + " ],\n", + " ),\n", " \"tipo_de_inspecao\": FieldWithOptions(\n", " 2,\n", " \"Tipo de inspeção:\",\n", @@ -329,14 +447,12 @@ " format_value=True,\n", " mapping={\n", " \"Bloqueio Administrativo\": [\n", - " \"nome_da_entidade\",\n", " \"observacao_tecnica_amostral\",\n", " \"utilizou_algum_instrumento\",\n", " \"utilizou_tecnicas_amostrais\",\n", " ],\n", " \"Certificação\": [\n", " \"area_do_pacp\",\n", - " \"cnpjcpf_da_entidade\",\n", " \"documento_instaurador_do_pado\",\n", " \"endereco_da_inspecao\",\n", " \"entidade_com_cadastro_stel\",\n", @@ -344,6 +460,7 @@ " \"irregularidade\",\n", " \"latitude_coordenadas\",\n", " \"longitude_coordenadas\",\n", + " \"no_sei_relatorio_de_atividades\",\n", " \"observacao_tecnica_amostral\",\n", " \"qnt_produt_lacradosapreend\",\n", " \"situacao_constatada\",\n", @@ -352,7 +469,7 @@ " \"Medição de CEMRF (RNI)\": [\n", " \"campo_eletrico__pico_vm\",\n", " \"campo_eletrico_rms_vm\",\n", - " \"cnpjcpf_da_entidade\",\n", + " \"entidade_da_inspecao\",\n", " \"entidade_outorgada\",\n", " \"frequencia_final\",\n", " \"frequencia_inicial\",\n", @@ -360,28 +477,24 @@ " \"latitude_da_estacao\",\n", " \"longitude_coordenadas\",\n", " \"longitude_da_estacao\",\n", - " \"nome_da_entidade\",\n", " \"observacao_tecnica_amostral\",\n", - " \"servicos_da_inspecao\",\n", " \"tipo_de_medicao\",\n", " \"unidade_da_frequencia_final\",\n", " \"unidade_da_frequencia_inicial\",\n", " \"utilizou_tecnicas_amostrais\",\n", " ],\n", " \"Outorga - Aspectos não Técnicos\": [\n", - " \"cnpjcpf_da_entidade\",\n", + " \"entidade_da_inspecao\",\n", " \"irregularidade\",\n", " \"observacao_tecnica_amostral\",\n", " \"pai_instaurado_pela_anatel\",\n", - " \"servicos_da_inspecao\",\n", " \"situacao_constatada\",\n", " \"utilizou_tecnicas_amostrais\",\n", " ],\n", " \"Outorga - Aspectos Técnicos\": [\n", " \"altura_do_sistema_irradiante\",\n", - " \"cnpjcpf_da_entidade\",\n", - " \"cnpjcpf_da_entidade\",\n", " \"documento_instaurador_do_pado\",\n", + " \"entidade_da_inspecao\",\n", " \"entidade_outorgada\",\n", " \"esta_em_operacao\",\n", " \"frequencias\",\n", @@ -392,7 +505,6 @@ " \"longitude_coordenadas\",\n", " \"observacao_tecnica_amostral\",\n", " \"potencia_medida\",\n", - " \"servicos_da_inspecao\",\n", " \"situacao_constatada\",\n", " \"situacao_de_risco_a_vida\",\n", " \"unidade_de_frequencia\",\n", @@ -402,6 +514,7 @@ " \"utilizou_tecnicas_amostrais\",\n", " ],\n", " \"Uso do Espectro - Interferência\": [\n", + " \"entidade_da_inspecao\",\n", " \"foi_constatada_interferencia\",\n", " \"observacao_tecnica_amostral\",\n", " \"observacoes\",\n", @@ -409,11 +522,12 @@ " ],\n", " \"Uso do Espectro - Monitoração\": [\n", " \"acao_de_risco_a_vida_criada\",\n", + " \"entidade_da_inspecao\",\n", " \"frequencia_final\",\n", " \"frequencia_inicial\",\n", " \"latitude_coordenadas\",\n", " \"longitude_coordenadas\",\n", - " \"no_sei_relatorio_monitoramento\",\n", + " \"no_sei_relatorio_de_atividades\",\n", " \"qtd_de_emissoes\",\n", " \"qtd_identificadas\",\n", " \"qtd_licenciadas\",\n", @@ -474,8 +588,18 @@ " mandatory=True,\n", " options=[\"\", \"Hz\", \"kHz\", \"MHz\", \"GHz\", \"THz\"],\n", " ),\n", - " \"unidade_de_frequencia\": SimpleField(84, \"Unidade (Frequência):\"),\n", - " \"unidade_de_potencia\": SimpleField(82, \"Unidade (Potência):\"),\n", + " \"unidade_de_frequencia\": FieldWithOptions(\n", + " 84,\n", + " \"Unidade (Frequência):\",\n", + " mandatory=False,\n", + " options=[\"\", \"Hz\", \"kHz\", \"MHz\", \"GHz\", \"THz\"],\n", + " ),\n", + " \"unidade_de_potencia\": FieldWithOptions(\n", + " 82,\n", + " \"Unidade (Potência):\",\n", + " mandatory=False,\n", + " options=[\"\", \"mW\", \"W\", \"kW\", \"MW\", \"TW\", \"dBW\", \"dBm\"],\n", + " ),\n", " \"uso_de_produto_homologado\": FieldWithOptions(\n", " 132,\n", " \"Uso de produto homologado?\",\n", diff --git a/tests/test_certificacao.py b/tests/test_certificacao.py new file mode 100644 index 0000000..2a515fb --- /dev/null +++ b/tests/test_certificacao.py @@ -0,0 +1,95 @@ +import os +import random +from random import randint +from dotenv import load_dotenv +from fiscaliza.main import Fiscaliza +from fiscaliza.constants import MUNICIPIOS, SERVICOS +from fiscaliza.attrs import FIELDS + +load_dotenv(override=True) + +issue_id = "124176" +fiscaliza = Fiscaliza(os.environ["USERNAME"], os.environ["PASSWORD"], teste=True) +issue = fiscaliza.get_issue(issue_id) + +# | code-fold: true +dados = { + "endereco_da_inspecao": "Rua Machado de Assis, 27 - Morro Grande, Rio de Janeiro - RJ", + "campo_eletrico__pico_vm": randint(50, 100), + "campo_eletrico_rms_vm": randint(20, 100), + "coordenacao_responsavel": "FI2", + "cnpjcpf_da_entidade": "27865757000102", + "entidade_com_cadastro_stel": "Sim", + "entidade_da_inspecao": [ + "ZIP NET S/A (01109184000195)", + "¡BAZINGA! (27865757000102)", + ], + "entidade_outorgada": random.choice(["0", "1"]), + "esta_em_operacao": randint(0, 1), + "numero_da_estacao": "1493671", + "fiscais": ["Eric Magalhães Delgado", "Ronaldo da Silva Alves Batista"], + "fiscal_responsavel": "Eric Magalhães Delgado", + "foi_constatada_interferencia": randint(0, 1), + "frequencia_inicial": randint(70, 110), + "frequencia_final": randint(110, 117), + "gerar_relatorio": "1", + "gerar_plai": 1, + "tipo_do_processo_plai": random.choice(FIELDS["tipo_do_processo_plai"].options), + "coord_fi_plai": random.choice(FIELDS["coord_fi_plai"].options), + "html_path": "/mnt/c/Users/rsilva/code/fiscaliza/tests/Report_2024.02.18_T11.30.55_123456.html", + "uploads": [ + { + "path": "/mnt/c/Users/rsilva/code/fiscaliza/tests/Report_2024.02.18_T11.30.55_123456.json", + "filename": "Info.json", + } + ], + "identificada_a_origem": str(randint(0, 1)), + "unidade_da_frequencia_final": "MHz", + "unidade_da_frequencia_inicial": "MHz", + "horas_de_conclusao": randint(1, 8), + "horas_de_deslocamento": randint(1, 8), + "horas_de_execucao": randint(8, 40), + "horas_de_preparacao": randint(1, 8), + "houve_obice": random.randint(0, 1), + "houve_interferencia": random.choice(["Sim", "Não"]), + "latitude_coordenadas": -randint(0, 33), + "longitude_coordenadas": -randint(34, 73), + "latitude_da_estacao": -randint(0, 33), + "longitude_da_estacao": -randint(34, 73), + "nome_da_entidade": "Globo S/A", + "numero_do_pai": "123456", + "observacao_tecnica_amostral": "Simulação com o HTZ", + "pai_instaurado_pela_anatel": random.choice(["Sim", "Não"]), + "precisa_reservar_instrumentos": "0", # + "procedimentos": random.choices(FIELDS["procedimentos"].options[1:], k=2), # + "qnt_produt_lacradosapreend": "0", + "reserva_de_instrumentos": "0", + "no_sav": "Teste de Quebra\n de linha", + "documento_instaurador_do_pado": "0201235\n0201239\n0201237", + "no_do_lacre": "50", + "motivo_de_lai": random.choice(FIELDS["motivo_de_lai"].options), + "no_sei_do_aviso_lai": "", + "sanada_ou_mitigada": random.choice(["0", "1"]), + "servicos_da_inspecao": random.choices(list(SERVICOS.values()), k=2), + "situacao_constatada": "Irregular", + "situacao_de_risco_a_vida": "Sim", + "tipo_de_inspecao": "Certificação", + "ufmunicipio": random.choices(MUNICIPIOS, k=2), + "uso_de_produto_homologado": random.choice(["0", "1"]), + "utilizou_algum_instrumento": "0", + "utilizou_apoio_policial": random.choice( + FIELDS["utilizou_apoio_policial"].options[1:] + ), + "utilizou_tecnicas_amostrais": random.choice( + FIELDS["utilizou_tecnicas_amostrais"].options + ), + "description": "[PMEC 2024 Etapa 2] Monitorar canais e faixas de frequências relacionados às aplicações críticas (como, por exemplo, radionavegação e radiocomunicação aeronáutica e canais de emergência) na forma a ser estabelecida no Plano de Ação de Fiscalização.\r\n", + "start_date": "2024-03-01", + "due_date": "2024-05-30", +} + +# for tipo in FIELDS["tipo_de_inspecao"].options[1:]: +# dados["tipo_de_inspecao"] = tipo +# issue.update(dados) + +issue.update(dados) diff --git a/tests/test_monitoracao.py b/tests/test_monitoracao.py index 177ea35..fca5027 100644 --- a/tests/test_monitoracao.py +++ b/tests/test_monitoracao.py @@ -8,7 +8,7 @@ load_dotenv(override=True) -issue_id = "124191" +issue_id = "124176" fiscaliza = Fiscaliza(os.environ["USERNAME"], os.environ["PASSWORD"], teste=True) issue = fiscaliza.get_issue(issue_id) @@ -19,7 +19,8 @@ "campo_eletrico_rms_vm": randint(20, 100), "coordenacao_responsavel": "FI2", "cnpjcpf_da_entidade": "27865757000102", - "entidade_com_cadastro_stel": "Não", + "entidade_com_cadastro_stel": "Sim", + "entidade_da_inspecao": ["01109184000195", "27865757000102"], "entidade_outorgada": random.choice(["0", "1"]), "esta_em_operacao": randint(0, 1), "numero_da_estacao": "1493671",