From 4014037e505876ed8c4df229ace3a65092e64ca8 Mon Sep 17 00:00:00 2001 From: Murilo Ijanc Date: Mon, 27 Jul 2026 20:39:38 -0300 Subject: Expose the faixa a missing CEP would have belonged to A CEP that fails is rarely a random number: it is someone who knows the city and guessed the digits, and the classic guess is the -000 that 957 municipalities never received. The API now attaches that municipality to the 404, and a caller left with nothing but "does not exist" cannot tell a typo in a real city from a CEP that was never plausible. It goes in CEPIS-STATUS, not CEPIS-ADDRESS. The call has still failed and the address is still empty; putting a localidade where the address lives would let a caller that forgets to test the status read Areal as the answer to a CEP that does not exist. Parsing is best effort throughout. faixa is omitted rather than sent as null for a band no municipality owns, so its absence is the normal case and there is a flag instead of a marker value; a block that is missing, is not an object, or is short of localidade, uf, cep_ini, cep_fim and ceps is dropped and the not-found comes back clean. Failing a 404 the caller already understands over a decoration on it would be a poor trade. cep_ini and cep_fim are the CEPs that exist, not the bounds the Correios publish, and the span between them is mostly empty -- hence ceps beside them, so the pair is not read as a list. --- src/cepis-config.cob | 2 +- src/cepis-lookup-many.cob | 2 +- src/cepis-lookup.cob | 135 ++++++++++++++++++++++++++++++++++++++++++++ src/cepis-normalize-cep.cob | 2 + src/cepis-request.cob | 2 + 5 files changed, 141 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cepis-config.cob b/src/cepis-config.cob index 0f17fd1..87e0312 100644 --- a/src/cepis-config.cob +++ b/src/cepis-config.cob @@ -15,7 +15,7 @@ PROGRAM-ID. CEPIS-DEFAULT-CONFIG. DATA DIVISION. WORKING-STORAGE SECTION. -01 CEPIS-VERSION PIC X(5) VALUE "0.1.0". +01 CEPIS-VERSION PIC X(5) VALUE "0.2.0". LINKAGE SECTION. COPY "cepis.cpy". diff --git a/src/cepis-lookup-many.cob b/src/cepis-lookup-many.cob index d76fa72..65b6496 100644 --- a/src/cepis-lookup-many.cob +++ b/src/cepis-lookup-many.cob @@ -33,7 +33,7 @@ COPY "cepis-csv.cpy". 01 WS-NORM. COPY "cepis-status.cpy" REPLACING - ==:P:== BY ==WSN== ==:L1:== BY ==05==. + ==:P:== BY ==WSN== ==:L1:== BY ==05== ==:L2:== BY ==10==. 01 WS-CHUNK-MAX PIC 9(4) VALUE 500. 01 WS-BATCH-MAX PIC 9(4) VALUE 1000. diff --git a/src/cepis-lookup.cob b/src/cepis-lookup.cob index cc6e412..90f1502 100644 --- a/src/cepis-lookup.cob +++ b/src/cepis-lookup.cob @@ -22,6 +22,13 @@ PROGRAM-ID. CEPIS-LOOKUP. *> means the CEP is a whole municipality and the point is its centroid, *> with a radius of 7 to 40 km. Computing shipping from the second is *> wrong by tens of kilometres. +*> +*> A CEPIS-NOT-FOUND may come back with CEPIS-FAIXA filled: the +*> municipality the CEP would have belonged to. A CEP that fails is +*> rarely a random number -- it is usually someone who knows the city and +*> guessed the digits, and the classic guess is the -000 that 957 +*> municipalities never received. The lookup has still failed and the +*> address is still empty; CEPIS-FAIXA only names where the guess landed. *> --------------------------------------------------------------------- DATA DIVISION. @@ -38,6 +45,17 @@ COPY "cepis-json.cpy". 01 WS-LON PIC S9(3)V9(6). 01 WS-RAIO PIC 9(9). 01 WS-PRECISAO PIC X(16). +01 WS-FAIXA-FROM PIC 9(9). +01 WS-FAIXA-TO PIC 9(9). +01 WS-FAIXA-OK PIC X. +01 WS-F-LOCALIDADE PIC X(100). +01 WS-F-UF PIC X(2). +01 WS-F-ESTADO PIC X(60). +01 WS-F-REGIAO PIC X(30). +01 WS-F-IBGE PIC X(10). +01 WS-F-CEP-INI PIC X(8). +01 WS-F-CEP-FIM PIC X(8). +01 WS-F-CEPS PIC 9(9). 01 WS-I PIC 9(4). 01 WS-C PIC X. 01 WS-DOTS PIC 9(4). @@ -82,6 +100,7 @@ MAIN-START. STRING "CEP " WS-CEP " does not exist in the DNE database" DELIMITED BY SIZE INTO CEPIS-STATUS-MESSAGE END-STRING + PERFORM PARSE-FAIXA WHEN 400 SET CEPIS-INVALID-CEP TO TRUE PERFORM API-MESSAGE @@ -132,6 +151,13 @@ GET-GEO-MEMBER. CALL "CEPIS-JSON-GET" USING HTTP-BODY CEPIS-JSON-SCAN END-CALL. +*> ------------------------------------------------------------------- +GET-FAIXA-MEMBER. + MOVE WS-FAIXA-FROM TO JS-FROM + MOVE WS-FAIXA-TO TO JS-TO + CALL "CEPIS-JSON-GET" USING HTTP-BODY CEPIS-JSON-SCAN + END-CALL. + *> ------------------------------------------------------------------- PARSE-ADDRESS. MOVE "cep" TO JS-KEY @@ -289,6 +315,115 @@ PARSE-GEO. MOVE WS-PRECISAO TO ADDR-GEO-PRECISAO END-IF. +*> ------------------------------------------------------------------- +*> The 404 body may name the municipality the CEP would have belonged +*> to. It is context for the error and nothing else: the status stays +*> CEPIS-NOT-FOUND and CEPIS-ADDRESS stays empty, because a CEP that the +*> DNE does not have is not an address no matter which city it is near. +*> +*> "faixa" is omitted -- never sent as null -- when the CEP falls in a +*> band no municipality owns, so its absence is the normal case, not an +*> error. Everything here is therefore best effort: a faixa that is +*> missing, is not an object, or is short of the members below is +*> dropped, and the caller still gets a clean not-found. Turning a 404 +*> the caller already understands into CEPIS-INVALID-RESPONSE over a +*> decoration would be a poor trade. +*> +*> cep_ini and cep_fim must both be eight digits and ceps must be a +*> number, or the whole block is dropped. The span is sparse -- Areal's +*> 118 CEPs are spread over the ~4900 numbers between 25845001 and +*> 25849899 -- so handing a caller the pair without the count invites +*> reading it as a list of valid CEPs, which it is not. +PARSE-FAIXA. + INITIALIZE CEPIS-FAIXA + SET CEPIS-FAIXA-ABSENT TO TRUE + MOVE "faixa" TO JS-KEY + PERFORM GET-MEMBER + IF JS-TYPE NOT = "O" + EXIT PARAGRAPH + END-IF + MOVE JS-VSTART TO WS-FAIXA-FROM + MOVE JS-VEND TO WS-FAIXA-TO + MOVE "Y" TO WS-FAIXA-OK + + MOVE "localidade" TO JS-KEY + PERFORM GET-FAIXA-MEMBER + IF JS-TYPE = "S" AND JS-VALUE-LEN > 0 + MOVE JS-VALUE TO WS-F-LOCALIDADE + ELSE + MOVE "N" TO WS-FAIXA-OK + END-IF + + MOVE "uf" TO JS-KEY + PERFORM GET-FAIXA-MEMBER + IF JS-TYPE = "S" AND JS-VALUE-LEN = 2 + MOVE JS-VALUE TO WS-F-UF + ELSE + MOVE "N" TO WS-FAIXA-OK + END-IF + + MOVE "cep_ini" TO JS-KEY + PERFORM GET-FAIXA-MEMBER + IF JS-TYPE = "S" AND JS-VALUE-LEN = 8 + MOVE JS-VALUE TO WS-F-CEP-INI + ELSE + MOVE "N" TO WS-FAIXA-OK + END-IF + + MOVE "cep_fim" TO JS-KEY + PERFORM GET-FAIXA-MEMBER + IF JS-TYPE = "S" AND JS-VALUE-LEN = 8 + MOVE JS-VALUE TO WS-F-CEP-FIM + ELSE + MOVE "N" TO WS-FAIXA-OK + END-IF + + MOVE "ceps" TO JS-KEY + PERFORM GET-FAIXA-MEMBER + PERFORM CHECK-NUMERIC + IF WS-NUM-OK = "Y" + COMPUTE WS-F-CEPS ROUNDED = FUNCTION NUMVAL (JS-VALUE) + ELSE + MOVE "N" TO WS-FAIXA-OK + END-IF + + IF WS-FAIXA-OK NOT = "Y" + EXIT PARAGRAPH + END-IF + + *> The three below are decoration: a faixa the API sends without + *> them still names the municipality, so they never fail the block. + MOVE SPACES TO WS-F-ESTADO + MOVE "estado" TO JS-KEY + PERFORM GET-FAIXA-MEMBER + IF JS-TYPE = "S" + MOVE JS-VALUE TO WS-F-ESTADO + END-IF + + MOVE SPACES TO WS-F-REGIAO + MOVE "regiao" TO JS-KEY + PERFORM GET-FAIXA-MEMBER + IF JS-TYPE = "S" + MOVE JS-VALUE TO WS-F-REGIAO + END-IF + + MOVE SPACES TO WS-F-IBGE + MOVE "ibge" TO JS-KEY + PERFORM GET-FAIXA-MEMBER + IF JS-TYPE = "S" + MOVE JS-VALUE TO WS-F-IBGE + END-IF + + SET CEPIS-FAIXA-KNOWN TO TRUE + MOVE WS-F-LOCALIDADE TO CEPIS-FAIXA-LOCALIDADE + MOVE WS-F-UF TO CEPIS-FAIXA-UF + MOVE WS-F-ESTADO TO CEPIS-FAIXA-ESTADO + MOVE WS-F-REGIAO TO CEPIS-FAIXA-REGIAO + MOVE WS-F-IBGE TO CEPIS-FAIXA-IBGE + MOVE WS-F-CEP-INI TO CEPIS-FAIXA-CEP-INI + MOVE WS-F-CEP-FIM TO CEPIS-FAIXA-CEP-FIM + MOVE WS-F-CEPS TO CEPIS-FAIXA-CEPS. + *> ------------------------------------------------------------------- *> FUNCTION NUMVAL answers 0 for text it cannot read, which would turn *> "lat": "not a number" into a coordinate off the coast of Africa. The diff --git a/src/cepis-normalize-cep.cob b/src/cepis-normalize-cep.cob index 41615ad..cdbc954 100644 --- a/src/cepis-normalize-cep.cob +++ b/src/cepis-normalize-cep.cob @@ -36,6 +36,8 @@ MAIN-START. MOVE 0 TO CEPIS-STATUS-ATTEMPTS MOVE -1 TO CEPIS-STATUS-RETRY-AFTER-MS MOVE SPACES TO CEPIS-STATUS-MESSAGE + INITIALIZE CEPIS-FAIXA + SET CEPIS-FAIXA-ABSENT TO TRUE PERFORM VARYING WS-I FROM 1 BY 1 UNTIL WS-I > 20 MOVE CEPIS-CEP-INPUT(WS-I:1) TO WS-C diff --git a/src/cepis-request.cob b/src/cepis-request.cob index b45f5da..30c9477 100644 --- a/src/cepis-request.cob +++ b/src/cepis-request.cob @@ -143,6 +143,8 @@ RESET-STATE. MOVE 0 TO CEPIS-STATUS-HTTP MOVE -1 TO CEPIS-STATUS-RETRY-AFTER-MS MOVE 0 TO CEPIS-STATUS-ATTEMPTS + INITIALIZE CEPIS-FAIXA + SET CEPIS-FAIXA-ABSENT TO TRUE MOVE 0 TO HTTP-STATUS MOVE 0 TO HTTP-BODY-LEN MOVE "N" TO WS-DONE -- cgit v1.3