Τα δεδομένα του τακτικού προϋπολογισμού γίνονται διαθέσιμα από την κυβέρνηση με τη μορφή ενός PDF αρχείου, το οποίο περιέχει πίνακες με τα δεδομένα, σε μία μορφή που είναι μεν απλή για ανάγνωση από ανθρώπους, αλλά πρακτικά άχρηστη για επεξεργασία με υπολογιστή.

Για να κάνω τις οπτικοποιήσεις που μπορείς να βρεις σε αυτό το site, έπρεπε κατ’αρχάς να εξάγω τα δεδομένα και στη συνέχεια να τα μετατρέψω σε μία μορφή που να είναι εύκολα προσβάσιμη από κώδικα, ώστε να μπορώ να τα επεξεργαστώ και να δημιουργήσω τα διαγράμματα.

Αφού λοιπόν μπήκα στον κόπο, δεν ήταν πολλή παραπάνω δουλειά να το κάνω προσβάσιμο σε όλους με τη μορφή ενός ReST API. Το schema είναι εξαιρετικά απλό, και το όλο σύστημα είναι static files οπότε το κόστος για να προσφέρω τα δεδομένα είναι πρακτικά μηδενικό. Το μειονέκτημα είναι πως δεν προσφέρει καμία υπηρεσία υπερωτημάτων, αναζήτησης ή μορφοποίησης των αποτελεσμάτων. Εν τούτοις, αυτό που υπάρχει ήδη είναι αρκετά ευέλικτο ώστε η εφαρμογή να έχει όλα τα δεδομένα που χρειάζεται.

Όλα τα δεδομένα είναι διαθέσιμα στο repository της εφαρμογής με μορφή JSON, τα οποία μπορείς να αντιγράψεις και να κάνεις host (με άδεια CC NC-BY-SA 4.0).

Επίσης, μπορείς να βρεις ένα συνοδευτικό repository με όλα τα δεδομένα σε CSV μορφή, καθώς και τα εργαλεία που χρησιμοποίησα για να τα εξάγω από το PDF. Από εκεί μπορείς να χρησιμοποιήσεις τον python κώδικα που έγραψα για να μετατρέψεις τα CSV σε JSON, αν προτιμάς αυτή τη μορφή.

Ακολουθεί το documentation για το API (v1-1)

Έσοδα

Τα έσοδα αποτελούν μία δενδρική δομή, με κάθε κατηγορία να αποτελεί ένα αντικείμενο-κόμβο. Κάθε αντικείμενο έχει μηδέν ή περισσότερα αντικείμενα - απογόνους (children) και κάθε αντικείμενο έχει ακριβώς ένα γονιό (parent).

Κάθε αντικείμενο αντιπροσωπεύει ακριβώς μία γραμμή από τους πίνακες του τακτικού προϋπολογισμού για το 2025. Με την πληροφορία που υπάρχει στο πεδίο children είναι δυνατόν να περιηγηθείς σε όλη τη δομή, ακολουθώντας τα urls. Ξεκινώντας από τη ρίζα μπορείς να ανακτήσεις τα children objects και να ακολουθήσεις αναδρομικά τα links τους, οπότε μπορείς να κάνεις traverse όλο το δένδρο.

Κάθε αντικείμενο έχει ένα πεδίο amount που είναι το άθροισμα όλων των children του, ή το τελικό ποσό για τα αντικείμενα - φύλλα του δένδρου.

Τα αντικείμενα είναι διαθέσιμα σε URL της μορφής

https://radiki.dev/budget/v1-1/revenue/{id}

όπου {id} είναι ο αριθμητικός κωδικός της κατηγορίας εσόδων. Το μόνο HTTP method που υποστηρίζεται είναι το GET. Δεν υποστηρίζονται headers ή url parameters.

Το Response είναι ένα JSON κείμενο με όλες τις πληροφορίες για την κατηγορία, ως εξής:

