Anotatornia2 - poradnik administratora
--------------------------------------

Anotatornia2 jest systemem umożliwiającym ręczną anotację tekstów obejmującą korektę podziału na zdania, zmiany segmentacji i przypisanie segmentom właściwego opisu gramatycznego (przez wybór z listy interpretacji uzyskanych np. z programu Morfeusz bądź wpisanie własnej). Umożliwia pracę zarówno w trybie 2 anotatorów + superanotator (każdą próbkę niezależnie znakuje dwóch anotatorów a konflikty rozstrzyga ostatecznie superanotator) jak i w trybie 1 anotatora (wynik jego pracy zostaje od razu przyjęty jako ostateczna wersja). Każdy segment występuje w dwóch postaciach: transliterowanej i transkrybowanej co umożliwia pracę z tekstami starymi. Formatem danych wejściowych i wyjściowych jest XML zgodny z TEI oparty na wersji stosowanej w NKJP (patrz punkt VI.). Jeśli w plikach wejściowych pojawi się ujednoznacznienie (uzyskane np. z automatycznego tagera), to informację o wybranej interpretacji można załadować do systemu jako wynik pracy jednego z anotatorów (i konfrontować ją z pracą drugiego anotatora).

System udostępniony jest na licencji AGPL 3.0 https://www.gnu.org/licenses/agpl-3.0.en.html 


I. Konfiguracja systemu Anotatornia2 i uruchomienie serwera na Ubuntu 14.04
---------------------------------------------------------------------------

Wymagania:
- PostgreSQL 9.3
- Python 2.7
- nginx
- uWSGI
- wirtualne środowisko (virtualenv) z:
  - Django==1.8.6
  - lxml==3.5.0
  - psycopg2==2.7.1
  

1. Do wybranego folderu (na potrzeby niniejszej instrukcji niech to będzie /home/user/) należy rozpakować zawartość archiwum pobranego z sieci. Powinien zostać utworzony podfolder 'anotatornia2' i jeśli nie wspomniano inaczej wszystkie ścieżki poniżej odnoszą się do lokalizacji /home/user/anotatornia2.

2. Następnie skopiować plik anotatornia2/custom_config.py.example na anotatornia2/custom_config.py. Otworzyć go do edycji i uzupełnić nazwę bazy danych, nazwę i hasło użytkownika tej bazy danych oraz konfigurację konta pocztowego, z którego będą wysyłane maile dotyczące zmiany hasła.

3. Skopiować plik annotation/anot2_config.py.example na annotation/anot2_config.py. Otworzyć go do edycji i ustawić wartości odpowiedzialne za działanie systemu:

REF_MODE - czy serwer ma działać w trybie 2 anotatorów + superanotator (True), czy tylko 1 anotatora (False)
NO_INTERP - lista tagów TEI, dla których nie będzie ustawiana interpretacja (np. 'foreign' - obce fragmenty)
EXC_SET - liczba próbek, które są losowane za jednym razem
EXC_MAX - liczba próbek dostępnych do edycji, po której przekroczeniu nie można pobrać nowych
MAX_IGN - liczba (w %) segmentów, które mogą mieć interpretację "ign" w zatwierdzonej próbce (0, jeśli "ign" ma w ogóle nie być)
MAX_IGNNDM - j.w., ale z interpretacją "ignndm"
SORT_ORDER - porządek tagów używany w sortowaniu interpretacji (więcej poniżej w "Konfiguracja dla własnego tagsetu")

4. Założyć bazę zdefiniowaną w punkcie 2. Jeśli trzeba założyć też odpowiedniego użytkownika z prawami do tej bazy. Np.:

$ psql -U postgres -h localhost
postgres=# CREATE USER anotadmin WITH PASSWORD 'anotadmin';
postgres=# CREATE DATABASE anotdb;
postgres=# GRANT ALL PRIVILEGES ON DATABASE anotdb TO anotadmin;
postgres=# \q

