Skip to content

Commit

Permalink
DOP-4917: Add video options for video metadata (#619)
Browse files Browse the repository at this point in the history
  • Loading branch information
rayangler authored Sep 19, 2024
1 parent 62d6d27 commit 45d4a43
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 1 deletion.
17 changes: 17 additions & 0 deletions snooty/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -889,6 +889,23 @@ def did_you_mean(self) -> List[str]:
return list(self.major_versions)


class MissingStructuredDataFields(Diagnostic):
severity = Diagnostic.Level.warning

def __init__(
self,
directive_name: str,
fields: List[str],
start: Union[int, Tuple[int, int]],
end: Union[None, int, Tuple[int, int]] = None,
) -> None:
super().__init__(
f"""Fields for {directive_name} structured data SEO are partially defined. Missing the following options: {fields}""",
start,
end,
)


class MissingFacet(Diagnostic):
severity = Diagnostic.Level.warning

Expand Down
24 changes: 24 additions & 0 deletions snooty/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
MissingAssociatedToc,
MissingChild,
MissingFacet,
MissingStructuredDataFields,
RemovedLiteralBlockSyntax,
TabMustBeDirective,
TodoInfo,
Expand Down Expand Up @@ -1235,6 +1236,29 @@ def _locate_text(text: str) -> int:
if icon_argument:
self.validate_and_add_asset(doc, icon_argument, line)

elif name == "video":
sd_req_option_names = [
"title",
"thumbnail-url",
"upload-date",
"description",
]
missing_option_names = []

for option_name in sd_req_option_names:
val = options.get(option_name, None)
if not val:
missing_option_names.append(option_name)

# We want to encourage defining all of these options together or not at all for structured data SEO
missing_options_len = len(missing_option_names)
if missing_options_len > 0 and missing_options_len != len(
sd_req_option_names
):
self.diagnostics.append(
MissingStructuredDataFields(name, missing_option_names, line)
)

elif name in {"pubdate", "updated-date"}:
if "date" in node:
doc.options["date"] = node["date"]
Expand Down
11 changes: 10 additions & 1 deletion snooty/rstspec.toml
Original file line number Diff line number Diff line change
Expand Up @@ -895,7 +895,16 @@ options.type = "string"
[directive.video]
help = "Embed a video in your article"
argument_type = "uri"
example = """.. video:: ${1: full video url}"""
options.title = "string"
options.thumbnail-url = "uri"
options.upload-date = "iso_8601"
options.description = "string"
example = """.. video:: ${1: https://www.youtube.com/embed/XrJG994YxD8}
:title: {$2: Mastering Indexing for Perfect Query Matches}
:thumbnail-url: ${3: https://i.ytimg.com/vi/XrJG994YxD8/maxresdefault.jpg}
:upload-date: ${4: 2023-11-08T05:00:28-08:00}
:description: {$5: Learn how to make Atlas Search indexes to improve search speed and quality.}
"""

[directive.youtube]
help = "Embed a video from YouTube in your article"
Expand Down
9 changes: 9 additions & 0 deletions snooty/specparser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import dataclasses
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import (
Any,
Expand Down Expand Up @@ -357,6 +358,14 @@ def validator(argument: str) -> object:
return validator
elif isinstance(option_spec, PrimitiveType):
return VALIDATORS[option_spec]
elif isinstance(option_spec, str) and option_spec == "iso_8601":

def validator(argument: str) -> object:
# Error is thrown if format is wrong
datetime.fromisoformat(argument)
return argument

return validator
elif isinstance(option_spec, str) and option_spec in self.enum:
return lambda argument: tinydocutils.directives.choice(
argument, self.enum[option_spec]
Expand Down
88 changes: 88 additions & 0 deletions snooty/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
MalformedRelativePath,
MissingChild,
MissingFacet,
MissingStructuredDataFields,
RemovedLiteralBlockSyntax,
TabMustBeDirective,
UnexpectedDirectiveOrder,
Expand Down Expand Up @@ -4367,3 +4368,90 @@ def test_method_selector() -> None:
# Duplicate "cli" id
DuplicateOptionId,
]


def test_video() -> None:
path = FileId("test.txt")
project_config = ProjectConfig(ROOT_PATH, "")
parser = rstparser.Parser(project_config, JSONVisitor)

# Valid cases
page, diagnostics = parse_rst(
parser,
path,
"""
=========
Test Page
=========
.. video:: https://www.youtube.com/embed/XrJG994YxD8
.. video:: https://www.youtube.com/embed/XrJG994YxD8
:title: Mastering Indexing for Perfect Query Matches
:description: In this video, we learn about how Atlas Search indexes can be used to optimize queries.
:thumbnail-url: https://i.ytimg.com/vi/XrJG994YxD8/maxresdefault.jpg
:upload-date: 2023-11-08T05:00:28-08:00
.. video:: https://www.youtube.com/embed/XrJG994YxD8
:thumbnail-url: https://i.ytimg.com/vi/XrJG994YxD8/maxresdefault.jpg
:upload-date: 2023-11-08
.. video:: https://www.youtube.com/embed/XrJG994YxD8
:description: This is an educational video.
""",
)
page.finish(diagnostics)
assert len(diagnostics) == 2
assert isinstance(diagnostics[0], MissingStructuredDataFields)
assert "['title', 'description']" in diagnostics[0].message
assert isinstance(diagnostics[1], MissingStructuredDataFields)
assert "['title', 'thumbnail-url', 'upload-date']" in diagnostics[1].message

check_ast_testing_string(
page.ast,
"""
<root fileid="test.txt">
<section>
<heading id="test-page"><text>Test Page</text></heading>
<directive name="video">
<reference refuri="https://www.youtube.com/embed/XrJG994YxD8">
<text>https://www.youtube.com/embed/XrJG994YxD8</text>
</reference>
</directive>
<directive name="video" title="Mastering Indexing for Perfect Query Matches" description="In this video, we learn about how Atlas Search indexes can be used to optimize queries." thumbnail-url="https://i.ytimg.com/vi/XrJG994YxD8/maxresdefault.jpg" upload-date="2023-11-08T05:00:28-08:00">
<reference refuri="https://www.youtube.com/embed/XrJG994YxD8">
<text>https://www.youtube.com/embed/XrJG994YxD8</text>
</reference>
</directive>
<directive name="video" thumbnail-url="https://i.ytimg.com/vi/XrJG994YxD8/maxresdefault.jpg" upload-date="2023-11-08">
<reference refuri="https://www.youtube.com/embed/XrJG994YxD8">
<text>https://www.youtube.com/embed/XrJG994YxD8</text>
</reference>
</directive>
<directive name="video" description="This is an educational video.">
<reference refuri="https://www.youtube.com/embed/XrJG994YxD8">
<text>https://www.youtube.com/embed/XrJG994YxD8</text>
</reference>
</directive>
</section>
</root>
""",
)

# Error cases
page, diagnostics = parse_rst(
parser,
path,
"""
=========
Test Page
=========
.. video:: https://www.youtube.com/embed/XrJG994YxD8
:thumbnail-url: https://i.ytimg.com/vi/XrJG994YxD8/maxresdefault.jpg
:upload-date: 11-11-2011
""",
)
page.finish(diagnostics)
# Diagnostic due to invalid upload-date format
assert [type(x) for x in diagnostics] == [DocUtilsParseError]

0 comments on commit 45d4a43

Please sign in to comment.