{
  "self": "https://radiki.dev/budget/v1-1/revenue/542",
  "identifier": "542",
  "name": "Μακροπρόθεσμα δάνεια",
  "amount": 2027000000,
  "reference": {
    "page": 60,
    "row_number": 9
  },
  "parent": {
    "identifier": "54",
    "url": "https://radiki.dev/budget/v1-1/revenue/54"
  },
  "children": [
    {
      "identifier": "54202",
      "url": "https://radiki.dev/budget/v1-1/revenue/54202"
    }
  ]
}

Τα πεδία έχουν τα εξής types και ερμηνεία:

  • self (string): Tο URL στο οποίο είναι διαθέσιμο το αντικείμενο.

  • identifier (string): Το identifier του αντικειμένου, όπως εμφανίζεται στο κείμενο του προϋπολογισμού, στήλη 1. Η ρίζα (το αντικείμενο στο path revenue/index.json) δεν έχει αυτό το πεδίο.

  • name (string): Το όνομα του αντικειμένου, όπως εμφανίζεται στο κείμενο του προϋπολογισμού, στήλη 2. Η ρίζα (το αντικείμενο στο path revenue/index.json) δεν έχει αυτό το πεδίο.

  • amount (number): Το ποσό του αντικειμένου, όπως εμφανίζεται στο κείμενο του προϋπολογισμού, στήλη 3. Η ρίζα (το αντικείμενο στο path revenue/index.json) δεν έχει αυτό το πεδίο.

  • reference (object): Αναφορά για την τοποθεσία του αντικειμένου στο κείμενου του προϋπολογισμού. Η ρίζα (το αντικείμενο στο path revenue/index.json) δεν έχει αυτό το πεδίο. Αφού κάθε αντικείμενο αντιπροσωπεύει μία γραμμή του κειμένου του προϋπολογισμού, μπορεί να έχει ακριβή αναφορά στη σελίδα και γραμμή που εμφανίζεται. Αποτελείται από 2 πεδία:

    • page (number): Η σελίδα του κειμένου που εμφανίζεται το αντικείμενο, με βάση την εσωτερική αρίθμηση του PDF.
    • row_number (number): Η γραμμή του πίνακα στη συγκεκριμένη σελίδα που εμφανίζεται το αντικείμενο. Η αρίθμηση ξεκινά από την πρώτη γραμμή δεδομένων (δεν μετράται η επικεφαλίδα) και είναι με βάση το 1.
  • parent (object): Ένα αντικείμενο που περιγράφει το parent αυτού του αντικειμένου. Η ρίζα (το αντικείμενο στο path revenue/index.json) δεν έχει αυτό το πεδίο. Αποτελείται από τα πεδία:

    • identifier (string): To identifier του parent
    • url (string): Το URL του parent
  • children ([object]): Ένα array με τα παιδιά αυτού του αντικειμένου. Μπορεί να είναι άδειο. Κάθε παιδί αποτελεί μία υποκατηγορία της κατηγορίας που αντιπροσωπεύει αυτό το αντικείμενο και έχει την εξής δομή:

    • identifier (string): To identifier του παιδιού
    • url (string): Το URL του παιδιού

Ορίστε και ένα παράδειγμα πλοήγησης από τη ρίζα, ακολουθώντας 2 επίπεδα, για να καταλήξουμε στο αντικείμενο με κωδικό 542

$ curl https://radiki.dev/budget/v1-1/revenue/index.json

