forked from bfijalkowski/KWT-2024
new code
This commit is contained in:
parent
018f891442
commit
0c0ef36a89
224
lab/lab_12.ipynb
224
lab/lab_12.ipynb
@ -65,19 +65,34 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 6,
|
||||||
"id": "broken-workstation",
|
"id": "broken-workstation",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"ename": "KeyboardInterrupt",
|
||||||
|
"evalue": "",
|
||||||
|
"output_type": "error",
|
||||||
|
"traceback": [
|
||||||
|
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||||
|
"\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
|
||||||
|
"Cell \u001b[0;32mIn[6], line 10\u001b[0m\n\u001b[1;32m 6\u001b[0m f\u001b[38;5;241m.\u001b[39mwrite(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mdatetime\u001b[38;5;241m.\u001b[39mnow()\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m - \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mkey\u001b[38;5;241m.\u001b[39mname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 8\u001b[0m keyboard\u001b[38;5;241m.\u001b[39mon_press(log_key)\n\u001b[0;32m---> 10\u001b[0m \u001b[43mkeyboard\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mwait\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
|
||||||
|
"File \u001b[0;32m/opt/homebrew/lib/python3.11/site-packages/keyboard/__init__.py:886\u001b[0m, in \u001b[0;36mwait\u001b[0;34m(hotkey, suppress, trigger_on_release)\u001b[0m\n\u001b[1;32m 884\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 885\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m \u001b[38;5;28;01mTrue\u001b[39;00m:\n\u001b[0;32m--> 886\u001b[0m \u001b[43m_time\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msleep\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1e6\u001b[39;49m\u001b[43m)\u001b[49m\n",
|
||||||
|
"\u001b[0;31mKeyboardInterrupt\u001b[0m: "
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"import keyboard\n",
|
"import keyboard\n",
|
||||||
|
"from datetime import datetime\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"def log_key(key):\n",
|
||||||
|
" with open(\"txt/keylog.txt\", \"a\") as f:\n",
|
||||||
|
" f.write(f\"{datetime.now()} - {key.name}\\n\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"def report_key(event):\n",
|
"keyboard.on_press(log_key)\n",
|
||||||
" print(event)\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
"keyboard.on_release(callback=report_key)\n",
|
"keyboard.wait()\n"
|
||||||
"keyboard.wait()"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -96,6 +111,36 @@
|
|||||||
"### Ć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."
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 5,
|
||||||
|
"id": "e1fd5d69",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"<function keyboard.hook.<locals>.remove_()>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 5,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"import keyboard\n",
|
||||||
|
"import time\n",
|
||||||
|
"\n",
|
||||||
|
"def log_keystroke(event):\n",
|
||||||
|
" with open(\"txt/keystroke_log.txt\", \"a\") as log_file:\n",
|
||||||
|
" log_time = time.strftime(\"%Y-%m-%d %H:%M:%S\", time.localtime())\n",
|
||||||
|
" log_file.write(f\"{log_time} - {event.name}\\n\")\n",
|
||||||
|
"\n",
|
||||||
|
"keyboard.on_press(log_keystroke)\n",
|
||||||
|
"keyboard.wait()"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "valuable-bearing",
|
"id": "valuable-bearing",
|
||||||
@ -117,12 +162,76 @@
|
|||||||
"execution_count": 1,
|
"execution_count": 1,
|
||||||
"id": "possible-holder",
|
"id": "possible-holder",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
"source": [
|
{
|
||||||
"def calculate_typing_speed():\n",
|
"name": "stderr",
|
||||||
" return 0"
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"This process is not trusted! Input event monitoring will not be possible until it is added to accessibility clients.\n"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Rozpocznij pisanie. Naciśnij 'esc' aby zakończyć i zobaczyć wyniki.\n",
|
||||||
|
"Średnia prędkość pisania: 733.09 znaków na minutę\n",
|
||||||
|
"Średnia prędkość pisania: 146.62 słów na minutę\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from pynput import keyboard\n",
|
||||||
|
"from datetime import datetime, timedelta\n",
|
||||||
|
"\n",
|
||||||
|
"key_times = []\n",
|
||||||
|
"last_time = None\n",
|
||||||
|
"\n",
|
||||||
|
"def on_press(key):\n",
|
||||||
|
" global last_time\n",
|
||||||
|
" current_time = datetime.now()\n",
|
||||||
|
" \n",
|
||||||
|
" if last_time and (current_time - last_time) > timedelta(seconds=5):\n",
|
||||||
|
" key_times.clear()\n",
|
||||||
|
" \n",
|
||||||
|
" key_times.append(current_time)\n",
|
||||||
|
" last_time = current_time\n",
|
||||||
|
"\n",
|
||||||
|
"def calculate_speed():\n",
|
||||||
|
" if len(key_times) < 2:\n",
|
||||||
|
" print(\"Zbyt mało danych do obliczenia prędkości pisania.\")\n",
|
||||||
|
" return\n",
|
||||||
|
" \n",
|
||||||
|
" total_time = (key_times[-1] - key_times[0]).total_seconds() / 60\n",
|
||||||
|
" \n",
|
||||||
|
" num_chars = len(key_times)\n",
|
||||||
|
" num_words = num_chars / 5\n",
|
||||||
|
" \n",
|
||||||
|
" chars_per_minute = num_chars / total_time\n",
|
||||||
|
" words_per_minute = num_words / total_time\n",
|
||||||
|
" \n",
|
||||||
|
" print(f\"Średnia prędkość pisania: {chars_per_minute:.2f} znaków na minutę\")\n",
|
||||||
|
" print(f\"Średnia prędkość pisania: {words_per_minute:.2f} słów na minutę\")\n",
|
||||||
|
"\n",
|
||||||
|
"def on_release(key):\n",
|
||||||
|
" if key == keyboard.Key.esc:\n",
|
||||||
|
" calculate_speed()\n",
|
||||||
|
" return False\n",
|
||||||
|
"\n",
|
||||||
|
"with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:\n",
|
||||||
|
" print(\"Rozpocznij pisanie. Naciśnij 'esc' aby zakończyć i zobaczyć wyniki.\")\n",
|
||||||
|
" listener.join()\n",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "03ba2685",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "ceramic-birth",
|
"id": "ceramic-birth",
|
||||||
@ -141,28 +250,100 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 2,
|
"execution_count": 7,
|
||||||
"id": "close-riverside",
|
"id": "close-riverside",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stderr",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"This process is not trusted! Input event monitoring will not be possible until it is added to accessibility clients.\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"Rozpocznij pisanie. Naciśnij 'esc' aby zakończyć i zobaczyć wyniki.\n",
|
||||||
|
"Przerwa: 3.82 sekund\n",
|
||||||
|
"Kontekst przed przerwą: ...fsdjfdnsjfjdnfjdfnj[Key.esc]\n",
|
||||||
|
"Kontekst po przerwie: ...\n",
|
||||||
|
"\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"def find_pauses():\n",
|
"from pynput import keyboard\n",
|
||||||
" return []"
|
"from datetime import datetime, timedelta\n",
|
||||||
|
"\n",
|
||||||
|
"key_times = []\n",
|
||||||
|
"key_text = []\n",
|
||||||
|
"last_time = None\n",
|
||||||
|
"breaks = []\n",
|
||||||
|
"\n",
|
||||||
|
"def key_to_string(key):\n",
|
||||||
|
" try:\n",
|
||||||
|
" return key.char\n",
|
||||||
|
" except AttributeError:\n",
|
||||||
|
" if key == keyboard.Key.space:\n",
|
||||||
|
" return ' '\n",
|
||||||
|
" return f'[{str(key)}]'\n",
|
||||||
|
"\n",
|
||||||
|
"def on_press(key):\n",
|
||||||
|
" global last_time\n",
|
||||||
|
" current_time = datetime.now()\n",
|
||||||
|
" \n",
|
||||||
|
" key_times.append(current_time)\n",
|
||||||
|
" key_text.append(key_to_string(key))\n",
|
||||||
|
" \n",
|
||||||
|
" if last_time and (current_time - last_time) > timedelta(seconds=3):\n",
|
||||||
|
" context_start = max(0, len(key_text) - 20)\n",
|
||||||
|
" context_end = len(key_text)\n",
|
||||||
|
" context_before = ''.join(key_text[context_start:context_end])\n",
|
||||||
|
" \n",
|
||||||
|
" context_start = len(key_text)\n",
|
||||||
|
" context_end = min(len(key_text) + 20, len(key_text))\n",
|
||||||
|
" context_after = ''.join(key_text[context_start:context_end])\n",
|
||||||
|
" \n",
|
||||||
|
" breaks.append((current_time - last_time, context_before, context_after))\n",
|
||||||
|
" \n",
|
||||||
|
" last_time = current_time\n",
|
||||||
|
"\n",
|
||||||
|
"def report_breaks():\n",
|
||||||
|
" if not breaks:\n",
|
||||||
|
" print(\"Nie wykryto przerw dłuższych niż 3 sekundy.\")\n",
|
||||||
|
" return\n",
|
||||||
|
" \n",
|
||||||
|
" breaks.sort(reverse=True, key=lambda x: x[0])\n",
|
||||||
|
" \n",
|
||||||
|
" for duration, context_before, context_after in breaks:\n",
|
||||||
|
" print(f\"Przerwa: {duration.total_seconds():.2f} sekund\")\n",
|
||||||
|
" print(f\"Kontekst przed przerwą: ...{context_before}\")\n",
|
||||||
|
" print(f\"Kontekst po przerwie: {context_after}...\")\n",
|
||||||
|
" print()\n",
|
||||||
|
"\n",
|
||||||
|
"def on_release(key):\n",
|
||||||
|
" if key == keyboard.Key.esc:\n",
|
||||||
|
" report_breaks()\n",
|
||||||
|
" return False\n",
|
||||||
|
"\n",
|
||||||
|
"with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:\n",
|
||||||
|
" print(\"Rozpocznij pisanie. Naciśnij 'esc' aby zakończyć i zobaczyć wyniki.\")\n",
|
||||||
|
" listener.join()\n",
|
||||||
|
"\n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"author": "Rafał Jaworski",
|
"author": "Rafał Jaworski",
|
||||||
"email": "rjawor@amu.edu.pl",
|
"email": "rjawor@amu.edu.pl",
|
||||||
"lang": "pl",
|
|
||||||
"subtitle": "12. Key logging",
|
|
||||||
"title": "Komputerowe wspomaganie tłumaczenia",
|
|
||||||
"year": "2021",
|
|
||||||
"kernelspec": {
|
"kernelspec": {
|
||||||
"display_name": "Python 3",
|
"display_name": "Python 3",
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"name": "python3"
|
"name": "python3"
|
||||||
},
|
},
|
||||||
|
"lang": "pl",
|
||||||
"language_info": {
|
"language_info": {
|
||||||
"codemirror_mode": {
|
"codemirror_mode": {
|
||||||
"name": "ipython",
|
"name": "ipython",
|
||||||
@ -173,8 +354,11 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.8.10"
|
"version": "3.11.9"
|
||||||
}
|
},
|
||||||
|
"subtitle": "12. Key logging",
|
||||||
|
"title": "Komputerowe wspomaganie tłumaczenia",
|
||||||
|
"year": "2021"
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
"nbformat_minor": 5
|
"nbformat_minor": 5
|
||||||
|
@ -44,7 +44,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 3,
|
"execution_count": null,
|
||||||
"id": "familiar-terrace",
|
"id": "familiar-terrace",
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"scrolled": true
|
"scrolled": true
|
||||||
@ -91,6 +91,14 @@
|
|||||||
" print(line.rstrip())"
|
" print(line.rstrip())"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": null,
|
||||||
|
"id": "bee601d9",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"id": "dominant-insurance",
|
"id": "dominant-insurance",
|
||||||
@ -120,13 +128,41 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": 11,
|
||||||
"id": "economic-southeast",
|
"id": "economic-southeast",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"[('auto', 1)]\n",
|
||||||
|
"[('adam', 1), ('jest', 1), ('fajny', 1)]\n",
|
||||||
|
"[('brak', 1), ('słów', 1)]\n",
|
||||||
|
"[('chrzaszcz', 0)]\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"def correct_text(text):\n",
|
"from zipfile import ZipFile\n",
|
||||||
" return []"
|
"\n",
|
||||||
|
"dictionary = set\n",
|
||||||
|
"\n",
|
||||||
|
"with ZipFile('data/hunspell_pl.zip') as zip_f:\n",
|
||||||
|
" with zip_f.open('hunspell_pl.txt') as f:\n",
|
||||||
|
" dictionary = set([line.strip().lower() for line in f.read().decode('utf-8').splitlines()])\n",
|
||||||
|
"\n",
|
||||||
|
"def correct_text(phrase):\n",
|
||||||
|
" return [(word, 1) if word in dictionary\n",
|
||||||
|
" else (word, 0)\n",
|
||||||
|
" for word in phrase.lower().split()]\n",
|
||||||
|
"\n",
|
||||||
|
"# 0 - źle\n",
|
||||||
|
"# 1 - dobrze\n",
|
||||||
|
"print(correct_text('Auto'))\n",
|
||||||
|
"print(correct_text('Adam jest fajny'))\n",
|
||||||
|
"print(correct_text('Brak słów'))\n",
|
||||||
|
"print(correct_text('Chrzaszcz'))"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -168,13 +204,34 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 1,
|
"execution_count": 5,
|
||||||
"id": "built-sally",
|
"id": "built-sally",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"def L1(w):\n",
|
"import numpy as np\n",
|
||||||
" return []"
|
"\n",
|
||||||
|
"def levenshtein_distance(a, b):\n",
|
||||||
|
" n, m = len(a), len(b)\n",
|
||||||
|
" if n > m:\n",
|
||||||
|
" a, b = b, a\n",
|
||||||
|
" n, m = m, n\n",
|
||||||
|
"\n",
|
||||||
|
" current_row = np.arange(n + 1)\n",
|
||||||
|
" for i in range(1, m + 1):\n",
|
||||||
|
" previous_row, current_row = current_row, np.zeros(n + 1, dtype=int)\n",
|
||||||
|
" current_row[0] = i\n",
|
||||||
|
" for j in range(1, n + 1):\n",
|
||||||
|
" insertions = previous_row[j] + 1\n",
|
||||||
|
" deletions = current_row[j - 1] + 1\n",
|
||||||
|
" substitutions = previous_row[j - 1] + (a[j - 1] != b[i - 1])\n",
|
||||||
|
" current_row[j] = min(insertions, deletions, substitutions)\n",
|
||||||
|
"\n",
|
||||||
|
" return current_row[n]\n",
|
||||||
|
"\n",
|
||||||
|
"\n",
|
||||||
|
"def L1(word, dictionary, max_distance=1):\n",
|
||||||
|
" return [dict_word for dict_word in dictionary if levenshtein_distance(word, dict_word) <= max_distance]"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -187,13 +244,45 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": 2,
|
"execution_count": 6,
|
||||||
"id": "coordinated-cooperation",
|
"id": "coordinated-cooperation",
|
||||||
"metadata": {},
|
"metadata": {},
|
||||||
"outputs": [],
|
"outputs": [],
|
||||||
"source": [
|
"source": [
|
||||||
"def generate_suggestions(w):\n",
|
"def generate_suggestions(word, dictionary):\n",
|
||||||
" return []"
|
" return L1(word, dictionary)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 8,
|
||||||
|
"id": "0c7843bb",
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "stdout",
|
||||||
|
"output_type": "stream",
|
||||||
|
"text": [
|
||||||
|
"['ato', 'alto', 'huto', 'luto', 'autko', 'autor', 'auto', 'aut']\n",
|
||||||
|
"['adams', 'adm', 'dam', 'ada', 'edam', 'adad', 'adym', 'asam', 'adaś', 'adat', 'aram', 'adam']\n",
|
||||||
|
"['fajny', 'fajno', 'tajny', 'farny']\n",
|
||||||
|
"[]\n"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"from zipfile import ZipFile\n",
|
||||||
|
"\n",
|
||||||
|
"dictionary = set\n",
|
||||||
|
"\n",
|
||||||
|
"with ZipFile('data/hunspell_pl.zip') as zip_f:\n",
|
||||||
|
" with zip_f.open('hunspell_pl.txt') as f:\n",
|
||||||
|
" dictionary = set([line.strip().lower() for line in f.read().decode('utf-8').splitlines()])\n",
|
||||||
|
" \n",
|
||||||
|
"print(generate_suggestions('auto', dictionary))\n",
|
||||||
|
"print(generate_suggestions('adam', dictionary))\n",
|
||||||
|
"print(generate_suggestions('fajny', dictionary))\n",
|
||||||
|
"print(generate_suggestions('chrzazszcz', dictionary))"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -216,7 +305,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.8.10"
|
"version": "3.11.9"
|
||||||
},
|
},
|
||||||
"subtitle": "13,14. Korekta pisowni",
|
"subtitle": "13,14. Korekta pisowni",
|
||||||
"title": "Komputerowe wspomaganie tłumaczenia",
|
"title": "Komputerowe wspomaganie tłumaczenia",
|
||||||
|
Loading…
Reference in New Issue
Block a user