myman 0.0.6__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.
myman-0.0.6/PKG-INFO ADDED
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: myman
3
+ Version: 0.0.6
4
+ Summary: Draw from Sequence of 'My Man' Posts by Kevin Kruse
5
+ Author-email: Dirk Eddelbuettel <edd@debian.org>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest>=7.0; extra == "test"
10
+
11
+
12
+ ## myman: Sequence of posts by Kevin Kruse starting with 'My man ...'
13
+
14
+ [![CI](https://github.com/eddelbuettel/myman-py/workflows/ci/badge.svg)](https://github.com/eddelbuettel/myman-py/actions?query=workflow%3Aci)
15
+ [![License](https://img.shields.io/badge/license-GPL%20%28%3E=%202%29-brightgreen.svg?style=flat)](https://opensource.org/license/gpl-2.0)
16
+ [![Last Commit](https://img.shields.io/github/last-commit/eddelbuettel/myman-py)](https://github.com/eddelbuettel/myman-py)
17
+
18
+ ### Motivation
19
+
20
+ Starting on the afternoon of July 17, 2026, and lasting for one initial week, Kevin Kruse fired off
21
+ an astonishing array of over six hundred ninety eight BlueSky replies to [an initial post of
22
+ his][postone] featuring a certain government figure. On August 12, 2026, a second wave started [with
23
+ this post][posttwo] aiming at another government figure. (There were also two stray posts from the
24
+ interim period.) This was followed on August 17, 2026, with another series starting with [this
25
+ post][postthree]. A fourth wave started on August 27, 2026, with [this post][postfour]. A fifth
26
+ wave started on August 30, 2026, with [this post][postfive]. A sixth wave started on September 4,
27
+ 2026 with [this post][postsix]. The total now stands at one thousand three hundred ninety four
28
+ posts.
29
+
30
+ All posts start with "My man ..." and make for excellent input to a `fortunes`-like package. So this
31
+ small package obliges and offers a random draw each time its `myman()` function is called. This
32
+ Python package benefits from the corresponding [R package][rpkg] and uses the csv file created and
33
+ packaged there.
34
+
35
+ The Python package is equivalent to the R in all aspects but one: each call will only return one
36
+ post.
37
+
38
+ ### Example
39
+
40
+ The simple file `main.py` illustrates three calling examples with (numeric) index, target, or
41
+ topical index.
42
+
43
+ ```sh
44
+ $ ./main.py
45
+ My man looks like Pat Boone being tased.
46
+ -- about Stephen Miller on 2026-07-16
47
+
48
+ My man looks like a superhero whose power is Bryl Cream.
49
+ -- about Scott Bessent on 2026-08-31
50
+
51
+ My man looks like he's asked the maitre'd to remove a party of \
52
+ four he finds visually unpleasant.
53
+ -- about Scott Bessent on 2026-08-30
54
+
55
+ ```
56
+
57
+ ### Author
58
+
59
+ Dirk Eddelbuettel
60
+
61
+ ### License
62
+
63
+ GPL (>= 2)
64
+
65
+ [postone]: https://bsky.app/profile/did:plc:cnpe7qvcyjrhm6w7w7e4atur/post/3mqum4mxsuk2g
66
+ [posttwo]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mstvbjpagca2
67
+ [postthree]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mtcpiw7gi22j
68
+ [postfour]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mu3pugs2yk2f
69
+ [postfive]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mudbzy5ksk25
70
+ [postsix]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3muparqtdkk2w
71
+
72
+ [rpkg]: https://github.com/eddelbuettel/myman
myman-0.0.6/README.md ADDED
@@ -0,0 +1,62 @@
1
+
2
+ ## myman: Sequence of posts by Kevin Kruse starting with 'My man ...'
3
+
4
+ [![CI](https://github.com/eddelbuettel/myman-py/workflows/ci/badge.svg)](https://github.com/eddelbuettel/myman-py/actions?query=workflow%3Aci)
5
+ [![License](https://img.shields.io/badge/license-GPL%20%28%3E=%202%29-brightgreen.svg?style=flat)](https://opensource.org/license/gpl-2.0)
6
+ [![Last Commit](https://img.shields.io/github/last-commit/eddelbuettel/myman-py)](https://github.com/eddelbuettel/myman-py)
7
+
8
+ ### Motivation
9
+
10
+ Starting on the afternoon of July 17, 2026, and lasting for one initial week, Kevin Kruse fired off
11
+ an astonishing array of over six hundred ninety eight BlueSky replies to [an initial post of
12
+ his][postone] featuring a certain government figure. On August 12, 2026, a second wave started [with
13
+ this post][posttwo] aiming at another government figure. (There were also two stray posts from the
14
+ interim period.) This was followed on August 17, 2026, with another series starting with [this
15
+ post][postthree]. A fourth wave started on August 27, 2026, with [this post][postfour]. A fifth
16
+ wave started on August 30, 2026, with [this post][postfive]. A sixth wave started on September 4,
17
+ 2026 with [this post][postsix]. The total now stands at one thousand three hundred ninety four
18
+ posts.
19
+
20
+ All posts start with "My man ..." and make for excellent input to a `fortunes`-like package. So this
21
+ small package obliges and offers a random draw each time its `myman()` function is called. This
22
+ Python package benefits from the corresponding [R package][rpkg] and uses the csv file created and
23
+ packaged there.
24
+
25
+ The Python package is equivalent to the R in all aspects but one: each call will only return one
26
+ post.
27
+
28
+ ### Example
29
+
30
+ The simple file `main.py` illustrates three calling examples with (numeric) index, target, or
31
+ topical index.
32
+
33
+ ```sh
34
+ $ ./main.py
35
+ My man looks like Pat Boone being tased.
36
+ -- about Stephen Miller on 2026-07-16
37
+
38
+ My man looks like a superhero whose power is Bryl Cream.
39
+ -- about Scott Bessent on 2026-08-31
40
+
41
+ My man looks like he's asked the maitre'd to remove a party of \
42
+ four he finds visually unpleasant.
43
+ -- about Scott Bessent on 2026-08-30
44
+
45
+ ```
46
+
47
+ ### Author
48
+
49
+ Dirk Eddelbuettel
50
+
51
+ ### License
52
+
53
+ GPL (>= 2)
54
+
55
+ [postone]: https://bsky.app/profile/did:plc:cnpe7qvcyjrhm6w7w7e4atur/post/3mqum4mxsuk2g
56
+ [posttwo]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mstvbjpagca2
57
+ [postthree]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mtcpiw7gi22j
58
+ [postfour]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mu3pugs2yk2f
59
+ [postfive]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mudbzy5ksk25
60
+ [postsix]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3muparqtdkk2w
61
+
62
+ [rpkg]: https://github.com/eddelbuettel/myman
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["setuptools"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "myman"
7
+ version = "0.0.6"
8
+ description = "Draw from Sequence of 'My Man' Posts by Kevin Kruse"
9
+ authors = [
10
+ { name = "Dirk Eddelbuettel", email = "edd@debian.org" }
11
+ ]
12
+ requires-python = ">= 3.8"
13
+ readme = "README.md"
14
+ dependencies = []
15
+
16
+ [tool.setuptools.packages.find]
17
+ where = ["src"]
18
+
19
+ #[dependency-groups]
20
+ #dev = [
21
+ # "pytest>=8.3.5",
22
+ #]
23
+
24
+ [project.optional-dependencies]
25
+ test = ["pytest>=7.0"]
myman-0.0.6/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,49 @@
1
+ import csv
2
+ import os
3
+ from datetime import datetime
4
+ from typing import List, Dict, Optional
5
+ from importlib import resources
6
+
7
+ def load_data(verbose: Option[bool] = False) -> List[Dict]:
8
+ """
9
+ Loads the raw 'myman.csv' data using Python's built-in csv module.
10
+
11
+ Returns:
12
+ A list of dictionaries, where each dictionary represents a row.
13
+ """
14
+ records: List[Dict] = []
15
+ full_path = resources.files("myman").joinpath("data/myman.csv")
16
+ if verbose:
17
+ print(f"Loading data from: {os.path.abspath(full_path)}")
18
+
19
+ try:
20
+ with open(full_path, mode='r', newline='', encoding='utf-8') as file:
21
+ # Use DictReader to automatically handle headers
22
+ reader = csv.DictReader(file)
23
+
24
+ for row in reader:
25
+ # Structure the record for consistency
26
+ record = {
27
+ 'quote': row.get('post', '').strip(),
28
+ 'target': row.get('man', '').strip(),
29
+ #'date_str': row.get('created', '').strip()
30
+ # Date handling: Keep date as string initially for consistency,
31
+ # but attempt to normalize the format if possible.
32
+ 'date_obj': row.get('created', '').strip()
33
+ }
34
+ records.append(record)
35
+
36
+ if verbose:
37
+ print(f"Successfully loaded {len(records)} records.")
38
+ return records
39
+
40
+ except FileNotFoundError:
41
+ print(f"Error: The data file '{csv_file_path}' was not found.")
42
+ return []
43
+
44
+ if __name__ == "__main__":
45
+ # Example usage when running this script directly
46
+ data = load_data()
47
+ if data:
48
+ print("Data successfully loaded (first record sample):")
49
+ print(data[0])
@@ -0,0 +1,141 @@
1
+ import random
2
+ import re
3
+ from typing import List, Dict, Optional
4
+ # Import the loading function from the local module
5
+ from .load_data import load_data
6
+
7
+ # class MyMan:
8
+ # def __init__(self, quote, target, date):
9
+ # self.quote = quote
10
+ # self.target = target
11
+ # self.date = date
12
+
13
+ # # Overriding the __str__ method
14
+ # def __str__(self):
15
+ # return f"'{self.quote}' -- directed at {self.target} on {self.date}"
16
+
17
+ def is_string_an_int(arg):
18
+ try:
19
+ int(arg)
20
+ return True
21
+ except ValueError:
22
+ return False
23
+
24
+ def format_myman_output(record: Dict) -> str:
25
+ """
26
+ Formats the sampled record dictionary into the final display string format.
27
+ Format: My man [quote].\t -- about [target] on [date]
28
+ """
29
+ quote = record['quote']
30
+ target = record['target']
31
+ date_obj = record['date_obj']
32
+
33
+ # Date normalization for clean output
34
+ date_output = date_obj
35
+ if isinstance(date_obj, str):
36
+ # Clean up the date string for display consistency
37
+ date_output = date_obj.split(' ')[0]
38
+
39
+ return f"{quote}.\n\t -- about {target} on {date_output}\n"
40
+
41
+ def myman(ind: Optional[str] = None,
42
+ target: Optional[str] = None,
43
+ verbose: Option[bool] = False) -> str:
44
+ """
45
+ Randomly samples a single post (skeet) from the loaded dataset,
46
+ optionally filtering by a target keyword.
47
+
48
+ Args:
49
+ int: Optional index, or regex pattern string to match 'post' field
50
+ target: Optional regex pattern string to filter records by the 'target' field.
51
+
52
+ Returns:
53
+ A dictionary representing the sampled record, or None if no records are found.
54
+ """
55
+ # 1. Load all data first
56
+ data = load_data(verbose=verbose)
57
+ if not data:
58
+ return None
59
+
60
+ records_to_sample = data
61
+
62
+ # 2. Filter if a target is specified
63
+ if target:
64
+ if (verbose):
65
+ print(f"Filtering records by target regex: {target}")
66
+ filtered_records: List[Dict] = []
67
+ for record in data:
68
+ # Use regex search to check if the target keyword exists
69
+ if record['target'] and record['target'].strip():
70
+ try:
71
+ # Use re.search for robust regex matching
72
+ if re.search(target, record['target'], re.IGNORECASE):
73
+ filtered_records.append(record)
74
+ except re.error as e:
75
+ print(f"Error in regex pattern '{target}': {e}")
76
+ return None
77
+
78
+ if not filtered_records:
79
+ if (verbose):
80
+ print("No records found matching the target.")
81
+ return ""
82
+
83
+ records_to_sample = filtered_records
84
+
85
+ # 3.1 Index by position or match
86
+ if ind:
87
+ if (verbose):
88
+ print(f"Indexing records by '{ind}'")
89
+ matched_records: List[Dict] = []
90
+
91
+ if is_string_an_int(ind):
92
+ i = int(ind) - 1
93
+ matched_records = records_to_sample[i]
94
+ else:
95
+ for record in records_to_sample:
96
+ # Use regex search to check if the 'ind' pattern matches
97
+ if record['quote'] and record['quote'].strip():
98
+ try:
99
+ # Use re.search for robust regex matching
100
+ if re.search(ind, record['quote'], re.IGNORECASE):
101
+ matched_records.append(record)
102
+ except re.error as e:
103
+ print(f"Error in regex pattern '{ind}': {e}")
104
+ return None
105
+
106
+ if not matched_records:
107
+ if (verbose):
108
+ print("No records found matching the pattern.")
109
+ return ""
110
+ else:
111
+ # sample down to one
112
+ matched_records = random.choice(matched_records)
113
+
114
+ records_to_sample = matched_records
115
+
116
+ # 3.2 Or sample the record
117
+ else:
118
+ records_to_sample = random.choice(records_to_sample)
119
+ if (verbose):
120
+ print("Successfully sampled a record.")
121
+
122
+ if not records_to_sample:
123
+ if (verbose):
124
+ print("No records found matching the criteria.")
125
+ return ""
126
+
127
+ return format_myman_output(records_to_sample)
128
+
129
+ # Example usage when running this script directly
130
+ if __name__ == "__main__":
131
+ # Test 1: Sample a random record
132
+ print("\n--- Testing random sample (no target) ---")
133
+ sample_no_target = myman()
134
+ if sample_no_target:
135
+ print("Sampled Record:", sample_no_target)
136
+
137
+ # Test 2: Sample by a specific target (e.g., 'crypto')
138
+ print("\n--- Testing targeted sample (target=crypto) ---")
139
+ sample_target = myman(target="crypto")
140
+ if sample_target:
141
+ print("Sampled Record:", sample_target)
@@ -0,0 +1,51 @@
1
+ from typing import Optional, Dict
2
+ import random
3
+ import re
4
+ from .myman import myman
5
+
6
+ def format_myman_output(record: Dict) -> str:
7
+ """
8
+ Formats the sampled record dictionary into the final display string format.
9
+ Format: My man [quote].\t -- about [target] on [date]
10
+ """
11
+ quote = record['quote']
12
+ target = record['target']
13
+ date_obj = record['date_obj']
14
+
15
+ # Date normalization for clean output
16
+ date_output = date_obj
17
+ if isinstance(date_obj, str):
18
+ # Clean up the date string for display consistency
19
+ date_output = date_obj.split(' ')[0]
20
+
21
+ return f"My man {quote}.\t -- about {target} on {date_output}"
22
+
23
+ def run_myman(ind: Optional[str] = None, target: Optional[str] = None) -> str:
24
+ """
25
+ High-level function to execute the full cycle: sample data -> format output.
26
+ """
27
+ # Get the sampled record
28
+ from .myman import myman # Local import for circular dependency protection
29
+ sampled_record = myman(ind=ind, target=target)
30
+
31
+ if sampled_record:
32
+ # Format and return the final string
33
+ return format_myman_output(sampled_record)
34
+ else:
35
+ return "Could not generate content: Sample failed or data is unavailable."
36
+
37
+ if __name__ == "__main__":
38
+ # Example usage: generate and print a sample
39
+ print("="*50)
40
+ print("--- Running Python 'myman' Generator ---")
41
+ print("="*50)
42
+
43
+ # Test 1: General sample
44
+ final_output = run_myman(target=None)
45
+ print(final_output)
46
+
47
+ # Test 2: Targeted sample (e.g., 'crypto')
48
+ print("\n" + "="*50)
49
+ final_output_crypto = run_myman(target="crypto")
50
+ print(final_output_crypto)
51
+ print("="*50)
@@ -0,0 +1,72 @@
1
+ Metadata-Version: 2.4
2
+ Name: myman
3
+ Version: 0.0.6
4
+ Summary: Draw from Sequence of 'My Man' Posts by Kevin Kruse
5
+ Author-email: Dirk Eddelbuettel <edd@debian.org>
6
+ Requires-Python: >=3.8
7
+ Description-Content-Type: text/markdown
8
+ Provides-Extra: test
9
+ Requires-Dist: pytest>=7.0; extra == "test"
10
+
11
+
12
+ ## myman: Sequence of posts by Kevin Kruse starting with 'My man ...'
13
+
14
+ [![CI](https://github.com/eddelbuettel/myman-py/workflows/ci/badge.svg)](https://github.com/eddelbuettel/myman-py/actions?query=workflow%3Aci)
15
+ [![License](https://img.shields.io/badge/license-GPL%20%28%3E=%202%29-brightgreen.svg?style=flat)](https://opensource.org/license/gpl-2.0)
16
+ [![Last Commit](https://img.shields.io/github/last-commit/eddelbuettel/myman-py)](https://github.com/eddelbuettel/myman-py)
17
+
18
+ ### Motivation
19
+
20
+ Starting on the afternoon of July 17, 2026, and lasting for one initial week, Kevin Kruse fired off
21
+ an astonishing array of over six hundred ninety eight BlueSky replies to [an initial post of
22
+ his][postone] featuring a certain government figure. On August 12, 2026, a second wave started [with
23
+ this post][posttwo] aiming at another government figure. (There were also two stray posts from the
24
+ interim period.) This was followed on August 17, 2026, with another series starting with [this
25
+ post][postthree]. A fourth wave started on August 27, 2026, with [this post][postfour]. A fifth
26
+ wave started on August 30, 2026, with [this post][postfive]. A sixth wave started on September 4,
27
+ 2026 with [this post][postsix]. The total now stands at one thousand three hundred ninety four
28
+ posts.
29
+
30
+ All posts start with "My man ..." and make for excellent input to a `fortunes`-like package. So this
31
+ small package obliges and offers a random draw each time its `myman()` function is called. This
32
+ Python package benefits from the corresponding [R package][rpkg] and uses the csv file created and
33
+ packaged there.
34
+
35
+ The Python package is equivalent to the R in all aspects but one: each call will only return one
36
+ post.
37
+
38
+ ### Example
39
+
40
+ The simple file `main.py` illustrates three calling examples with (numeric) index, target, or
41
+ topical index.
42
+
43
+ ```sh
44
+ $ ./main.py
45
+ My man looks like Pat Boone being tased.
46
+ -- about Stephen Miller on 2026-07-16
47
+
48
+ My man looks like a superhero whose power is Bryl Cream.
49
+ -- about Scott Bessent on 2026-08-31
50
+
51
+ My man looks like he's asked the maitre'd to remove a party of \
52
+ four he finds visually unpleasant.
53
+ -- about Scott Bessent on 2026-08-30
54
+
55
+ ```
56
+
57
+ ### Author
58
+
59
+ Dirk Eddelbuettel
60
+
61
+ ### License
62
+
63
+ GPL (>= 2)
64
+
65
+ [postone]: https://bsky.app/profile/did:plc:cnpe7qvcyjrhm6w7w7e4atur/post/3mqum4mxsuk2g
66
+ [posttwo]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mstvbjpagca2
67
+ [postthree]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mtcpiw7gi22j
68
+ [postfour]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mu3pugs2yk2f
69
+ [postfive]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3mudbzy5ksk25
70
+ [postsix]: https://bsky.app/profile/kevinmkruse.bsky.social/post/3muparqtdkk2w
71
+
72
+ [rpkg]: https://github.com/eddelbuettel/myman
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/myman/__init__.py
4
+ src/myman/load_data.py
5
+ src/myman/myman.py
6
+ src/myman/output_formatter.py
7
+ src/myman.egg-info/PKG-INFO
8
+ src/myman.egg-info/SOURCES.txt
9
+ src/myman.egg-info/dependency_links.txt
10
+ src/myman.egg-info/requires.txt
11
+ src/myman.egg-info/top_level.txt
12
+ tests/test_myman.py
@@ -0,0 +1,3 @@
1
+
2
+ [test]
3
+ pytest>=7.0
@@ -0,0 +1 @@
1
+ myman
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env python3
2
+
3
+ import pytest
4
+ from myman import myman
5
+
6
+ class TestMyManIndex:
7
+ """Test basic indexing."""
8
+
9
+ def test_index_one(self):
10
+ """Test that index '1' returns exptected value."""
11
+ s = myman.myman(ind=1)
12
+ assert s == "My man looks like Pat Boone being tased.\n\t -- about Stephen Miller on 2026-07-16\n"
13
+
14
+ def test_zero_on_bad_target(self):
15
+ """Test that empty string is return on mismatched subset"""
16
+ s = myman.myman(target="abc123")
17
+ assert s == ""
18
+
19
+ def test_zero_on_bad_index(self):
20
+ """Test that empty string is return on mismatched subset"""
21
+ s = myman.myman(ind="abc123")
22
+ assert s == ""
23
+
24
+ if __name__ == "__main__":
25
+ pytest.main([__file__, "-v"])