forked from bfijalkowski/KWT-2024
Laboratorium #12
This commit is contained in:
parent
9e978980fd
commit
1ff5f22caf
211
lab/lab_12.ipynb
211
lab/lab_12.ipynb
@ -3,12 +3,14 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "virtual-accreditation",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "virtual-accreditation"
|
||||
},
|
||||
"source": [
|
||||
"![Logo 1](https://git.wmi.amu.edu.pl/AITech/Szablon/raw/branch/master/Logotyp_AITech1.jpg)\n",
|
||||
"<div class=\"alert alert-block alert-info\">\n",
|
||||
"<h1> Komputerowe wspomaganie tłumaczenia </h1>\n",
|
||||
"<h2> 12. <i>Key logging</i> [laboratoria]</h2> \n",
|
||||
"<h2> 12. <i>Key logging</i> [laboratoria]</h2>\n",
|
||||
"<h3>Rafał Jaworski (2021)</h3>\n",
|
||||
"</div>\n",
|
||||
"\n",
|
||||
@ -18,7 +20,9 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "featured-afghanistan",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "featured-afghanistan"
|
||||
},
|
||||
"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."
|
||||
]
|
||||
@ -26,7 +30,9 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "severe-protein",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "severe-protein"
|
||||
},
|
||||
"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."
|
||||
]
|
||||
@ -34,15 +40,19 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "constant-underground",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "constant-underground"
|
||||
},
|
||||
"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. "
|
||||
"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": {},
|
||||
"metadata": {
|
||||
"id": "analyzed-lodging"
|
||||
},
|
||||
"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."
|
||||
]
|
||||
@ -50,7 +60,9 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "incredible-stress",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "incredible-stress"
|
||||
},
|
||||
"source": [
|
||||
"Zapoznajmy się najpierw z programem typu key logger:"
|
||||
]
|
||||
@ -58,7 +70,9 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "arctic-horror",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "arctic-horror"
|
||||
},
|
||||
"source": [
|
||||
"`sudo pip3 install keyboard`"
|
||||
]
|
||||
@ -67,9 +81,13 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "broken-workstation",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "broken-workstation"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!pip install keyboard\n",
|
||||
"\n",
|
||||
"import keyboard\n",
|
||||
"\n",
|
||||
"\n",
|
||||
@ -83,7 +101,9 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "polish-census",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "polish-census"
|
||||
},
|
||||
"source": [
|
||||
"UWAGA! Aby uruchomić powyższy kod na Linuxie konieczne są uprawnienia administratora (pytanie poza konkursem - dlaczego?)"
|
||||
]
|
||||
@ -91,15 +111,40 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "incoming-hands",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "incoming-hands"
|
||||
},
|
||||
"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."
|
||||
"### Ć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.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"source": [
|
||||
"import keyboard\n",
|
||||
"import datetime\n",
|
||||
"\n",
|
||||
"# Funkcja zapisu do pliku\n",
|
||||
"def log_key(event):\n",
|
||||
" with open(\"key_log.txt\", \"a\") as log_file:\n",
|
||||
" log_file.write(f\"{datetime.datetime.now()}: {event.name}\\n\")\n",
|
||||
"\n",
|
||||
"keyboard.on_release(callback=log_key)\n",
|
||||
"keyboard.wait()"
|
||||
],
|
||||
"metadata": {
|
||||
"id": "CjzM4sFTGIfJ"
|
||||
},
|
||||
"id": "CjzM4sFTGIfJ",
|
||||
"execution_count": null,
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "valuable-bearing",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "valuable-bearing"
|
||||
},
|
||||
"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."
|
||||
]
|
||||
@ -107,26 +152,85 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "boxed-maple",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "boxed-maple"
|
||||
},
|
||||
"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": 1,
|
||||
"execution_count": null,
|
||||
"id": "possible-holder",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "possible-holder"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def calculate_typing_speed():\n",
|
||||
" return 0"
|
||||
"import datetime\n",
|
||||
"\n",
|
||||
"def read_log_file(file_path):\n",
|
||||
" with open(file_path, \"r\") as file:\n",
|
||||
" lines = file.readlines()\n",
|
||||
" return lines\n",
|
||||
"\n",
|
||||
"def parse_log_lines(lines):\n",
|
||||
" key_events = []\n",
|
||||
" for line in lines:\n",
|
||||
" timestamp_str, key = line.strip().split(\": \")\n",
|
||||
" timestamp = datetime.datetime.strptime(timestamp_str, \"%Y-%m-%d %H:%M:%S.%f\")\n",
|
||||
" key_events.append((timestamp, key))\n",
|
||||
" return key_events\n",
|
||||
"\n",
|
||||
"def calculate_typing_speed(key_events):\n",
|
||||
" total_chars = 0\n",
|
||||
" total_words = 0\n",
|
||||
" start_time = None\n",
|
||||
" end_time = None\n",
|
||||
" last_time = None\n",
|
||||
" time_intervals = []\n",
|
||||
"\n",
|
||||
" for timestamp, key in key_events:\n",
|
||||
" if start_time is None:\n",
|
||||
" start_time = timestamp\n",
|
||||
" if last_time is not None and (timestamp - last_time).total_seconds() > 5:\n",
|
||||
" start_time = timestamp\n",
|
||||
" last_time = timestamp\n",
|
||||
" total_chars += 1\n",
|
||||
" if key == 'space':\n",
|
||||
" total_words += 1\n",
|
||||
" end_time = timestamp\n",
|
||||
"\n",
|
||||
" if total_chars == 0:\n",
|
||||
" return 0, 0 # No typing detected\n",
|
||||
"\n",
|
||||
" total_time = (end_time - start_time).total_seconds() / 60 # in minutes\n",
|
||||
" cpm = total_chars / total_time\n",
|
||||
" wpm = total_words / total_time\n",
|
||||
"\n",
|
||||
" return cpm, wpm\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def calculate_typing_speed(file_name):\n",
|
||||
" log_lines = read_log_file(file_name)\n",
|
||||
" key_events = parse_log_lines(log_lines)\n",
|
||||
" cpm, wpm = calculate_typing_speed(key_events)\n",
|
||||
"\n",
|
||||
" print(f\"Średnia prędkość pisania: {cpm:.2f} znaków na minutę\")\n",
|
||||
" print(f\"Średnia prędkość pisania: {wpm:.2f} słów na minutę\")\n",
|
||||
" return cpm, wpm\n",
|
||||
"\n",
|
||||
"calculate_typing_speed(\"key_log.txt\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ceramic-birth",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "ceramic-birth"
|
||||
},
|
||||
"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."
|
||||
]
|
||||
@ -134,20 +238,72 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "great-cable",
|
||||
"metadata": {},
|
||||
"metadata": {
|
||||
"id": "great-cable"
|
||||
},
|
||||
"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": 2,
|
||||
"execution_count": 4,
|
||||
"id": "close-riverside",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"base_uri": "https://localhost:8080/",
|
||||
"height": 305
|
||||
},
|
||||
"id": "close-riverside",
|
||||
"outputId": "f26a611b-3cba-4e96-f922-8af210c7bf45"
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"output_type": "error",
|
||||
"ename": "NameError",
|
||||
"evalue": "name 'read_log_file' is not defined",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
|
||||
"\u001b[0;32m<ipython-input-4-edc7383ffb03>\u001b[0m in \u001b[0;36m<cell line: 32>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 30\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 31\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 32\u001b[0;31m \u001b[0mreport_pauses\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfind_pauses\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"key_log.txt\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
|
||||
"\u001b[0;32m<ipython-input-4-edc7383ffb03>\u001b[0m in \u001b[0;36mfind_pauses\u001b[0;34m(file_name, min_pause_duration)\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mfind_pauses\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfile_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmin_pause_duration\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m3\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mlog_lines\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mread_log_file\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfile_name\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mkey_events\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mparse_log_lines\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlog_lines\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0mpauses\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfind_pauses\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkey_events\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mprevious_time\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
|
||||
"\u001b[0;31mNameError\u001b[0m: name 'read_log_file' is not defined"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"def find_pauses():\n",
|
||||
" return []"
|
||||
"def find_pauses(file_name, min_pause_duration=3):\n",
|
||||
" log_lines = read_log_file(file_name)\n",
|
||||
" key_events = parse_log_lines(log_lines)\n",
|
||||
" pauses = find_pauses(key_events)\n",
|
||||
" previous_time = None\n",
|
||||
" context = []\n",
|
||||
"\n",
|
||||
" for i, (timestamp, key) in enumerate(key_events):\n",
|
||||
" if previous_time:\n",
|
||||
" pause_duration = (timestamp - previous_time).total_seconds()\n",
|
||||
" if pause_duration > min_pause_duration:\n",
|
||||
" start_context = max(0, i-20)\n",
|
||||
" end_context = min(len(key_events), i+20)\n",
|
||||
" context_before = ''.join(key[1] for key in key_events[start_context:i])\n",
|
||||
" context_after = ''.join(key[1] for key in key_events[i:end_context])\n",
|
||||
" pauses.append((pause_duration, context_before, context_after))\n",
|
||||
" previous_time = timestamp\n",
|
||||
" context.append(key)\n",
|
||||
"\n",
|
||||
" pauses.sort(reverse=True, key=lambda x: x[0])\n",
|
||||
" return pauses\n",
|
||||
"\n",
|
||||
"def report_pauses(pauses):\n",
|
||||
" for pause in pauses:\n",
|
||||
" duration, context_before, context_after = pause\n",
|
||||
" print(f\"Przerwa: {duration:.2f} sekund\")\n",
|
||||
" print(f\"Kontekst przed: {context_before[-20:]}\")\n",
|
||||
" print(f\"Kontekst po: {context_after[:20]}\")\n",
|
||||
" print(\"-\" * 40)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"report_pauses(find_pauses(\"key_log.txt\"))\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
@ -174,6 +330,9 @@
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.8.10"
|
||||
},
|
||||
"colab": {
|
||||
"provenance": []
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
Loading…
Reference in New Issue
Block a user