أمثلة على استخدام واجهة برمجة التطبيقات

وصفات جاهزة في curl وPython وJavaScript للاستعلام عن البيانات العامة ومزامنة قاعدة البيانات الخاصة بك وإرسال قراءات ovitrap وحظر الزيارات.

/ar-sa/apiالعودة إلى المرجع

الخطوات الأولى

جميع الاستدعاءات تأتي من نفس قاعدة البيانات وترجع JSON. لا تتطلب نقاط النهاية العامة المصادقة؛ تتطلب تلك الخاصة مفتاح الوصول الخاص بك.

العنوان الأساسي

/ar-sa/api

بادئة اللغة هي جزء من عنوان URL (ar-sa), ولكنها لا تغير البيانات التي تم إرجاعها - فالسجلات تأتي من البنك تمامًا كما تم تسجيلها في الميدان.

اختبار سريع
curl -G -d "municipality=Ponta Pora" --data-urlencode "country=Brasil" \
  /ar-sa/api/lastcountingpublic

إذا كانت الاستجابة عبارة عن قائمة JSON، فهذا يعني أن التكامل يعمل بالفعل.

المفتاح يذهب في URL. على كافة نقاط النهاية الخاصة - بما في ذلك POST - key تتم قراءته من سلسلة الاستعلام (?key=SUA_CHAVE). إرساله فقط في نص النموذج يعود 404 "Wrong key". تستمر الحقول الأخرى في الظهور في نص الطلب.

استشارة البيانات العامة

نقطة النهاية /api/lastcountingpublic إرجاع أحدث أعداد البيض مع خطوط الطول والعرض الوبائية والأسبوع والسنة. وهي نقطة البداية للخرائط واللوحات والدراسات.

GET/api/lastcountingpublicعام
curl
curl -G \
  --data-urlencode "municipality=Ponta Pora" \
  --data-urlencode "date_start=2025-01-01" \
  --data-urlencode "date_end=2025-12-31" \
  /ar-sa/api/lastcountingpublic
Python
import requests

BASE = "/ar-sa/api"

resposta = requests.get(
    BASE + "/lastcountingpublic",
    params={
        "municipality": "Ponta Pora",
        "date_start": "2025-01-01",
        "date_end": "2025-12-31",
    },
    timeout=60,
)
resposta.raise_for_status()

for contagem in resposta.json():
    print(contagem["date"], contagem["ovitrap_id"], contagem["eggs"])
JavaScript
const BASE = "/ar-sa/api";

const params = new URLSearchParams({
    state: "MS",
    date_start: "2025-01-01",
});

const resposta = await fetch(BASE + "/lastcountingpublic?" + params);
const contagens = await resposta.json();

console.log(contagens.length, "contagens");

نفس معلمات الموقع (country, state, municipality) وهي تنطبق على نقاط النهاية العامة الأخرى: مصائد البيض، والكتل، وEDL، وصيانة EDL والنقاط الاستراتيجية. مجرد تغيير مسار نقطة النهاية يؤدي بالفعل إلى تغيير مجموعة البيانات.

بدون أي معلمات موقع، تقوم نقطة النهاية بإرجاع بيانات للبرازيل بأكملها. التصفية حسب البلدية أو الولاية تجعل الاستجابة أسرع بكثير.

تصفح كافة الصفحات

دعم نقاط النهاية المرقّمة page من 1 إلى 100. وفوق ذلك، تستجيب واجهة برمجة التطبيقات (API) بالنص "Maximum pagination is 100" والحالة 200 — أي أن النص لم يعد قائمة. تحقق دائمًا من النوع قبل التكرار.

Python
import requests

BASE = "/ar-sa/api"


def baixar_tudo(endpoint, **filtros):
    """Percorre as paginas ate a API devolver uma pagina vazia."""
    registros = []

    for pagina in range(1, 101):
        resposta = requests.get(
            BASE + endpoint,
            params=dict(filtros, page=pagina),
            timeout=60,
        )
        resposta.raise_for_status()
        dados = resposta.json()

        # limite de paginacao atingido: a API devolve uma string, nao uma lista
        if not isinstance(dados, list):
            print("Aviso:", dados)
            break

        if not dados:
            break

        registros.extend(dados)

    return registros


