convoviz 0.3.8__py3-none-any.whl → 0.4.0__py3-none-any.whl

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.
@@ -8,6 +8,7 @@ Goals:
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import logging
11
12
  from collections import defaultdict
12
13
  from collections.abc import Callable, Iterable
13
14
  from datetime import UTC, datetime
@@ -25,6 +26,8 @@ from convoviz.config import GraphConfig, get_default_config
25
26
  from convoviz.models import ConversationCollection
26
27
  from convoviz.utils import get_asset_path
27
28
 
29
+ logger = logging.getLogger(__name__)
30
+
28
31
  WEEKDAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
29
32
 
30
33
 
@@ -742,6 +745,7 @@ def generate_summary_graphs(
742
745
  cfg = config or get_default_config().graph
743
746
 
744
747
  user_ts = collection.timestamps("user")
748
+ logger.info(f"Generating summary graphs to {output_dir}")
745
749
 
746
750
  tasks: list[tuple[str, str, Callable[[], Figure]]] = [
747
751
  ("Overview", "overview.png", lambda: generate_summary_dashboard(collection, cfg)),
@@ -1,5 +1,6 @@
1
1
  """Word cloud generation for conversation text."""
2
2
 
3
+ import logging
3
4
  import os
4
5
  from concurrent.futures import ProcessPoolExecutor
5
6
  from functools import lru_cache
@@ -15,6 +16,8 @@ from wordcloud import WordCloud
15
16
  from convoviz.config import WordCloudConfig
16
17
  from convoviz.models import ConversationCollection
17
18
 
19
+ logger = logging.getLogger(__name__)
20
+
18
21
  # Languages for stopwords
19
22
  STOPWORD_LANGUAGES = [
20
23
  "arabic",
@@ -149,6 +152,7 @@ def generate_wordclouds(
149
152
  progress_bar: Whether to show progress bars
150
153
  """
151
154
  output_dir.mkdir(parents=True, exist_ok=True)
155
+ logger.info(f"Generating wordclouds to {output_dir}")
152
156
 
153
157
  week_groups = collection.group_by_week()
154
158
  month_groups = collection.group_by_month()
@@ -188,6 +192,7 @@ def generate_wordclouds(
188
192
  max_workers = max(1, cpu_count // 2)
189
193
 
190
194
  # Use parallel processing for speedup on multi-core systems
195
+ logger.debug(f"Starting wordcloud generation with {max_workers} workers for {len(tasks)} tasks")
191
196
  with ProcessPoolExecutor(max_workers=max_workers) as executor:
192
197
  list(
193
198
  tqdm(
convoviz/cli.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Command-line interface for convoviz."""
2
2
 
3
+ import logging
3
4
  from pathlib import Path
4
5
 
5
6
  import typer
@@ -10,6 +11,7 @@ from convoviz.config import FolderOrganization, OutputKind, get_default_config
10
11
  from convoviz.exceptions import ConfigurationError, InvalidZipError
11
12
  from convoviz.interactive import run_interactive_config
12
13
  from convoviz.io.loaders import find_latest_zip
14
+ from convoviz.logging_config import setup_logging
13
15
  from convoviz.pipeline import run_pipeline
14
16
  from convoviz.utils import default_font_path
15
17
 
@@ -57,8 +59,26 @@ def run(
57
59
  "-i/-I",
58
60
  help="Force interactive mode on or off.",
59
61
  ),
62
+ verbose: int = typer.Option(
63
+ 0,
64
+ "--verbose",
65
+ "-v",
66
+ help="Increase verbosity. Use -vv for debug.",
67
+ count=True,
68
+ ),
69
+ log_file: Path | None = typer.Option(
70
+ None,
71
+ "--log-file",
72
+ help="Path to log file. Defaults to a temporary file.",
73
+ ),
60
74
  ) -> None:
61
75
  """Convert ChatGPT export data to markdown and generate visualizations."""
76
+ # Setup logging immediately
77
+ log_path = setup_logging(verbose, log_file)
78
+ logger = logging.getLogger("convoviz.cli")
79
+ console.print(f"[dim]Logging to: {log_path}[/dim]")
80
+ logger.debug(f"Logging initialized. Output: {log_path}")
81
+
62
82
  if ctx.invoked_subcommand is not None:
63
83
  return
64
84
 
@@ -114,10 +134,13 @@ def run(
114
134
  try:
115
135
  run_pipeline(config)
116
136
  except (InvalidZipError, ConfigurationError) as e:
137
+ logger.error(f"Known error: {e}")
117
138
  console.print(f"[bold red]Error:[/bold red] {escape(str(e))}")
118
139
  raise typer.Exit(code=1) from None
119
140
  except Exception as e:
141
+ logger.exception("Unexpected error occurred")
120
142
  console.print(f"[bold red]Unexpected error:[/bold red] {escape(str(e))}")
143
+ console.print(f"[dim]See log file for details: {log_path}[/dim]")
121
144
  raise typer.Exit(code=1) from None
122
145
 
123
146
 
convoviz/interactive.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Interactive configuration prompts using questionary."""
2
2
 
3
+ import logging
3
4
  from pathlib import Path
4
5
  from typing import Literal, Protocol, cast
5
6
 
@@ -26,6 +27,8 @@ CUSTOM_STYLE = Style(
26
27
  ]
27
28
  )
28
29
 
30
+ logger = logging.getLogger(__name__)
31
+
29
32
 
30
33
  class _QuestionaryPrompt[T](Protocol):
31
34
  def ask(self) -> T | None: ...
@@ -74,6 +77,7 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
74
77
  Updated configuration based on user input
75
78
  """
76
79
  config = initial_config or get_default_config()
80
+ logger.info("Starting interactive configuration")
77
81
 
78
82
  # Set sensible defaults if not already set
79
83
  if not config.input_path:
@@ -97,6 +101,7 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
97
101
 
98
102
  if input_result:
99
103
  config.input_path = Path(input_result)
104
+ logger.debug(f"User selected input: {config.input_path}")
100
105
 
101
106
  # Prompt for output folder
102
107
  output_result: str = _ask_or_cancel(
@@ -109,6 +114,7 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
109
114
 
110
115
  if output_result:
111
116
  config.output_folder = Path(output_result)
117
+ logger.debug(f"User selected output: {config.output_folder}")
112
118
 
113
119
  # Prompt for outputs to generate
114
120
  output_choices = [
@@ -126,6 +132,7 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
126
132
  )
127
133
 
128
134
  config.outputs = set(selected_outputs) if selected_outputs else set()
135
+ logger.debug(f"User selected outputs: {config.outputs}")
129
136
 
130
137
  # Prompt for markdown settings (only if markdown output is selected)
131
138
  if OutputKind.MARKDOWN in config.outputs:
@@ -144,6 +151,7 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
144
151
  )
145
152
  if result:
146
153
  setattr(headers, role, result)
154
+ logger.debug(f"User selected headers: {headers}")
147
155
 
148
156
  # Prompt for markdown flavor
149
157
  flavor_result = cast(
@@ -160,6 +168,7 @@ def run_interactive_config(initial_config: ConvovizConfig | None = None) -> Conv
160
168
 
161
169
  if flavor_result:
162
170
  config.conversation.markdown.flavor = flavor_result
171
+ logger.debug(f"User selected flavor: {config.conversation.markdown.flavor}")
163
172
 
164
173
  # Prompt for YAML headers
165
174
  yaml_config = config.conversation.yaml
convoviz/io/assets.py CHANGED
@@ -1,8 +1,11 @@
1
1
  "Asset management functions."
2
2
 
3
+ import logging
3
4
  import shutil
4
5
  from pathlib import Path
5
6
 
7
+ logger = logging.getLogger(__name__)
8
+
6
9
 
7
10
  def resolve_asset_path(source_dir: Path, asset_id: str) -> Path | None:
8
11
  """Find the actual file for a given asset ID in the source directory.
@@ -26,6 +29,7 @@ def resolve_asset_path(source_dir: Path, asset_id: str) -> Path | None:
26
29
  # 1. Try exact match
27
30
  exact_path = (source_dir / asset_id).resolve()
28
31
  if exact_path.exists() and exact_path.is_file() and exact_path.is_relative_to(source_dir):
32
+ logger.debug(f"Resolved asset (exact): {asset_id} -> {exact_path}")
29
33
  return exact_path
30
34
 
31
35
  # 2. Try prefix match in root
@@ -37,6 +41,7 @@ def resolve_asset_path(source_dir: Path, asset_id: str) -> Path | None:
37
41
  if p.is_file() and p.resolve().is_relative_to(source_dir)
38
42
  ]
39
43
  if files:
44
+ logger.debug(f"Resolved asset (prefix root): {asset_id} -> {files[0]}")
40
45
  return files[0]
41
46
  except Exception:
42
47
  pass
@@ -53,6 +58,7 @@ def resolve_asset_path(source_dir: Path, asset_id: str) -> Path | None:
53
58
  if p.is_file() and p.resolve().is_relative_to(dalle_dir)
54
59
  ]
55
60
  if files:
61
+ logger.debug(f"Resolved asset (dalle): {asset_id} -> {files[0]}")
56
62
  return files[0]
57
63
  except Exception:
58
64
  pass
@@ -69,6 +75,7 @@ def resolve_asset_path(source_dir: Path, asset_id: str) -> Path | None:
69
75
  if p.is_file() and p.resolve().is_relative_to(user_dir)
70
76
  ]
71
77
  if files:
78
+ logger.debug(f"Resolved asset (user dir): {asset_id} -> {files[0]}")
72
79
  return files[0]
73
80
  except Exception:
74
81
  pass
@@ -92,7 +99,11 @@ def copy_asset(source_path: Path, dest_dir: Path) -> str:
92
99
  dest_path = assets_dir / source_path.name
93
100
 
94
101
  if not dest_path.exists():
95
- shutil.copy2(source_path, dest_path)
102
+ try:
103
+ shutil.copy2(source_path, dest_path)
104
+ logger.debug(f"Copied asset: {source_path.name}")
105
+ except Exception as e:
106
+ logger.warning(f"Failed to copy asset {source_path}: {e}")
96
107
 
97
108
  # Return forward-slash path for Markdown compatibility even on Windows
98
109
  return f"assets/{source_path.name}"
convoviz/io/loaders.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Loading functions for conversations and collections."""
2
2
 
3
+ import logging
3
4
  from pathlib import Path, PurePosixPath
4
5
  from zipfile import ZipFile
5
6
 
@@ -8,6 +9,8 @@ from orjson import loads
8
9
  from convoviz.exceptions import InvalidZipError
9
10
  from convoviz.models import Conversation, ConversationCollection
10
11
 
12
+ logger = logging.getLogger(__name__)
13
+
11
14
 
12
15
  def _is_safe_zip_member_name(name: str) -> bool:
13
16
  """Return True if a ZIP entry name is safe to extract.
@@ -46,6 +49,7 @@ def extract_archive(filepath: Path) -> Path:
46
49
  """
47
50
  folder = filepath.with_suffix("")
48
51
  folder.mkdir(parents=True, exist_ok=True)
52
+ logger.info(f"Extracting archive: {filepath} to {folder}")
49
53
 
50
54
  with ZipFile(filepath) as zf:
51
55
  for member in zf.infolist():
@@ -115,6 +119,7 @@ def load_collection_from_json(filepath: Path | str) -> ConversationCollection:
115
119
  Loaded ConversationCollection object
116
120
  """
117
121
  filepath = Path(filepath)
122
+ logger.debug(f"Loading collection from JSON: {filepath}")
118
123
  with filepath.open(encoding="utf-8") as f:
119
124
  data = loads(f.read())
120
125
 
convoviz/io/writers.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Writing functions for conversations and collections."""
2
2
 
3
+ import logging
3
4
  from os import utime as os_utime
4
5
  from pathlib import Path
5
6
  from urllib.parse import quote
@@ -13,6 +14,8 @@ from convoviz.models import Conversation, ConversationCollection
13
14
  from convoviz.renderers import render_conversation
14
15
  from convoviz.utils import sanitize
15
16
 
17
+ logger = logging.getLogger(__name__)
18
+
16
19
  # Month names for folder naming
17
20
  _MONTH_NAMES = [
18
21
  "January",
@@ -102,6 +105,7 @@ def save_conversation(
102
105
  markdown = render_conversation(conversation, config, headers, asset_resolver=asset_resolver)
103
106
  with final_path.open("w", encoding="utf-8") as f:
104
107
  f.write(markdown)
108
+ logger.debug(f"Saved conversation: {final_path}")
105
109
 
106
110
  # Set modification time
107
111
  timestamp = conversation.update_time.timestamp()
@@ -135,6 +139,7 @@ def _generate_year_index(year_dir: Path, year: str) -> None:
135
139
 
136
140
  index_path = year_dir / "_index.md"
137
141
  index_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
142
+ logger.debug(f"Generated year index: {index_path}")
138
143
 
139
144
 
140
145
  def _generate_month_index(month_dir: Path, year: str, month: str) -> None:
@@ -162,6 +167,7 @@ def _generate_month_index(month_dir: Path, year: str, month: str) -> None:
162
167
 
163
168
  index_path = month_dir / "_index.md"
164
169
  index_path.write_text("\n".join(lines) + "\n", encoding="utf-8")
170
+ logger.debug(f"Generated month index: {index_path}")
165
171
 
166
172
 
167
173
  def save_collection(
@@ -0,0 +1,69 @@
1
+ """Logging configuration for convoviz."""
2
+
3
+ import logging
4
+ import tempfile
5
+ from pathlib import Path
6
+
7
+ from rich.logging import RichHandler
8
+
9
+
10
+ def setup_logging(
11
+ verbosity: int = 0,
12
+ log_file: Path | None = None,
13
+ ) -> Path:
14
+ """Set up logging configuration.
15
+
16
+ Args:
17
+ verbosity: Level of verbosity (0=WARNING, 1=INFO, 2=DEBUG)
18
+ log_file: Path to log file. If None, a temporary file is created.
19
+
20
+ Returns:
21
+ Path to the log file used.
22
+ """
23
+ # clear existing handlers
24
+ root_logger = logging.getLogger()
25
+ root_logger.handlers.clear()
26
+
27
+ # Determine log level for console
28
+ if verbosity >= 2:
29
+ console_level = logging.DEBUG
30
+ elif verbosity >= 1:
31
+ console_level = logging.INFO
32
+ else:
33
+ console_level = logging.WARNING
34
+
35
+ # Console handler (Rich)
36
+ console_handler = RichHandler(
37
+ rich_tracebacks=True,
38
+ markup=True,
39
+ show_time=False,
40
+ show_path=False,
41
+ )
42
+ console_handler.setLevel(console_level)
43
+
44
+ # File handler
45
+ if log_file is None:
46
+ # Create temp file if not specified
47
+ with tempfile.NamedTemporaryFile(prefix="convoviz_", suffix=".log", delete=False) as tf:
48
+ log_file = Path(tf.name)
49
+
50
+ # Ensure parent dir exists
51
+ if not log_file.parent.exists():
52
+ log_file.parent.mkdir(parents=True, exist_ok=True)
53
+
54
+ file_handler = logging.FileHandler(log_file, encoding="utf-8")
55
+ file_handler.setLevel(logging.DEBUG) # Always log DEBUG to file
56
+ file_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
57
+ file_handler.setFormatter(file_formatter)
58
+
59
+ # Configure root logger
60
+ # We set root level to DEBUG so that the handlers can filter as they please
61
+ root_logger.setLevel(logging.DEBUG)
62
+ root_logger.addHandler(console_handler)
63
+ root_logger.addHandler(file_handler)
64
+
65
+ # Reduce noise from explicit libraries if necessary
66
+ logging.getLogger("matplotlib").setLevel(logging.WARNING)
67
+ logging.getLogger("PIL").setLevel(logging.WARNING)
68
+
69
+ return log_file
convoviz/pipeline.py CHANGED
@@ -1,5 +1,6 @@
1
1
  """Main processing pipeline for convoviz."""
2
2
 
3
+ import logging
3
4
  from pathlib import Path
4
5
  from shutil import rmtree
5
6
 
@@ -15,6 +16,7 @@ from convoviz.io.loaders import (
15
16
  from convoviz.io.writers import save_collection
16
17
 
17
18
  console = Console()
19
+ logger = logging.getLogger(__name__)
18
20
 
19
21
 
20
22
  def _safe_uri(path: Path) -> str:
@@ -46,6 +48,7 @@ def run_pipeline(config: ConvovizConfig) -> None:
46
48
  if not input_path.exists():
47
49
  raise InvalidZipError(str(input_path), reason="File does not exist")
48
50
 
51
+ logger.info(f"Starting pipeline with input: {input_path}")
49
52
  console.print(f"Loading data from {input_path} [bold yellow]📂[/bold yellow] ...\n")
50
53
 
51
54
  # Load collection based on input type
@@ -62,6 +65,7 @@ def run_pipeline(config: ConvovizConfig) -> None:
62
65
  else:
63
66
  # Assume zip
64
67
  collection = load_collection_from_zip(input_path)
68
+ logger.info(f"Loaded collection with {len(collection.conversations)} conversations")
65
69
 
66
70
  # Try to merge bookmarklet data if available
67
71
  bookmarklet_json = find_latest_bookmarklet_json()
@@ -70,6 +74,7 @@ def run_pipeline(config: ConvovizConfig) -> None:
70
74
  try:
71
75
  bookmarklet_collection = load_collection_from_json(bookmarklet_json)
72
76
  collection.update(bookmarklet_collection)
77
+ logger.info("Merged bookmarklet data")
73
78
  except Exception as e:
74
79
  console.print(
75
80
  f"[bold yellow]Warning:[/bold yellow] Failed to load bookmarklet data: {e}"
@@ -114,6 +119,7 @@ def run_pipeline(config: ConvovizConfig) -> None:
114
119
  folder_organization=config.folder_organization,
115
120
  progress_bar=True,
116
121
  )
122
+ logger.info("Markdown generation complete")
117
123
  console.print(
118
124
  f"\nDone [bold green]✅[/bold green] ! "
119
125
  f"Check the output [bold blue]📄[/bold blue] here: {_safe_uri(markdown_folder)} 🔗\n"
@@ -138,6 +144,7 @@ def run_pipeline(config: ConvovizConfig) -> None:
138
144
  config.graph,
139
145
  progress_bar=True,
140
146
  )
147
+ logger.info("Graph generation complete")
141
148
  console.print(
142
149
  f"\nDone [bold green]✅[/bold green] ! "
143
150
  f"Check the output [bold blue]📈[/bold blue] here: {_safe_uri(graph_folder)} 🔗\n"
@@ -162,6 +169,7 @@ def run_pipeline(config: ConvovizConfig) -> None:
162
169
  config.wordcloud,
163
170
  progress_bar=True,
164
171
  )
172
+ logger.info("Wordcloud generation complete")
165
173
  console.print(
166
174
  f"\nDone [bold green]✅[/bold green] ! "
167
175
  f"Check the output [bold blue]🔡☁️[/bold blue] here: {_safe_uri(wordcloud_folder)} 🔗\n"
@@ -172,5 +180,5 @@ def run_pipeline(config: ConvovizConfig) -> None:
172
180
  f"Explore the full gallery [bold yellow]🖼️[/bold yellow] at: {_safe_uri(output_folder)} 🔗\n\n"
173
181
  "I hope you enjoy the outcome 🤞.\n\n"
174
182
  "If you appreciate it, kindly give the project a star 🌟 on GitHub:\n\n"
175
- "➡️ https://github.com/mohamed-chs/chatgpt-history-export-to-md 🔗\n\n"
183
+ "➡️ https://github.com/mohamed-chs/convoviz 🔗\n\n"
176
184
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: convoviz
3
- Version: 0.3.8
3
+ Version: 0.4.0
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
@@ -19,7 +19,7 @@ Requires-Dist: typer>=0.21.0
19
19
  Requires-Dist: nltk>=3.9.2 ; extra == 'viz'
20
20
  Requires-Dist: wordcloud>=1.9.5 ; extra == 'viz'
21
21
  Requires-Python: >=3.12
22
- Project-URL: Repository, https://github.com/mohamed-chs/chatgpt-history-export-to-md
22
+ Project-URL: Repository, https://github.com/mohamed-chs/convoviz
23
23
  Provides-Extra: viz
24
24
  Description-Content-Type: text/markdown
25
25
 
@@ -45,29 +45,23 @@ See examples [here](demo).
45
45
 
46
46
  ### 2. Install the tool 🛠
47
47
 
48
- First, install [uv](https://github.com/astral-sh/uv) (if you don't have it):
48
+ One command to install everything:
49
49
 
50
- MacOS / Linux:
50
+ **Linux / macOS:**
51
51
 
52
52
  ```bash
53
- curl -LsSf https://astral.sh/uv/install.sh | sh
53
+ curl -fsSL https://raw.githubusercontent.com/mohamed-chs/convoviz/main/install.sh | bash
54
54
  ```
55
55
 
56
- Windows:
56
+ **Windows (PowerShell):**
57
57
 
58
58
  ```powershell
59
- powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
59
+ irm https://raw.githubusercontent.com/mohamed-chs/convoviz/main/install.ps1 | iex
60
60
  ```
61
61
 
62
- Then install convoviz:
62
+ This installs [uv](https://github.com/astral-sh/uv) (if needed) and convoviz with graphs and word clouds.
63
63
 
64
- ```bash
65
- uv tool install "convoviz[viz]"
66
- ```
67
-
68
- The `[viz]` extra includes graphs and word clouds. Skip it for a faster markdown-only install.
69
-
70
- ### Alternative: pip
64
+ #### Alternative: pip
71
65
 
72
66
  ```bash
73
67
  python3 -m venv .venv
@@ -106,6 +100,8 @@ Options: `markdown`, `graphs`, `wordclouds`. In interactive mode, you'll be prom
106
100
  - `--zip` / `-z` is kept as an alias for `--input` for convenience.
107
101
  - You can force non-interactive mode with `--no-interactive`.
108
102
  - Use `--flat` to put all Markdown files in a single folder instead of organizing by date.
103
+ - Use `--verbose` or `-v` for detailed logging (use `-vv` for debug logs).
104
+ - Use `--log-file` to specify a custom log file (logs default to a temporary file if not specified).
109
105
 
110
106
  For more options, run:
111
107
 
@@ -123,7 +119,7 @@ And that's it! After running the script, head over to the output folder to see y
123
119
 
124
120
  I hope you find this tool useful. I'm continuously looking to improve on this, but I need your help for that.
125
121
 
126
- 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))
122
+ 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/convoviz/issues))
127
123
 
128
124
  And if you've had a great experience, consider giving the project a star ⭐. It keeps me motivated and helps others discover it!
129
125
 
@@ -1,8 +1,8 @@
1
1
  convoviz/__init__.py,sha256=UjwkFEmRXhE-3qhsoGTG9XhtoL2cP0o4h3Sp9fcA2Ic,858
2
2
  convoviz/__main__.py,sha256=1qiGW13_SgL7wJi8iioIN-AAHGkNGnEl5q_RcPUrI0s,143
3
3
  convoviz/analysis/__init__.py,sha256=1dHjnw88mepSY3Q3U8lEvSqkuWPtkzpyYMgq0CIWoXk,654
4
- convoviz/analysis/graphs.py,sha256=4uGxVVnbDnBODPlu3g9jZPl6X3JEb4Ri1jXEekhV7-8,29681
5
- convoviz/analysis/wordcloud.py,sha256=oTcuyfYr-dffBah9Ou9JUPxJxFlrH1WQVHGcz2orgnQ,5966
4
+ convoviz/analysis/graphs.py,sha256=OGLkyKx9Fq7r4jK-A46oy1NWttQ5F2MLxO4mfW6wkfc,29796
5
+ convoviz/analysis/wordcloud.py,sha256=3MfBg_Aq7-nz0Zc-FhkxEvTljImIQU0rmXZv81WUBDM,6178
6
6
  convoviz/assets/colormaps.txt,sha256=59TSGz428AxY14AEvymAH2IJ2RT9Mlp7Sy0N12NEdXQ,108
7
7
  convoviz/assets/fonts/AmaticSC-Regular.ttf,sha256=83clh7a3urnTLud0_yZofuIb6BdyC2LMI9jhE6G2LvU,142696
8
8
  convoviz/assets/fonts/ArchitectsDaughter-Regular.ttf,sha256=fnrj5_N_SlY2Lj3Ehqz5aKECPZVJlJAflgsOU94_qIM,37756
@@ -36,26 +36,27 @@ convoviz/assets/fonts/YsabeauSC-Regular.ttf,sha256=G4lkq34KKqZOaoomtxFz_KlwVmxg5
36
36
  convoviz/assets/fonts/YsabeauSC-Thin.ttf,sha256=hZGOZNTRrxbiUPE2VDeLbtnaRwkMOBaVQbq7Fwx-34c,116932
37
37
  convoviz/assets/fonts/Zeyada-Regular.ttf,sha256=fKhkrp9VHt_3Aw8JfkfkPeC2j3CilLWuPUudzBeawPQ,57468
38
38
  convoviz/assets/stopwords.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
39
- convoviz/cli.py,sha256=yOcRIJlF4yxFF4FXAl3XoTs-Qkl-y01bRm-sdWVVj9k,4098
39
+ convoviz/cli.py,sha256=vD4SnS3xthKwNBniWNUy9x1maloBs-xRgcsPHfXzwm4,4888
40
40
  convoviz/config.py,sha256=qo4JPkJRx1UgvVN_x-XmycxdjU9XwPlqoZWJLsDMSAs,3592
41
41
  convoviz/exceptions.py,sha256=bQpIKls48uOQpagEJAxpXf5LF7QoagRRfbD0MjWC7Ak,1476
42
- convoviz/interactive.py,sha256=ImwQTB_JF33SgbCO7ggr12VTx5P4CJksx_vW2kDYV_4,8252
42
+ convoviz/interactive.py,sha256=Xq55VJJ4OQ9NlY4nlb2NRLzxmA5-ot521N8tGqyPix4,8691
43
43
  convoviz/io/__init__.py,sha256=y70TYypJ36_kaEA04E2wa1EDaKQVjprKItoKR6MMs4M,471
44
- convoviz/io/assets.py,sha256=WLauNEvk9QRo0Q52KE_bPyCRFa1CjM54L1j8SsTfGwg,2894
45
- convoviz/io/loaders.py,sha256=RuGiGzpyNcgwTxOM-m2ehhyh2mP1-k1YamK8-VynR3g,5713
46
- convoviz/io/writers.py,sha256=z81PKd7dO8XzVju_Nc5naeDr1rQSdo7K-ZszjhRXUzI,6848
44
+ convoviz/io/assets.py,sha256=5zcZPlQa9niDw9o-sqJIKgLc0OJ9auzd6KAve5WfBkQ,3459
45
+ convoviz/io/loaders.py,sha256=SqmBWUBqT5lsCf01yy-FUhwIxbiKTFMQnj4k213DsGI,5891
46
+ convoviz/io/writers.py,sha256=-HTvj7D9sqM8M-RsGwd44AquxCVmcDVHgta22QlfNzU,7068
47
+ convoviz/logging_config.py,sha256=PRuOKij8UD6sKdg3lAsu9lUsTUZ3O6_6uffnyg07M1U,2060
47
48
  convoviz/models/__init__.py,sha256=6gAfrk6KJT2QxdvX_v15mUdfIqEw1xKxwQlKSfyA5eI,532
48
49
  convoviz/models/collection.py,sha256=L658yKMNC6IZrfxYxZBe-oO9COP_bzVfRznnNon7tfU,4467
49
50
  convoviz/models/conversation.py,sha256=ssx1Z6LM9kJIx3OucQW8JVoAc8zCdxj1iOLtie2B3ak,5678
50
51
  convoviz/models/message.py,sha256=0CJ9hJ1rQiesn1SgHqFgEgKUgS7XAPGtSunQl5q8Pl4,8316
51
52
  convoviz/models/node.py,sha256=1vBAtKVscYsUBDnKAOyLxuZaK9KoVF1dFXiKXRHxUnY,1946
52
- convoviz/pipeline.py,sha256=ClOazQRiOnf4TKogoR1YLam1w7n4k6tznLrEOaB0u8M,6376
53
+ convoviz/pipeline.py,sha256=1kLtsNDN3LYNudyPBlyKwQZ8zWCmRKveP3VWfIgichw,6765
53
54
  convoviz/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
55
  convoviz/renderers/__init__.py,sha256=IQgwD9NqtUgbS9zwyPBNZbBIZcFrbZ9C7WMAV-X3Xdg,261
55
56
  convoviz/renderers/markdown.py,sha256=55PACkd-F0mmBXWXQ5SrfJr3UNrK_z2spQnePdk1UsQ,7849
56
57
  convoviz/renderers/yaml.py,sha256=XG1s4VhDdx-TiqekTkgED87RZ1lVQ7IwrbA-sZHrs7k,4056
57
58
  convoviz/utils.py,sha256=IQEKYHhWOnYxlr4GwAHoquG0BXTlVRkORL80oUSaIeQ,3417
58
- convoviz-0.3.8.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
59
- convoviz-0.3.8.dist-info/entry_points.txt,sha256=HYsmsw5vt36yYHB05uVU48AK2WLkcwshly7m7KKuZMY,54
60
- convoviz-0.3.8.dist-info/METADATA,sha256=TmVm-7-Z5ybVewNbWxNeu2dNkidmd-sO228pLsW7ufA,4860
61
- convoviz-0.3.8.dist-info/RECORD,,
59
+ convoviz-0.4.0.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
60
+ convoviz-0.4.0.dist-info/entry_points.txt,sha256=HYsmsw5vt36yYHB05uVU48AK2WLkcwshly7m7KKuZMY,54
61
+ convoviz-0.4.0.dist-info/METADATA,sha256=3bFkn8R1Onbowe0MxMFUiUzXsrzxOEF_Fenlm1ssZQQ,4958
62
+ convoviz-0.4.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.26
2
+ Generator: uv 0.9.28
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any