thuk 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thuk-1.0.1/PKG-INFO +100 -0
- thuk-1.0.1/README.md +89 -0
- thuk-1.0.1/pyproject.toml +23 -0
- thuk-1.0.1/setup.cfg +4 -0
- thuk-1.0.1/src/thuk/__init__.py +4 -0
- thuk-1.0.1/src/thuk/__main__.py +2 -0
- thuk-1.0.1/src/thuk/cli.py +152 -0
- thuk-1.0.1/src/thuk/data/contents.json +330 -0
- thuk-1.0.1/src/thuk/data/django_code.pdf +0 -0
- thuk-1.0.1/src/thuk/data/django_code.txt +847 -0
- thuk-1.0.1/src/thuk/data/docker_commands.pdf +0 -0
- thuk-1.0.1/src/thuk/data/docker_commands.txt +660 -0
- thuk-1.0.1/src/thuk/data/git_commands.txt +65 -0
- thuk-1.0.1/src/thuk/data/unit2_theory.pdf +0 -0
- thuk-1.0.1/src/thuk/data/unit2_theory.txt +1161 -0
- thuk-1.0.1/src/thuk/data/unit5_theory.pdf +0 -0
- thuk-1.0.1/src/thuk/data/unit5_theory.txt +691 -0
- thuk-1.0.1/src/thuk/reference.py +91 -0
- thuk-1.0.1/src/thuk.egg-info/PKG-INFO +100 -0
- thuk-1.0.1/src/thuk.egg-info/SOURCES.txt +22 -0
- thuk-1.0.1/src/thuk.egg-info/dependency_links.txt +1 -0
- thuk-1.0.1/src/thuk.egg-info/entry_points.txt +2 -0
- thuk-1.0.1/src/thuk.egg-info/top_level.txt +1 -0
- thuk-1.0.1/tests/test_thuk.py +103 -0
thuk-1.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: thuk
|
|
3
|
+
Version: 1.0.1
|
|
4
|
+
Summary: Offline terminal reference for Unit 2, Unit 5, Django, Docker and Git
|
|
5
|
+
Project-URL: Repository, https://github.com/omparekh54-lgtm/thuk
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Classifier: Topic :: Education
|
|
9
|
+
Requires-Python: >=3.9
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# Thuk
|
|
13
|
+
|
|
14
|
+
An offline Python study library with a nested terminal browser for five documents:
|
|
15
|
+
|
|
16
|
+
1. Unit 2 Theory
|
|
17
|
+
2. Unit 5 Theory
|
|
18
|
+
3. Django Code (complete Unit 2 coding reference)
|
|
19
|
+
4. Docker Commands (complete Unit 5 coding reference)
|
|
20
|
+
5. Git Commands (supplementary cheat sheet)
|
|
21
|
+
|
|
22
|
+
The full supplied text and all four original PDFs are bundled. No runtime dependencies, API keys or internet access are needed after installation. Code and commands in references are displayed for reading, never executed.
|
|
23
|
+
|
|
24
|
+
## Install and run
|
|
25
|
+
|
|
26
|
+
Python 3.9 or newer and Git:
|
|
27
|
+
|
|
28
|
+
```powershell
|
|
29
|
+
python -m pip install git+https://github.com/omparekh54-lgtm/thuk.git
|
|
30
|
+
python -m thuk
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Install while your virtual environment is active if you want to use Thuk inside it. The `thuk` command also works when your Python Scripts directory is on PATH (normally automatic in an active virtual environment).
|
|
34
|
+
|
|
35
|
+
Without Git, download the repository ZIP, extract it, open a terminal inside the extracted folder, and run `python -m pip install .`.
|
|
36
|
+
|
|
37
|
+
Not published to PyPI yet.
|
|
38
|
+
|
|
39
|
+
## Browse
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
THUK
|
|
43
|
+
==========================================
|
|
44
|
+
1. Unit 2 Theory
|
|
45
|
+
2. Unit 5 Theory
|
|
46
|
+
3. Django Code
|
|
47
|
+
4. Docker Commands
|
|
48
|
+
5. Git Commands
|
|
49
|
+
0. Exit
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Choose a document to see its contents, with main numbered chapters only. Choose a chapter to read it, including all of its subsections. Subsections such as 2.1 and 2.2 are not separate menu options.
|
|
53
|
+
|
|
54
|
+
After reading, you stay in the same document's contents menu. In an interactive pager, press `q` to finish reading. Choose `0. Back to documents` to return to the five-document menu; `0. Exit` there closes Thuk. Ctrl+C or end-of-input exits cleanly.
|
|
55
|
+
|
|
56
|
+
A `Document introduction` entry preserves each document's preface and original contents page. PDF extraction can change table spacing; open the original PDF to see diagrams and exact formatting.
|
|
57
|
+
|
|
58
|
+
## Search within a document
|
|
59
|
+
|
|
60
|
+
Choose `S. Search this document`, then type a keyword or phrase such as `migrations` or `docker run`. Search is case-insensitive and treats the query literally.
|
|
61
|
+
|
|
62
|
+
Thuk lists matching sections with match counts and preview lines. Choose a result to open its section. Every matching keyword in that section is highlighted as `[[keyword]]`, which is readable on Windows, Linux and macOS. After reading a search result you return to that document's contents. `0` at search results returns without opening a result; a blank query cancels search.
|
|
63
|
+
|
|
64
|
+
Search maps each hit to its main chapter and opens that chapter with matching keywords highlighted. Contents-page hits appear under Document introduction.
|
|
65
|
+
|
|
66
|
+
## Other commands
|
|
67
|
+
|
|
68
|
+
```powershell
|
|
69
|
+
thuk 1 # Open Unit 2's contents menu
|
|
70
|
+
thuk 3 # Open the Django code contents menu
|
|
71
|
+
thuk 3 --topic 2 --plain # Print topic 2 and exit
|
|
72
|
+
thuk 1 --full --plain # Print the entire document
|
|
73
|
+
thuk 3 --search migrations # Interactive document search
|
|
74
|
+
thuk --search Docker # Print matches across all documents
|
|
75
|
+
thuk 1 --pdf # Open the original PDF in your desktop viewer
|
|
76
|
+
thuk 4 --export-pdf docker.pdf # Save the original PDF
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Use `python -m thuk` instead of `thuk` if the executable isn't on PATH. In piped/noninteractive CLI search, results are printed with topic numbers; open one using `--topic NUMBER`. PDF export replaces the destination file if it exists. Git is a text-only reference and has no source PDF.
|
|
80
|
+
|
|
81
|
+
## Python API
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from thuk import get_text, list_topics, get_topic, search_topics
|
|
85
|
+
|
|
86
|
+
print(list_topics(1))
|
|
87
|
+
print(get_topic(1, 2))
|
|
88
|
+
print(search_topics('Django', 1))
|
|
89
|
+
print(get_text(1))
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Development
|
|
93
|
+
|
|
94
|
+
```sh
|
|
95
|
+
python -m pip install .
|
|
96
|
+
python -m unittest discover -s tests -v
|
|
97
|
+
python -m pip wheel --no-deps . -w dist
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The topic index is stored in `src/thuk/data/contents.json` using zero-based start and exclusive end line offsets in the bundled text. Update that index whenever reference text line boundaries change. The four original PDFs retain their source authorship.
|
thuk-1.0.1/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Thuk
|
|
2
|
+
|
|
3
|
+
An offline Python study library with a nested terminal browser for five documents:
|
|
4
|
+
|
|
5
|
+
1. Unit 2 Theory
|
|
6
|
+
2. Unit 5 Theory
|
|
7
|
+
3. Django Code (complete Unit 2 coding reference)
|
|
8
|
+
4. Docker Commands (complete Unit 5 coding reference)
|
|
9
|
+
5. Git Commands (supplementary cheat sheet)
|
|
10
|
+
|
|
11
|
+
The full supplied text and all four original PDFs are bundled. No runtime dependencies, API keys or internet access are needed after installation. Code and commands in references are displayed for reading, never executed.
|
|
12
|
+
|
|
13
|
+
## Install and run
|
|
14
|
+
|
|
15
|
+
Python 3.9 or newer and Git:
|
|
16
|
+
|
|
17
|
+
```powershell
|
|
18
|
+
python -m pip install git+https://github.com/omparekh54-lgtm/thuk.git
|
|
19
|
+
python -m thuk
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Install while your virtual environment is active if you want to use Thuk inside it. The `thuk` command also works when your Python Scripts directory is on PATH (normally automatic in an active virtual environment).
|
|
23
|
+
|
|
24
|
+
Without Git, download the repository ZIP, extract it, open a terminal inside the extracted folder, and run `python -m pip install .`.
|
|
25
|
+
|
|
26
|
+
Not published to PyPI yet.
|
|
27
|
+
|
|
28
|
+
## Browse
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
THUK
|
|
32
|
+
==========================================
|
|
33
|
+
1. Unit 2 Theory
|
|
34
|
+
2. Unit 5 Theory
|
|
35
|
+
3. Django Code
|
|
36
|
+
4. Docker Commands
|
|
37
|
+
5. Git Commands
|
|
38
|
+
0. Exit
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Choose a document to see its contents, with main numbered chapters only. Choose a chapter to read it, including all of its subsections. Subsections such as 2.1 and 2.2 are not separate menu options.
|
|
42
|
+
|
|
43
|
+
After reading, you stay in the same document's contents menu. In an interactive pager, press `q` to finish reading. Choose `0. Back to documents` to return to the five-document menu; `0. Exit` there closes Thuk. Ctrl+C or end-of-input exits cleanly.
|
|
44
|
+
|
|
45
|
+
A `Document introduction` entry preserves each document's preface and original contents page. PDF extraction can change table spacing; open the original PDF to see diagrams and exact formatting.
|
|
46
|
+
|
|
47
|
+
## Search within a document
|
|
48
|
+
|
|
49
|
+
Choose `S. Search this document`, then type a keyword or phrase such as `migrations` or `docker run`. Search is case-insensitive and treats the query literally.
|
|
50
|
+
|
|
51
|
+
Thuk lists matching sections with match counts and preview lines. Choose a result to open its section. Every matching keyword in that section is highlighted as `[[keyword]]`, which is readable on Windows, Linux and macOS. After reading a search result you return to that document's contents. `0` at search results returns without opening a result; a blank query cancels search.
|
|
52
|
+
|
|
53
|
+
Search maps each hit to its main chapter and opens that chapter with matching keywords highlighted. Contents-page hits appear under Document introduction.
|
|
54
|
+
|
|
55
|
+
## Other commands
|
|
56
|
+
|
|
57
|
+
```powershell
|
|
58
|
+
thuk 1 # Open Unit 2's contents menu
|
|
59
|
+
thuk 3 # Open the Django code contents menu
|
|
60
|
+
thuk 3 --topic 2 --plain # Print topic 2 and exit
|
|
61
|
+
thuk 1 --full --plain # Print the entire document
|
|
62
|
+
thuk 3 --search migrations # Interactive document search
|
|
63
|
+
thuk --search Docker # Print matches across all documents
|
|
64
|
+
thuk 1 --pdf # Open the original PDF in your desktop viewer
|
|
65
|
+
thuk 4 --export-pdf docker.pdf # Save the original PDF
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use `python -m thuk` instead of `thuk` if the executable isn't on PATH. In piped/noninteractive CLI search, results are printed with topic numbers; open one using `--topic NUMBER`. PDF export replaces the destination file if it exists. Git is a text-only reference and has no source PDF.
|
|
69
|
+
|
|
70
|
+
## Python API
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from thuk import get_text, list_topics, get_topic, search_topics
|
|
74
|
+
|
|
75
|
+
print(list_topics(1))
|
|
76
|
+
print(get_topic(1, 2))
|
|
77
|
+
print(search_topics('Django', 1))
|
|
78
|
+
print(get_text(1))
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Development
|
|
82
|
+
|
|
83
|
+
```sh
|
|
84
|
+
python -m pip install .
|
|
85
|
+
python -m unittest discover -s tests -v
|
|
86
|
+
python -m pip wheel --no-deps . -w dist
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The topic index is stored in `src/thuk/data/contents.json` using zero-based start and exclusive end line offsets in the bundled text. Update that index whenever reference text line boundaries change. The four original PDFs retain their source authorship.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "thuk"
|
|
7
|
+
version = "1.0.1"
|
|
8
|
+
description = "Offline terminal reference for Unit 2, Unit 5, Django, Docker and Git"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
classifiers = ["Programming Language :: Python :: 3", "Operating System :: OS Independent", "Topic :: Education"]
|
|
12
|
+
|
|
13
|
+
[project.urls]
|
|
14
|
+
Repository = "https://github.com/omparekh54-lgtm/thuk"
|
|
15
|
+
|
|
16
|
+
[project.scripts]
|
|
17
|
+
thuk = "thuk.cli:main"
|
|
18
|
+
|
|
19
|
+
[tool.setuptools.packages.find]
|
|
20
|
+
where = ["src"]
|
|
21
|
+
|
|
22
|
+
[tool.setuptools.package-data]
|
|
23
|
+
thuk = ["data/*.txt", "data/*.pdf", "data/*.json"]
|
thuk-1.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
"""Thuk: offline study documents with contents and keyword search."""
|
|
2
|
+
from .reference import SECTIONS, get_text, search, list_topics, get_topic, search_topics
|
|
3
|
+
__version__ = '1.0.1'
|
|
4
|
+
__all__ = ['SECTIONS', 'get_text', 'search', 'list_topics', 'get_topic', 'search_topics']
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""Nested document browser and keyword search."""
|
|
2
|
+
import argparse
|
|
3
|
+
import pydoc
|
|
4
|
+
import re
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
from . import __version__
|
|
8
|
+
from .reference import SECTIONS, get_text, get_topic, list_topics, search_topics, export_pdf, open_pdf
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def highlight(text, keyword, color=False):
|
|
12
|
+
if not keyword:
|
|
13
|
+
return text
|
|
14
|
+
pattern = re.compile(re.escape(keyword), re.IGNORECASE)
|
|
15
|
+
return pattern.sub(lambda m: ('\033[1;33m' + m[0] + '\033[0m') if color else '[[' + m[0] + ']]', text)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def display(section, topic=None, plain=False, keyword=None):
|
|
19
|
+
title = SECTIONS[section][0]
|
|
20
|
+
body = get_text(section) if topic is None else get_topic(section, topic)
|
|
21
|
+
if topic is not None:
|
|
22
|
+
title += ' / ' + list_topics(section)[topic - 1]['title']
|
|
23
|
+
# Markers remain visible even in Windows pagers that strip ANSI escapes.
|
|
24
|
+
text = title + '\n' + '=' * 60 + '\n\n' + highlight(body, keyword)
|
|
25
|
+
if keyword:
|
|
26
|
+
text = f'Search: {keyword} (matches marked with [[...]]).\n\n' + text
|
|
27
|
+
if plain or not sys.stdout.isatty():
|
|
28
|
+
print(text)
|
|
29
|
+
else:
|
|
30
|
+
pydoc.pager(text)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def ask(prompt):
|
|
34
|
+
return input(prompt).strip()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def search_menu(section, plain=False, query=None):
|
|
38
|
+
if query is None:
|
|
39
|
+
query = ask('Keyword (blank to cancel): ')
|
|
40
|
+
if not query.strip():
|
|
41
|
+
return
|
|
42
|
+
results = search_topics(query, section)
|
|
43
|
+
if not results:
|
|
44
|
+
print('No matches found in this document.')
|
|
45
|
+
return
|
|
46
|
+
while True:
|
|
47
|
+
print(f'\nSearch results in {SECTIONS[section][0]} for "{query}"')
|
|
48
|
+
for i, result in enumerate(results, 1):
|
|
49
|
+
print(f"{i}. {result['title']} ({len(result['matches'])} matching lines)")
|
|
50
|
+
for line, snippet in result['matches'][:2]:
|
|
51
|
+
print(f' Line {line}: {highlight(snippet, query)}')
|
|
52
|
+
print('0. Back to document contents')
|
|
53
|
+
choice = ask('Open result: ')
|
|
54
|
+
if choice == '0':
|
|
55
|
+
return
|
|
56
|
+
if choice.isdigit() and 1 <= int(choice) <= len(results):
|
|
57
|
+
display(section, results[int(choice) - 1]['topic'], plain, query)
|
|
58
|
+
return
|
|
59
|
+
else:
|
|
60
|
+
print('Choose a result number or 0.')
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def document_menu(section, plain=False):
|
|
64
|
+
topics = list_topics(section)
|
|
65
|
+
while True:
|
|
66
|
+
print('\n' + SECTIONS[section][0] + ' - Contents\n' + '=' * 60)
|
|
67
|
+
for number, item in enumerate(topics, 1):
|
|
68
|
+
indent = ' ' if item['level'] == 2 else ''
|
|
69
|
+
print(f"{number}. {indent}{item['title']}")
|
|
70
|
+
print('S. Search this document')
|
|
71
|
+
print('0. Back to documents')
|
|
72
|
+
choice = ask('Select a section or S to search: ').lower()
|
|
73
|
+
if choice == '0':
|
|
74
|
+
return
|
|
75
|
+
if choice == 's':
|
|
76
|
+
search_menu(section, plain)
|
|
77
|
+
elif choice.isdigit() and 1 <= int(choice) <= len(topics):
|
|
78
|
+
display(section, int(choice), plain)
|
|
79
|
+
else:
|
|
80
|
+
print('Choose a section number, S, or 0.')
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def menu(plain=False, pdf=False):
|
|
84
|
+
while True:
|
|
85
|
+
print('\nTHUK\n' + '=' * 42)
|
|
86
|
+
for number, (title, _) in SECTIONS.items():
|
|
87
|
+
print(f'{number}. {title}')
|
|
88
|
+
print('0. Exit\n' + '=' * 42)
|
|
89
|
+
choice = ask('Select a document (1-5): ')
|
|
90
|
+
if choice == '0':
|
|
91
|
+
return 0
|
|
92
|
+
if choice not in {str(n) for n in SECTIONS}:
|
|
93
|
+
print('Choose a document from 1 to 5, or 0 to exit.')
|
|
94
|
+
continue
|
|
95
|
+
if pdf:
|
|
96
|
+
try:
|
|
97
|
+
print(f'Opened: {open_pdf(int(choice))}')
|
|
98
|
+
except (ValueError, OSError, subprocess.CalledProcessError) as exc:
|
|
99
|
+
print(f'Cannot open PDF: {exc}')
|
|
100
|
+
else:
|
|
101
|
+
document_menu(int(choice), plain)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def main(argv=None):
|
|
105
|
+
parser = argparse.ArgumentParser(description='Thuk offline document browser')
|
|
106
|
+
parser.add_argument('section', nargs='?', type=int, choices=SECTIONS, help='document number (1-5)')
|
|
107
|
+
parser.add_argument('--topic', type=int, help='read one topic by its contents-menu number')
|
|
108
|
+
parser.add_argument('--version', action='version', version=f'Thuk {__version__}')
|
|
109
|
+
group = parser.add_mutually_exclusive_group()
|
|
110
|
+
group.add_argument('--pdf', action='store_true', help='open the original PDF')
|
|
111
|
+
group.add_argument('--export-pdf', metavar='PATH', help='save the original PDF')
|
|
112
|
+
group.add_argument('--search', metavar='TEXT', help='search the selected document, or all documents')
|
|
113
|
+
group.add_argument('--full', action='store_true', help='print the complete document')
|
|
114
|
+
parser.add_argument('--plain', action='store_true', help='disable the interactive pager')
|
|
115
|
+
args = parser.parse_args(argv)
|
|
116
|
+
if (args.topic is not None or args.export_pdf or args.full) and args.section is None:
|
|
117
|
+
parser.error('--topic, --export-pdf and --full require a document number')
|
|
118
|
+
if args.topic is not None and (args.pdf or args.export_pdf or args.full or args.search is not None):
|
|
119
|
+
parser.error('--topic cannot be combined with PDF, full-document or search options')
|
|
120
|
+
try:
|
|
121
|
+
if args.search is not None:
|
|
122
|
+
if not args.search.strip():
|
|
123
|
+
raise ValueError('Enter a non-empty search query.')
|
|
124
|
+
if args.section is not None and sys.stdin.isatty():
|
|
125
|
+
search_menu(args.section, args.plain, args.search)
|
|
126
|
+
document_menu(args.section, args.plain)
|
|
127
|
+
else:
|
|
128
|
+
found = False
|
|
129
|
+
for section in ([args.section] if args.section else SECTIONS):
|
|
130
|
+
for result in search_topics(args.search, section):
|
|
131
|
+
found = True
|
|
132
|
+
print(f"{SECTIONS[section][0]} / {result['title']} (topic {result['topic']})")
|
|
133
|
+
for line, snippet in result['matches']:
|
|
134
|
+
print(f' {line}: {highlight(snippet, args.search)}')
|
|
135
|
+
if not found:
|
|
136
|
+
print('No matches found.')
|
|
137
|
+
elif args.section is None:
|
|
138
|
+
return menu(args.plain, args.pdf)
|
|
139
|
+
elif args.export_pdf:
|
|
140
|
+
print(export_pdf(args.section, args.export_pdf))
|
|
141
|
+
elif args.pdf:
|
|
142
|
+
print(f'Opened: {open_pdf(args.section)}')
|
|
143
|
+
elif args.topic is not None or args.full:
|
|
144
|
+
display(args.section, args.topic, args.plain)
|
|
145
|
+
else:
|
|
146
|
+
document_menu(args.section, args.plain)
|
|
147
|
+
return 0
|
|
148
|
+
except (EOFError, KeyboardInterrupt, BrokenPipeError):
|
|
149
|
+
return 0
|
|
150
|
+
except (ValueError, OSError, subprocess.CalledProcessError) as exc:
|
|
151
|
+
print(f'Thuk: {exc}', file=sys.stderr)
|
|
152
|
+
return 1
|