Zgodba enega malega projekta dvanajst let (o BIRMA.NET prvič in odkrito iz prve roke)

Rojstvo tega projekta lahko štejemo za majhno idejo, ki se mi je porodila nekje konec leta 2007, ki ji je bilo usojeno, da končno obliko dobi šele 12 let kasneje (v tem trenutku – seveda, čeprav trenutna izvedba, po avtorju zelo zadovoljivo).

Vse se je začelo, ko sem v procesu opravljanja tedanjih službenih obveznosti v knjižnici opozoril na dejstvo, da je proces vnosa podatkov iz skeniranega besedila kazal knjižnih (in notnih) publikacij v obstoječo bazo podatkov oz. navidezno bistveno poenostavimo in avtomatiziramo, pri čemer izkoristimo lastnost urejenosti in ponovljivosti vseh podatkov, potrebnih za vnos, kot so ime avtorja članka (če govorimo o zbirki člankov), naslov članek (ali podnaslov, ki se odraža v kazalu vsebine) in številko strani trenutne postavke kazala vsebine. Sprva sem bil praktično prepričan, da je sistem, primeren za izvedbo te naloge, zlahka najti na internetu. Ko je bilo nekaj presenečenja nad dejstvom, da nisem našel takšnega projekta, sem se odločil, da ga poskusim izvesti sam.

Po dokaj kratkem času je začel delovati prvi prototip, ki sem ga takoj začel uporabljati pri vsakodnevnih opravilih, hkrati pa ga razhroščeval na vseh primerih, ki so mi prišli pod roke. Na srečo sem se na svojem običajnem delovnem mestu, kjer nikakor nisem bil programer, tedaj še vedno izognil vidnim “zastojom” pri delu, med katerim sem intenzivno razhroščeval svojo idejo – kar je v trenutnih realnostih, ki pomenijo skoraj nepredstavljivo dnevna poročila o opravljenem delu čez dan. Proces piljenja programa je skupaj trajal nič manj kot kakšno leto, a tudi po tem rezultatu težko rečemo, da je povsem uspešen – na začetku je bilo postavljenih preveč različnih konceptov, ki niso bili povsem jasni za implementacijo: neobvezni elementi, ki lahko biti preskočen; ogled elementov naprej (z namenom zamenjave prejšnjih elementov v rezultatih iskanja); celo naš poskus implementacije nečesa podobnega regularnim izrazom (ki ima edinstveno sintakso). Moram reči, da sem pred tem nekoliko opustil programiranje (za kakšnih 8 let, če ne več), tako da je nova priložnost, da svoje znanje uporabim pri zanimivem in potrebnem opravilu, popolnoma pritegnila mojo pozornost. Ni presenetljivo, da je nastala izvorna koda – v odsotnosti jasnih pristopov k njeni zasnovi z moje strani – hitro postala nepredstavljiva mešanica različnih delov v jeziku C z nekaterimi elementi C++ in vidiki vizualnega programiranja (sprva odločili smo se za uporabo takšnega oblikovalskega sistema, kot je Borland C++ Builder - "skoraj Delphi, vendar v C"). Vse to pa je na koncu obrodilo sadove v avtomatizaciji vsakodnevnih dejavnosti naše knjižnice.

