Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  1. Переходимо за посиланням https://procedure.prozorro.sale/api/search/byDateModified/2021-01-01?limit=100 - це ендпоінт пошуку по даті зміни, запущений з 1.01.2021 з максимально доступним лімітом (100 записів)
  2. Отримуємо і забираємо повні дані 100 найстаріших процедур
  3. З отриманого сету процедур обираємо ту, що була змінена останньою, беремо значення dateModified для цієї процедури, додаємо до нього одну мілісекунду
  4. Підставляємо отримане значення в посилання з п.1 замість 2021-01-01, таким чином отримуємо наступні 100 процедур (наприклад, https://procedure.prozorro.sale/api/search/byDateModified/2021-01-04T14:15:31.763000Z?limit=100)


Expand
titleПриклад "скачати" всі процедури у локальну базу
import os
import time
import logging
from datetime import datetime, timedelta, timezone

import requests
import pymongo
from dotenv import load_dotenv

load_dotenv()

# Налаштування
MONGO_URI = os.getenv("MONGO_URI", "mongodb://localhost:27017/")
DB_NAME = os.getenv("DB_NAME", "testdb")
COLLECTION_NAME = os.getenv("COLLECTION_NAME", "cbd3_procedures")
BASE_URL = "https://procedure.prozorro.sale/api/search/byDateModified/"
START_DATE = "2021-10-01T00:00:00.000001Z"
LIMIT = 100
REQUEST_TIMEOUT = 30
NO_PROGRESS_LIMIT = 5 # скільки разів підряд можна "не просунутись", перш ніж зупинитись

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

client = pymongo.MongoClient(MONGO_URI)
db = client[DB_NAME]
collection = db[COLLECTION_NAME]

def parse_z(dt_str: str) -> datetime:
# "2025-05-07T17:25:32.907000Z" -> aware datetime UTC
if dt_str.endswith("Z"):
dt_str = dt_str[:-1]
return datetime.fromisoformat(dt_str).replace(tzinfo=timezone.utc)

def to_z(dt: datetime) -> str:
return dt.astimezone(timezone.utc).isoformat().replace("+00:00", "Z")

def fetch_and_store_data():
date_modified = parse_z(START_DATE)
no_progress_streak = 0
last_url = None

try:
while True:
url = f"{BASE_URL}{to_z(date_modified)}?limit={LIMIT}"
if url == last_url:
no_progress_streak += 1
logger.warning(f"Same URL encountered again ({no_progress_streak}/{NO_PROGRESS_LIMIT}).")
if no_progress_streak >= NO_PROGRESS_LIMIT:
logger.info("No progress guard triggered. Stopping.")
break
else:
no_progress_streak = 0

last_url = url
logger.info(f"Fetching data from: {url}")

try:
resp = requests.get(url, timeout=REQUEST_TIMEOUT)
resp.raise_for_status()
except requests.RequestException as e:
logger.error(f"Request failed: {e}")
break

data = resp.json()

# Порожній або не список — фініш
if not isinstance(data, list) or not data:
logger.info("No more data to fetch. Stopping execution.")
break

# Записуємо/оновлюємо
inserted_count = 0
for item in data:
res = collection.update_one(
{"_id": item["_id"]},
{"$set": item},
upsert=True
)
if res.upserted_id:
inserted_count += 1

logger.info(f"Inserted {inserted_count} new records into MongoDB.")

# Вираховуємо наступний курсор
# Беремо максимальний dateModified з поточної порції
try:
max_date_str = max(obj["dateModified"] for obj in data)
batch_max = parse_z(max_date_str)
except (KeyError, ValueError, TypeError) as e:
logger.error(f"Bad dateModified in response: {e}. Stopping.")
break

next_cursor = max(date_modified, batch_max) + timedelta(microseconds=1)

if next_cursor <= date_modified:
# safety (по ідеї не має спрацьовувати, але лишимо)
no_progress_streak += 1
logger.warning(f"Cursor did not advance (streak {no_progress_streak}/{NO_PROGRESS_LIMIT}).")
if no_progress_streak >= NO_PROGRESS_LIMIT:
logger.info("No progress guard triggered. Stopping.")
break
else:
date_modified = next_cursor
logger.info(f"Next dateModified: {to_z(date_modified)}")

time.sleep(1) # не душимо API
finally:
client.close()

if __name__ == "__main__":
fetch_and_store_data()


Напрями роботи і параметри процедур

...

https://confluence-sale.prozorro.org/pages/viewpage.action?pageId=42729635

ЦБД-нова

https://gitlab.prozorro.sale/public-projects/documentations/-/tree/master/technical_specification ТЗ: Procedure (Сервіс процедур)

Також зі структурою даних для різних процедур можна знайомитись у Swagger: https://procedure-staging.prozorro.sale/api/doc

...