convoviz 0.2.12__tar.gz → 0.2.13__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.
- {convoviz-0.2.12 → convoviz-0.2.13}/PKG-INFO +4 -2
- {convoviz-0.2.12 → convoviz-0.2.13}/README.md +3 -1
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/analysis/graphs.py +26 -6
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/analysis/wordcloud.py +60 -29
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/config.py +1 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/interactive.py +1 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/io/writers.py +1 -3
- {convoviz-0.2.12 → convoviz-0.2.13}/pyproject.toml +4 -7
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/__init__.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/__main__.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/analysis/__init__.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/colormaps.txt +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/AmaticSC-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/ArchitectsDaughter-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/BebasNeue-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Borel-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Courgette-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/CroissantOne-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Handjet-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/IndieFlower-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Kalam-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Lobster-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/MartianMono-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/MartianMono-Thin.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Montserrat-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Mooli-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Pacifico-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/PlayfairDisplay-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Raleway-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/RobotoMono-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/RobotoMono-Thin.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/RobotoSlab-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/RobotoSlab-Thin.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Ruwudu-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Sacramento-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/SedgwickAveDisplay-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/ShadowsIntoLight-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/TitilliumWeb-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Yellowtail-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/YsabeauOffice-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/YsabeauSC-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/YsabeauSC-Thin.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/fonts/Zeyada-Regular.ttf +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/assets/stopwords.txt +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/cli.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/exceptions.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/io/__init__.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/io/assets.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/io/loaders.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/models/__init__.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/models/collection.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/models/conversation.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/models/message.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/models/node.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/pipeline.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/py.typed +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/renderers/__init__.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/renderers/markdown.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/renderers/yaml.py +0 -0
- {convoviz-0.2.12 → convoviz-0.2.13}/convoviz/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: convoviz
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.13
|
|
4
4
|
Summary: Get analytics and visualizations on your ChatGPT data!
|
|
5
5
|
Keywords: markdown,chatgpt,openai,visualization,analytics,json,export,data-analysis,obsidian
|
|
6
6
|
Author: Mohamed Cheikh Sidiya
|
|
@@ -49,7 +49,7 @@ See examples [here](demo).
|
|
|
49
49
|
|
|
50
50
|
### 2. Install the tool 🛠
|
|
51
51
|
|
|
52
|
-
Try it without installing using uv ([astral-sh/uv](https://github.com/astral-sh/uv)):
|
|
52
|
+
Try it without installing using uv ([astral-sh/uv](https://github.com/astral-sh/uv?tab=readme-ov-file#highlights)):
|
|
53
53
|
|
|
54
54
|
```bash
|
|
55
55
|
uvx convoviz
|
|
@@ -108,6 +108,8 @@ The main outputs are:
|
|
|
108
108
|
- **`Word-Clouds/`**: weekly/monthly/yearly word clouds
|
|
109
109
|
- **`custom_instructions.json`**: extracted custom instructions
|
|
110
110
|
|
|
111
|
+

|
|
112
|
+
|
|
111
113
|
## Share Your Feedback! 💌
|
|
112
114
|
|
|
113
115
|
I hope you find this tool useful. I'm continuously looking to improve on this, but I need your help for that.
|
|
@@ -23,7 +23,7 @@ See examples [here](demo).
|
|
|
23
23
|
|
|
24
24
|
### 2. Install the tool 🛠
|
|
25
25
|
|
|
26
|
-
Try it without installing using uv ([astral-sh/uv](https://github.com/astral-sh/uv)):
|
|
26
|
+
Try it without installing using uv ([astral-sh/uv](https://github.com/astral-sh/uv?tab=readme-ov-file#highlights)):
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
29
|
uvx convoviz
|
|
@@ -82,6 +82,8 @@ The main outputs are:
|
|
|
82
82
|
- **`Word-Clouds/`**: weekly/monthly/yearly word clouds
|
|
83
83
|
- **`custom_instructions.json`**: extracted custom instructions
|
|
84
84
|
|
|
85
|
+

|
|
86
|
+
|
|
85
87
|
## Share Your Feedback! 💌
|
|
86
88
|
|
|
87
89
|
I hope you find this tool useful. I'm continuously looking to improve on this, but I need your help for that.
|
|
@@ -347,7 +347,9 @@ def generate_length_histogram(
|
|
|
347
347
|
color="#cf222e",
|
|
348
348
|
)
|
|
349
349
|
|
|
350
|
-
ax.set_title(
|
|
350
|
+
ax.set_title(
|
|
351
|
+
"Conversation length (user prompts)", fontproperties=font_prop, fontsize=14, pad=14
|
|
352
|
+
)
|
|
351
353
|
ax.set_xlabel("User prompts per conversation", fontproperties=font_prop)
|
|
352
354
|
ax.set_ylabel("Conversations", fontproperties=font_prop)
|
|
353
355
|
ax.set_xlim(left=0, right=cap)
|
|
@@ -619,7 +621,9 @@ def generate_summary_dashboard(
|
|
|
619
621
|
locator = mdates.AutoDateLocator(minticks=4, maxticks=10)
|
|
620
622
|
ax_ts.xaxis.set_major_locator(locator)
|
|
621
623
|
ax_ts.xaxis.set_major_formatter(mdates.ConciseDateFormatter(locator))
|
|
622
|
-
ax_ts.set_title(
|
|
624
|
+
ax_ts.set_title(
|
|
625
|
+
"Monthly activity (user prompts)", fontproperties=font_prop, fontsize=13, pad=10
|
|
626
|
+
)
|
|
623
627
|
ax_ts.set_ylabel("User prompts", fontproperties=font_prop)
|
|
624
628
|
ax_ts.set_xlabel(f"Month ({_tz_label(cfg)})", fontproperties=font_prop)
|
|
625
629
|
_apply_tick_font(ax_ts, font_prop)
|
|
@@ -741,11 +745,27 @@ def generate_summary_graphs(
|
|
|
741
745
|
|
|
742
746
|
tasks: list[tuple[str, str, Callable[[], Figure]]] = [
|
|
743
747
|
("Overview", "overview.png", lambda: generate_summary_dashboard(collection, cfg)),
|
|
744
|
-
(
|
|
745
|
-
|
|
746
|
-
|
|
748
|
+
(
|
|
749
|
+
"Activity heatmap",
|
|
750
|
+
"activity_heatmap.png",
|
|
751
|
+
lambda: generate_activity_heatmap(collection, cfg),
|
|
752
|
+
),
|
|
753
|
+
(
|
|
754
|
+
"Daily activity",
|
|
755
|
+
"daily_activity.png",
|
|
756
|
+
lambda: generate_daily_activity_lineplot(collection, cfg),
|
|
757
|
+
),
|
|
758
|
+
(
|
|
759
|
+
"Monthly activity",
|
|
760
|
+
"monthly_activity.png",
|
|
761
|
+
lambda: generate_monthly_activity_barplot(collection, cfg),
|
|
762
|
+
),
|
|
747
763
|
("Model usage", "model_usage.png", lambda: generate_model_piechart(collection, cfg)),
|
|
748
|
-
(
|
|
764
|
+
(
|
|
765
|
+
"Conversation lengths",
|
|
766
|
+
"conversation_lengths.png",
|
|
767
|
+
lambda: generate_length_histogram(collection, cfg),
|
|
768
|
+
),
|
|
749
769
|
(
|
|
750
770
|
"Conversation lifetimes",
|
|
751
771
|
"conversation_lifetimes.png",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Word cloud generation for conversation text."""
|
|
2
2
|
|
|
3
|
+
import os
|
|
4
|
+
from concurrent.futures import ProcessPoolExecutor
|
|
3
5
|
from functools import lru_cache
|
|
4
6
|
from pathlib import Path
|
|
5
7
|
|
|
@@ -110,6 +112,25 @@ def generate_wordcloud(text: str, config: WordCloudConfig) -> Image:
|
|
|
110
112
|
return result
|
|
111
113
|
|
|
112
114
|
|
|
115
|
+
def _generate_and_save_wordcloud(args: tuple[str, str, Path, WordCloudConfig]) -> bool:
|
|
116
|
+
"""Worker function for parallel wordcloud generation.
|
|
117
|
+
|
|
118
|
+
Must be at module level for pickling by ProcessPoolExecutor.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
args: Tuple of (text, filename, output_dir, config)
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
True if wordcloud was generated, False if skipped (empty text)
|
|
125
|
+
"""
|
|
126
|
+
text, filename, output_dir, config = args
|
|
127
|
+
if not text.strip():
|
|
128
|
+
return False
|
|
129
|
+
img = generate_wordcloud(text, config)
|
|
130
|
+
img.save(output_dir / filename, optimize=True)
|
|
131
|
+
return True
|
|
132
|
+
|
|
133
|
+
|
|
113
134
|
def generate_wordclouds(
|
|
114
135
|
collection: ConversationCollection,
|
|
115
136
|
output_dir: Path,
|
|
@@ -119,6 +140,8 @@ def generate_wordclouds(
|
|
|
119
140
|
) -> None:
|
|
120
141
|
"""Generate word clouds for weekly, monthly, and yearly groupings.
|
|
121
142
|
|
|
143
|
+
Uses parallel processing to speed up generation on multi-core systems.
|
|
144
|
+
|
|
122
145
|
Args:
|
|
123
146
|
collection: Collection of conversations
|
|
124
147
|
output_dir: Directory to save the word clouds
|
|
@@ -131,35 +154,43 @@ def generate_wordclouds(
|
|
|
131
154
|
month_groups = collection.group_by_month()
|
|
132
155
|
year_groups = collection.group_by_year()
|
|
133
156
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
):
|
|
157
|
+
# Build list of all tasks: (text, filename, output_dir, config)
|
|
158
|
+
tasks: list[tuple[str, str, Path, WordCloudConfig]] = []
|
|
159
|
+
|
|
160
|
+
for week, group in week_groups.items():
|
|
139
161
|
text = group.plaintext("user", "assistant")
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
for month, group in tqdm(
|
|
146
|
-
month_groups.items(),
|
|
147
|
-
desc="Creating monthly wordclouds 🔡☁️",
|
|
148
|
-
disable=not progress_bar,
|
|
149
|
-
):
|
|
162
|
+
# Format: 2024-W15.png (ISO week format)
|
|
163
|
+
filename = f"{week.strftime('%Y-W%W')}.png"
|
|
164
|
+
tasks.append((text, filename, output_dir, config))
|
|
165
|
+
|
|
166
|
+
for month, group in month_groups.items():
|
|
150
167
|
text = group.plaintext("user", "assistant")
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
for year, group in tqdm(
|
|
157
|
-
year_groups.items(),
|
|
158
|
-
desc="Creating yearly wordclouds 🔡☁️",
|
|
159
|
-
disable=not progress_bar,
|
|
160
|
-
):
|
|
168
|
+
# Format: 2024-03-March.png (consistent with folder naming)
|
|
169
|
+
filename = f"{month.strftime('%Y-%m-%B')}.png"
|
|
170
|
+
tasks.append((text, filename, output_dir, config))
|
|
171
|
+
|
|
172
|
+
for year, group in year_groups.items():
|
|
161
173
|
text = group.plaintext("user", "assistant")
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
174
|
+
# Format: 2024.png
|
|
175
|
+
filename = f"{year.strftime('%Y')}.png"
|
|
176
|
+
tasks.append((text, filename, output_dir, config))
|
|
177
|
+
|
|
178
|
+
if not tasks:
|
|
179
|
+
return
|
|
180
|
+
|
|
181
|
+
# Determine worker count: use config if set, otherwise half CPU count (min 1)
|
|
182
|
+
max_workers = config.max_workers
|
|
183
|
+
if max_workers is None:
|
|
184
|
+
cpu_count = os.cpu_count() or 2
|
|
185
|
+
max_workers = max(1, cpu_count // 2)
|
|
186
|
+
|
|
187
|
+
# Use parallel processing for speedup on multi-core systems
|
|
188
|
+
with ProcessPoolExecutor(max_workers=max_workers) as executor:
|
|
189
|
+
list(
|
|
190
|
+
tqdm(
|
|
191
|
+
executor.map(_generate_and_save_wordcloud, tasks),
|
|
192
|
+
total=len(tasks),
|
|
193
|
+
desc="Creating wordclouds 🔡☁️",
|
|
194
|
+
disable=not progress_bar,
|
|
195
|
+
)
|
|
196
|
+
)
|
|
@@ -146,9 +146,7 @@ def _generate_month_index(month_dir: Path, year: str, month: str) -> None:
|
|
|
146
146
|
month: The month folder name (e.g., "03-March")
|
|
147
147
|
"""
|
|
148
148
|
month_name = month.split("-", 1)[1] if "-" in month else month
|
|
149
|
-
files = sorted(
|
|
150
|
-
[f.name for f in month_dir.glob("*.md") if f.name != "_index.md"]
|
|
151
|
-
)
|
|
149
|
+
files = sorted([f.name for f in month_dir.glob("*.md") if f.name != "_index.md"])
|
|
152
150
|
|
|
153
151
|
lines = [
|
|
154
152
|
f"# {month_name} {year}",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "convoviz"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.13"
|
|
4
4
|
description = "Get analytics and visualizations on your ChatGPT data!"
|
|
5
5
|
license = "MIT"
|
|
6
6
|
keywords = [
|
|
@@ -62,9 +62,9 @@ convoviz = "convoviz.cli:main_entry"
|
|
|
62
62
|
|
|
63
63
|
[dependency-groups]
|
|
64
64
|
dev = [
|
|
65
|
-
"mypy>=1.19.1",
|
|
66
65
|
"pytest>=8.4.2",
|
|
67
66
|
"ruff>=0.14.10",
|
|
67
|
+
"ty>=0.0.11",
|
|
68
68
|
"types-tqdm>=4.67.0.20250809",
|
|
69
69
|
]
|
|
70
70
|
|
|
@@ -92,11 +92,8 @@ ignore = [
|
|
|
92
92
|
[tool.ruff.lint.isort]
|
|
93
93
|
known-first-party = ["convoviz"]
|
|
94
94
|
|
|
95
|
-
[tool.
|
|
96
|
-
|
|
97
|
-
warn_return_any = true
|
|
98
|
-
warn_unused_configs = true
|
|
99
|
-
ignore_missing_imports = true
|
|
95
|
+
[tool.ty.environment]
|
|
96
|
+
python-version = "3.12"
|
|
100
97
|
|
|
101
98
|
[tool.pytest.ini_options]
|
|
102
99
|
testpaths = ["tests"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|