Hkrati sem se za vsak slučaj odločil, da se udeležim tečajev za usposabljanje profesionalnih razvijalcev programske opreme. Ne vem, ali se je tam dejansko mogoče naučiti »biti programer« iz nič, a glede na veščine, ki sem jih takrat že imel, sem lahko nekoliko obvladal tehnologije, ki so bile takrat bolj relevantne, npr. kot C#, Visual Studio za razvoj pod .NET, kot tudi nekatere tehnologije, povezane z Javo, HTML in SQL. Celotno usposabljanje je trajalo skupno dve leti in je služilo kot izhodišče za še en moj projekt, ki se je na koncu razvlekel čez nekaj let – vendar je to tema za posebno publikacijo. Tukaj bi bilo primerno le omeniti, da sem poskusil prilagoditi razvoj, ki sem ga že imel na opisanem projektu, da bi ustvaril popolno okensko aplikacijo v C# in WinForms, ki izvaja potrebno funkcionalnost, in jo uporabil kot osnovo za prihajajoči diplomski projekt.
Sčasoma se mi je ta ideja začela zdeti vredna, da bi jo izrazili na takih letnih konferencah z udeležbo predstavnikov različnih knjižnic, kot sta "LIBKOM" in "KRIM". Ideja da, ne pa moja takratna izvedba. Potem sem tudi upal, da ga bo kdo prepisal z bolj kompetentnimi pristopi. Tako ali drugače sem se do leta 2013 odločil, da napišem poročilo o svojem predhodnem delu in ga pošljem organizacijskemu odboru konference s prošnjo za štipendijo za sodelovanje na konferenci. Na moje nekoliko presenečenje je bila moja prijava odobrena in začel sem nekaj izboljšav projekta, da bi ga pripravil za predstavitev na konferenci.

Do takrat je projekt že dobil novo ime BIRMA, pridobil različne dodatne (ne toliko v celoti implementirane, ampak predpostavljene) zmogljivosti - vse podrobnosti najdete v mojem poročilu.

Po pravici povedano bi BIRMO 2013 težko rekli nekaj popolnega; Odkrito povedano, je bilo to zelo hekano plovilo, narejeno v naglici. Kar zadeva kodo, posebnih novosti praktično ni bilo, razen precej nemočnega poskusa ustvariti nekakšno enotno sintakso za razčlenjevalnik, ki na videz spominja na formatni jezik IRBIS 64 (in pravzaprav tudi na sistem ISIS - z oklepaji kot cikličnimi strukturami; zakaj Takrat se mi je zdelo precej kul). Razčlenjevalnik je brezupno naletel na te krogce oklepajev ustreznega tipa (saj so oklepaji opravljali še eno vlogo, in sicer so med razčlenjevanjem označevali neobvezne strukture, ki jih je mogoče preskočiti). Ponovno napotujem vse, ki se želijo podrobneje seznaniti s takrat težko predstavljivo, neupravičeno sintakso BIRME, na moje takratno poročilo.

Na splošno, razen tega, da se mučim z lastnim razčlenjevalnikom, nimam ničesar več za povedati o kodi te različice - razen povratne pretvorbe obstoječih virov v C++ ob ohranjanju nekaterih tipičnih lastnosti kode .NET (če sem iskren, je težko razumem, kaj točno me je spodbudilo, da sem vse premaknil nazaj - verjetno neumen strah, da bi svoje izvorne kode ohranil v skrivnosti, kot da bi šlo za nekaj enakovrednega skrivnemu receptu Coca-Cole).

Morda je v tej neumni odločitvi tudi razlog za težave pri parjenju nastale knjižnice DLL z obstoječim vmesnikom domače delovne postaje za vnos podatkov v elektronski katalog (da, nisem omenil še enega pomembnega dejstva: odslej bodo vsi koda “motorja” BIRMA je bila pričakovana, je ločena od vmesniškega dela in zapakirana v ustrezen DLL). Zakaj je bilo za te namene potrebno napisati ločeno delovno postajo, ki je tako ali tako po svojem videzu in načinu interakcije z uporabnikom brez sramu kopirala isto delovno postajo "Katalogizator" sistema IRBIS 64 - to je ločeno vprašanje. Skratka: dal je potrebno trdnost mojemu takratnemu razvoju za diplomsko nalogo (sicer sam neprebavljivi razčlenjevalec nekako ni bil dovolj). Poleg tega sem nato naletel na nekaj težav pri izvajanju vmesnika delovne postaje Cataloger z lastnimi moduli, implementiranimi v C++ in C#, in neposrednem dostopu do svojega motorja.

