lestash 1.64.2__tar.gz → 1.64.4__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 (67) hide show
  1. {lestash-1.64.2 → lestash-1.64.4}/PKG-INFO +2 -2
  2. {lestash-1.64.2 → lestash-1.64.4}/pyproject.toml +1 -1
  3. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/sources.py +14 -0
  4. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/database.py +14 -3
  5. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/google_auth.py +1 -0
  6. {lestash-1.64.2 → lestash-1.64.4}/tests/test_history.py +16 -0
  7. {lestash-1.64.2 → lestash-1.64.4}/.gitignore +0 -0
  8. {lestash-1.64.2 → lestash-1.64.4}/README.md +0 -0
  9. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/__init__.py +0 -0
  10. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/__main__.py +0 -0
  11. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/__init__.py +0 -0
  12. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/config.py +0 -0
  13. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/db.py +0 -0
  14. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/embeddings.py +0 -0
  15. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/enrich.py +0 -0
  16. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/google.py +0 -0
  17. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/items.py +0 -0
  18. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/main.py +0 -0
  19. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/media.py +0 -0
  20. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/cli/profiles.py +0 -0
  21. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/__init__.py +0 -0
  22. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/config.py +0 -0
  23. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/db_admin.py +0 -0
  24. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/embeddings.py +0 -0
  25. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/enrichment.py +0 -0
  26. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/exceptions.py +0 -0
  27. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/google_drive.py +0 -0
  28. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/logging.py +0 -0
  29. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/TUNING.md +0 -0
  30. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/__init__.py +0 -0
  31. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/annotations.py +0 -0
  32. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/cleanup.py +0 -0
  33. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/extractor.py +0 -0
  34. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/images.py +0 -0
  35. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/links.py +0 -0
  36. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/ocr.py +0 -0
  37. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/persistence.py +0 -0
  38. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/runner.py +0 -0
  39. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/types.py +0 -0
  40. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/pdf_enrich/version.py +0 -0
  41. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/core/text_extract.py +0 -0
  42. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/models/__init__.py +0 -0
  43. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/models/item.py +0 -0
  44. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/models/source.py +0 -0
  45. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/models/tag.py +0 -0
  46. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/plugins/__init__.py +0 -0
  47. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/plugins/base.py +0 -0
  48. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/plugins/loader.py +0 -0
  49. {lestash-1.64.2 → lestash-1.64.4}/src/lestash/plugins/publisher.py +0 -0
  50. {lestash-1.64.2 → lestash-1.64.4}/tests/__init__.py +0 -0
  51. {lestash-1.64.2 → lestash-1.64.4}/tests/conftest.py +0 -0
  52. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/__init__.py +0 -0
  53. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/conftest.py +0 -0
  54. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/test_annotations.py +0 -0
  55. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/test_cleanup.py +0 -0
  56. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/test_extractor.py +0 -0
  57. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/test_images.py +0 -0
  58. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/test_links.py +0 -0
  59. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/test_ocr.py +0 -0
  60. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/test_persistence.py +0 -0
  61. {lestash-1.64.2 → lestash-1.64.4}/tests/pdf_enrich/test_runner.py +0 -0
  62. {lestash-1.64.2 → lestash-1.64.4}/tests/test_db_admin.py +0 -0
  63. {lestash-1.64.2 → lestash-1.64.4}/tests/test_items_display.py +0 -0
  64. {lestash-1.64.2 → lestash-1.64.4}/tests/test_post_cache.py +0 -0
  65. {lestash-1.64.2 → lestash-1.64.4}/tests/test_publisher.py +0 -0
  66. {lestash-1.64.2 → lestash-1.64.4}/tests/test_sources_enable.py +0 -0
  67. {lestash-1.64.2 → lestash-1.64.4}/tests/test_text_extract.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: lestash
3
- Version: 1.64.2
3
+ Version: 1.64.4
4
4
  Summary: Personal knowledge base CLI - aggregate content from multiple sources
5
5
  License: AGPL-3.0-only
6
6
  Requires-Python: >=3.12
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lestash"
3
- version = "1.64.2"
3
+ version = "1.64.4"
4
4
  description = "Personal knowledge base CLI - aggregate content from multiple sources"
5
5
  readme = "README.md"
6
6
  license = {text = "AGPL-3.0-only"}
