convoviz 0.2.11__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.
Files changed (61) hide show
  1. {convoviz-0.2.11 → convoviz-0.2.13}/PKG-INFO +4 -2
  2. {convoviz-0.2.11 → convoviz-0.2.13}/README.md +3 -1
  3. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/analysis/graphs.py +26 -6
  4. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/analysis/wordcloud.py +60 -29
  5. convoviz-0.2.13/convoviz/assets/stopwords.txt +1 -0
  6. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/config.py +1 -0
  7. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/interactive.py +1 -0
  8. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/io/writers.py +1 -3
  9. {convoviz-0.2.11 → convoviz-0.2.13}/pyproject.toml +4 -7
  10. convoviz-0.2.11/convoviz/assets/stopwords.txt +0 -75
  11. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/__init__.py +0 -0
  12. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/__main__.py +0 -0
  13. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/analysis/__init__.py +0 -0
  14. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/colormaps.txt +0 -0
  15. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/AmaticSC-Regular.ttf +0 -0
  16. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/ArchitectsDaughter-Regular.ttf +0 -0
  17. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/BebasNeue-Regular.ttf +0 -0
  18. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Borel-Regular.ttf +0 -0
  19. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Courgette-Regular.ttf +0 -0
  20. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/CroissantOne-Regular.ttf +0 -0
  21. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Handjet-Regular.ttf +0 -0
  22. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/IndieFlower-Regular.ttf +0 -0
  23. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Kalam-Regular.ttf +0 -0
  24. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Lobster-Regular.ttf +0 -0
  25. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/MartianMono-Regular.ttf +0 -0
  26. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/MartianMono-Thin.ttf +0 -0
  27. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Montserrat-Regular.ttf +0 -0
  28. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Mooli-Regular.ttf +0 -0
  29. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Pacifico-Regular.ttf +0 -0
  30. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/PlayfairDisplay-Regular.ttf +0 -0
  31. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Raleway-Regular.ttf +0 -0
  32. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/RobotoMono-Regular.ttf +0 -0
  33. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/RobotoMono-Thin.ttf +0 -0
  34. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/RobotoSlab-Regular.ttf +0 -0
  35. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/RobotoSlab-Thin.ttf +0 -0
  36. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Ruwudu-Regular.ttf +0 -0
  37. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Sacramento-Regular.ttf +0 -0
  38. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/SedgwickAveDisplay-Regular.ttf +0 -0
  39. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/ShadowsIntoLight-Regular.ttf +0 -0
  40. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/TitilliumWeb-Regular.ttf +0 -0
  41. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Yellowtail-Regular.ttf +0 -0
  42. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/YsabeauOffice-Regular.ttf +0 -0
  43. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/YsabeauSC-Regular.ttf +0 -0
  44. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/YsabeauSC-Thin.ttf +0 -0
  45. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/assets/fonts/Zeyada-Regular.ttf +0 -0
  46. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/cli.py +0 -0
  47. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/exceptions.py +0 -0
  48. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/io/__init__.py +0 -0
  49. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/io/assets.py +0 -0
  50. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/io/loaders.py +0 -0
  51. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/models/__init__.py +0 -0
  52. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/models/collection.py +0 -0
  53. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/models/conversation.py +0 -0
  54. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/models/message.py +0 -0
  55. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/models/node.py +0 -0
  56. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/pipeline.py +0 -0
  57. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/py.typed +0 -0
  58. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/renderers/__init__.py +0 -0
  59. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/renderers/markdown.py +0 -0
  60. {convoviz-0.2.11 → convoviz-0.2.13}/convoviz/renderers/yaml.py +0 -0
  61. {convoviz-0.2.11 → 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.11
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
+ ![wordcloud example](demo/wordcloud-example.png)
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
+ ![wordcloud example](demo/wordcloud-example.png)
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("Conversation length (user prompts)", fontproperties=font_prop, fontsize=14, pad=14)
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("Monthly activity (user prompts)", fontproperties=font_prop, fontsize=13, pad=10)
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
- ("Activity heatmap", "activity_heatmap.png", lambda: generate_activity_heatmap(collection, cfg)),
745
- ("Daily activity", "daily_activity.png", lambda: generate_daily_activity_lineplot(collection, cfg)),
746
- ("Monthly activity", "monthly_activity.png", lambda: generate_monthly_activity_barplot(collection, cfg)),
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
- ("Conversation lengths", "conversation_lengths.png", lambda: generate_length_histogram(collection, cfg)),
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
- for week, group in tqdm(
135
- week_groups.items(),
136
- desc="Creating weekly wordclouds 🔡☁️",
137
- disable=not progress_bar,
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
- if text.strip():
141
- img = generate_wordcloud(text, config)
142
- # Format: 2024-W15.png (ISO week format)
143
- img.save(output_dir / f"{week.strftime('%Y-W%W')}.png", optimize=True)
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
- if text.strip():
152
- img = generate_wordcloud(text, config)
153
- # Format: 2024-03-March.png (consistent with folder naming)
154
- img.save(output_dir / f"{month.strftime('%Y-%m-%B')}.png", optimize=True)
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
- if text.strip():
163
- img = generate_wordcloud(text, config)
164
- # Format: 2024.png
165
- img.save(output_dir / f"{year.strftime('%Y')}.png", optimize=True)
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
+ )
@@ -70,6 +70,7 @@ class WordCloudConfig(BaseModel):
70
70
  include_numbers: bool = False
71
71
  width: int = 600
72
72
  height: int = 600
73
+ max_workers: int | None = None # None = use half CPU count
73
74
 
74
75
 
75
76
  class GraphConfig(BaseModel):
@@ -26,6 +26,7 @@ CUSTOM_STYLE = Style(
26
26
  ]
27
27
  )
28
28
 
29
+
29
30
  class _QuestionaryPrompt[T](Protocol):
30
31
  def ask(self) -> T | None: ...
31
32
 
@@ -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.11"
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.mypy]
96
- python_version = "3.12"
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"]
@@ -1,75 +0,0 @@
1
- # Python
2
- def
3
- class
4
- import
5
- from
6
- as
7
- elif
8
- finally
9
- yield
10
- pass
11
- lambda
12
- async
13
- await
14
- nonlocal
15
- assert
16
- self
17
- cls
18
- # JavaScript / TypeScript
19
- const
20
- let
21
- var
22
- function
23
- export
24
- default
25
- extends
26
- implements
27
- static
28
- # Java / C#
29
- final
30
- abstract
31
- new
32
- super
33
- package
34
- throws
35
- synchronized
36
- volatile
37
- transient
38
- native
39
- strictfp
40
- override
41
- # C / C++
42
- unsigned
43
- signed
44
- typedef
45
- sizeof
46
- extern
47
- register
48
- restrict
49
- inline
50
- template
51
- typename
52
- virtual
53
- friend
54
- mutable
55
- explicit
56
- operator
57
- typeid
58
- # Rust
59
- mut
60
- fn
61
- pub
62
- mod
63
- trait
64
- impl
65
- where
66
- loop
67
- unsafe
68
- crate
69
- dyn
70
- # Go
71
- func
72
- chan
73
- defer
74
- fallthrough
75
- goto
File without changes
File without changes
File without changes