Na splošno, nenavadno, prav ta precej neroden prototip prihodnjega BIRMA.NET-a je bil usojen, da postane moj "delovni konj" za naslednja štiri leta. Ni mogoče reči, da v tem času nisem vsaj poskusil najti poti za novo, celovitejšo izvedbo dolgoletne ideje. Med drugimi novostmi naj bi že obstajala ugnezdena ciklična zaporedja, ki bi lahko vključevala izbirne elemente - tako sem nameraval uresničiti idejo o univerzalnih predlogah za bibliografske opise publikacij in razne druge zanimivosti. Vendar je bilo v mojih takratnih praktičnih dejavnostih po vsem tem malo povpraševanja in izvedba, ki sem jo imel takrat, je povsem zadostovala za vnos kazal. Poleg tega je smer razvoja naše knjižnice začela vse bolj odstopati v smeri digitalizacije muzejskega arhiva, poročanja in drugih zame malo zanimivih dejavnosti, zaradi česar sem jo na koncu prisilil, da sem jo dokončno zapustil in se prepustil tistim, ki bi biti bolj zadovoljen z vsem tem.

Paradoksalno, prav po teh dramatičnih dogodkih se je zdelo, da je projekt BIRMA, ki je takrat že imel vse značilnosti tipičnega dolgotrajnega projekta, začel zaživeti dolgo pričakovano novo življenje! Imel sem več prostega časa za prazne misli, spet sem začel prečesavati svetovni splet v iskanju nečesa podobnega (na srečo sem zdaj že lahko slutil, da vse to iščem ne kjer koli, ampak na GitHubu) in nekje v Na v začetku tega leta sem končno naletel na ustrezen izdelek znanega podjetja Salesforce pod nepomembnim imenom Gorp. Sam je zmogel skoraj vse, kar sem potreboval od takega razčlenjevalnika - namreč inteligentno izolirati posamezne fragmente iz poljubnega, a jasno strukturiranega besedila, pri tem pa imeti končnemu uporabniku dokaj prijazen vmesnik, vključno s tako razumljivimi bistvi, kot je npr. vzorec, predlogo in pojav, hkrati pa uporablja že poznano sintakso regularnih izrazov, ki zaradi razdelitve na določene pomenske skupine za razčlenjevanje postane neprimerljivo bolj berljiva.

Na splošno sem se odločil, da je to tisti Gorp (Zanima me, kaj to ime pomeni? Morda nekakšen "splošno usmerjen navaden razčlenjevalnik"?) – točno to, kar sem iskal že dolgo. Res je, njegova takojšnja implementacija za lastne potrebe je imela tako težavo, da je ta motor zahteval prestrogo upoštevanje strukturnega zaporedja izvornega besedila. Za nekatera poročila, kot so dnevniške datoteke (razvijalci so jih namreč postavili kot jasne primere uporabe projekta), je to povsem primerno, za ista besedila skeniranih kazal pa je malo verjetno. Navsezadnje se lahko ista stran s kazalom začne z besedami »Kazalo«, »Vsebina« in vsemi drugimi predhodnimi opisi, ki nam jih ni treba umestiti v rezultate predvidene analize (in jih ročno izrezati vsakič je tudi neprijetno). Poleg tega lahko stran med posameznimi ponavljajočimi se elementi, kot so ime avtorja, naslov in številka strani, vsebuje določeno količino smeti (na primer risbe in samo naključne znake), kar bi bilo prav tako lepo, če bi lahko odrezati. Vendar zadnji vidik še ni bil tako pomemben, ampak zaradi prvega obstoječa izvedba ni mogla začeti iskati potrebnih struktur v besedilu od določenega mesta, temveč ga je preprosto obdelala od vsega začetka, ni našla določil vzorce in... končal svoje delo. Očitno je bilo potrebnih nekaj popravkov, da bi omogočili vsaj nekaj prostora med ponavljajočimi se strukturami, in to me je spodbudilo nazaj k delu.