ovitrampas = baixar_tudo("/getmunicipalityovitrapspublic", municipality="Ponta Pora")
print(len(ovitrampas), "ovitrampas")
هل وصلت إلى الصفحة 100؟ قم بتضييق نطاق التصفية بدلاً من تجربة الصفحة 101: ابحث عن المقاطعة حسب المقاطعة، أو استخدمها date_start e date_end لتقسيم الفترة إلى فترات أصغر.

مزامنة تزايدية

للحفاظ على قاعدة معكوسة، لا تقم بتنزيل كل شيء مرة أخرى مع كل تشغيل. نقطة النهاية /api/lastcountingpublic مقبول id, الذي يُرجع فقط الأعداد من هذا المعرف، وأيضًا date (تاريخ الادراج) e date_collect (data de coleta).

Python
import json
import os
import requests

BASE = "/ar-sa/api"
ESTADO = "ultimo_id.json"


def ultimo_id_lido():
    if os.path.exists(ESTADO):
        with open(ESTADO) as arquivo:
            return json.load(arquivo)["counting_id"]
    return 0


def sincronizar():
    ultimo = ultimo_id_lido()
    novas = []

    for pagina in range(1, 101):
        resposta = requests.get(
            BASE + "/lastcountingpublic",
            params={"municipality": "Ponta Pora", "id": ultimo, "page": pagina},
            timeout=60,
        )
        resposta.raise_for_status()
        dados = resposta.json()

        if not isinstance(dados, list) or not dados:
            break

        novas.extend(dados)

    if novas:
        maior = max(item["counting_id"] for item in novas)
        with open(ESTADO, "w") as arquivo:
            json.dump({"counting_id": maior}, arquivo)

    return novas


print(len(sincronizar()), "contagens novas")

احتفظ دائمًا بالأكبر counting_id تم الاستلام، وليس تاريخ التنفيذ: قد يشير العدد الذي تم إدخاله في الحقل اليوم إلى أسبوع سابق، ولا يسمح عامل التصفية حسب المعرف بإفلات هذه السجلات.

تصدير إلى CSV

تأتي التعدادات العامة بالفعل مع الإحداثيات، لذا يمكنك إنشاء جدول بيانات أو تغذية خريطة دون أي انضمام إضافي.

Python
import csv
import requests

BASE = "/ar-sa/api"

COLUNAS = [
    "counting_id",
    "municipality",
    "state_code",
    "ovitrap_id",
    "latitude",
    "longitude",
    "week",
    "year",
    "eggs",
    "date",
    "date_collect",
]

resposta = requests.get(
    BASE + "/lastcountingpublic",
    params={"state": "MS", "date_start": "2025-01-01"},
    timeout=60,
)
contagens = resposta.json()

with open("contagens.csv", "w", newline="", encoding="utf-8") as arquivo:
    escritor = csv.DictWriter(arquivo, fieldnames=COLUNAS, extrasaction="ignore")
    escritor.writeheader()
    escritor.writerows(contagens)

print("Arquivo contagens.csv gerado com", len(contagens), "linhas")

إرسال البيانات

تستخدم الأمثلة التالية واجهة برمجة التطبيقات الخاصة وتتطلب مفتاحًا. يحدد النطاق الجغرافي للمفتاح ما يمكنه تسجيله - لا يسجل مفتاح البلدية إلا في البلدية نفسها.

POST/api/postcountingخاص

إرسال قراءة ovitrap الموجودة

يرسل عدد البيض لمصيدة البيض المسجلة بالفعل. يقع Ovitrap بجانب ovitrap_group_id ضمن بلدية المفتاح، ويتم حساب الأسبوع الوبائي من الميدان date.

curl
curl -X POST \
  -d "ovitrap_group_id=97" \
  -d "ovitrap_lat=-7.000000" \
  -d "ovitrap_lng=-8.000000" \
  -d "date=2025-01-20" \
  -d "counting_observation_id=1" \
  -d "counting_eggs=5" \
  "/ar-sa/api/postcounting?key=SUA_CHAVE"
Python
import requests

BASE = "/ar-sa/api"
CHAVE = "SUA_CHAVE"

resposta = requests.post(
    BASE + "/postcounting",
    params={"key": CHAVE},          # a chave vai na URL
    data={                          # os dados vao no corpo
        "ovitrap_group_id": 97,
        "ovitrap_lat": -7.000000,
        "ovitrap_lng": -8.000000,
        "date": "2025-01-20",
        "counting_observation_id": 1,
        "counting_eggs": 5,
    },
    timeout=60,
)

