Euskadi.eus-eko bilatzailearen APIa

Euskadi.eus-eko azpiegituraren funtzionalitateak API Java baten barnean daude eta honako ekintza hauek burutzea ahalbidetzen dute:

  • Euskadi.eus-en katalogaziorako erabiltzen diren etiketa-taxonomiaren kudeaketa.
  • Edukiekin eta bere hizkuntza-bertsioekin joka daiteke: sortu, ezabatu, eraldatu, ordenatu...
  • Atari, web-orri eta orrien barruko eremu bisualekin lan egin daiteke.
  • Edukiak argitara daitezke, atari-orriak, etab.
  • Edozein eduki-bilaketa motoretan indexatu daiteke.
  • Edukiak edo atari-orriak bilatu daitezke.

API desberdinak (edukiak, ataria, argitaratzailea, bilatzailea, katalogazio etiketak, etb.) abstraitzen dira programatzailearen logikatik.

API mota guzti horien artean, bilatzailearena da interesgarrienetakoa, kanpotik deituta edukiak aurkitu eta eraldatu daitezkeelako. API honek bi zapore ditu:

Pseudo-RESTerako APIa

Pseudo-RESTerako APIaren bidez nabigatzailearen URL batean eskaera bat kodetzea posible da. Eskaera honek euskadi.eus-eko bilaketa muinaren kontra egiten du eta honek XML formatuan bueltatzen dio emaitza.

API Java

API Java-k query-ak sortzea eta lortutako emaitzak erabiltzea errazago egiten du Pseudo-REST APIarekin alderatuta.

1.Query-a sortu

R01MSearchQuery qry = R01MSearchQuery.create()

     .typedInAnyOfTheeseClusters(R01MTypoClusterOID.forId("euskadi"))     

     .typedInAnyOfTheeseFamilies(R01MTypoFamilyOID.forId("procedimientos_administrativos"))

     .typedInAnyOfTheeseTypes(R01MTypoTypeOID.forId("ayuda_subvencion"))

     .mustMeetTheeseMetaDataConditions(R01MSearchQueryNumberMetaData

                                           .forMetaData("procedureStatus")

                                           .usingCondition(R01MSearchQueryNumberMetaDataCondition.EQUALS)

                                           .with(16))

     .mustHaveStructureLabel("label1");

2.Bilaketa saioa sortu

Bilaketa sesio bat sortu behar da emaitzak zerbitzaritik lortzeko: zenbat emaitza, paginazioa, item kopurua:

R01MSearchSession session = R01MSearchSession.forQuery(qry);

R01MSearchResultItem[] items = session.getCurrentPageSearchResults()

StringBuffer dbg = new StringBuffer();

    if (items != null) {

           for (R01MSearchResultItem item : items) {

            dbg.append("-").append(item.getContentName())

                 .append(": ").append(item.getDocumentName()).append("\r\n");

           }

    }

Adibide osoa

Jarraitu beharreko urratsak hauek dira:

      1. Query-a osatu aukeratutako edukiak iragazteko
      2. Bilaketa saio bat lortu (query-a burutu)
      3. Bilaketaren emaitzak manipulatu
      4. Bilatzaileak bueltatutako meta-datuak lortu:
        • Meta-datu amonkomunak: edukiaren izena, hizkuntza bertsioa, publikazio-data, etb.
        • Berariazko meta-datuak, eduki bakoitzak eduki-mota zehatza izateagatik dituenak.

Adibidez: laguntza baten meta-datu zehatz batzuk indexatzen dira gero horien arabera bilatu ahal izateko (Adbez. laguntzaren epea); meta-datu hauek bilaketa emaitzetan lortzen ditugu. Bilatzaileak eduki motaren meta-datuak ez dituenez beharrezkoa da berariazko meta-datuak dituen XML-ra sartzea.

OHARRA: opendata.euskadi.eus-en publikatutako dokumentuetan euskadi.eus-en edukien egitura nolakoa den ikus daiteke:

