convoviz 0.2.14__tar.gz → 0.3.5__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 (62) hide show
  1. {convoviz-0.2.14 → convoviz-0.3.5}/PKG-INFO +42 -39
  2. {convoviz-0.2.14 → convoviz-0.3.5}/README.md +38 -34
  3. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/__init__.py +10 -1
  4. convoviz-0.3.5/convoviz/analysis/__init__.py +22 -0
  5. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/cli.py +12 -3
  6. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/config.py +13 -0
  7. convoviz-0.3.5/convoviz/interactive.py +255 -0
  8. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/pipeline.py +81 -72
  9. {convoviz-0.2.14 → convoviz-0.3.5}/pyproject.toml +9 -4
  10. convoviz-0.2.14/convoviz/analysis/__init__.py +0 -9
  11. convoviz-0.2.14/convoviz/interactive.py +0 -232
  12. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/__main__.py +0 -0
  13. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/analysis/graphs.py +0 -0
  14. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/analysis/wordcloud.py +0 -0
  15. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/colormaps.txt +0 -0
  16. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/AmaticSC-Regular.ttf +0 -0
  17. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/ArchitectsDaughter-Regular.ttf +0 -0
  18. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/BebasNeue-Regular.ttf +0 -0
  19. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Borel-Regular.ttf +0 -0
  20. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Courgette-Regular.ttf +0 -0
  21. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/CroissantOne-Regular.ttf +0 -0
  22. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Handjet-Regular.ttf +0 -0
  23. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/IndieFlower-Regular.ttf +0 -0
  24. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Kalam-Regular.ttf +0 -0
  25. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Lobster-Regular.ttf +0 -0
  26. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/MartianMono-Regular.ttf +0 -0
  27. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/MartianMono-Thin.ttf +0 -0
  28. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Montserrat-Regular.ttf +0 -0
  29. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Mooli-Regular.ttf +0 -0
  30. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Pacifico-Regular.ttf +0 -0
  31. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/PlayfairDisplay-Regular.ttf +0 -0
  32. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Raleway-Regular.ttf +0 -0
  33. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/RobotoMono-Regular.ttf +0 -0
  34. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/RobotoMono-Thin.ttf +0 -0
  35. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/RobotoSlab-Regular.ttf +0 -0
  36. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/RobotoSlab-Thin.ttf +0 -0
  37. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Ruwudu-Regular.ttf +0 -0
  38. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Sacramento-Regular.ttf +0 -0
  39. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/SedgwickAveDisplay-Regular.ttf +0 -0
  40. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/ShadowsIntoLight-Regular.ttf +0 -0
  41. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/TitilliumWeb-Regular.ttf +0 -0
  42. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Yellowtail-Regular.ttf +0 -0
  43. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/YsabeauOffice-Regular.ttf +0 -0
  44. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/YsabeauSC-Regular.ttf +0 -0
  45. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/YsabeauSC-Thin.ttf +0 -0
  46. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/fonts/Zeyada-Regular.ttf +0 -0
  47. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/assets/stopwords.txt +0 -0
  48. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/exceptions.py +0 -0
  49. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/io/__init__.py +0 -0
  50. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/io/assets.py +0 -0
  51. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/io/loaders.py +0 -0
  52. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/io/writers.py +0 -0
  53. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/models/__init__.py +0 -0
  54. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/models/collection.py +0 -0
  55. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/models/conversation.py +0 -0
  56. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/models/message.py +0 -0
  57. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/models/node.py +0 -0
  58. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/py.typed +0 -0
  59. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/renderers/__init__.py +0 -0
  60. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/renderers/markdown.py +0 -0
  61. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/renderers/yaml.py +0 -0
  62. {convoviz-0.2.14 → convoviz-0.3.5}/convoviz/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: convoviz
3
- Version: 0.2.14
3
+ Version: 0.3.5
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
@@ -9,24 +9,23 @@ License-Expression: MIT
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.12
12
- Requires-Dist: matplotlib>=3.9.4
13
- Requires-Dist: nltk>=3.9.2
14
12
  Requires-Dist: orjson>=3.11.5
15
- Requires-Dist: pillow>=11.3.0
16
13
  Requires-Dist: pydantic>=2.12.5
17
14
  Requires-Dist: pydantic-settings>=2.7.0