print(resposta.status_code, resposta.json())
# 200 "Contagem registrada"
انتباه: إذا كانت الإحداثيات المرسلة مختلفة عن تلك المسجلة، يتم تحديث مصيدة البيض بالموقع الجديد. أرسل دائمًا الإحداثيات الحقيقية للنقطة، وليس إحداثيات الوكيل وقت القراءة.
POST/api/postcountingخاص

تثبيت ovitrap جديد بجانب القراءة

إذا ovitrap_group_id غير موجود في البلدية بعد، يتم إنشاء مصيدة البيض بنفس الطلب. أرسل أيضًا العنوان والنوع إن أمكن (ovitrap_type_id: 1 الحضرية، نمط; 2 rural).

Python
import requests

BASE = "/ar-sa/api"
CHAVE = "SUA_CHAVE"

resposta = requests.post(
    BASE + "/postcounting",
    params={"key": CHAVE},
    data={
        "ovitrap_group_id": 96,
        "ovitrap_address_district": "Centro",
        "ovitrap_address_street": "Rua das Flores",
        "ovitrap_address_number": "123",
        "ovitrap_address_complement": "",
        "ovitrap_address_sector": "Setor 04",
        "ovitrap_responsable": "Maria Souza",
        "ovitrap_block_id": "12",
        "ovitrap_type_id": 2,          # 1 = urbana (padrao), 2 = rural
        "ovitrap_lat": -7.000000,
        "ovitrap_lng": -8.000000,
        "date": "2025-01-20",
        "counting_date_collect": "2025-01-27",
        "counting_observation_id": 1,
        "counting_eggs": 5,
    },
    timeout=60,
)

print(resposta.status_code, resposta.json())
إجابات محددة:
  • 400 — نقص ovitrap_lat, ovitrap_lng ou ovitrap_group_id.
  • 400ovitrap_type_id يختلف عن 1 أو 2
  • 404 — يوجد بالفعل إحصاء لهذا البيض، السنة والأسبوع.
  • 500 — يقع تاريخ الإرسال في أسبوع وبائي مستقبلي أو غير صالح.
POST/api/postactionخاص

سجل زيارة جماعية

يستخدم block_id لبدء الزيارة في كتلة مسجلة بالفعل. إذا لم تكن الكتلة موجودة بعد، أرسل block_group_id مع بيانات الكتلة ويتم إنشاؤها تلقائيًا داخل البلدية الرئيسية.

Python — الكتلة الموجودة
import requests

BASE = "/ar-sa/api"
CHAVE = "SUA_CHAVE"

visita = {
    "block_id": 97,
    "date": "2025-01-20",

    "block_land_residence": 10,
    "action_land_residence_out": 2,
    "action_land_residence_breedings": 1,
    "action_land_residence_treated": 1,

    "action_deposit_a1_quantity": 4,
    "action_deposit_a1_eliminated": 2,
    "action_deposit_a1_treated": 1,
    "action_deposit_a1_larvicid": 10,

    "action_observation": "Foco encontrado em pneus nos fundos do imovel.",
}

resposta = requests.post(
    BASE + "/postaction", params={"key": CHAVE}, data=visita, timeout=60
)

if resposta.status_code == 409:
    print("Ja existe visita para esse quarteirao nessa semana")
else:
    print(resposta.status_code, resposta.json())
Python — إنشاء الكتلة
visita = {
    "block_group_id": 97,
    "date": "2025-01-20",
    "block_address_district": "Centro Historico",
    "block_address_sector": "Setor 04 - Norte",
    "block_coordinates": "[[-7.123, -34.845], [-7.124, -34.846]]",
    "block_lat": -7.123456,
    "block_lng": -34.845678,
    "block_responsable": "Joao da Silva",

    "block_land_residence": 10,
    "action_land_residence_out": 2,
    "action_land_residence_breedings": 1,
    "action_land_residence_treated": 1,

    "action_observation": "Area com alta densidade de recipientes descartaveis.",
}

resposta = requests.post(
    BASE + "/postaction", params={"key": CHAVE}, data=visita, timeout=60
)
print(resposta.status_code, resposta.json())