Eduki motaren berariazko meta-datuak dituen XML-ra sartzeko bi aukera dauzkazu:

  1. HTTP bidez meta-datuak dituen XML fitxategira sartu
  2. Edukia duen ZIP bat deskargatu eta bertatik XML bat lortu

Bi kasuetan garrantzitsua da Euskadi.eus-eko edukien direktorio-egitura ezagutzea. Laburbilduta hauxe da egitura hori:

[contenido]

|- [contentName]-content.xml
|- [contentName]-idxContent.xml
|- [lang]_[version]
|_[data]

|- [dataFile] <<< Fitxategi hau da interestzen zaiguna eduki motaren berariazko datuak baititu

Beraz, emaitza bakoitzeko hauxe egin behar da:

  1. Hizkuntza bertsioaren datuak dituen direktorioa lortu
  2. Berariazko datuak dituen fitxategiaren izena lortu
  3. Berariazko datuak dituen fitxategia deskargatu
  4. Datuetara sartzeko XML fitxategia parseatu

Aurreko urratsen adibide bat honako hau da:

/**
* Construir una query para buscar las ayudas de Euskadi.eus
* @param items los items a procesar
* @return
*/
private static R01MSearchQuery _buildQuery() {public static void main(String[] args) {
   try {
       // Construir la query
       R01MSearchQuery qry = _buildQuery();
       
       // Inicializar la sesioacute;n
       R01MSearchSession session = R01MSearchSession.forQuery(qry);
       
       System.out.println(session.queryAsURL());
       
       // Un poco de debug
       String dbg = Strings.create()
                           .add("[Resultados de la bsqueda: \r\n")
                           .add("\t- query como XML: ").addLine(session.queryAsXML())
                           .add("\t- query codificada en la URL: ").addLine(session.queryAsURL())
                           .add("\t- Resumen de la ejecución{} resultados en {} páginas de {} items (max) cada una")
                                   .customizeWith(session.getPager().getTotalNumberOfItems(),
                                                  session.getPager().getPageCount(),
                                                  session.getPager().getPageSize())
                           .asString();
       System.out.println(dbg);
       
       // Recoger resultados
       R01MSearchResultItem[] resultItems = session.getCurrentPageSearchResults();
       System.out.println(_processResultItems(resultItems));
       
       // Ir a la siguiente p´gina de resultados
       resultItems = session.goToNextPage();
       System.out.println(_processResultItems(resultItems));
       
       // ... mas...
       
    } catch (Exception ex) {
       ex.printStackTrace(System.out);
    }
}

    R01MSearchQuery qry = R01MSearchQuery.create()
                                    .typedInAnyOfTheeseClusters(R01MTypoClusterOID.forId("euskadi"))
                                    .typedInAnyOfTheeseFamilies(R01MTypoFamilyOID.forId("procedimientos_administrativos"))
                                    .typedInAnyOfTheeseTypes(R01MTypoTypeOID.forId("ayuda_subvencion"))
                                    .mustMeetTheeseMetaDataConditions(R01MSearchQueryNumberMetaData
                                                                             .forMetaData("procedureStatus")
                                                                             .usingCondition(EQUALS)
                                                                             .with(16));

    return qry;
}

 

