\n",
"\n",
"![Logo 2](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech2.jpg)"
]
},
{
"cell_type": "markdown",
"id": "featured-afghanistan",
"metadata": {},
"source": [
"Badania nad komputerowym wspomaganiem tłumaczenia często prowadzone są przy użyciu metodologii testowania interfejsów użytkownika - UI/UX testing. Program typu CAT traktuje się wówczas jak każdy inny program komputerowy i przeprowadza testy wydajności i użyteczności."
]
},
{
"cell_type": "markdown",
"id": "severe-protein",
"metadata": {},
"source": [
"Testy takie prowadzone są zawsze na użytkownikach końcowych, w tym przypadku - na tłumaczach. Podstawowym celem testów jest próba zaobserwowania faktycznego sposobu pracy tłumacza - które funkcje programu są przez niego wykorzystywane najczęściej, jakich innych narzędzi poza CAT-em używa on do swojej pracy, które funkcje programu działają zgodnie, a które niezgodnie z intuicją użytkownika oraz wiele innych czynników. Aby wszystkie te analizy były możliwe, konieczne jest zgromadzenie jak największej ilości danych dotyczących przebiegu testu."
]
},
{
"cell_type": "markdown",
"id": "constant-underground",
"metadata": {},
"source": [
"Testy są przede wszystkim nagrywane. Nagrywany jest zarówno ekran komputera (screen capture), jak i sam użytkownik pracujący przy komputerze. To jednak nie wszystko - często stosuje się specjalne techniki eye-trackingu, które są w stanie określić, w który punk ekranu użytkownik aktualnie patrzy. Dane pozyskane w ten sposób używane są do analizy czasu znalezienia przez użytkownika potrzebnej mu funkcji oraz zidentyfikowania miejsc, gdzie tej funkcji poszukiwał. Można również wyznaczyć obszary ekranu, które często skupiają uwagę użytkownika. "
]
},
{
"cell_type": "markdown",
"id": "analyzed-lodging",
"metadata": {},
"source": [
"Dodatkowo stosuje się jeszcze jedną technikę, która jest szczególnie przydatna z punktu widzenia analizy procesu tłumaczenia. Wykonuje się pełny key logging, tj. zapisuje się każde uderzenie użytkownika w dowolny klawisz na klawiaturze wraz z precyzyjnym czasem tego uderzenia. Dane pozyskane w ten sposób pozwalają na przeprowadzenie szeregu interesujących analiz."
]
},
{
"cell_type": "markdown",
"id": "incredible-stress",
"metadata": {},
"source": [
"Zapoznajmy się najpierw z programem typu key logger:"
]
},
{
"cell_type": "markdown",
"id": "arctic-horror",
"metadata": {},
"source": [
"`sudo pip3 install keyboard`"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "broken-workstation",
"metadata": {},
"outputs": [],
"source": [
"import keyboard\n",
"\n",
"\n",
"def report_key(event):\n",
" print(event)\n",
"\n",
"keyboard.on_release(callback=report_key)\n",
"keyboard.wait()"
]
},
{
"cell_type": "markdown",
"id": "polish-census",
"metadata": {},
"source": [
"UWAGA! Aby uruchomić powyższy kod na Linuxie konieczne są uprawnienia administratora (pytanie poza konkursem - dlaczego?)"
]
},
{
"cell_type": "markdown",
"id": "incoming-hands",
"metadata": {},
"source": [
"### Ćwiczenie 1: Wykorzystując powyższy kod napisz keylogger, który zapisuje wszystkie uderzenia w klawisze do pliku. Format pliku jest dowolny, każdy wpis musi zawierać precyzyjną godzinę uderzenia oraz uderzony klawisz. Uruchom program i przepisz paragraf dowolnie wybranego tekstu."
]
},
{
"cell_type": "markdown",
"id": "valuable-bearing",
"metadata": {},
"source": [
"Celem powyższego ćwiczenia jest pozyskanie danych testowych. Dalsze analizy będziemy prowadzili już bez key loggera, starając się korzystać jedynie z danych zapisanych w pliku. Oczywiście, jeśli zajdzie taka konieczność, można w każdej chwili wygenerować sobie nowy plik."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "983ebbed",
"metadata": {},
"outputs": [],
"source": [
"import keyboard\n",
"from datetime import datetime\n",
"\n",
"# Ścieżka do pliku, w którym będą zapisywane dane\n",
"log_file = \"keylog.txt\"\n",
"\n",
"def report_key(event):\n",
" with open(log_file, \"a\") as f:\n",
" # Pobieramy aktualny czas\n",
" timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')\n",
" # Zapisujemy czas i wciśnięty klawisz do pliku\n",
" f.write(f\"{timestamp} - {event.name}\\n\")\n",
"\n",
"# Ustawienie callbacka dla zdarzeń klawiatury\n",
"keyboard.on_release(callback=report_key)\n",
"\n",
"# Czekanie na zdarzenia klawiatury\n",
"keyboard.wait()\n"
]
},
{
"cell_type": "markdown",
"id": "boxed-maple",
"metadata": {},
"source": [
"### Ćwiczenie 2: Napisz program, który wyliczy średnią prędkość pisania. Wykryj, kiedy użytkownik zaczął pisać. Nie bierz pod uwagę przerw dłuższych niż 5 sekund. Podaj prędkość pisania w znakach na minutę oraz słowach na minutę."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "possible-holder",
"metadata": {},
"outputs": [],
"source": [
"from datetime import timedelta\n",
"\n",
"key_events = []\n",
"\n",
"def report_key(event):\n",
" # Pobieramy aktualny czas\n",
" timestamp = datetime.now()\n",
" # Zapisujemy czas i wciśnięty klawisz do listy\n",
" key_events.append((timestamp, event.name))\n",
"\n",
"# Ustawienie callbacka dla zdarzeń klawiatury\n",
"keyboard.on_release(callback=report_key)\n",
"\n",
"def calculate_typing_speed():\n",
" if not key_events:\n",
" return \"No key events recorded.\"\n",
"\n",
" total_time = timedelta()\n",
" total_chars = 0\n",
" total_words = 0\n",
" prev_time = key_events[0][0]\n",
"\n",
" for i in range(1, len(key_events)):\n",
" current_time = key_events[i][0]\n",
" key = key_events[i][1]\n",
"\n",
" # Obliczamy czas między kolejnymi naciśnięciami klawiszy\n",
" time_diff = current_time - prev_time\n",
"\n",
" # Jeśli różnica czasu jest mniejsza niż 5 sekund, dodajemy do całkowitego czasu\n",
" if time_diff <= timedelta(seconds=5):\n",
" total_time += time_diff\n",
" total_chars += 1\n",
" if key == \"space\":\n",
" total_words += 1\n",
"\n",
" prev_time = current_time\n",
"\n",
" # Dodajemy ostatnie słowo (bo nie zawsze kończy się spacją)\n",
" total_words += 1\n",
"\n",
" # Obliczamy prędkość pisania\n",
" total_minutes = total_time.total_seconds() / 60\n",
" chars_per_minute = total_chars / total_minutes if total_minutes > 0 else 0\n",
" words_per_minute = total_words / total_minutes if total_minutes > 0 else 0\n",
"\n",
" return f\"Typing Speed: {chars_per_minute:.2f} chars/min, {words_per_minute:.2f} words/min\"\n",
"\n",
"# Uruchomienie keyloggera i czekanie na zdarzenia klawiatury\n",
"keyboard.wait()\n",
"\n",
"# Po zakończeniu pisania, wyliczamy prędkość pisania\n",
"print(calculate_typing_speed())"
]
},
{
"cell_type": "markdown",
"id": "ceramic-birth",
"metadata": {},
"source": [
"Wróćmy teraz do procesu tłumaczenia. Analiza uderzeń klawiszy wykonanych podczas tłumaczenia pozwala wykryć dłuższe pauzy. Pauzy te najczęściej wskazują miejsca, w których tłumacz musi się głębiej zastanowić nad tłumaczeniem danego słowa lub frazy. Przerwę tę wykorzystuje na przykład na sprawdzenie tłumaczenia lub definicji w słowniku, przeglądanie wyników z pamięci tłumaczeń lub korzystanie z innych pomocy (eye-tracking mógłby w tym przypadku rozstrzygnąć, czym w istocie zajmuje się w tym momencie tłuamcz). Jest też możliwe, że tłumacz poświęca pauzę na tzw. cognitive pause-and-unload - rodzaj zamyślenia, pozwalający oczyścić myśli. Z punktu widzenia projektowania systemu wspomagającego tłumaczenie niezwykle istotna jest informacja, nad czym tłumacz musi się dłużej zastanowić. Minimalizacja liczby i czasu trwania takich przerw jest szansą na usprawnienie procesu tłumaczenia."
]
},
{
"cell_type": "markdown",
"id": "great-cable",
"metadata": {},
"source": [
"### Ćwiczenie 3: Napisz program do wykrywania przerw w pisaniu. Raportuj długość oraz miejsce wystąpienia przerwy podając 20-znakowy kontekst z każdej strony. Wykryj każdą przerwę dłuższą niż 3 sekundy, posortuj wyniki malejąco po długości przerwy."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "close-riverside",
"metadata": {},
"outputs": [],
"source": [
"key_events = []\n",
"\n",
"def report_key(event):\n",
" # Pobieramy aktualny czas\n",
" timestamp = datetime.now()\n",
" # Zapisujemy czas i wciśnięty klawisz do listy\n",
" key_events.append((timestamp, event.name))\n",
"\n",
"# Ustawienie callbacka dla zdarzeń klawiatury\n",
"keyboard.on_release(callback=report_key)\n",
"\n",
"def find_pauses():\n",
" if not key_events:\n",
" return \"No key events recorded.\"\n",
"\n",
" pauses = []\n",
" prev_time = key_events[0][0]\n",
" full_text = ''.join([key[1] if key[1] != \"space\" else \" \" for key in key_events])\n",
"\n",
" for i in range(1, len(key_events)):\n",
" current_time = key_events[i][0]\n",
" key = key_events[i][1]\n",
"\n",
" # Obliczamy czas między kolejnymi naciśnięciami klawiszy\n",
" time_diff = current_time - prev_time\n",
"\n",
" # Jeśli różnica czasu jest większa niż 3 sekundy, zapisujemy przerwę\n",
" if time_diff > timedelta(seconds=3):\n",
" start_idx = max(0, i - 21)\n",
" end_idx = min(len(full_text), i + 20)\n",
" context = full_text[start_idx:end_idx]\n",
" pauses.append((time_diff.total_seconds(), context))\n",
"\n",
" prev_time = current_time\n",
"\n",
" # Sortowanie przerw malejąco po długości\n",
" pauses.sort(reverse=True, key=lambda x: x[0])\n",
"\n",
" return pauses\n",
"\n",
"# Uruchomienie keyloggera i czekanie na zdarzenia klawiatury\n",
"keyboard.wait()\n",
"\n",
"# Po zakończeniu pisania, wykrywamy przerwy\n",
"pauses = find_pauses()\n",
"\n",
"# Wyświetlanie przerw\n",
"for pause in pauses:\n",
" length, context = pause\n",
" print(f\"Pause length: {length:.2f} seconds, Context: '{context}'\")\n"
]
}
],
"metadata": {
"author": "Rafał Jaworski",
"email": "rjawor@amu.edu.pl",
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"lang": "pl",
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.14"
},
"subtitle": "12. Key logging",
"title": "Komputerowe wspomaganie tłumaczenia",
"year": "2021"
},
"nbformat": 4,
"nbformat_minor": 5
}