5. Założyć właściwą strukturę bazy danych (polecenie "migrate") oraz główne konto administracyjne w systemie Anotatornia2. Polecenia wydawane są po uruchomieniu właściwego środowiska wirtualnego, np.:

(virtenv)$ ./manage.py migrate
(virtenv)$ ./manage.py createsuperuser

Dostęp do administracji w gotowym systemie (np. dla założenia użytkowników lub modyfikacji ich uprawnień) możliwy będzie poprzez adres /admin.


6. Stworzyć plik konfiguracyjny dla uWSGI np. 'anotatornia2.ini':

$ sudo mkdir -p /etc/uwsgi/sites
$ cd /etc/uwsgi/sites 
$ sudo touch anotatornia2.ini

Przykładowa treść tego pliku (na 'base' pełna ścieżka do folderu, w którym jest rozpakowany projekt, na 'home' ścieżka do wirtualnego środowiska):

----------
[uwsgi]
project = anotatornia2
base = /home/user

chdir = %(base)/%(project)
home = %(base)/virtenv/
module = %(project).wsgi:application

master = true
processes = 5
touch-reload = %(base)/%(project)/%(project)/wsgi.py

socket = %(base)/%(project)/%(project).sock
chmod-socket = 664
vacuum = true
-----------


7. Ustawić skrypt automatycznie startujący serwery opisane w plikach w /etc/uwsgi/sites, np.:

$ sudo vi /etc/init/uwsgi.conf

Przykładowa treść tego pliku (na 'setuid' nazwa użytkownika uruchamiającego):

-----------
description "uWSGI application server in Emperor mode"

start on runlevel [2345]
stop on runlevel [!2345]

setuid user
setgid www-data

exec /usr/local/bin/uwsgi --emperor /etc/uwsgi/sites
------------


8. Stworzyć konfigurację serwera dla nginx, np.:

$ sudo vi /etc/nginx/sites-available/anotatornia2

i podlinkować ją w sited_enabled:

sudo ln -s /etc/nginx/sites-available/anotatornia2 /etc/nginx/sites-enabled

Przykładowa treść pliku:

------------
# Default server
server {
    return 404;
}

server {
    listen 80;
    server_name anotatornia.domena.pl;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        root /home/user/anotatornia2;
    }

    location / {
        include         uwsgi_params;
        uwsgi_pass      unix:/home/user/anotatornia2/anotatornia2.sock;
        uwsgi_read_timeout  5m;
    }
}
------------


9. Po przetestowaniu konfiguracji nginx:

$ sudo service nginx configtest

można restartować usługi:

$ sudo service nginx restart
$ sudo service uwsgi restart



II. Konfiguracja Anotatorni2 dla własnego tagsetu
-------------------------------------------------

1. annotation/static/annotation/tagset.json
W pliku tagset.json znajduje się słownik, w którym poszczególnym POS przypisano listę interpretacji, które mają się pojawić na liście do wyboru przy ręcznym wpisywaniu interpretacji. Np.:

{"dig": [""], "pant": ["imperf", "perf", "biasp"], "interp": [""], "inf": ["imperf", "perf", "biasp"], "ppraet": ["sg:nom:manim1:imperf:aff", "sg:nom:manim1:imperf:neg", "sg:nom:manim1:perf:aff"]}


Do utworzenia tego pliku z tagsetu Morfeusza (plik .tagset) można użyć skryptu tools/tagset.py. Należy w nim zmodyfikować odpowiednio SORT_ORDER definiujący kolejność sortowania poszczególnych tagów (powinien odpowiadać wartości SORT_ORDER wpisanej do pliku annotation/anot2_config.py - patrz punkt I.3). Dodatkowo należy uzupełnić słownik o pos i tagi specjalne (spoza tagsetu Morfeusza), jeśli takie mają być używane. Wywołanie skryptu:

$ python tagset.py [nazwa_tagsetu_morfeusza]

utworzy plik tagset.json. Należy go umieścić w annotation/static/annotation/ 