Druga težava je bila, da je bil projekt sam implementiran v Javi, in če sem v prihodnosti načrtoval implementacijo nekaterih načinov povezovanja te tehnologije z znanimi aplikacijami za vnos podatkov v obstoječe baze podatkov (kot je Irbisov "Cataloguer"), potem vsaj vsaj naredite to v C# in .NET. Ne gre za to, da je Java sama po sebi slab jezik – nekoč sem jo celo uporabil za izvedbo zanimive okenske aplikacije, ki je izvajala funkcionalnost domačega programabilnega kalkulatorja (kot del tečaja). In glede sintakse je zelo podoben istemu C-sharpu. No, to je samo plus: lažje bom dokončal obstoječi projekt. Vendar se nisem želel znova potopiti v ta precej nenavaden svet okenskih (ali bolje rečeno namiznih) tehnologij Java - navsezadnje sam jezik ni bil "prilagojen" za takšno uporabo in sploh nisem hrepenel po ponovitvi prejšnje izkušnje. Morda prav zato, ker je C# v navezi z WinForms veliko bližje Delphiju, s katerim smo mnogi nekoč začeli. K sreči se je potrebna rešitev našla zelo hitro – v obliki projekta IKVM.NET, ki olajša prevajanje obstoječih programov Java v upravljano kodo .NET. Res je, sam projekt so avtorji do takrat že opustili, vendar mi je njegova zadnja izvedba omogočila, da sem dokaj uspešno izvedel potrebna dejanja za izvorna besedila Gorp.

Tako sem naredil vse potrebne spremembe in vse skupaj sestavil v DLL ustreznega tipa, ki bi ga zlahka »pobrali« kateri koli projekti za .NET Framework, ustvarjeni v Visual Studiu. Medtem sem ustvaril še en sloj za priročno predstavitev vrnjenih rezultatov Gorp, v obliki ustreznih podatkovnih struktur, ki bi jih bilo priročno obdelati v pogledu tabele (za osnovo tako vrstice kot stolpci; slovarski ključi in številski indeksi). No, sami potrebni pripomočki za obdelavo in prikaz rezultatov so bili napisani precej hitro.

Tudi proces prilagajanja predlog za nov motor, da bi ga naučili razčleniti obstoječe vzorce skeniranih besedil kazal, ni povzročal posebnih zapletov. Pravzaprav se mi sploh ni bilo treba sklicevati na moje prejšnje predloge: preprosto sem ustvaril vse potrebne predloge iz nič. Poleg tega, če so predloge, zasnovane za delo s prejšnjo različico sistema, postavile dokaj ozek okvir za besedila, ki jih je bilo mogoče pravilno razčleniti z njihovo pomočjo, je novi mehanizem že omogočil razvoj dokaj univerzalnih predlog, primernih za več vrst oznak na enkrat. Poskušal sem celo napisati nekakšno obsežno predlogo za poljubno besedilo kazala, čeprav seveda tudi z vsemi novimi možnostmi, ki so se mi odpirale, vključno zlasti z omejeno možnostjo izvajanja istih ugnezdenih ponavljajočih se zaporedij ( kot so na primer priimki in začetnice več avtorjev zapored), se je to izkazalo za utopijo.

Morda bo v prihodnosti mogoče implementirati določen koncept metapredlog, ki bo lahko preveril skladnost izvornega besedila z več razpoložljivimi predlogami hkrati in nato v skladu z dobljenimi rezultati izbral najprimernejši z uporabo nekakšnega inteligentnega algoritma. Toda zdaj me je bolj skrbelo drugo vprašanje. Kot razčlenjevalnik Gorp, kljub vsestranskosti in modifikacijam, ki sem jih naredil, še vedno ni bil sposoben narediti ene na videz preproste stvari, ki jo je moj samonapisani razčlenjevalec lahko naredil od prve različice. Namreč: imel je sposobnost najti in iz izvornega besedila izluščiti vse fragmente, ki se ujemajo z masko, določeno v uporabljeni predlogi na pravem mestu, pri tem pa ga sploh ni zanimalo, kaj dano besedilo vsebuje v presledkih med temi fragmenti. Doslej sem nov motor le nekoliko izboljšal in mu omogočil iskanje vseh možnih novih ponovitev danega zaporedja takšnih mask s trenutnega položaja, pri čemer sem pustil možnost prisotnosti nizov poljubnih znakov v besedilu, ki so bili popolnoma neupoštevani pri razčlenjevanju, zaprti med zaznane ponavljajoče se strukture. Vendar to ni omogočilo nastavitve naslednje maske ne glede na rezultate iskanja prejšnjega fragmenta z ustrezno masko: strogost opisane strukture besedila še vedno ni dopuščala poljubnih vključkov nepravilnih znakov.