{
  "self": "https://radiki.dev/budget/v1-1/revenue/index.json",
  "children": [
    {
      "identifier": "11",
      "url": "https://radiki.dev/budget/v1-1/revenue/11"
    },
    {
      "identifier": "12",
      "url": "https://radiki.dev/budget/v1-1/revenue/12"
    },
    {
      "identifier": "13",
      "url": "https://radiki.dev/budget/v1-1/revenue/13"
    },
    {
      "identifier": "14",
      "url": "https://radiki.dev/budget/v1-1/revenue/14"
    },
    {
      "identifier": "15",
      "url": "https://radiki.dev/budget/v1-1/revenue/15"
    },
    {
      "identifier": "31",
      "url": "https://radiki.dev/budget/v1-1/revenue/31"
    },
    {
      "identifier": "43",
      "url": "https://radiki.dev/budget/v1-1/revenue/43"
    },
    {
      "identifier": "44",
      "url": "https://radiki.dev/budget/v1-1/revenue/44"
    },
    {
      "identifier": "45",
      "url": "https://radiki.dev/budget/v1-1/revenue/45"
    },
    {
      "identifier": "52",
      "url": "https://radiki.dev/budget/v1-1/revenue/52"
    },
    {
      "identifier": "53",
      "url": "https://radiki.dev/budget/v1-1/revenue/53"
    },
    {
      "identifier": "54",
      "url": "https://radiki.dev/budget/v1-1/revenue/54"
    },
    {
      "identifier": "57",
      "url": "https://radiki.dev/budget/v1-1/revenue/57"
    }
  ]
}

$ curl https://radiki.dev/budget/v1-1/revenue/54

{
  "self": "https://radiki.dev/budget/v1-1/revenue/54",
  "identifier": "54",
  "name": "Δάνεια",
  "amount": 1202027000000,
  "reference": {
    "page": 60,
    "row_number": 4
  },
  "parent": {
    "identifier": "",
    "url": "https://radiki.dev/budget/v1-1/revenue/"
  },
  "children": [
    {
      "identifier": "541",
      "url": "https://radiki.dev/budget/v1-1/revenue/541"
    },
    {
      "identifier": "542",
      "url": "https://radiki.dev/budget/v1-1/revenue/542"
    }
  ]
}

$ curl https://radiki.dev/budget/v1-1/revenue/542

{
  "self": "https://radiki.dev/budget/v1-1/revenue/542",
  "identifier": "542",
  "name": "Μακροπρόθεσμα δάνεια",
  "amount": 2027000000,
  "reference": {
    "page": 60,
    "row_number": 9
  },
  "parent": {
    "identifier": "54",
    "url": "https://radiki.dev/budget/v1-1/revenue/54"
  },
  "children": [
    {
      "identifier": "54202",
      "url": "https://radiki.dev/budget/v1-1/revenue/54202"
    }
  ]
}

Έξοδα

Τα έξοδα είναι δομημένα κάπως διαφορετικά από τα έσοδα, με τη βασική διαφορά την απουσία μοναδικών κωδικών για κάθε εγγραφή. Αντί αυτού, η ταυτότητα κάθε αντικειμένου ορίζεται από το μοναδικό μονοπάτι που το συνδέει με τη ρίζα.

Για παράδειγμα, η μείζονα κατηγορία εξόδων Πιστώσεις υπό κατανομή εμφανίζεται σαν αντικείμενο-παιδί σε πολλούς φορείς εξόδων, πάντα με τον ίδιο κωδικό, 23. Η ταυτότητα του σαν αντικείμενο, επομένως, εξαρτάται από τον φορέα εξόδων στον οποίο ανήκει, ο οποίος με τη σειρά του εξαρτάται από το τμήμα στο οποίο ανήκει.

Το αποτέλεσμα είναι πως για τις Πιστώσεις υπό κατανομή (κωδικός 23) για τον φορέα Δαπάνες μεταναστευτικών ροών του Υπουργείου Εθνικής Οικονομίας και Οικονομικών (κωδικός 1024-701-0000000) του τμήματος Υπουργείο Εθνικής Οικονομίας και Οικονομικών (κωδικός 1024) η πλήρης ταυτότητα είναι

1024/1024-701-0000000/23

Η δεύτερη διαφορά των εξόδων από τα έσοδα είναι πως τα έξοδα έχουν ακριβώς 3 επίπεδα - τμήματα, φορείς και μείζονες κατηγορίες. Επομένως, τα μονοπάτια έχουν πιο προβλέψιμη δομή και αυτό αντικατοπτρίζεται στη δομή των URL του API.

Πιο συγκεκριμένα, η ρίζα είναι διαθέσιμη στο URL