القائمة الكاملة لمجالات العقارات والودائع (a1, a2, b, c, d1, d2, e) هو في مرجع نقطة النهاية. تفترض الحقول الرقمية غير المرسلة صفرًا، ولكن أي قيمة ليست رقمًا تُرجع 400.

إزالة السجلات

تعتبر عمليات الإزالة نهائية وتقتصر دائمًا على البلدية الرئيسية.

curl
# leitura de uma ovitrampa (identificada pela ovitrampa + data)
curl -X POST -d "ovitrap_group_id=97" -d "date=2025-01-20" \
  "/ar-sa/api/postdeletecounting?key=SUA_CHAVE"

# a ovitrampa inteira
curl -X POST -d "ovitrap_group_id=97" \
  "/ar-sa/api/postdeleteovitrap?key=SUA_CHAVE"

# visita de um quarteirao (quarteirao + data)
curl -X POST -d "block_group_id=97" -d "date=2025-01-20" \
  "/ar-sa/api/postdeleteaction?key=SUA_CHAVE"

# o quarteirao inteiro
curl -X POST -d "block_group_id=97" \
  "/ar-sa/api/postdeleteblock?key=SUA_CHAVE"
لتصحيح عدد تم نشره بقيمة خاطئة، قم بإزالة القراءة باستخدام /api/postdeletecounting وإرسالها مرة أخرى مع /api/postcounting — تؤدي إعادة الإرسال إلى إرجاع خطأ مكرر.

معالجة الأخطاء

نص الاستجابة دائمًا عبارة عن سلسلة JSON مع الرسالة. ومن الجدير التعامل مع كل حالة، لأنه ليس كل خطأ يستحق محاولة أخرى:

الموقفشفرةما يجب القيام به
"Wrong key"404تأكد من أن المفتاح موجود في سلسلة الاستعلام وليس في النص الأساسي.
العد المزدوج404هناك بالفعل قراءة لمصيدة البيض هذا الأسبوع؛ يرجى الإزالة قبل إعادة الإرسال.
زيارة مكررة409هناك بالفعل زيارة للكتلة هذا الأسبوع.
خارج النطاق403الكتلة لا تنتمي إلى بلدية تشافي. لا تكرر.
الحقل المطلوب مفقود400تصحيح التقديم؛ التكرار يرجع نفس الخطأ
أسبوع أو سنة غير صالحة500يقع التاريخ خارج الأسبوع الوبائي المقبول. تصحيح المجال date.
Python
import time
import requests

NAO_REPETIR = {400, 403, 404, 409}


def enviar(url, chave, dados, tentativas=3):
    for tentativa in range(tentativas):
        try:
            resposta = requests.post(
                url, params={"key": chave}, data=dados, timeout=60
            )
        except requests.RequestException as erro:
            print("Falha de rede:", erro)
            time.sleep(5 * (tentativa + 1))
            continue

        if resposta.status_code in NAO_REPETIR:
            print("Erro definitivo:", resposta.status_code, resposta.json())
            return None

        if resposta.ok:
            return resposta.json()

        # 500 e demais erros de servidor: espera progressiva e tenta de novo
        time.sleep(5 * (tentativa + 1))

    return None

الممارسات الجيدة

  • قم بالتصفية حسب البلدية أو الولاية كلما أمكن ذلك - بدون تصفية، يقوم الاستعلام بمسح البلد بأكمله.
  • تفضل المزامنة المتزايدة بواسطة id خفض القاعدة بأكملها كل يوم.
  • إرسال الطلبات في سلسلة. لم يتم تصميم واجهة برمجة التطبيقات (API) لعشرات المكالمات المتزامنة لكل عميل.
  • تأكد من أن الاستجابة عبارة عن قائمة قبل التكرار: تأتي رسائل حد الترحيل بالحالة 200.
  • احفظ counting_id و block_id المستلمة - فهي التي تسمح لك بمطابقة سجلات Conta Ovos مع تلك الموجودة في نظامك.
  • تعامل مع الأسبوع الوبائي باعتباره المجال الحاسم: تقبل Conta Ovos قراءة واحدة فقط لكل بيضة في الأسبوع، وزيارة واحدة لكل كتلة في الأسبوع.
  • اتبع الصفحة mudanças قبل تحديث التكامل الخاص بك.