In če se za primere kazal, na katere sem naletel, ta težava še ni zdela tako resna, potem ko poskušam uporabiti nov mehanizem razčlenjevanja za podobno nalogo razčlenjevanja vsebine spletnega mesta (tj. isto razčlenjevanje), omejitve so tu, pojavile so se z vso svojo očitnostjo. Navsezadnje je precej enostavno nastaviti potrebne maske za fragmente spletnega označevanja, med katerimi naj se nahajajo podatki, ki jih iščemo (ki jih je treba ekstrahirati), a kako lahko prisilimo razčlenjevalnik, da takoj preide na naslednjega podoben fragment, kljub vsem možnim oznakam in atributom HTML, ki jih je mogoče postaviti v presledke med njimi?

Po kratkem premisleku sem se odločil predstaviti nekaj vzorcev storitev (%vse_pred) и (%vse_po), ki služijo očitnemu namenu zagotavljanja, da je vse, kar je lahko vsebovano v izvornem besedilu, preskočeno pred katerim koli vzorcem (masko), ki jim sledi. Še več, če (%vse_pred) potem preprosto ignoriral vse te samovoljne vključitve (%vse_po), nasprotno, dovolil, da so bili dodani želenemu fragmentu po premiku s prejšnjega fragmenta. Sliši se precej preprosto, toda za implementacijo tega koncepta sem moral znova prečesati vire gorp, da sem naredil potrebne spremembe, da ne bi pokvaril že implementirane logike. Na koncu nam je to uspelo (čeprav je bila napisana tudi zelo, zelo prva, čeprav zelo hroščeča, izvedba mojega razčlenjevalnika in še hitreje - v nekaj tednih). Od zdaj naprej je sistem dobil resnično univerzalno obliko - nič manj kot 12 let po prvih poskusih njegovega delovanja.

Seveda to ni konec naših sanj. Prav tako lahko popolnoma prepišete razčlenjevalnik predloge gorp v C# z uporabo katere koli od razpoložljivih knjižnic za implementacijo brezplačne slovnice. Menim, da je treba kodo bistveno poenostaviti, kar nam bo omogočilo, da se znebimo zapuščine v obliki obstoječih virov Jave. Toda z obstoječim tipom motorja je povsem mogoče narediti različne zanimive stvari, vključno s poskusom implementacije metapredlog, ki sem jih že omenil, da ne omenjam razčlenjevanja različnih podatkov iz različnih spletnih mest (vendar ne izključujem da so obstoječa specializirana programska orodja za to primernejša – samo z njihovo uporabo še nisem imel ustreznih izkušenj).

Mimogrede, to poletje sem že prejel vabilo po e-pošti od podjetja, ki uporablja tehnologije Salesforce (razvijalec izvirnega Gorp), opraviti razgovor za nadaljnje delo v Rigi. Na žalost trenutno nisem pripravljen na takšne prerazporeditve.

Če bo to gradivo vzbudilo nekaj zanimanja, bom v drugem delu poskušal podrobneje opisati tehnologijo za sestavljanje in naknadno razčlenjevanje predlog na primeru izvedbe, uporabljene v Salesforce Gorp (moji lastni dodatki, z izjemo nekaj že opisanih funkcijskih besed, praktično ne spremenijo sintakse same predloge, tako da skoraj vsa dokumentacija za izvirni sistem Gorp Primerno tudi za mojo različico).

Vir: www.habr.com

Dodaj komentar