$ curl https://radiki.dev/budget/v1-1/expenses/index.json

και επιστρέφει ένα αντικείμενο με 2 πεδία.

{
  "self": "https://radiki.dev/budget/v1-1/expenses/index.json",
  "children": [
  ...
    {
      "identifier": "1024",
      "url": "https://radiki.dev/budget/v1-1/expenses/1024/index.json"
    },
    {
      "identifier": "1029",
      "url": "https://radiki.dev/budget/v1-1/expenses/1029/index.json"
    },
  ...
  ]
}
  • self (string): Tο URL στο οποίο είναι διαθέσιμο το αντικείμενο.

  • children ([object]): Ένα array με τα παιδιά αυτού του αντικειμένου. Μπορεί να είναι άδειο. Κάθε παιδί αποτελεί μία υποκατηγορία της κατηγορίας που αντιπροσωπεύει αυτό το αντικείμενο και έχει την εξής δομή:

    • identifier (string): To identifier του παιδιού
    • url (string): Το URL του παιδιού

ακριβώς όπως και στα έσοδα.

Τα μέλη του array children είναι τα τμήματα εξόδων. Αν ζητήσεις το URL ενός από αυτά θα λάβεις τους φορείς εξόδων του τμήματος. Για παράδειγμα:

$ curl https://radiki.dev/budget/v1-1/expenses/1024/index.json

{
  "self": "https://radiki.dev/budget/v1-1/expenses/1024/index.json",
  "identifier": "1024",
  "name": "ΥΠΟΥΡΓΕΙΟ ΕΘΝΙΚΗΣ ΟΙΚΟΝΟΜΙΑΣ ΚΑΙ ΟΙΚΟΝΟΜΙΚΩΝ",
  "amount": 1243381464000,
  "reference": {
    "page": 74,
    "row_number": 11
  },
  "children": [
    ...
    {
      "identifier": "1024-204-0310300",
      "url": "https://radiki.dev/budget/v1-1/expenses/1024/1024-204-0310300/index.json"
    },
    {
      "identifier": "1024-204-0310400",
      "url": "https://radiki.dev/budget/v1-1/expenses/1024/1024-204-0310400/index.json"
    },
    ...
  ]
}

Η δομή έχει τα εξής πεδία:

  • self (string): Tο URL στο οποίο είναι διαθέσιμο το αντικείμενο.

  • identifier (string): Το identifier του αντικειμένου, όπως εμφανίζεται στο κείμενο του προϋπολογισμού, στήλη 1

  • name (string): Το όνομα του αντικειμένου, όπως εμφανίζεται στο κείμενο του προϋπολογισμού, στήλη 2

  • amount (number): Το ποσό του αντικειμένου, όπως εμφανίζεται στο κείμενο του προϋπολογισμού, στήλη 3

  • reference (object): Αναφορά για την τοποθεσία του αντικειμένου στο κείμενου του προϋπολογισμού. Αφού κάθε αντικείμενο αντιπροσωπεύει μία γραμμή του κειμένου του προϋπολογισμού, μπορεί να έχει ακριβή αναφορά στη σελίδα και γραμμή που εμφανίζεται. Αποτελείται από 2 πεδία:

    • page (number): Η σελίδα του κειμένου που εμφανίζεται το αντικείμενο, με βάση την εσωτερική αρίθμηση του PDF.
    • row_number (number): Η γραμμή του πίνακα στη συγκεκριμένη σελίδα που εμφανίζεται το αντικείμενο. Η αρίθμηση ξεκινά από την πρώτη γραμμή δεδομένων (δεν μετράται η επικεφαλίδα) και είναι με βάση το 1.
  • parent (object): Ένα αντικείμενο που περιγράφει το parent αυτού του αντικειμένου. Για όλα τα αντικείμενα που αντιπροσωπεύουν τμήματα το parent είναι το ίδιο, και είναι η ρίζα του δένδρου. Αποτελείται από τα πεδία:

    • identifier (string): To identifier του parent, ένα κενό string
    • url (string): Το URL του parent, ίδιο με το URL της ρίζας
  • children ([object]): Ένα array με τα παιδιά αυτού του αντικειμένου. Μπορεί να είναι άδειο. Κάθε παιδί αποτελεί μία υποκατηγορία της κατηγορίας που αντιπροσωπεύει αυτό το αντικείμενο και έχει την εξής δομή:

    • identifier (string): To identifier του παιδιού
    • url (string): Το URL του παιδιού