2. annotation/static/annotation/js/valid-msd.js
Stosownie do potrzeb i używanego tagsetu należy również zmodyfikować plik valid-msd.js sprawdzający poprawność wprowadzonej ręcznie interpetacji. Dla poszczególnych POS, które będą dopuszczone w ręcznej anotacji należy zdefiniować reguły (wyrażeniem regularnym), które będą użyte do sprawdzenia poprawności. Dla każdego POS reguła zapisana jest jako wartość "r", a w "t" należy umieścić fragment HTML, który zostanie wyświetlony jako podpowiedź, jeśli wpisana interpretacja nie będzie pasowała do reguły.

3. Po dokonaniu zmian należy pamiętać o uaktualnieniu katalogu /static na serwerze, np.:
(virtenv)$ ./manage.py collectstatic


III. Zmiana logo i treści komunikatów przy logowaniu/zmianie hasła
------------------------------------------------------------------

1. Aby zmienić logo (obrazek pojawiający się na stronie logowania z lewej strony formularza) należy nadpisać plik annotation/static/annotation/logo.png. Po podmianie należy wykonać polecenie collectstatic, np.:
(virtenv)$ ./manage.py collectstatic


2. Pliki odpowiadające za wygląd stron logowania/zmiany hasła oraz treść maila wysyłanego użytkownikom proszącym o zresetowanie hasła znajdują się w annotation/templates/registration. Można je dostosować do własnych potrzeb.


IV. Import próbek do Anotarni2
-------------------------------

Format plików wejściowych i wynikowych bazuje na tym opracowanym dla ręcznie znakowanego NKJP. Próbki z danego tekstu muszą się znajdować w folderze, którego nazwa będzie identyfikatorem tego tekstu w systemie. Wewnątrz muszą się znajdować następujące pliki:

- text.xml - treści próbek z danego tekstu
- header.xml - opis tekstu
- ann_segmentation.xml - opis segmentacji poszczególnych próbek
- ann_morphosyntax.xml - opis poszczególnych segmentów

Wszystkie foldery z tekstami do importu muszą być zebrane w jeden folder zbiorczy (np. text_dir). Po uruchomieniu środowiska wirtualnego dostępne są następujące polecenia:

Import danych (dołączenie nowych próbek do istniejącej bazy):
-------------------------------------------------------------
(virtenv)$ ./manage.py load_excerpts text_dir

Usunięcie wszystkich danych z bazy (uwaga!) i import próbek:
------------------------------------------------------------
(virtenv)$ ./manage.py load_excerpts --clear-load text_dir

Import wersji wstępnie ujednoznacznionej (tagera):
--------------------------------------------------
Jeśli teksty zawierają wersję ujednoznacznioną, to można do bazy doimportować tę wersję jako wersję użytkownika 'tager'. Uwaga - wersja tagera musi być importowana z tych samych plików co wersja dla "zwykłych" anotatorów. Aby doimportować wersję tagera należy:
- upewnić się, że w systemie istnieje konto użytkownika 'tager'
- upewnić się, że baza pracuje w trybie 2 użytkowników + superanotator
- po zaimportowaniu próbek do systemu (load_excerpts) należy wywołać:
(virtenv)$ ./manage.py load_tager text_dir


V. Eksport danych z Anotatorni2
--------------------------------
Eksportowane są wszystkie próbki o statusie "zakończone".

Eksport próbek do domyślnego folderu ("anot2_[data_eksportu]"):
---------------------------------------------------------------
(virtenv)$ ./manage.py write_tei

Eksport próbek do folderu o nazwie "folder":
--------------------------------------------
(virtenv)$ ./manage.py write_tei --dirname folder

Eksport próbek uwzględniający wyłącznie wybrane warianty segmentacyjne:
-----------------------------------------------------------------------
(virtenv)$ ./manage.py write_tei --nochoice


VI. Struktura plików XML
------------------------
Przykładowe teksty do importu znajdują się w sample_texts a próbka eksportu w sample_export. Wykorzystana została struktura plików z ręcznie znakowanego NKJP z drobnymi uzupełnieniami. Najważniejsze opisy i zmiany zebrane są poniżej. 