/**
* Procesar los resultados de bsqueda: acceder a algunos datos:
*      - MetaDatos comunes como el nombre del contenido y versión idiomáca
*      - MetaDatos específicos: XML del tipo de contenido
* @param items los items a procesar
* @return
*/
private static String _processResultItems(final R01MSearchResultItem[] items) {
    StringBuffer dbg = new StringBuffer();

    if (items != null) {

       for (R01MSearchResultItem item : items) {
           ItemData itemData = new ItemData();
       
           // Algunos metaDatos comunes
           // -------------------------
           itemData.setContentName(item.getContentName());
           itemData.setLang(item.getDocumentLanguage());
           itemData.setLangVersionName(item.getDocumentName());
           
            // Algunos meta-datos relevantes dependientes del tipo de contenido
           // pero que son devueltos por el buscador
           // ----------------------------------------------------------------
           Map indexedMD = item.getDocumentMetaData();
           itemData.setProcedureStatus(indexedMD != null ? indexedMD.get("procedureStatus").toString() : null);
           

           // MetaDatos específicos disponibles en un XML que hay que descargar de euskadi.eus
           // --------------------------------------------------------------------------------
           // Buscar el datafile (esta parte NO estaba pensada para ser utilizad en OpenData, pero
           // es prácticamente la única opción que hay WTF!)
           // item.getDocumentDataFilesGeneratedFilesDocumentRelativePaths() es un Mapa
           // que relaciona los oids de los datafiles (que es lo que se necesita) con el fichero HTML
           // generado
           // Ej:  
           //          procedimiento_ayuda_v2;main:abandono_2010.html 
           //          
           //      
           // (se necesita el oid: r01dpd013a780b8c401e41497a285409dc514c34e)
           Map dataFiles = item.getDocumentDataFilesGeneratedFilesDocumentRelativePaths();
           String dataFileOid = !CollectionUtils.isNullOrEmpty(dataFiles) ?
                                         CollectionUtils.of(dataFiles.keySet()).pickOneElement()
                                           :
                                         null;
           
           String dataFileXML = null;
           if (dataFileOid != null) {

               try {

                   String dataFileURL = "http://www.euskadi.eus/contenidos/" +

                                              item.getDocumentWorkAreaRelativePath() +

                                              "/data/" + item.getDocumentLanguage() + "_" + dataFileOid;

                   dataFileXML = HttpClient.forUrl(dataFileURL)

                                           .withoutParameters()

                                           .usingGETRequestMethod()

                                           .loadAsString();

                  dataFileXML = dataFileXML.trim();

               } catch (Exception ex) {

                   ex.printStackTrace(System.out);

               }

           }
           itemData.setDataFileXML(dataFileXML);
           
           // Assets reusables (ZIP con el contenido)
           // ---------------------------------------
           Map rispAssets = item.getContentRispDocumentsInfo();
           if (!CollectionUtils.isNullOrEmpty(rispAssets)) {
               Map assets = new HashMap(rispAssets.size());
               for (Map.Entry assetInfo : rispAssets.entrySet()) {
                   Path assetPath = Path.of(assetInfo.getValue().split(",")[0]);
                   assets.put(assetInfo.getKey(),assetPath);
               }
               itemData.setReusableAssets(assets);
           }
           // Debug:
           System.out.println("\r\n\r\n\r\n_________________________________________________\r\n" +
                              itemData.debugInfo());
       }
    }
    return dbg.toString();
}

 

@Accessors(prefix="_")
static class ItemData implements Debuggable {
    @Getter @Setter private String _contentName;
    @Getter @Setter private String _lang;
    @Getter @Setter private String _langVersionName;
    @Getter @Setter private String _procedureStatus;
    @Getter @Setter private Map _reusableAssets;
    @Getter @Setter private String _dataFileXML;
    
    @Override
    public String debugInfo() {
       return Strings.create().add("     Content Name: ").addLine(_contentName)
                              .add("Lang Version Name: ").add("(").add(_lang).add(")").addLine(_langVersionName)
                              .add(" Procedure status: ").addLine(_procedureStatus)
                              .add("  Reusable Assets: ").addLine((_reusableAssets != null ? 
                                                                                _reusableAssets.values().toString()
                                                                                :
                                                                                ""))
                              .add("     DataFile XML: ").add(_dataFileXML)
                              .asString();
    }
}

 

Beraz, opendata.euskadi.eus-etik datuak lortzea erraza da nahiz eta oraindik ere sailtasun pixka bat baduen, programatzaileak honako hau kontuan hartu behar baitu:

  • Euskadi.eus-eko direktorioen egitura ezagutu behar du eta eduki-mota bakoitzeko meta-datu zehatzak non dauden ere bai (euskadi.eus-eko eduki motak eta familiak).
  • Meta-datu zehatzak dituen XML fitxategia parseatu behar du.