Κάθε τμήμα εξόδων έχει 0 ή περισσότερα παιδιά στο array children, κάθε ένα από τα οποία αντιπροσωπεύει ένα φορέα εξόδων.

Συνεχίζοντας το παράδειγμα, η πλοήγηση στους φορείς του Υπουργείου Οικονομικών μπορεί να γίνει ως εξής:

$ curl https://radiki.dev/budget/v1-1/expenses/1024/1024-204-0310300/index.json

{
  "self": "https://radiki.dev/budget/v1-1/expenses/1024/1024-204-0310300/index.json",
  "identifier": "1024-204-0310300",
  "name": "Δημοσιονομική Υπηρεσία Εποπτείας και Ελέγχου της Γενικής
        Γραμματείας Δημοσιονομικής Πολιτικής στον νομό Αρκαδίας",
  "amount": 23000,
  "reference": {
    "page": 269,
    "row_number": 5
  },
  "parent": {
    "identifier": "1024",
    "url": "https://radiki.dev/budget/v1-1/expenses/1024"
  },
  "children": [
    {
      "identifier": "24",
      "url": "https://radiki.dev/budget/v1-1/expenses/1024/1024-204-0310300/24"
    }
  ]
}

Το αποτέλεσμα είναι ένα αντικείμενο με ακριβώς την ίδια δομή που είδαμε παραπάνω για τα τμήματα, με μόνη διαφορά πως το πεδίο parent είναι διαφορετικό για κάθε φορέα.

Το τελευταίο επίπεδο του δένδρου είναι οι μείζονες κατηγορίες, οι οποίες εμφανίζονται σαν παιδιά των φορέων (προσοχή στην απουσία του επιθέματος index.json στα URL των παιδιών).

$ curl https://radiki.dev/budget/v1-1/expenses/1024/1024-204-0310300/24

{
  "self": "https://radiki.dev/budget/v1-1/expenses/1024/1024-204-0310300/24",
  "identifier": "24",
  "name": "Αγορές αγαθών και υπηρεσιών",
  "amount": 23000,
  "reference": {
    "page": 269,
    "row_number": 6
  },
  "parent": {
    "identifier": "1024-204-0310300",
    "url": "https://radiki.dev/budget/v1-1/expenses/1024/1024-204-0310300"
  }
}

Τα αντικείμενα που επιστρέφονται αντιπροσωπεύουν μείζονες κατηγορίες εξόδων για τον φορέα εξόδων που είναι το parent αντικείμενο. Η δομή είναι διαφορετική με τα προηγούμενα μόνο στο ότι δεν έχει πεδίο children, αφού οι μείζονες κατηγορίες είναι τα φύλλα της δενδρικής δομής.

Δείγματα client κώδικα

Όλα τα visualizations σε αυτό το site λαμβάνουν τα δεδομένα τους από το παραπάνω API. Για αυτό το λόγο έγραψα μία συλλογή από utilities, σε javascript, τα οποία μπορείς να χρησιμοποιήσεις είτε ως έχουν, είτε ως έμπνευση για να φτιάξεις τα δικά σου.

Αυτά είναι διαθέσιμα εδώ.

Στο ίδιο repository μπορείς να ανοίξεις bug reports αν βρεις κάποιο πρόβλημα ή αν έχει μια καλή ιδέα για να βελτιωθεί το API.

Αν αποφασίσεις να φτιάξεις κάτι με αυτά τα δεδομένα, θα χαρώ πολύ να το δω, να το συζητήσουμε και να το προωθήσω όσο μπορώ.

Καλή διασκέδαση.