@@ -239,6 +239,20 @@ def sync_source(
239
239
  except ImportError:
240
240
  pass
241
241
 
242
+ # Resolve parent_id for YouTube transcripts captured before
243
+ # (or alongside) their video item.
244
+ if name == "youtube":
245
+ try:
246
+ from lestash_youtube.source import (
247
+ resolve_youtube_transcript_parents,
248
+ )
249
+
250
+ resolved = resolve_youtube_transcript_parents(conn)
251
+ if resolved:
252
+ console.print(f" [dim]Resolved {resolved} transcript parent(s)[/dim]")
253
+ except ImportError:
254
+ pass
255
+
242
256
  # Update source last_sync
243
257
  conn.execute(
244
258
  """
@@ -10,6 +10,7 @@ import logging
10
10
  import sqlite3
11
11
  from collections.abc import Iterable, Iterator
12
12
  from contextlib import contextmanager
13
+ from datetime import UTC, datetime
13
14
  from pathlib import Path
14
15
  from typing import TYPE_CHECKING
15
16
 
@@ -21,7 +22,7 @@ if TYPE_CHECKING:
21
22
  logger = logging.getLogger(__name__)
22
23
 
23
24
  # Current schema version - increment when adding migrations
24
- SCHEMA_VERSION = 11
25
+ SCHEMA_VERSION = 12
25
26
 
26
27
  # Base schema (version 0) - applied to new databases
27
28
  SCHEMA = """
@@ -34,7 +35,7 @@ CREATE TABLE IF NOT EXISTS items (
34
35
  title TEXT,
35
36
  content TEXT NOT NULL,
36
37
  author TEXT,
37
- created_at DATETIME,
38
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
38
39
  fetched_at DATETIME DEFAULT CURRENT_TIMESTAMP,
39
40
  is_own_content BOOLEAN DEFAULT FALSE,
40
41
  metadata TEXT,
@@ -379,6 +380,15 @@ MIGRATIONS = [
379
380
  CREATE INDEX IF NOT EXISTS idx_syndications_item ON syndications(item_id);
380
381
  """,
381
382
  ),
383
+ (
384
+ 12,
385
+ "Backfill items.created_at NULLs with fetched_at",
386
+ # items.created_at had no default, so rows written without a date
387
+ # (API-created notes, gemini imports, etc.) stored NULL. fetched_at is
388
+ # the best available capture-time proxy. Insert-layer defaults in
389
+ # upsert_item()/create_item() prevent future NULLs.
390
+ "UPDATE items SET created_at = fetched_at WHERE created_at IS NULL;",
391
+ ),
382
392
  ]
383
393
 
384
394
 
@@ -606,6 +616,7 @@ def upsert_item(conn: sqlite3.Connection, item: ItemCreate) -> int:
606
616
  import json
607
617
 
608
618
  metadata_json = json.dumps(item.metadata) if item.metadata else None
619
+ created_at = item.created_at or datetime.now(UTC)
609
620
  pre_max = max_history_id(conn)
610
621
  conn.execute(
611
622
  """
@@ -632,7 +643,7 @@ def upsert_item(conn: sqlite3.Connection, item: ItemCreate) -> int:
632
643
  item.title,
633
644
  item.content,
634
645
  item.author,
635
- item.created_at,
646
+ created_at,
636
647
  item.is_own_content,
637
648
  metadata_json,
638
649
  item.parent_id,
@@ -15,6 +15,7 @@ from google_auth_oauthlib.flow import InstalledAppFlow
15
15
 
16
16
  # Default scopes — callers can request additional scopes
17
17
  DEFAULT_SCOPES = [
18
+ "https://www.googleapis.com/auth/youtube.readonly",
18
19
  "https://www.googleapis.com/auth/drive.readonly",
19
20
  ]
20
21
 
@@ -447,6 +447,22 @@ class TestChangeReason:
447
447
  assert rows[0]["change_reason"] == "sync"
448
448
  assert rows[0]["content_old"] == "original"
449
449
 
450
+ def test_upsert_item_defaults_created_at(self, test_db):
451
+ """Upsert without a created_at should not store NULL."""
452
+ with get_connection(test_db) as conn:
453
+ upsert_item(
454
+ conn,
455
+ ItemCreate(
456
+ source_type="note",
457
+ source_id="undated",
458
+ content="no date",
459
+ ),
460
+ )
461
+ row = conn.execute(
462
+ "SELECT created_at FROM items WHERE source_id = ?", ("undated",)
463
+ ).fetchone()
464
+ assert row[0] is not None
465
+
450
466
  def test_pdf_enricher_marks_history_as_enricher(self, test_db):
451
467
  from lestash.core.pdf_enrich.persistence import mark_source_unavailable
452
468
 
File without changes
File without changes
File without changes
File without changes
File without changes