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 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
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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,2 @@
1
+ from .cli import main
2
+ raise SystemExit(main())
@@ -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