text.xml:
---------
Plik zawiera treści wszystkich próbek z danego tekstu.

"div" - treść pojedynczej próbki. Atrybut "xml:id" obowiązkowy, według schematu "txt_[div_nr]-div", gdzie [div_nr] jest numerem kolejnym próbki.
"ab" - treść pojedynczego akapitu próbki. Atrybut "xml:id" obowiązkowy, według schematu "txt_[div_nr].[ab_nr]-ab", gdzie [div_nr] jest numerem próbki, [ab_nr] - numerem kolejnym akapitu w próbce.

pozostałe elementy - zgodne z TEI. Jeśli mają treść (są niepuste) to muszą posiadać atrybut "xml:id" tworzony według schematu: [parent_id].[nr]-[tag_name] gdzie parent_id jest identyfikatorem elementu nadrzędnego (bez końcówki po "-"), [nr] - numerem kolejnym elementu, [tag-name] - nazwą elementu.

header.xml:
-----------
Zawartość dowolna, zgodna z TEI.

ann_segmentation.xml:
---------------------
Zawiera opis wszystkich segmentów próbek z danego tekstu. W zależności od wybranej opcji eksportu mogą się w nim znaleźć wszystkie warianty segmentacyjne albo tylko te, które zostały wybrane podczas anotacji. Konstrukcja atrybutów taka, jak w NKJP. Poniżej najważniejsze uwagi i uzupełnienia:

"p" - pojedyncza próbka. Atrybut "xml_id" obowiązkowy, według schematu "segm_[nr]-p", gdzie [nr] jest numerem próbki odpowiadającym numerowi danej próbki w text.xml.
"s" - zdanie. Atrybut "xml_id" obowiązkowy, według schematu "segm_[p_nr].[nr]-s", gdzie [p_nr] jest numerem próbki, [nr] - numerem ostatniego segmentu w ramach tego zdania.
"choice" i "nkjp:paren" - stosowane do zapisu wariantów segmentacyjnych (jak w NKJP).
"seg" - segment. Atrybuty:
  "xml:id" - obowiązkowy, według schematu "segm_[p_nr].[nr]-seg", gdzie [p_nr] jest numerem próbki, [nr] - numerem kolejnym segmentu w ramach próbki.
  "corresp" - ustawiany dla elementów tekstowych (a więc nie takich deklarowanych jako EMPTY)
  "type" - nazwa elementu XML, w którym znajduje się dany segment (tylko, jeśli nie jest to "ab").
  "nkjp:nps" - wartość "true" jeśli przed segmentem nie występuje spacja
  "nkjp:korbeusz" - wartość "false" jeśli segment nie pochodził z oryginalnej (zaimportowanej) segmentacji (tylko w eksporcie)
"w" - zapis segmentu (treść w wersji oryginalnej - transliterowanej). Nie jest konieczny (nie było go w NKJP)

ann_morphosyntax.xml:
---------------------
W pliku tym znajdują się opisy tylko tych segmentów zdefiniowanych w ann_segmentation.xml, które mają treść (nie są odpowiednikami pustych elementów XML w text.xml). 

"p", "s", "seg" - znaczenie jak w ann_segmentation.xml. Poza "xml:id" konstruowanym w analogiczny sposób obowiązkowy jest atrybut "corresp" wskazujący odpowiedni element w pliku ann_segmentation.xml

Opis wybranych elementów dalszej struktury:
<f name="orth"> - zapis wersji transkrybowanej segmentu
<f name="translit"> - zapis wersji transliterowanej segmentu
<f name="disamb"> - opis wybranej interpretacji. Może się pojawić w tekstach importowanych do systemu Anotatornia2 jako efekt pracy np. automatycznego tagera. Jeśli tager ustali interpretację, której nie ma na liście w części <f name="interps">, to atrybut "fVal" musi mieć wartość pustą. Zostanie ona wtedy w systemie potraktowana jak ręcznie wprowadzona.

