athena-python-pptx 0.4.2__tar.gz → 0.5.0__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.
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/PKG-INFO +2 -1
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/docs/API_PARITY_EXCEPTIONS.md +88 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/__init__.py +1 -1
- athena_python_pptx-0.5.0/pptx/_references.py +73 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/commands.py +128 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/dml/color.py +44 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/presentation.py +22 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/__init__.py +137 -2
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/slides.py +79 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pyproject.toml +6 -1
- athena_python_pptx-0.4.2/pptx/_references.py +0 -165
- athena_python_pptx-0.4.2/uv.lock +0 -1163
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/.gitignore +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/API_PARITY_REPORT.md +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/CHANGELOG.md +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/CLAUDE.md +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/DEV-GUIDE.md +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/PARITY_QUESTIONS.md +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/PUBLISHING.md +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/README.md +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/docs/athena-api.json +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/docs/athena-api.md +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/_athena_extension.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/_ptc.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/action.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/batching.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/__init__.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/axis.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/category.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/chart.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/data.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/datalabel.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/legend.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/marker.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/plot.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/point.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/series.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/chart/xlsx.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/client.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/decorators.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/dml/__init__.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/dml/chtfmt.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/dml/effect.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/dml/fill.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/dml/line.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/docgen.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/enum/__init__.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/enum/action.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/enum/chart.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/enum/dml.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/enum/lang.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/enum/shapes.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/enum/text.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/errors.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/exc.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/media.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/package.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/parts/__init__.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/parts/_base.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/parts/chart.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/parts/coreprops.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/parts/embeddedpackage.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/parts/image.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/parts/media.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/parts/presentation.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/parts/slide.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/autoshape.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/base.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/connector.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/freeform.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/graphfrm.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/group.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/picture.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/placeholder.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shapes/shapetree.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/shared.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/slide.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/spec.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/table.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/text/__init__.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/text/fonts.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/text/layout.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/text/text.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/types.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/typing.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/units.py +0 -0
- {athena_python_pptx-0.4.2 → athena_python_pptx-0.5.0}/pptx/util.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: athena-python-pptx
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: Drop-in replacement for python-pptx that connects to PPTX Studio for real-time collaboration
|
|
5
5
|
Project-URL: Homepage, https://github.com/pptx-studio/python-sdk
|
|
6
6
|
Project-URL: Documentation, https://docs.pptx-studio.com/sdk/python
|
|
@@ -21,6 +21,7 @@ Classifier: Topic :: Office/Business :: Office Suites
|
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
22
|
Classifier: Typing :: Typed
|
|
23
23
|
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: athena-references>=0.1.0
|
|
24
25
|
Requires-Dist: requests>=2.28.0
|
|
25
26
|
Provides-Extra: dev
|
|
26
27
|
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
@@ -1347,3 +1347,91 @@ upstream surface preserved, additions tagged with
|
|
|
1347
1347
|
|
|
1348
1348
|
Total Tier B closures: 16 implemented, 6 audited (already
|
|
1349
1349
|
applicable or N/A by REST architecture).
|
|
1350
|
+
|
|
1351
|
+
---
|
|
1352
|
+
|
|
1353
|
+
## Athena Source Citations — SDK-native (added post-v0.4.3)
|
|
1354
|
+
|
|
1355
|
+
These methods are **additions** to python-pptx, not deviations — python-pptx
|
|
1356
|
+
has no concept of Athena asset references or citations. The user has approved
|
|
1357
|
+
SDK-native citations. They let an agent attach an Athena source citation to a
|
|
1358
|
+
slide or shape; the studio server applies the citation into the deck's
|
|
1359
|
+
`citations` Y.Array as a unified record that is byte-compatible with agora's
|
|
1360
|
+
`build_deck_source_citation`
|
|
1361
|
+
(`agora/agora/web/api/asset/utils/presentations/citation_operations.py`) and the
|
|
1362
|
+
Olympus `Citation` type (`olympus/src/store/citation.types.ts`). So the deck's
|
|
1363
|
+
existing badge + sidebar rendering surfaces agent-authored citations identically
|
|
1364
|
+
to user-authored ones.
|
|
1365
|
+
|
|
1366
|
+
A citation record is:
|
|
1367
|
+
|
|
1368
|
+
```jsonc
|
|
1369
|
+
{
|
|
1370
|
+
"id": "citation_<uuid12>",
|
|
1371
|
+
"destinationAnchor": { "type": "slide", "slideId": "...", "slideIndex": 0 },
|
|
1372
|
+
// or { "type": "shape", "slideId": "...", "slideIndex": 0, "shapeIds": ["..."] }
|
|
1373
|
+
"citation_string": "<source Spaces URL>",
|
|
1374
|
+
"displayValue": "Q4 Sales", // optional
|
|
1375
|
+
"active": true,
|
|
1376
|
+
"created_at": "2026-…T…Z",
|
|
1377
|
+
"created_by": "Athena"
|
|
1378
|
+
}
|
|
1379
|
+
```
|
|
1380
|
+
|
|
1381
|
+
`citation_string` is the serialized source `AssetReference` (whole asset, or
|
|
1382
|
+
asset + sub-anchor) in Spaces-URL form. The SDK leaves it to the server to
|
|
1383
|
+
derive (from `source_ref` + `source_anchor`) so a single canonical serializer
|
|
1384
|
+
produces it.
|
|
1385
|
+
|
|
1386
|
+
### `Slide.add_citation(source, *, anchor=None, display_value=None) -> str`
|
|
1387
|
+
|
|
1388
|
+
Attach a citation to a whole slide. Emits `AddSlideCitation`; the server
|
|
1389
|
+
resolves `slideId` from `slideIndex` and writes a `slide` destination anchor.
|
|
1390
|
+
Returns the created citation id.
|
|
1391
|
+
|
|
1392
|
+
```python
|
|
1393
|
+
from pptx._references import AssetReference, SheetRangeAnchor
|
|
1394
|
+
|
|
1395
|
+
cid = slide.add_citation(
|
|
1396
|
+
AssetReference(id="asset_xlsx_abc123"),
|
|
1397
|
+
anchor=SheetRangeAnchor(sheet_id=0, range="A1:F20"), # optional
|
|
1398
|
+
display_value="Q4 Sales · A1:F20", # optional
|
|
1399
|
+
)
|
|
1400
|
+
```
|
|
1401
|
+
|
|
1402
|
+
### `Shape.cite(source, *, anchor=None, display_value=None) -> str`
|
|
1403
|
+
|
|
1404
|
+
Shape-scoped citation — emits `AddShapeCitation` carrying this shape's id, so
|
|
1405
|
+
the server writes a `shape` destination anchor with `shapeIds`. Returns the
|
|
1406
|
+
created citation id.
|
|
1407
|
+
|
|
1408
|
+
```python
|
|
1409
|
+
shape = slide.shapes.add_textbox(Inches(1), Inches(1), Inches(4), Inches(1))
|
|
1410
|
+
cid = shape.cite(AssetReference(id="asset_doc_xyz"))
|
|
1411
|
+
```
|
|
1412
|
+
|
|
1413
|
+
### `Slide.remove_citation(citation_id, *, soft=True)` / `Presentation.remove_citation(citation_id, *, soft=True)`
|
|
1414
|
+
|
|
1415
|
+
Remove a citation from the deck by id. Citations are deck-scoped, so both
|
|
1416
|
+
spellings emit the same `RemoveCitation` command — `Slide.remove_citation` is a
|
|
1417
|
+
convenience when an agent already holds a slide. `soft` (default) flips
|
|
1418
|
+
`active` to `False` in place (reversible, readers skip inactive citations);
|
|
1419
|
+
`soft=False` splices the record out of the array.
|
|
1420
|
+
|
|
1421
|
+
```python
|
|
1422
|
+
prs.remove_citation(cid) # soft delete (default)
|
|
1423
|
+
slide.remove_citation(cid, soft=False) # hard splice
|
|
1424
|
+
```
|
|
1425
|
+
|
|
1426
|
+
### `pptx._references` re-homed on the shared `athena-references` package
|
|
1427
|
+
|
|
1428
|
+
`pptx._references` now re-exports the anchor + version-policy dataclasses from
|
|
1429
|
+
the shared `athena-references` package (the single Python source of truth, kept
|
|
1430
|
+
in lockstep with `packages/references` and `agora/agora/utils/asset_references`).
|
|
1431
|
+
The `to_dict()` wire output of the four anchors the module historically exported
|
|
1432
|
+
(`SheetCellAnchor` / `SheetRangeAnchor` / `SheetTableAnchor` / `SlideAnchor`),
|
|
1433
|
+
plus `VersionPolicyLatest` / `VersionPolicyPinned`, is byte-identical to the
|
|
1434
|
+
prior in-module definitions. `AssetReference` remains a thin in-module wrapper
|
|
1435
|
+
that additionally accepts a plain `dict` `meta` (the historical SDK shape) and
|
|
1436
|
+
serializes it verbatim, preserving byte-identical `to_dict()` output for
|
|
1437
|
+
existing callers.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Thin Python mirror of the @athenaintel/references TypeScript package.
|
|
2
|
+
|
|
3
|
+
Used by the linked-OLE / linked-table / citation SDK methods (Athena
|
|
4
|
+
extensions beyond the python-pptx surface). Mirrors only what the SDK needs to
|
|
5
|
+
construct ``AssetReference`` + ``Anchor`` payloads — full reference
|
|
6
|
+
serialization (URI / URL / Spaces format) is server-side.
|
|
7
|
+
|
|
8
|
+
The anchor + version-policy dataclasses are re-exported from the shared
|
|
9
|
+
``athena-references`` package (the single Python source of truth, kept in
|
|
10
|
+
lockstep with ``packages/references`` and ``agora/agora/utils/asset_references``).
|
|
11
|
+
``AssetReference`` is a thin back-compat wrapper that additionally accepts a
|
|
12
|
+
plain ``dict`` ``meta`` (the historical SDK shape) and serializes it verbatim,
|
|
13
|
+
preserving byte-identical ``to_dict()`` output for existing SDK callers.
|
|
14
|
+
|
|
15
|
+
Note (Athena extension):
|
|
16
|
+
This module is NOT part of python-pptx — it exists to construct
|
|
17
|
+
Athena-asset references for the SDK's linked-object / citation methods.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
__athena_extension_module__: bool = True
|
|
23
|
+
__athena_extension_description__: str = (
|
|
24
|
+
"Cross-studio asset reference types (Athena studio-linking, no upstream)."
|
|
25
|
+
)
|
|
26
|
+
__athena_extension_since__: str = "0.1.71"
|
|
27
|
+
|
|
28
|
+
from dataclasses import dataclass
|
|
29
|
+
from typing import Any, Optional
|
|
30
|
+
|
|
31
|
+
from athena_references import (
|
|
32
|
+
Anchor,
|
|
33
|
+
SheetCellAnchor,
|
|
34
|
+
SheetRangeAnchor,
|
|
35
|
+
SheetTableAnchor,
|
|
36
|
+
SlideAnchor,
|
|
37
|
+
VersionPolicy,
|
|
38
|
+
VersionPolicyLatest,
|
|
39
|
+
VersionPolicyPinned,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class AssetReference:
|
|
45
|
+
"""Athena asset reference. ``id`` is the asset UUID.
|
|
46
|
+
|
|
47
|
+
Re-homes the canonical anchor types onto the shared ``athena-references``
|
|
48
|
+
package while preserving the historical SDK contract that ``meta`` may be a
|
|
49
|
+
plain ``dict`` (serialized verbatim) in addition to ``None``.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
id: str
|
|
53
|
+
meta: Optional[dict[str, Any]] = None
|
|
54
|
+
|
|
55
|
+
def to_dict(self) -> dict[str, Any]:
|
|
56
|
+
"""Serialize to the canonical wire shape."""
|
|
57
|
+
out: dict[str, Any] = {"referenced": "asset", "id": self.id}
|
|
58
|
+
if self.meta is not None:
|
|
59
|
+
out["meta"] = dict(self.meta)
|
|
60
|
+
return out
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
__all__ = [
|
|
64
|
+
"AssetReference",
|
|
65
|
+
"SheetCellAnchor",
|
|
66
|
+
"SheetRangeAnchor",
|
|
67
|
+
"SheetTableAnchor",
|
|
68
|
+
"SlideAnchor",
|
|
69
|
+
"Anchor",
|
|
70
|
+
"VersionPolicyLatest",
|
|
71
|
+
"VersionPolicyPinned",
|
|
72
|
+
"VersionPolicy",
|
|
73
|
+
]
|
|
@@ -772,6 +772,131 @@ class AddLinkedTable(Command):
|
|
|
772
772
|
)
|
|
773
773
|
|
|
774
774
|
|
|
775
|
+
@dataclass
|
|
776
|
+
class AddSlideCitation(Command):
|
|
777
|
+
"""Attach an Athena source citation to a whole slide.
|
|
778
|
+
|
|
779
|
+
Records that a deck slide is sourced from another Athena asset. The
|
|
780
|
+
studio server appends a unified citation record to the deck's
|
|
781
|
+
``citations`` Y.Array, byte-compatible with agora's
|
|
782
|
+
``build_deck_source_citation`` and the Olympus ``Citation`` type, so the
|
|
783
|
+
deck's existing badge + sidebar rendering surfaces agent-authored links
|
|
784
|
+
identically to user-authored ones.
|
|
785
|
+
|
|
786
|
+
Note (Athena extension):
|
|
787
|
+
This command is NOT part of python-pptx. See
|
|
788
|
+
``docs/API_PARITY_EXCEPTIONS.md`` for the deviation rationale.
|
|
789
|
+
|
|
790
|
+
Args:
|
|
791
|
+
slide_index: Zero-based index of the cited slide.
|
|
792
|
+
source_ref: Source ``AssetReference`` JSON (``referenced='asset'`` +
|
|
793
|
+
``id``); the asset the slide is sourced from.
|
|
794
|
+
source_anchor: Optional ``Anchor`` JSON within the source (e.g. a
|
|
795
|
+
spreadsheet range); omit to cite the whole asset.
|
|
796
|
+
display_value: Optional human-readable badge label.
|
|
797
|
+
citation_string: Optional pre-serialized source Spaces URL; the
|
|
798
|
+
server derives it from ``source_ref`` + ``source_anchor`` when
|
|
799
|
+
unset.
|
|
800
|
+
client_id: Client-provided ID (optional).
|
|
801
|
+
"""
|
|
802
|
+
|
|
803
|
+
slide_index: int
|
|
804
|
+
source_ref: dict
|
|
805
|
+
source_anchor: Optional[dict] = None
|
|
806
|
+
display_value: Optional[str] = None
|
|
807
|
+
citation_string: Optional[str] = None
|
|
808
|
+
client_id: Optional[str] = None
|
|
809
|
+
|
|
810
|
+
@property
|
|
811
|
+
def command_type(self) -> str:
|
|
812
|
+
return "AddSlideCitation"
|
|
813
|
+
|
|
814
|
+
def validate(self) -> None:
|
|
815
|
+
if self.slide_index < 0:
|
|
816
|
+
raise ValidationError("slide_index must be non-negative", "slide_index")
|
|
817
|
+
if not isinstance(self.source_ref, dict) or not self.source_ref.get("id"):
|
|
818
|
+
raise ValidationError(
|
|
819
|
+
"source_ref must be an AssetReference dict with an 'id' field",
|
|
820
|
+
"source_ref",
|
|
821
|
+
)
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
@dataclass
|
|
825
|
+
class AddShapeCitation(Command):
|
|
826
|
+
"""Attach an Athena source citation to one or more shapes on a slide.
|
|
827
|
+
|
|
828
|
+
Shape-scoped analogue of :class:`AddSlideCitation`. The deck citation's
|
|
829
|
+
``destinationAnchor`` is a ``shape`` anchor carrying ``shapeIds`` so the
|
|
830
|
+
canvas can draw per-shape highlight overlays.
|
|
831
|
+
|
|
832
|
+
Note (Athena extension):
|
|
833
|
+
This command is NOT part of python-pptx. See
|
|
834
|
+
``docs/API_PARITY_EXCEPTIONS.md`` for the deviation rationale.
|
|
835
|
+
|
|
836
|
+
Args:
|
|
837
|
+
slide_index: Zero-based index of the slide the shapes live on.
|
|
838
|
+
shape_ids: Non-empty list of shape ids on that slide to cite.
|
|
839
|
+
source_ref: Source ``AssetReference`` JSON.
|
|
840
|
+
source_anchor: Optional ``Anchor`` JSON within the source.
|
|
841
|
+
display_value: Optional human-readable badge label.
|
|
842
|
+
citation_string: Optional pre-serialized source Spaces URL.
|
|
843
|
+
client_id: Client-provided ID (optional).
|
|
844
|
+
"""
|
|
845
|
+
|
|
846
|
+
slide_index: int
|
|
847
|
+
shape_ids: list[str]
|
|
848
|
+
source_ref: dict
|
|
849
|
+
source_anchor: Optional[dict] = None
|
|
850
|
+
display_value: Optional[str] = None
|
|
851
|
+
citation_string: Optional[str] = None
|
|
852
|
+
client_id: Optional[str] = None
|
|
853
|
+
|
|
854
|
+
@property
|
|
855
|
+
def command_type(self) -> str:
|
|
856
|
+
return "AddShapeCitation"
|
|
857
|
+
|
|
858
|
+
def validate(self) -> None:
|
|
859
|
+
if self.slide_index < 0:
|
|
860
|
+
raise ValidationError("slide_index must be non-negative", "slide_index")
|
|
861
|
+
if not self.shape_ids:
|
|
862
|
+
raise ValidationError("shape_ids must be non-empty", "shape_ids")
|
|
863
|
+
if not isinstance(self.source_ref, dict) or not self.source_ref.get("id"):
|
|
864
|
+
raise ValidationError(
|
|
865
|
+
"source_ref must be an AssetReference dict with an 'id' field",
|
|
866
|
+
"source_ref",
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
@dataclass
|
|
871
|
+
class RemoveCitation(Command):
|
|
872
|
+
"""Remove a citation from a deck's ``citations`` Y.Array by id.
|
|
873
|
+
|
|
874
|
+
``soft`` (default) flips the record's ``active`` flag to ``False`` in
|
|
875
|
+
place (readers skip inactive citations; Keryx rollback can restore it);
|
|
876
|
+
``soft=False`` splices the record out of the array.
|
|
877
|
+
|
|
878
|
+
Note (Athena extension):
|
|
879
|
+
This command is NOT part of python-pptx. See
|
|
880
|
+
``docs/API_PARITY_EXCEPTIONS.md`` for the deviation rationale.
|
|
881
|
+
|
|
882
|
+
Args:
|
|
883
|
+
citation_id: Id of the citation record to remove.
|
|
884
|
+
soft: Soft-delete (flip ``active``) when ``True`` (default); hard
|
|
885
|
+
splice when ``False``.
|
|
886
|
+
"""
|
|
887
|
+
|
|
888
|
+
citation_id: str
|
|
889
|
+
soft: bool = True
|
|
890
|
+
|
|
891
|
+
@property
|
|
892
|
+
def command_type(self) -> str:
|
|
893
|
+
return "RemoveCitation"
|
|
894
|
+
|
|
895
|
+
def validate(self) -> None:
|
|
896
|
+
if not self.citation_id:
|
|
897
|
+
raise ValidationError("citation_id is required", "citation_id")
|
|
898
|
+
|
|
899
|
+
|
|
775
900
|
@dataclass
|
|
776
901
|
class AddTable(Command):
|
|
777
902
|
"""
|
|
@@ -3098,6 +3223,9 @@ AnyCommand = Union[
|
|
|
3098
3223
|
AddOleObject,
|
|
3099
3224
|
AddLinkedOleObject,
|
|
3100
3225
|
AddLinkedTable,
|
|
3226
|
+
AddSlideCitation,
|
|
3227
|
+
AddShapeCitation,
|
|
3228
|
+
RemoveCitation,
|
|
3101
3229
|
AddTable,
|
|
3102
3230
|
SetTableCell,
|
|
3103
3231
|
SetTableRowCount,
|
|
@@ -50,6 +50,50 @@ def theme_color_to_scheme_name(theme_color: "MSO_THEME_COLOR") -> Optional[str]:
|
|
|
50
50
|
return _THEME_COLOR_TO_SCHEME_NAME.get(value)
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
# Reverse of ``_THEME_COLOR_TO_SCHEME_NAME`` for the read path: map an OOXML
|
|
54
|
+
# ``<a:schemeClr val="...">`` name back to its canonical ``MSO_THEME_COLOR``.
|
|
55
|
+
# The forward map is many-to-one (both DARK_1 and TEXT_1 spell 'tx1'); on the
|
|
56
|
+
# way back python-pptx surfaces the index-13..16 spellings for the bg*/tx*
|
|
57
|
+
# names (TEXT_1/BACKGROUND_1/TEXT_2/BACKGROUND_2) and the dk*/lt* aliases for
|
|
58
|
+
# DARK_1/LIGHT_1/DARK_2/LIGHT_2, which is what we reproduce here.
|
|
59
|
+
_SCHEME_NAME_TO_THEME_COLOR = {
|
|
60
|
+
"tx1": 13, # TEXT_1
|
|
61
|
+
"bg1": 14, # BACKGROUND_1
|
|
62
|
+
"tx2": 16, # TEXT_2
|
|
63
|
+
"bg2": 15, # BACKGROUND_2
|
|
64
|
+
"dk1": 1, # DARK_1
|
|
65
|
+
"lt1": 2, # LIGHT_1
|
|
66
|
+
"dk2": 3, # DARK_2
|
|
67
|
+
"lt2": 4, # LIGHT_2
|
|
68
|
+
"accent1": 5,
|
|
69
|
+
"accent2": 6,
|
|
70
|
+
"accent3": 7,
|
|
71
|
+
"accent4": 8,
|
|
72
|
+
"accent5": 9,
|
|
73
|
+
"accent6": 10,
|
|
74
|
+
"hlink": 11, # HYPERLINK
|
|
75
|
+
"folHlink": 12, # FOLLOWED_HYPERLINK
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def scheme_name_to_theme_color(scheme_name: Optional[str]) -> Optional["MSO_THEME_COLOR"]:
|
|
80
|
+
"""Convert an OOXML ``<a:schemeClr>`` name back to an ``MSO_THEME_COLOR``.
|
|
81
|
+
|
|
82
|
+
Inverse of :func:`theme_color_to_scheme_name`, used by the read path so a
|
|
83
|
+
re-opened deck exposes ``fill.fore_color.theme_color`` /
|
|
84
|
+
``line.color.theme_color`` instead of ``None``. Returns ``None`` for
|
|
85
|
+
unknown names (e.g. ``phClr``) so callers fall back to sRGB.
|
|
86
|
+
"""
|
|
87
|
+
if not scheme_name:
|
|
88
|
+
return None
|
|
89
|
+
value = _SCHEME_NAME_TO_THEME_COLOR.get(scheme_name)
|
|
90
|
+
if value is None:
|
|
91
|
+
return None
|
|
92
|
+
from ..enum.dml import MSO_THEME_COLOR
|
|
93
|
+
|
|
94
|
+
return MSO_THEME_COLOR(value)
|
|
95
|
+
|
|
96
|
+
|
|
53
97
|
class RGBColor:
|
|
54
98
|
"""
|
|
55
99
|
Immutable RGB color value, matching the standard python-pptx API.
|
|
@@ -871,6 +871,28 @@ class Presentation:
|
|
|
871
871
|
for i, slide in enumerate(self._slides._slides):
|
|
872
872
|
slide._slide_index = i
|
|
873
873
|
|
|
874
|
+
@athena_extension(
|
|
875
|
+
description=(
|
|
876
|
+
"Presentation.remove_citation — remove an Athena source citation "
|
|
877
|
+
"from the deck by id. Not in python-pptx."
|
|
878
|
+
),
|
|
879
|
+
)
|
|
880
|
+
def remove_citation(self, citation_id: str, *, soft: bool = True) -> None:
|
|
881
|
+
"""Remove an Athena source citation from the deck by id.
|
|
882
|
+
|
|
883
|
+
Citations are deck-scoped records in the deck's ``citations`` Y.Array.
|
|
884
|
+
``soft`` (default) flips the record's ``active`` flag to ``False`` in
|
|
885
|
+
place (readers skip inactive citations; Keryx rollback can restore it);
|
|
886
|
+
``soft=False`` splices the record out of the array.
|
|
887
|
+
|
|
888
|
+
Note (Athena extension):
|
|
889
|
+
This method is NOT part of python-pptx. See
|
|
890
|
+
``docs/API_PARITY_EXCEPTIONS.md`` for the deviation rationale.
|
|
891
|
+
"""
|
|
892
|
+
from .commands import RemoveCitation
|
|
893
|
+
|
|
894
|
+
self._buffer.add(RemoveCitation(citation_id=citation_id, soft=soft))
|
|
895
|
+
|
|
874
896
|
@athena_extension(
|
|
875
897
|
issue=1036,
|
|
876
898
|
since="0.1.81",
|
|
@@ -88,6 +88,61 @@ def _is_athena_url(url: str) -> bool:
|
|
|
88
88
|
return any(host == suffix or host.endswith("." + suffix) for suffix in _ATHENA_URL_SUFFIXES)
|
|
89
89
|
|
|
90
90
|
|
|
91
|
+
def _build_citation_wire(
|
|
92
|
+
*,
|
|
93
|
+
source: "AssetReference",
|
|
94
|
+
anchor: Optional["Anchor"],
|
|
95
|
+
) -> tuple[dict, Optional[dict], None]:
|
|
96
|
+
"""Build the ``(source_ref, source_anchor, citation_string)`` wire triple
|
|
97
|
+
for a citation command (Athena extension).
|
|
98
|
+
|
|
99
|
+
Type-checks ``source`` and duck-types the optional ``anchor`` exactly as
|
|
100
|
+
``add_linked_ole_object`` does, then serializes both to their canonical
|
|
101
|
+
wire dicts. ``citation_string`` is left ``None`` so the studio server
|
|
102
|
+
derives the Spaces URL from ``source_ref`` + ``source_anchor`` — keeping a
|
|
103
|
+
single canonical serializer (matching agora's output) rather than a second
|
|
104
|
+
SDK-side one.
|
|
105
|
+
"""
|
|
106
|
+
from .._references import AssetReference
|
|
107
|
+
|
|
108
|
+
if not isinstance(source, AssetReference):
|
|
109
|
+
raise TypeError(
|
|
110
|
+
f"source must be an AssetReference; got {type(source).__name__}",
|
|
111
|
+
)
|
|
112
|
+
source_anchor: Optional[dict] = None
|
|
113
|
+
if anchor is not None:
|
|
114
|
+
if not hasattr(anchor, "to_dict"):
|
|
115
|
+
raise TypeError(
|
|
116
|
+
"anchor must be an Anchor dataclass (SheetRangeAnchor, "
|
|
117
|
+
"SlideAnchor, etc.) — got "
|
|
118
|
+
f"{type(anchor).__name__}",
|
|
119
|
+
)
|
|
120
|
+
source_anchor = anchor.to_dict()
|
|
121
|
+
return source.to_dict(), source_anchor, None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _add_citation_command(buffer: Optional["CommandBuffer"], cmd: Any) -> str:
|
|
125
|
+
"""Enqueue a citation command and return the created citation id.
|
|
126
|
+
|
|
127
|
+
Reads the id back from the command response
|
|
128
|
+
(``created.citationIds[0]`` / ``created.citationId``) when the buffer
|
|
129
|
+
flushes synchronously; falls back to the command's ``client_id`` (or empty
|
|
130
|
+
string) when the response doesn't carry one (e.g. queued in a batch).
|
|
131
|
+
"""
|
|
132
|
+
if buffer is None:
|
|
133
|
+
return getattr(cmd, "client_id", "") or ""
|
|
134
|
+
response = buffer.add(cmd)
|
|
135
|
+
if response and response.get("created"):
|
|
136
|
+
created = response["created"]
|
|
137
|
+
citation_ids = created.get("citationIds")
|
|
138
|
+
if citation_ids:
|
|
139
|
+
return citation_ids[0]
|
|
140
|
+
citation_id = created.get("citationId")
|
|
141
|
+
if citation_id:
|
|
142
|
+
return citation_id
|
|
143
|
+
return getattr(cmd, "client_id", "") or ""
|
|
144
|
+
|
|
145
|
+
|
|
91
146
|
# Default render scale for xlsx-range screenshots embedded in slides.
|
|
92
147
|
# scale=3 matches xlsx-studio's ``range-as-asset`` default — slide inserts
|
|
93
148
|
# sit at 6–9″ widths, where scale=2 leaves text visibly soft.
|
|
@@ -635,11 +690,27 @@ class FillFormat:
|
|
|
635
690
|
def __init__(self, shape: Shape):
|
|
636
691
|
self._shape = shape
|
|
637
692
|
self._solid_color: Optional[str] = shape._properties.get("fillColorHex")
|
|
693
|
+
# Scheme (theme) fill read back from the snapshot. Initializing it here
|
|
694
|
+
# (rather than only in ``_on_color_change``) both fixes a latent
|
|
695
|
+
# AttributeError when ``_scheme_color`` is read before any write and
|
|
696
|
+
# lets a re-opened theme-colored fill round-trip on re-export.
|
|
697
|
+
self._scheme_color: Optional[str] = shape._properties.get("fillSchemeColor")
|
|
638
698
|
self._transparency: float = 0.0
|
|
639
|
-
|
|
699
|
+
# A scheme fill with no explicit sRGB is still a solid fill.
|
|
700
|
+
self._type: Optional[str] = 'solid' if (self._solid_color or self._scheme_color) else None
|
|
640
701
|
# Initialize fore_color ColorFormat
|
|
641
702
|
rgb = RGBColor.from_string(self._solid_color) if self._solid_color else None
|
|
642
703
|
self._fore_color_format = _FillColorFormat(self, rgb=rgb)
|
|
704
|
+
# Surface ``fore_color.theme_color`` for a scheme fill (no sRGB hex) so
|
|
705
|
+
# the agent "restyle a deck I just opened" workflow reads back the theme
|
|
706
|
+
# color instead of None. Set the attribute directly to avoid triggering
|
|
707
|
+
# the setter's write-emit on a pure read.
|
|
708
|
+
if self._scheme_color and not self._solid_color:
|
|
709
|
+
from ..dml.color import scheme_name_to_theme_color
|
|
710
|
+
|
|
711
|
+
theme = scheme_name_to_theme_color(self._scheme_color)
|
|
712
|
+
if theme is not None:
|
|
713
|
+
self._fore_color_format._theme_color = theme
|
|
643
714
|
|
|
644
715
|
def solid(self) -> None:
|
|
645
716
|
"""
|
|
@@ -940,11 +1011,25 @@ class LineFormat:
|
|
|
940
1011
|
def __init__(self, shape: Shape):
|
|
941
1012
|
self._shape = shape
|
|
942
1013
|
self._color_hex: Optional[str] = shape._properties.get("strokeColorHex")
|
|
1014
|
+
# Scheme (theme) line color; initialized here so it round-trips and so
|
|
1015
|
+
# the setters that reference ``_scheme_color`` never hit an
|
|
1016
|
+
# AttributeError on a fresh read.
|
|
1017
|
+
self._scheme_color: Optional[str] = shape._properties.get("strokeSchemeColor")
|
|
943
1018
|
self._width_emu: int = shape._properties.get("strokeWidthEmu", 12700) # Default 1pt
|
|
944
|
-
|
|
1019
|
+
# Read the persisted dash style back (defaults to solid) so a re-opened
|
|
1020
|
+
# deck exposes ``line.dash_style`` instead of always 'solid'. The Y.Doc
|
|
1021
|
+
# and SDK share the same dash vocabulary, so no mapping is needed.
|
|
1022
|
+
self._dash_style: str = shape._properties.get("strokeDashStyle") or 'solid'
|
|
945
1023
|
# Initialize color ColorFormat
|
|
946
1024
|
rgb = RGBColor.from_string(self._color_hex) if self._color_hex else None
|
|
947
1025
|
self._color_format = _LineColorFormat(self, rgb=rgb)
|
|
1026
|
+
# Surface ``color.theme_color`` for a scheme line color (no sRGB hex).
|
|
1027
|
+
if self._scheme_color and not self._color_hex:
|
|
1028
|
+
from ..dml.color import scheme_name_to_theme_color
|
|
1029
|
+
|
|
1030
|
+
theme = scheme_name_to_theme_color(self._scheme_color)
|
|
1031
|
+
if theme is not None:
|
|
1032
|
+
self._color_format._theme_color = theme
|
|
948
1033
|
|
|
949
1034
|
@property
|
|
950
1035
|
def color(self) -> _LineColorFormat:
|
|
@@ -5869,6 +5954,56 @@ class Shape:
|
|
|
5869
5954
|
if isinstance(by_id, dict):
|
|
5870
5955
|
by_id.pop(self._shape_id, None)
|
|
5871
5956
|
|
|
5957
|
+
def cite(
|
|
5958
|
+
self,
|
|
5959
|
+
source: "AssetReference",
|
|
5960
|
+
*,
|
|
5961
|
+
anchor: Optional["Anchor"] = None,
|
|
5962
|
+
display_value: Optional[str] = None,
|
|
5963
|
+
) -> str:
|
|
5964
|
+
"""Attach an Athena source citation to this shape.
|
|
5965
|
+
|
|
5966
|
+
Records that this shape is sourced from another Athena asset. The
|
|
5967
|
+
studio server appends a unified citation record to the deck's
|
|
5968
|
+
``citations`` array (byte-compatible with agora's
|
|
5969
|
+
``build_deck_source_citation`` and the Olympus ``Citation`` type), so
|
|
5970
|
+
the deck's badge + sidebar rendering surfaces it identically to a
|
|
5971
|
+
user-authored link.
|
|
5972
|
+
|
|
5973
|
+
Args:
|
|
5974
|
+
source: Source ``AssetReference`` from ``pptx._references`` — the
|
|
5975
|
+
asset this shape is sourced from.
|
|
5976
|
+
anchor: Optional ``Anchor`` within the source (e.g.
|
|
5977
|
+
``SheetRangeAnchor``); omit to cite the whole asset.
|
|
5978
|
+
display_value: Optional human-readable badge label.
|
|
5979
|
+
|
|
5980
|
+
Returns:
|
|
5981
|
+
The created citation id (``citation_<...>``).
|
|
5982
|
+
|
|
5983
|
+
Raises:
|
|
5984
|
+
TypeError: if ``source`` is not an ``AssetReference`` or ``anchor``
|
|
5985
|
+
isn't an Anchor dataclass.
|
|
5986
|
+
|
|
5987
|
+
Note (Athena extension):
|
|
5988
|
+
This method is NOT part of python-pptx. See
|
|
5989
|
+
``docs/API_PARITY_EXCEPTIONS.md`` for the deviation rationale.
|
|
5990
|
+
"""
|
|
5991
|
+
from ..commands import AddShapeCitation as AddShapeCitationCmd
|
|
5992
|
+
|
|
5993
|
+
source_ref, source_anchor, citation_string = _build_citation_wire(
|
|
5994
|
+
source=source,
|
|
5995
|
+
anchor=anchor,
|
|
5996
|
+
)
|
|
5997
|
+
cmd = AddShapeCitationCmd(
|
|
5998
|
+
slide_index=self._slide.slide_index,
|
|
5999
|
+
shape_ids=[self._shape_id],
|
|
6000
|
+
source_ref=source_ref,
|
|
6001
|
+
source_anchor=source_anchor,
|
|
6002
|
+
display_value=display_value,
|
|
6003
|
+
citation_string=citation_string,
|
|
6004
|
+
)
|
|
6005
|
+
return _add_citation_command(self._buffer, cmd)
|
|
6006
|
+
|
|
5872
6007
|
# -------------------------------------------------------------------------
|
|
5873
6008
|
# Fill and line styling
|
|
5874
6009
|
# -------------------------------------------------------------------------
|
|
@@ -21,6 +21,7 @@ from .typing import DeckSnapshot, ElementSnapshot, SlideId, SlideSnapshot
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
22
|
from .batching import CommandBuffer
|
|
23
23
|
from .presentation import Presentation
|
|
24
|
+
from ._references import AssetReference, Anchor
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
def _parse_layout_index(layout_path: Optional[str]) -> Optional[int]:
|
|
@@ -1167,6 +1168,84 @@ class Slide:
|
|
|
1167
1168
|
)
|
|
1168
1169
|
return warnings
|
|
1169
1170
|
|
|
1171
|
+
@athena_only(
|
|
1172
|
+
description=(
|
|
1173
|
+
"Slide.add_citation — attach an Athena source citation to a whole "
|
|
1174
|
+
"slide. Not in python-pptx."
|
|
1175
|
+
),
|
|
1176
|
+
)
|
|
1177
|
+
def add_citation(
|
|
1178
|
+
self,
|
|
1179
|
+
source: "AssetReference",
|
|
1180
|
+
*,
|
|
1181
|
+
anchor: Optional["Anchor"] = None,
|
|
1182
|
+
display_value: Optional[str] = None,
|
|
1183
|
+
) -> str:
|
|
1184
|
+
"""Attach an Athena source citation to this whole slide.
|
|
1185
|
+
|
|
1186
|
+
Records that this slide is sourced from another Athena asset. The
|
|
1187
|
+
studio server appends a unified citation record to the deck's
|
|
1188
|
+
``citations`` array (byte-compatible with agora's
|
|
1189
|
+
``build_deck_source_citation`` and the Olympus ``Citation`` type), so
|
|
1190
|
+
the deck's badge + sidebar rendering surfaces it identically to a
|
|
1191
|
+
user-authored link.
|
|
1192
|
+
|
|
1193
|
+
Args:
|
|
1194
|
+
source: Source ``AssetReference`` from ``pptx._references`` — the
|
|
1195
|
+
asset this slide is sourced from.
|
|
1196
|
+
anchor: Optional ``Anchor`` within the source (e.g.
|
|
1197
|
+
``SheetRangeAnchor``); omit to cite the whole asset.
|
|
1198
|
+
display_value: Optional human-readable badge label.
|
|
1199
|
+
|
|
1200
|
+
Returns:
|
|
1201
|
+
The created citation id (``citation_<...>``).
|
|
1202
|
+
|
|
1203
|
+
Note (Athena extension):
|
|
1204
|
+
This method is NOT part of python-pptx. See
|
|
1205
|
+
``docs/API_PARITY_EXCEPTIONS.md`` for the deviation rationale.
|
|
1206
|
+
"""
|
|
1207
|
+
from .commands import AddSlideCitation
|
|
1208
|
+
from .shapes import _add_citation_command, _build_citation_wire
|
|
1209
|
+
|
|
1210
|
+
source_ref, source_anchor, citation_string = _build_citation_wire(
|
|
1211
|
+
source=source,
|
|
1212
|
+
anchor=anchor,
|
|
1213
|
+
)
|
|
1214
|
+
cmd = AddSlideCitation(
|
|
1215
|
+
slide_index=self._slide_index,
|
|
1216
|
+
source_ref=source_ref,
|
|
1217
|
+
source_anchor=source_anchor,
|
|
1218
|
+
display_value=display_value,
|
|
1219
|
+
citation_string=citation_string,
|
|
1220
|
+
)
|
|
1221
|
+
return _add_citation_command(self._buffer, cmd)
|
|
1222
|
+
|
|
1223
|
+
@athena_only(
|
|
1224
|
+
description=(
|
|
1225
|
+
"Slide.remove_citation — remove a citation from the deck by id. "
|
|
1226
|
+
"Not in python-pptx."
|
|
1227
|
+
),
|
|
1228
|
+
)
|
|
1229
|
+
def remove_citation(self, citation_id: str, *, soft: bool = True) -> None:
|
|
1230
|
+
"""Remove a citation from the deck's ``citations`` array by id.
|
|
1231
|
+
|
|
1232
|
+
``soft`` (default) flips the record's ``active`` flag to ``False`` in
|
|
1233
|
+
place (readers skip inactive citations; Keryx rollback can restore it);
|
|
1234
|
+
``soft=False`` splices the record out of the array.
|
|
1235
|
+
|
|
1236
|
+
Citations are deck-scoped, so this is identical to
|
|
1237
|
+
:meth:`Presentation.remove_citation`; it's exposed on ``Slide`` for
|
|
1238
|
+
convenience when an agent is already holding a slide.
|
|
1239
|
+
|
|
1240
|
+
Note (Athena extension):
|
|
1241
|
+
This method is NOT part of python-pptx. See
|
|
1242
|
+
``docs/API_PARITY_EXCEPTIONS.md`` for the deviation rationale.
|
|
1243
|
+
"""
|
|
1244
|
+
from .commands import RemoveCitation
|
|
1245
|
+
|
|
1246
|
+
if self._buffer is not None:
|
|
1247
|
+
self._buffer.add(RemoveCitation(citation_id=citation_id, soft=soft))
|
|
1248
|
+
|
|
1170
1249
|
@property
|
|
1171
1250
|
def name(self) -> Optional[str]:
|
|
1172
1251
|
"""
|