18
15
  Requires-Dist: questionary>=2.1.1
19
16
  Requires-Dist: rich>=14.2.0
20
17
  Requires-Dist: tqdm>=4.67.1
21
18
  Requires-Dist: typer>=0.21.0
22
- Requires-Dist: wordcloud>=1.9.5
19
+ Requires-Dist: nltk>=3.9.2 ; extra == 'viz'
20
+ Requires-Dist: wordcloud>=1.9.5 ; extra == 'viz'
23
21
  Requires-Python: >=3.12
24
22
  Project-URL: Repository, https://github.com/mohamed-chs/chatgpt-history-export-to-md
23
+ Provides-Extra: viz
25
24
  Description-Content-Type: text/markdown
26
25
 
27
26
  # Convoviz 📊: Visualize your entire ChatGPT data
28
27
 
29
- Convert your ChatGPT history into well-formatted Markdown files. Additionally, visualize your data with word clouds 🔡☁️, view your prompt history graphs 📈, and access all your custom instructions 🤖 in a single location.
28
+ Convert your ChatGPT history into well-formatted Markdown files. Visualize your data with word clouds 🔡☁️ and usage graphs 📈.
30
29
 
31
30
  ![GitHub last commit](https://img.shields.io/github/last-commit/mohamed-chs/chatgpt-history-export-to-md)
32
31
  ![GitHub issues](https://img.shields.io/github/issues/mohamed-chs/chatgpt-history-export-to-md)
@@ -43,27 +42,40 @@ See examples [here](demo).
43
42
 
44
43
  ### 1. Export Your ChatGPT Data 🗂
45
44
 
46
- - Sign in at [chat.openai.com](https://chat.openai.com).
45
+ - Sign in at [chatgpt.com](https://chatgpt.com).
47
46
  - Navigate: Profile Name (bottom left) -> **Settings** -> **Data controls** -> **Export** -> **Confirm export**.
48
47
  - Await email from OpenAI and download the `.zip` file.
49
48
 
50
49
  ### 2. Install the tool 🛠
51
50
 
52
- Try it without installing using uv ([astral-sh/uv](https://github.com/astral-sh/uv?tab=readme-ov-file#highlights)):
51
+ First, install [uv](https://github.com/astral-sh/uv) (if you don't have it):
52
+
53
+ MacOS / Linux:
53
54
 
54
55
  ```bash
55
- uvx convoviz
56
+ curl -LsSf https://astral.sh/uv/install.sh | sh
56
57
  ```
57
58
 
58
- You can install it with uv (Recommended):
59
+ Windows:
60
+
61
+ ```powershell
62
+ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
63
+ ```
64
+
65
+ Then install convoviz:
59
66
 
60
67
  ```bash
61
- uv tool install convoviz
68
+ uv tool install "convoviz[viz]"
62
69
  ```
63
70
 
64
- or pipx:
71
+ The `[viz]` extra includes graphs and word clouds. Skip it for a faster markdown-only install.
72
+
73
+ ### Alternative: pip
74
+
65
75
  ```bash
66
- pipx install convoviz
76
+ python3 -m venv .venv
77
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
78
+ pip install "convoviz[viz]"
67
79
  ```
68
80
 
69
81
  ### 3. Run the tool 🏃‍♂️
@@ -82,9 +94,21 @@ You can provide arguments directly to skip the prompts:
82
94
  convoviz --input path/to/your/export.zip --output path/to/output/folder
83
95
  ```
84
96
 
85
- Notes:
97
+ ##### Selective Output
98
+
99
+ By default, all outputs are generated. Use `--outputs` to pick specific ones:
100
+
101
+ ```bash
102
+ convoviz --input export.zip --outputs markdown --outputs graphs
103
+ ```
104
+
105
+ Options: `markdown`, `graphs`, `wordclouds`. In interactive mode, you'll be prompted.
106
+
107
+ ##### Other Notes
108
+
86
109
  - `--zip` / `-z` is kept as an alias for `--input` for convenience.
87
110
  - You can force non-interactive mode with `--no-interactive`.
111
+ - Use `--flat` to put all Markdown files in a single folder instead of organizing by date.
88
112
 
89
113
  For more options, run:
90
114
 
@@ -96,31 +120,13 @@ convoviz --help
96
120
 
97
121
  And that's it! After running the script, head over to the output folder to see your neatly formatted Markdown files and visualizations.
98
122
 
99
- The main outputs are:
100
-
101
- - **`Markdown/`**: one `.md` file per conversation
102
- - **`Graphs/`**: a small set of high-signal plots, including:
103
- - `overview.png` (dashboard)
104
- - `activity_heatmap.png` (weekday × hour)
105
- - `daily_activity.png` / `monthly_activity.png`
106
- - `model_usage.png`, `conversation_lengths.png`
107
- - `weekday_pattern.png`, `hourly_pattern.png`, `conversation_lifetimes.png`
108
- - **`Word-Clouds/`**: weekly/monthly/yearly word clouds
109
- - **`custom_instructions.json`**: extracted custom instructions
110
-
111
123
  ![wordcloud example](demo/wordcloud-example.png)
112
124
 
113
125
  ## Share Your Feedback! 💌
114
126
 
115
127
  I hope you find this tool useful. I'm continuously looking to improve on this, but I need your help for that.
116
128
 
117
- Whether you're a tech wizard or you're new to all this, I'd love to hear about your journey with the tool. Found a quirk? Have a suggestion? Or just want to send some good vibes? I'm all ears!
118
-
119
- **Here's how you can share your thoughts:**
120
-
121
- 1. **GitHub Issues**: For more specific feedback or if you've stumbled upon a bug, please open an [issue](https://github.com/mohamed-chs/chatgpt-history-export-to-md/issues). This helps me track and address them effectively.
122
-
123
- 2. **GitHub Discussions**: If you just want to share your general experience, have a suggestion, or maybe a cool idea for a new feature, jump into the [discussions](https://github.com/mohamed-chs/chatgpt-history-export-to-md/discussions) page. It's a more casual space where we can chat.
129
+ Whether you're a tech wizard or you're new to all this, I'd love to hear about your journey with the tool. Found a quirk? Have a suggestion? Or just want to send some good vibes? I'm all ears! (see [issues](https://github.com/mohamed-chs/chatgpt-history-export-to-md/issues))
124
130
 
125
131
  And if you've had a great experience, consider giving the project a star ⭐. It keeps me motivated and helps others discover it!
126
132
 
@@ -128,9 +134,6 @@ And if you've had a great experience, consider giving the project a star ⭐. It
128
134
 
129
135
  This is just a small thing I coded to help me see my convos in beautiful markdown. It was originally built with [Obsidian](https://obsidian.md/) (my go-to note-taking app) in mind, but the default output is standard Markdown.
130
136
 
131
- You can choose obsidian flavored md in the cli to get extra features like:
132
- - model reasoning (`reasoning_recap`, `thoughts`) rendered as collapsible `> [!NOTE]-` callouts instead of being hidden.
133
-
134
137
  I wasn't a fan of the clunky, and sometimes paid, browser extensions.
135
138
 
136
139
  It was also a great opportunity to learn more about Python and type annotations. I had mypy, pyright, and ruff all on strict mode, 'twas fun.
@@ -139,12 +142,12 @@ It should(?) also work as library, so you can import and use the models and func
139
142
 
140
143
  ### Offline / reproducible runs
141
144
 
142
- Convoviz uses NLTK stopwords for word clouds. If youre offline and NLTK data isnt already installed, pre-download it once:
145
+ Word clouds use NLTK stopwords. If you're offline and NLTK data isn't installed yet, pre-download it:
143
146
 
144
147
  ```bash
145
- uv run python -c "import nltk; nltk.download('stopwords')"
148
+ python -c "import nltk; nltk.download('stopwords')"
146
149
  ```
147
150
 
148
151
  ### Bookmarklet
149
152
 
150
- Theres also a JavaScript bookmarklet flow under `js/` (experimental) for exporting additional conversation data outside the official ZIP export.
153
+ There's also a JavaScript bookmarklet flow under `js/` (experimental) for exporting additional conversation data outside the official ZIP export.
@@ -1,6 +1,6 @@
1
1
  # Convoviz 📊: Visualize your entire ChatGPT data
2
2
 
3
- Convert your ChatGPT history into well-formatted Markdown files. Additionally, visualize your data with word clouds 🔡☁️, view your prompt history graphs 📈, and access all your custom instructions 🤖 in a single location.
3
+ Convert your ChatGPT history into well-formatted Markdown files. Visualize your data with word clouds 🔡☁️ and usage graphs 📈.
4
4
 
5
5
  ![GitHub last commit](https://img.shields.io/github/last-commit/mohamed-chs/chatgpt-history-export-to-md)
6
6
  ![GitHub issues](https://img.shields.io/github/issues/mohamed-chs/chatgpt-history-export-to-md)
@@ -17,27 +17,40 @@ See examples [here](demo).
17
17
 
18
18
  ### 1. Export Your ChatGPT Data 🗂
19
19
 
20
- - Sign in at [chat.openai.com](https://chat.openai.com).
20
+ - Sign in at [chatgpt.com](https://chatgpt.com).
21
21
  - Navigate: Profile Name (bottom left) -> **Settings** -> **Data controls** -> **Export** -> **Confirm export**.
22
22
  - Await email from OpenAI and download the `.zip` file.
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?tab=readme-ov-file#highlights)):
26
+ First, install [uv](https://github.com/astral-sh/uv) (if you don't have it):
27
+
28
+ MacOS / Linux:
27
29
 
28
30
  ```bash
29
- uvx convoviz
31
+ curl -LsSf https://astral.sh/uv/install.sh | sh
30
32
  ```
31
33
 
32
- You can install it with uv (Recommended):
34
+ Windows:
35
+
36
+ ```powershell
37
+ powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
38
+ ```
39
+
40
+ Then install convoviz:
33
41
 
34
42
  ```bash
35
- uv tool install convoviz
43
+ uv tool install "convoviz[viz]"
36
44
  ```
37
45
 
38
- or pipx:
46
+ The `[viz]` extra includes graphs and word clouds. Skip it for a faster markdown-only install.
47
+
48
+ ### Alternative: pip
49
+
39
50
  ```bash
40
- pipx install convoviz
51
+ python3 -m venv .venv
52
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
53
+ pip install "convoviz[viz]"
41
54
  ```
42
55
 
43
56
  ### 3. Run the tool 🏃‍♂️
@@ -56,9 +69,21 @@ You can provide arguments directly to skip the prompts:
56
69
  convoviz --input path/to/your/export.zip --output path/to/output/folder
57
70
  ```
58
71
 
59
- Notes:
72
+ ##### Selective Output
73
+
74
+ By default, all outputs are generated. Use `--outputs` to pick specific ones:
75
+
76
+ ```bash
77
+ convoviz --input export.zip --outputs markdown --outputs graphs
78
+ ```
79
+
80
+ Options: `markdown`, `graphs`, `wordclouds`. In interactive mode, you'll be prompted.
81
+
82
+ ##### Other Notes
83
+
60
84
  - `--zip` / `-z` is kept as an alias for `--input` for convenience.
61
85
  - You can force non-interactive mode with `--no-interactive`.
86
+ - Use `--flat` to put all Markdown files in a single folder instead of organizing by date.
62
87
 
63
88
  For more options, run:
64
89
 
@@ -70,31 +95,13 @@ convoviz --help
70
95
 
71
96
  And that's it! After running the script, head over to the output folder to see your neatly formatted Markdown files and visualizations.
72
97
 
73
- The main outputs are:
74
-
75
- - **`Markdown/`**: one `.md` file per conversation
76
- - **`Graphs/`**: a small set of high-signal plots, including:
77
- - `overview.png` (dashboard)
78
- - `activity_heatmap.png` (weekday × hour)
79
- - `daily_activity.png` / `monthly_activity.png`
80
- - `model_usage.png`, `conversation_lengths.png`
81
- - `weekday_pattern.png`, `hourly_pattern.png`, `conversation_lifetimes.png`
82
- - **`Word-Clouds/`**: weekly/monthly/yearly word clouds
83
- - **`custom_instructions.json`**: extracted custom instructions
84
-
85
98
  ![wordcloud example](demo/wordcloud-example.png)
86
99
 
87
100
  ## Share Your Feedback! 💌
88
101
 
89
102
  I hope you find this tool useful. I'm continuously looking to improve on this, but I need your help for that.
90
103
 
91
- Whether you're a tech wizard or you're new to all this, I'd love to hear about your journey with the tool. Found a quirk? Have a suggestion? Or just want to send some good vibes? I'm all ears!
92
-
93
- **Here's how you can share your thoughts:**
94
-
95
- 1. **GitHub Issues**: For more specific feedback or if you've stumbled upon a bug, please open an [issue](https://github.com/mohamed-chs/chatgpt-history-export-to-md/issues). This helps me track and address them effectively.
96
-
97
- 2. **GitHub Discussions**: If you just want to share your general experience, have a suggestion, or maybe a cool idea for a new feature, jump into the [discussions](https://github.com/mohamed-chs/chatgpt-history-export-to-md/discussions) page. It's a more casual space where we can chat.
104
+ Whether you're a tech wizard or you're new to all this, I'd love to hear about your journey with the tool. Found a quirk? Have a suggestion? Or just want to send some good vibes? I'm all ears! (see [issues](https://github.com/mohamed-chs/chatgpt-history-export-to-md/issues))
98
105
 
99
106
  And if you've had a great experience, consider giving the project a star ⭐. It keeps me motivated and helps others discover it!
100
107
 
@@ -102,9 +109,6 @@ And if you've had a great experience, consider giving the project a star ⭐. It
102
109
 
103
110
  This is just a small thing I coded to help me see my convos in beautiful markdown. It was originally built with [Obsidian](https://obsidian.md/) (my go-to note-taking app) in mind, but the default output is standard Markdown.
104
111
 
105
- You can choose obsidian flavored md in the cli to get extra features like:
106
- - model reasoning (`reasoning_recap`, `thoughts`) rendered as collapsible `> [!NOTE]-` callouts instead of being hidden.
107
-
108
112
  I wasn't a fan of the clunky, and sometimes paid, browser extensions.
109
113
 
110
114
  It was also a great opportunity to learn more about Python and type annotations. I had mypy, pyright, and ruff all on strict mode, 'twas fun.
@@ -113,12 +117,12 @@ It should(?) also work as library, so you can import and use the models and func
113
117
 
114
118
  ### Offline / reproducible runs
115
119
 
116
- Convoviz uses NLTK stopwords for word clouds. If youre offline and NLTK data isnt already installed, pre-download it once:
120
+ Word clouds use NLTK stopwords. If you're offline and NLTK data isn't installed yet, pre-download it:
117
121
 
118
122
  ```bash
119
- uv run python -c "import nltk; nltk.download('stopwords')"
123
+ python -c "import nltk; nltk.download('stopwords')"
120
124
  ```
121
125
 
122
126
  ### Bookmarklet
123
127
 
124
- Theres also a JavaScript bookmarklet flow under `js/` (experimental) for exporting additional conversation data outside the official ZIP export.
128
+ There's also a JavaScript bookmarklet flow under `js/` (experimental) for exporting additional conversation data outside the official ZIP export.
@@ -1,6 +1,6 @@
1
1
  """Convoviz - ChatGPT data visualization and export tool."""
2
2
 
3
- from convoviz import analysis, config, io, models, renderers, utils
3
+ from convoviz import config, io, models, renderers, utils
4
4
  from convoviz.config import ConvovizConfig, get_default_config
5
5
  from convoviz.models import Conversation, ConversationCollection, Message, Node
6
6
  from convoviz.pipeline import run_pipeline
@@ -23,3 +23,12 @@ __all__ = [
23
23
  "get_default_config",
24
24
  "run_pipeline",
25
25
  ]
26
+
27
+
28
+ def __getattr__(name: str):
29
+ """Lazy import for optional submodules like analysis."""
30
+ if name == "analysis":
31
+ from convoviz import analysis
32
+
33
+ return analysis
34
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,22 @@
1
+ """Data analysis and visualization for convoviz.
2
+
3
+ Requires the [viz] extra: uv tool install "convoviz[viz]"
4
+ """
5
+
6
+ __all__ = [
7
+ "generate_week_barplot",
8
+ "generate_wordcloud",
9
+ ]
10
+
11
+
12
+ def __getattr__(name: str):
13
+ """Lazy import for visualization functions requiring optional dependencies."""
14
+ if name == "generate_week_barplot":
15
+ from convoviz.analysis.graphs import generate_week_barplot
16
+
17
+ return generate_week_barplot
18
+ if name == "generate_wordcloud":
19
+ from convoviz.analysis.wordcloud import generate_wordcloud
20
+
21
+ return generate_wordcloud
22
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -4,8 +4,9 @@ from pathlib import Path
4
4
 
5
5
  import typer
6
6
  from rich.console import Console
7
+ from rich.markup import escape
7
8
 
8
- from convoviz.config import FolderOrganization, get_default_config
9
+ from convoviz.config import FolderOrganization, OutputKind, get_default_config
9
10
  from convoviz.exceptions import ConfigurationError, InvalidZipError
10
11
  from convoviz.interactive import run_interactive_config
11
12
  from convoviz.io.loaders import find_latest_zip
@@ -38,6 +39,12 @@ def run(
38
39
  "-o",
39
40
  help="Path to the output directory.",
40
41
  ),
42
+ outputs: list[OutputKind] | None = typer.Option(
43
+ None,
44
+ "--outputs",
45
+ help="Output types to generate (repeatable). Options: markdown, graphs, wordclouds. "
46
+ "If not specified, all outputs are generated.",
47
+ ),
41
48
  flat: bool = typer.Option(
42
49
  False,
43
50
  "--flat",
@@ -63,6 +70,8 @@ def run(
63
70
  config.input_path = input_path
64
71
  if output_dir:
65
72
  config.output_folder = output_dir
73
+ if outputs:
74
+ config.outputs = set(outputs)
66
75
  if flat:
67
76
  config.folder_organization = FolderOrganization.FLAT
68
77
 
@@ -105,10 +114,10 @@ def run(
105
114
  try:
106
115
  run_pipeline(config)
107
116
  except (InvalidZipError, ConfigurationError) as e:
108
- console.print(f"[bold red]Error:[/bold red] {e}")
117
+ console.print(f"[bold red]Error:[/bold red] {escape(str(e))}")
109
118
  raise typer.Exit(code=1) from None
110
119
  except Exception as e:
111
- console.print(f"[bold red]Unexpected error:[/bold red] {e}")
120
+ console.print(f"[bold red]Unexpected error:[/bold red] {escape(str(e))}")
112
121
  raise typer.Exit(code=1) from None
113
122
 
114
123
 
@@ -14,6 +14,18 @@ class FolderOrganization(str, Enum):
14
14
  DATE = "date" # Nested by year/month (default)
15
15
 
16
16
 
17
+ class OutputKind(str, Enum):
18
+ """Types of outputs that can be generated."""
19
+
20
+ MARKDOWN = "markdown" # Conversation markdown files
21
+ GRAPHS = "graphs" # Usage analytics graphs
22
+ WORDCLOUDS = "wordclouds" # Word cloud visualizations
23
+
24
+
25
+ # Default: generate all outputs
26
+ ALL_OUTPUTS: frozenset[OutputKind] = frozenset(OutputKind)
27
+
28
+
17
29
  class AuthorHeaders(BaseModel):
18
30
  """Headers for different message authors in markdown output."""
19
31
 
@@ -93,6 +105,7 @@ class ConvovizConfig(BaseModel):
93
105
  input_path: Path | None = None
94
106
  output_folder: Path = Field(default_factory=lambda: Path.home() / "Documents" / "ChatGPT-Data")
95
107
  folder_organization: FolderOrganization = FolderOrganization.DATE
108
+ outputs: set[OutputKind] = Field(default_factory=lambda: set(ALL_OUTPUTS))
96
109
  message: MessageConfig = Field(default_factory=MessageConfig)
97
110
  conversation: ConversationConfig = Field(default_factory=ConversationConfig)
98
111
  wordcloud: WordCloudConfig = Field(default_factory=WordCloudConfig)
@@ -0,0 +1,255 @@
1
+ """Interactive configuration prompts using questionary."""
2
+
3
+ from pathlib import Path
4
+ from typing import Literal, Protocol, cast
5
+
6
+ from questionary import Choice, Style, checkbox, select
7
+ from questionary import path as qst_path
8
+ from questionary import text as qst_text
9
+
10
+ from convoviz.config import ConvovizConfig, OutputKind, get_default_config
11
+ from convoviz.io.loaders import find_latest_zip, validate_zip
12
+ from convoviz.utils import colormaps, default_font_path, font_names, font_path, validate_header
13
+
14
+ CUSTOM_STYLE = Style(
15
+ [
16
+ ("qmark", "fg:#34eb9b bold"),
17
+ ("question", "bold fg:#e0e0e0"),
18
+ ("answer", "fg:#34ebeb bold"),
19
+ ("pointer", "fg:#e834eb bold"),
20
+ ("highlighted", "fg:#349ceb bold"),
21
+ ("selected", "fg:#34ebeb"),
22
+ ("separator", "fg:#eb3434"),
23
+ ("instruction", "fg:#eb9434"),
24
+ ("text", "fg:#b2eb34"),
25
+ ("disabled", "fg:#858585 italic"),
26
+ ]
27
+ )
28
+
29
+
30
+ class _QuestionaryPrompt[T](Protocol):
31
+ def ask(self) -> T | None: ...
32
+
33
+
34
+ def _ask_or_cancel[T](prompt: _QuestionaryPrompt[T]) -> T:
35
+ """Ask a questionary prompt; treat Ctrl+C/Ctrl+D as cancelling the run.
36
+
37
+ questionary's `.ask()` returns `None` on cancellation (Ctrl+C / Ctrl+D). We
38
+ convert that to `KeyboardInterrupt` so callers can abort the whole
39
+ interactive session with a single Ctrl+C.
40
+ """
41
+
42
+ result = prompt.ask()
43
+ if result is None:
44
+ raise KeyboardInterrupt
45
+ return result
46
+
47
+
48
+ def _validate_input_path(raw: str) -> bool | str:
49
+ path = Path(raw)
50
+ if not path.exists():
51
+ return "Path must exist"
52
+
53
+ if path.is_dir():
54
+ if (path / "conversations.json").exists():
55
+ return True
56
+ return "Directory must contain conversations.json"
57
+
58
+ if path.suffix.lower() == ".json":
59
+ return True
60
+
61
+ if path.suffix.lower() == ".zip":
62
+ return True if validate_zip(path) else "ZIP must contain conversations.json"
63
+
64
+ return "Input must be a .zip, a .json, or a directory containing conversations.json"
65
+
66
+
67
+ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> ConvovizConfig:
68
+ """Run interactive prompts to configure convoviz.
69
+
70
+ Args:
71
+ initial_config: Optional starting configuration (uses defaults if None)
72
+
73
+ Returns:
74
+ Updated configuration based on user input
75
+ """
76
+ config = initial_config or get_default_config()
77
+
78
+ # Set sensible defaults if not already set
79
+ if not config.input_path:
80
+ latest = find_latest_zip()
81
+ if latest:
82
+ config.input_path = latest
83
+
84
+ if not config.wordcloud.font_path:
85
+ config.wordcloud.font_path = default_font_path()
86
+
87
+ # Prompt for input path
88
+ input_default = str(config.input_path) if config.input_path else ""
89
+ input_result: str = _ask_or_cancel(
90
+ qst_path(
91
+ "Enter the path to the export ZIP, conversations JSON, or extracted directory:",
92
+ default=input_default,
93
+ validate=_validate_input_path,
94
+ style=CUSTOM_STYLE,
95
+ )
96
+ )
97
+
98
+ if input_result:
99
+ config.input_path = Path(input_result)
100
+
101
+ # Prompt for output folder
102
+ output_result: str = _ask_or_cancel(
103
+ qst_path(
104
+ "Enter the path to the output folder:",
105
+ default=str(config.output_folder),
106
+ style=CUSTOM_STYLE,
107
+ )
108
+ )
109
+
110
+ if output_result:
111
+ config.output_folder = Path(output_result)
112
+
113
+ # Prompt for outputs to generate
114
+ output_choices = [
115
+ Choice(title="Markdown conversations", value=OutputKind.MARKDOWN, checked=True),
116
+ Choice(title="Graphs (usage analytics)", value=OutputKind.GRAPHS, checked=True),
117
+ Choice(title="Word clouds", value=OutputKind.WORDCLOUDS, checked=True),
118
+ ]
119
+
120
+ selected_outputs: list[OutputKind] = _ask_or_cancel(
121
+ checkbox(
122
+ "Select outputs to generate:",
123
+ choices=output_choices,
124
+ style=CUSTOM_STYLE,
125
+ )
126
+ )
127
+
128
+ config.outputs = set(selected_outputs) if selected_outputs else set()
129
+
130
+ # Prompt for markdown settings (only if markdown output is selected)
131
+ if OutputKind.MARKDOWN in config.outputs:
132
+ # Prompt for author headers
133
+ headers = config.message.author_headers
134
+ for role in ["user", "assistant"]:
135
+ current = getattr(headers, role)
136
+ result: str = _ask_or_cancel(
137
+ qst_text(
138
+ f"Enter the message header for '{role}':",
139
+ default=current,
140
+ validate=lambda t: validate_header(t)
141
+ or "Must be a valid markdown header (e.g., # Title)",
142
+ style=CUSTOM_STYLE,
143
+ )
144
+ )
145
+ if result:
146
+ setattr(headers, role, result)
147
+
148
+ # Prompt for markdown flavor
149
+ flavor_result = cast(
150
+ Literal["standard", "obsidian"],
151
+ _ask_or_cancel(
152
+ select(
153
+ "Select the markdown flavor:",
154
+ choices=["standard", "obsidian"],
155
+ default=config.conversation.markdown.flavor,
156
+ style=CUSTOM_STYLE,
157
+ )
158
+ ),
159
+ )
160
+
161
+ if flavor_result:
162
+ config.conversation.markdown.flavor = flavor_result
163
+
164
+ # Prompt for YAML headers
165
+ yaml_config = config.conversation.yaml
166
+ yaml_choices = [
167
+ Choice(title=field, checked=getattr(yaml_config, field))
168
+ for field in [
169
+ "title",
170
+ "tags",
171
+ "chat_link",
172
+ "create_time",
173
+ "update_time",
174
+ "model",
175
+ "used_plugins",
176
+ "message_count",
177
+ "content_types",
178
+ "custom_instructions",
179
+ ]
180
+ ]
181
+
182
+ selected: list[str] = _ask_or_cancel(
183
+ checkbox(
184
+ "Select YAML metadata headers to include:",
185
+ choices=yaml_choices,
186
+ style=CUSTOM_STYLE,
187
+ )
188
+ )
189
+
190
+ selected_set = set(selected)
191
+ for field_name in [
192
+ "title",
193
+ "tags",
194
+ "chat_link",
195
+ "create_time",
196
+ "update_time",
197
+ "model",
198
+ "used_plugins",
199
+ "message_count",
200
+ "content_types",
201
+ "custom_instructions",
202
+ ]:
203
+ setattr(yaml_config, field_name, field_name in selected_set)
204
+
205
+ # Prompt for wordcloud settings (only if wordclouds output is selected)
206
+ if OutputKind.WORDCLOUDS in config.outputs:
207
+ # Prompt for font
208
+ available_fonts = font_names()
209
+ if available_fonts:
210
+ current_font = (
211
+ config.wordcloud.font_path.stem
212
+ if config.wordcloud.font_path
213
+ else available_fonts[0]
214
+ )
215
+ font_result: str = _ask_or_cancel(
216
+ select(
217
+ "Select the font for word clouds:",
218
+ choices=available_fonts,
219
+ default=current_font if current_font in available_fonts else available_fonts[0],
220
+ style=CUSTOM_STYLE,
221
+ )
222
+ )
223
+
224
+ if font_result:
225
+ config.wordcloud.font_path = font_path(font_result)
226
+
227
+ # Prompt for colormap
228
+ available_colormaps = colormaps()
229
+ if available_colormaps:
230
+ colormap_result: str = _ask_or_cancel(
231
+ select(
232
+ "Select the color theme for word clouds:",
233
+ choices=available_colormaps,
234
+ default=config.wordcloud.colormap
235
+ if config.wordcloud.colormap in available_colormaps
236
+ else available_colormaps[0],
237
+ style=CUSTOM_STYLE,
238
+ )
239
+ )
240
+
241
+ if colormap_result:
242
+ config.wordcloud.colormap = colormap_result
243
+
244
+ # Prompt for custom stopwords
245
+ stopwords_result: str = _ask_or_cancel(
246
+ qst_text(
247
+ "Enter custom stopwords (comma-separated):",
248
+ default=config.wordcloud.custom_stopwords,
249
+ style=CUSTOM_STYLE,
250
+ )
251
+ )
252
+
253
+ config.wordcloud.custom_stopwords = stopwords_result
254
+
255
+ return config