Compare commits

..

8 Commits

Author SHA1 Message Date
Jamie Hardt 9349f4ba1e Add Python 3.14 support (#21)
* Update pyproject.toml

Nudged version again

* Update python-package.yml

* Delete toot.yml

* Update pyproject.toml

Added verion 3.14 to classifiers

---------

Co-authored-by: Jamie Hardt <jamie@squad51.us>
2026-09-19 16:42:06 -07:00
Jamie Hardt e0e3024280 Bug ruff 2026 (#19)
* Updated Ruff to v0.16 and fixed for default checks 

* Nudge version and removed license classifier

---------

Co-authored-by: Jamie Hardt <jamie@squad51.us>
2026-09-19 16:35:56 -07:00
Jamie Hardt 2fc8a653cd Update .readthedocs.yaml
Removing dead lines
2026-09-17 21:07:26 -07:00
Jamie Hardt 183a7ef687 Update .readthedocs.yaml 2026-09-17 21:06:06 -07:00
Jamie Hardt e0fe946455 Update .readthedocs.yaml 2026-09-17 21:02:20 -07:00
Jamie Hardt 12ecb86bdf Docs update (#20)
* Update .readthedocs.yaml

Updated RTD version to `ubuntu-lts-latest`

* Update .readthedocs.yaml

RTD config fix
2026-09-17 20:57:00 -07:00
Jamie Hardt 6f2367c9fd Update pyproject.toml 2026-09-17 20:52:45 -07:00
jamie 8a23d7c702 Merge branch 'master' of https://github.com/iluvcapra/ptulsconv 2026-09-17 20:46:57 -07:00
18 changed files with 139 additions and 198 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
steps: steps:
- uses: actions/checkout@v2.5.0 - uses: actions/checkout@v2.5.0
-22
View File
@@ -1,22 +0,0 @@
name: Test Toot
on:
workflow_dispatch:
jobs:
print-tag:
runs-on: ubuntu-latest
steps:
- name: Report to Mastodon
uses: cbrgm/mastodon-github-action@v1.0.1
env:
MASTODON_URL: ${{ secrets.MASTODON_URL }}
MASTODON_ACCESS_TOKEN: ${{ secrets.MASTODON_ACCESS_TOKEN }}
with:
message: |
This is a test toot, automatically posted by a github action.
${{ github.server_url }}/${{ github.repository }}
${{ github.ref }}
-16
View File
@@ -14,9 +14,6 @@ build:
pre_install: pre_install:
- pip install --upgrade pip - pip install --upgrade pip
- pip install --upgrade --upgrade-strategy only-if-needed --group doc . - pip install --upgrade --upgrade-strategy only-if-needed --group doc .
# build:
# html:
# - python -m sphinx -T -b html -d _build/doctrees -D language=en . $READTHEDOCS_OUTPUT/html
post_build: post_build:
- echo "Command run at 'post_build' step" - echo "Command run at 'post_build' step"
- echo `date` - echo `date`
@@ -29,16 +26,3 @@ sphinx:
#If using Sphinx, optionally build your docs in additional formats such as PDF #If using Sphinx, optionally build your docs in additional formats such as PDF
formats: formats:
- pdf - pdf
# FIXME: We should be installing the `doc` dependency group and not the `doc`
# extra.
#Optionally declare the Python requirements required to build your docs
python:
install:
- method: pip
path: .
extra_requirements:
- doc
+2 -2
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "ptulsconv" name = "ptulsconv"
version = "4.0.0" version = "4.0.2"
description = "Read Pro Tools Text exports and generate PDF ADR Reports, JSON" description = "Read Pro Tools Text exports and generate PDF ADR Reports, JSON"
readme = "README.md" readme = "README.md"
requires-python = ">=3.9" requires-python = ">=3.9"
@@ -9,7 +9,6 @@ license-files = ["LICENSE"]
keywords = ["text-processing", "parsers", "film", keywords = ["text-processing", "parsers", "film",
"broadcast", "editing", "editorial"] "broadcast", "editing", "editorial"]
classifiers = [ classifiers = [
'License :: OSI Approved :: MIT License',
'Topic :: Multimedia', 'Topic :: Multimedia',
'Topic :: Multimedia :: Sound/Audio', 'Topic :: Multimedia :: Sound/Audio',
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
@@ -17,6 +16,7 @@ classifiers = [
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Development Status :: 5 - Production/Stable", "Development Status :: 5 - Production/Stable",
"Topic :: Text Processing :: Filters" "Topic :: Text Processing :: Filters"
] ]
+2 -5
View File
@@ -9,7 +9,6 @@ from ptulsconv.commands import convert
from ptulsconv.docparser.adr_entity import ADR_TAG_MAPPING, GENERIC_TAG_MAPPING from ptulsconv.docparser.adr_entity import ADR_TAG_MAPPING, GENERIC_TAG_MAPPING
from ptulsconv.reporting import ( from ptulsconv.reporting import (
print_banner_style, print_banner_style,
print_fatal_error,
print_section_header_style, print_section_header_style,
print_status_style, print_status_style,
) )
@@ -111,7 +110,7 @@ def main():
elif options.show_formats: elif options.show_formats:
dump_formats() dump_formats()
sys.exit(0) sys.exit(0)
try:
major_mode = options.output_format major_mode = options.output_format
if len(args) < 2: if len(args) < 2:
@@ -120,9 +119,7 @@ def main():
) )
convert(major_mode=major_mode, warnings=options.warnings) convert(major_mode=major_mode, warnings=options.warnings)
else: else:
convert( convert(input_file=args[1], major_mode=major_mode, warnings=options.warnings)
input_file=args[1], major_mode=major_mode, warnings=options.warnings
)
if __name__ == "__main__": if __name__ == "__main__":
+7 -6
View File
@@ -6,18 +6,19 @@ from __future__ import annotations
import math import math
import re import re
from collections import namedtuple
from fractions import Fraction from fractions import Fraction
from typing import SupportsFloat from typing import NamedTuple
class TimecodeFormat( class TimecodeFormat(NamedTuple):
namedtuple("_TimecodeFormat", "frame_duration logical_fps drop_frame")
):
""" """
A struct reperesenting a timecode datum. A struct reperesenting a timecode datum.
""" """
frame_duration: Fraction
logical_fps: int
drop_frame: bool
def smpte_to_seconds(self, smpte: str) -> Fraction | None: def smpte_to_seconds(self, smpte: str) -> Fraction | None:
frame_count = smpte_to_frame_count( frame_count = smpte_to_frame_count(
smpte, self.logical_fps, drop_frame_hint=self.drop_frame smpte, self.logical_fps, drop_frame_hint=self.drop_frame
@@ -27,7 +28,7 @@ class TimecodeFormat(
else: else:
return frame_count * self.frame_duration return frame_count * self.frame_duration
def seconds_to_smpte(self, seconds: SupportsFloat) -> str: def seconds_to_smpte(self, seconds: Fraction) -> str:
frame_count = int(seconds / self.frame_duration) frame_count = int(seconds / self.frame_duration)
return frame_count_to_smpte(frame_count, self.logical_fps, self.drop_frame) return frame_count_to_smpte(frame_count, self.logical_fps, self.drop_frame)
-1
View File
@@ -13,7 +13,6 @@ from collections.abc import Iterator
from fractions import Fraction from fractions import Fraction
from itertools import chain from itertools import chain
from json import JSONEncoder from json import JSONEncoder
from zoneinfo import ZoneInfo
import ptsl import ptsl
+1 -4
View File
@@ -2,7 +2,4 @@
Docparser module Docparser module
""" """
from .pt_doc_parser import parse_document # pyright: ignore[reportUnusedImport, reportUnusedImport] # noqa: F401, I001
from .pt_doc_parser import parse_document
__all__ = [parse_document]
+11 -9
View File
@@ -1,10 +1,9 @@
from __future__ import annotations from __future__ import annotations
from collections import namedtuple
from collections.abc import Generator, Iterator from collections.abc import Generator, Iterator
from dataclasses import dataclass from dataclasses import dataclass
from fractions import Fraction from fractions import Fraction
from typing import Callable from typing import Callable, NamedTuple
from ptulsconv.docparser import doc_entity from ptulsconv.docparser import doc_entity
@@ -27,12 +26,15 @@ class TagCompiler:
items. items.
""" """
Intermediate = namedtuple( class Intermediate(NamedTuple):
"Intermediate", track_content: str
"track_content track_tags track_comment_tags " clip_content: str
"clip_content clip_tags clip_tag_mode start " track_tags: dict[str, str]
"finish", track_comment_tags: dict[str, str]
) clip_tags: dict[str, str]
clip_tag_mode: TagPreModes
start: Fraction
finish: Fraction
session: doc_entity.SessionDescriptor session: doc_entity.SessionDescriptor
@@ -181,7 +183,7 @@ class TagCompiler:
if item.clip_tag_mode == TagPreModes.TIMESPAN: if item.clip_tag_mode == TagPreModes.TIMESPAN:
time_spans.append((item.clip_tags, item.start, item.finish)) time_spans.append((item.clip_tags, item.start, item.finish))
else: else:
yield item, list(time_spans) yield item, tuple(time_spans)
@staticmethod @staticmethod
def _time_span_tags(at_time: Fraction, applicable_spans) -> dict: def _time_span_tags(at_time: Fraction, applicable_spans) -> dict:
@@ -1,5 +1,4 @@
from enum import Enum from enum import Enum
from typing import Dict
from parsimonious import Grammar, NodeVisitor from parsimonious import Grammar, NodeVisitor
@@ -37,7 +36,7 @@ def parse_tags(prompt: str) -> "TaggedStringResult":
class TaggedStringResult: class TaggedStringResult:
content: str content: str
tag_dict: Dict[str, str] tag_dict: dict[str, str]
mode: TagPreModes mode: TagPreModes
def __init__(self, content, tag_dict, mode): def __init__(self, content, tag_dict, mode):
@@ -53,7 +52,7 @@ class TagListVisitor(NodeVisitor):
return TaggedStringResult( return TaggedStringResult(
content=next(iter(line_opt), None), content=next(iter(line_opt), None),
tag_dict=next(iter(tag_list_opt), dict()), tag_dict=next(iter(tag_list_opt), {}),
mode=TagPreModes(next(iter(modifier_opt), "Normal")), mode=TagPreModes(next(iter(modifier_opt), "Normal")),
) )
@@ -74,7 +73,7 @@ class TagListVisitor(NodeVisitor):
@staticmethod @staticmethod
def visit_tag_list(_, visited_children): def visit_tag_list(_, visited_children):
retdict = dict() retdict = {}
for child in visited_children: for child in visited_children:
if child[0] is not None: if child[0] is not None:
k, v = child[0] k, v = child[0]
+1
View File
@@ -1,6 +1,7 @@
""" """
Methods for converting string reprentations of film footage. Methods for converting string reprentations of film footage.
""" """
from __future__ import annotations from __future__ import annotations
import re import re
+21 -16
View File
@@ -26,7 +26,7 @@ class ReportCanvas(canvas.Canvas):
def showPage(self): def showPage(self):
self._saved_page_states.append(dict(self.__dict__)) self._saved_page_states.append(dict(self.__dict__))
self._startPage() self._startPage() # pyright: ignore[reportAttributeAccessIssue]
def save(self): def save(self):
"""add page info to each page (page x of y)""" """add page info to each page (page x of y)"""
@@ -41,9 +41,11 @@ class ReportCanvas(canvas.Canvas):
self.saveState() self.saveState()
self.setFont("Helvetica", 10) # FIXME make this customizable self.setFont("Helvetica", 10) # FIXME make this customizable
self.drawString( self.drawString(
0.5 * inch, 0.5 * inch, "Page %d of %d" % (self._pageNumber, page_count) 0.5 * inch,
0.5 * inch,
f"Page {self._pageNumber} of {page_count}", # pyright: ignore[reportAttributeAccessIssue]
) )
right_edge = self._pagesize[0] - 0.5 * inch right_edge = self._pagesize[0] - 0.5 * inch # pyright: ignore[reportAttributeAccessIssue]
self.drawRightString( self.drawRightString(
right_edge, 0.5 * inch, self._report_date.strftime("%m/%d/%Y %H:%M") right_edge, 0.5 * inch, self._report_date.strftime("%m/%d/%Y %H:%M")
) )
@@ -62,7 +64,7 @@ class ADRDocTemplate(BaseDocTemplate):
def make_doc_template( def make_doc_template(
page_size, page_size: tuple[float, float],
filename, filename,
document_title, document_title,
title: str, title: str,
@@ -71,18 +73,22 @@ def make_doc_template(
client: str, client: str,
document_subheader: str, document_subheader: str,
left_margin=0.5 * inch, left_margin=0.5 * inch,
fonts: list[TTFont] = [], fonts: list[TTFont] | None = None,
) -> ADRDocTemplate: ) -> ADRDocTemplate:
if fonts is None:
fonts = []
right_margin = top_margin = bottom_margin = 0.5 * inch right_margin = top_margin = bottom_margin = 0.5 * inch
page_box: GRect = GRect(0.0, 0.0, page_size[0], page_size[1]) page_box: GRect = GRect(0.0, 0.0, page_size[0], page_size[1])
_, page_box = page_box.split_x(left_margin, direction="l") _, page_box = page_box.split_x(left_margin, direction="l") # pyright: ignore[reportAssignmentType]
_, page_box = page_box.split_x(right_margin, direction="r") _, page_box = page_box.split_x(right_margin, direction="r") # pyright: ignore[reportAssignmentType]
_, page_box = page_box.split_y(bottom_margin, direction="u") _, page_box = page_box.split_y(bottom_margin, direction="u") # pyright: ignore[reportAssignmentType]
_, page_box = page_box.split_y(top_margin, direction="d") _, page_box = page_box.split_y(top_margin, direction="d") # pyright: ignore[reportAssignmentType]
footer_box, page_box = page_box.split_y(0.25 * inch, direction="u") footer_box, page_box = page_box.split_y(0.25 * inch, direction="u") # pyright: ignore[reportAssignmentType]
header_box, page_box = page_box.split_y(0.75 * inch, direction="d") header_box, page_box = page_box.split_y(0.75 * inch, direction="d") # pyright: ignore[reportAssignmentType]
title_box, report_box = header_box.split_x(3.5 * inch, direction="r") title_box, report_box = header_box.split_x(3.5 * inch, direction="r") # pyright: ignore[reportOptionalMemberAccess]
def on_page_lambda(c, _): def on_page_lambda(c, _):
draw_header_footer( draw_header_footer(
@@ -97,7 +103,6 @@ def make_doc_template(
doc_title=document_header, doc_title=document_header,
) )
frames = [Frame(page_box.min_x, page_box.min_y, page_box.width, page_box.height)] frames = [Frame(page_box.min_x, page_box.min_y, page_box.width, page_box.height)]
page_template = PageTemplate(id="Main", frames=frames, onPage=on_page_lambda) page_template = PageTemplate(id="Main", frames=frames, onPage=on_page_lambda)
@@ -292,7 +297,7 @@ class GRect:
rem = self rem = self
for item in x_list: for item in x_list:
s, rem = rem.split_x(item, direction) s, rem = rem.split_x(item, direction) # pyright: ignore[reportOptionalMemberAccess]
ret_list.append(s) ret_list.append(s)
return ret_list, rem return ret_list, rem
@@ -302,7 +307,7 @@ class GRect:
rem = self rem = self
for item in y_list: for item in y_list:
s, rem = rem.split_y(item, direction) s, rem = rem.split_y(item, direction) # pyright: ignore[reportOptionalMemberAccess]
ret_list.append(s) ret_list.append(s)
return ret_list, rem return ret_list, rem
@@ -399,7 +404,7 @@ class GRect:
cp.rect(self.min_x, self.min_y, self.width, self.height) cp.rect(self.min_x, self.min_y, self.width, self.height)
a_canvas.clipPath(cp, stroke=0, fill=0) a_canvas.clipPath(cp, stroke=0, fill=0)
w, h = flowable.wrap(inset_rect.width, inset_rect.height) _w, h = flowable.wrap(inset_rect.width, inset_rect.height)
flowable.drawOn(a_canvas, inset_rect.x, inset_rect.max_y - h) flowable.drawOn(a_canvas, inset_rect.x, inset_rect.max_y - h)
+33 -38
View File
@@ -1,11 +1,8 @@
from typing import List, Optional from __future__ import annotations
from reportlab.lib import colors from reportlab.lib import colors
from reportlab.lib.pagesizes import letter, portrait from reportlab.lib.pagesizes import letter, portrait
from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.styles import getSampleStyleSheet
# from reportlab.pdfbase import pdfmetrics
# from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.units import inch from reportlab.lib.units import inch
from reportlab.platypus import Paragraph, Spacer, Table from reportlab.platypus import Paragraph, Spacer, Table
@@ -14,15 +11,13 @@ from .__init__ import make_doc_template, time_format
def build_columns( def build_columns(
lines: List[ADRLine], lines: list[ADRLine],
reel_list: Optional[List[str]], reel_list: list[str] | None,
show_priorities=False, show_priorities=False,
include_omitted=False, include_omitted=False,
): ):
columns = list() columns = []
reel_numbers = reel_list or sorted( reel_numbers = reel_list or sorted({x.reel for x in lines if x.reel is not None})
set([x.reel for x in lines if x.reel is not None])
)
num_column_width = 15.0 / 32.0 * inch num_column_width = 15.0 / 32.0 * inch
@@ -160,17 +155,17 @@ def build_columns(
): ):
columns.append( columns.append(
{ {
"heading": "P%i" % n, "heading": f"P{n}",
"value_getter": lambda recs: len( "value_getter": lambda recs, N=n: len(
[r for r in recs if r.priority == n] [r for r in recs if r.priority == N]
), ),
"value_getter2": ( "value_getter2": (
lambda recs: time_format( lambda recs, N=n: time_format(
sum( sum(
[ [
r.time_budget_mins or 0.0 r.time_budget_mins or 0.0
for r in recs for r in recs
if r.priority == n if r.priority == N
] ]
) )
) )
@@ -243,22 +238,20 @@ def build_columns(
return columns return columns
def populate_columns(lines: List[ADRLine], columns, include_omitted, _page_size): def populate_columns(lines: list[ADRLine], columns, include_omitted, _page_size):
data = list() data = []
styles = list() styles = []
columns_widths = list() columns_widths = []
sorted_character_numbers: List[str] = sorted( sorted_character_numbers: list[str | None] = sorted(
set([x.character_id for x in lines]), key=lambda x: str(x) {x.character_id for x in lines}, key=lambda x: str(x)
) )
# construct column styles
for i, c in enumerate(columns): for i, c in enumerate(columns):
styles.extend(c["style_getter"](i)) styles.extend(c["style_getter"](i))
columns_widths.append(c["width"]) columns_widths.append(c["width"])
data.append(list(map(lambda x: x["heading"], columns))) data.append([x["heading"] for x in columns])
if not include_omitted: if not include_omitted:
lines = [x for x in lines if not x.omitted] lines = [x for x in lines if not x.omitted]
@@ -266,8 +259,8 @@ def populate_columns(lines: List[ADRLine], columns, include_omitted, _page_size)
for n in sorted_character_numbers: for n in sorted_character_numbers:
char_records = [x for x in lines if x.character_id == n] char_records = [x for x in lines if x.character_id == n]
if len(char_records) > 0: if len(char_records) > 0:
row_data = list() row_data = []
row_data2 = list() row_data2 = []
for col in columns: for col in columns:
row1_index = len(data) row1_index = len(data)
@@ -291,8 +284,8 @@ def populate_columns(lines: List[ADRLine], columns, include_omitted, _page_size)
data.append(row_data) data.append(row_data)
data.append(row_data2) data.append(row_data2)
summary_row1 = list() summary_row1 = []
summary_row2 = list() summary_row2 = []
row1_index = len(data) row1_index = len(data)
for col in columns: for col in columns:
@@ -316,12 +309,15 @@ def populate_columns(lines: List[ADRLine], columns, include_omitted, _page_size)
def output_report( def output_report(
lines: List[ADRLine], lines: list[ADRLine],
reel_list: List[str], reel_list: list[str],
page_size: tuple[float, float] | None = None,
include_omitted=False, include_omitted=False,
page_size=portrait(letter),
font_name="Helvetica", font_name="Helvetica",
): ):
if page_size is None:
page_size = portrait(letter)
columns = build_columns(lines, include_omitted=include_omitted, reel_list=reel_list) columns = build_columns(lines, include_omitted=include_omitted, reel_list=reel_list)
data, style, columns_widths = populate_columns( data, style, columns_widths = populate_columns(
lines, columns, include_omitted, page_size lines, columns, include_omitted, page_size
@@ -334,16 +330,17 @@ def output_report(
# pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc')) # pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc'))
title = "%s Line Count" % lines[0].title title = f"{lines[0].title} Line Count"
filename = title + ".pdf" filename = title + ".pdf"
doc = make_doc_template( doc = make_doc_template(
page_size=page_size, page_size=page_size,
filename=filename, filename=filename,
document_title=title, document_title=title,
title=lines[0].title, title=lines[0].title,
document_subheader=lines[0].spot, document_subheader=lines[0].spot or "",
client=lines[0].client, client=lines[0].client or "",
supervisor=lines[0].supervisor, supervisor=lines[0].supervisor or "",
document_header="Line Count", document_header="Line Count",
) )
@@ -364,8 +361,6 @@ def output_report(
omitted_count = len([x for x in lines if x.omitted]) omitted_count = len([x for x in lines if x.omitted])
if not include_omitted and omitted_count > 0: if not include_omitted and omitted_count > 0:
story.append( story.append(Paragraph(f"* {omitted_count} Omitted lines are excluded.", style))
Paragraph("* %i Omitted lines are excluded." % omitted_count, style)
)
doc.build(story) doc.build(story)
+32 -41
View File
@@ -1,5 +1,4 @@
from __future__ import annotations
from typing import List
from reportlab.lib.pagesizes import letter, portrait from reportlab.lib.pagesizes import letter, portrait
from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.styles import getSampleStyleSheet
@@ -13,7 +12,7 @@ from .__init__ import make_doc_template, time_format
def build_aux_data_field(line: ADRLine): def build_aux_data_field(line: ADRLine):
entries = list() entries = []
if line.reason is not None: if line.reason is not None:
entries.append("Reason: " + line.reason) entries.append("Reason: " + line.reason)
if line.note is not None: if line.note is not None:
@@ -27,40 +26,30 @@ def build_aux_data_field(line: ADRLine):
tag_field = "" tag_field = ""
if line.effort: if line.effort:
bg_color = "red" bg_color = "red"
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % ( tag_field += (
bg_color, f"<font backColor={bg_color} textColor={fg_color} fontSize=11>EFF</font> "
fg_color,
"EFF",
) )
elif line.tv: elif line.tv:
bg_color = "blue" bg_color = "blue"
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % ( tag_field += (
bg_color, f"<font backColor={bg_color} textColor={fg_color} fontSize=11>TV</font> "
fg_color,
"TV",
) )
elif line.adlib: elif line.adlib:
bg_color = "purple" bg_color = "purple"
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font> " % ( tag_field += (
bg_color, f"<font backColor={bg_color} textColor={fg_color} fontSize=11>ADLIB</font> "
fg_color,
"ADLIB",
) )
elif line.optional: elif line.optional:
bg_color = "green" bg_color = "green"
tag_field += "<font backColor=%s textColor=%s fontSize=11>%s</font>" % ( tag_field += f"font backColor={bg_color} textColor={fg_color} fontSize=11>OPTIONAL</font>"
bg_color,
fg_color,
"OPTIONAL",
)
entries.append(tag_field) entries.append(tag_field)
return "<br />".join(entries) return "<br />".join(entries)
def build_story(lines: List[ADRLine], tc_rate: TimecodeFormat, font_name="Helvetica"): def build_story(lines: list[ADRLine], tc_rate: TimecodeFormat, font_name="Helvetica"):
story = list() story = []
this_scene = None this_scene = None
scene_style = getSampleStyleSheet()["Normal"] scene_style = getSampleStyleSheet()["Normal"]
@@ -78,15 +67,14 @@ def build_story(lines: List[ADRLine], tc_rate: TimecodeFormat, font_name="Helvet
("BOTTOMPADDING", (0, 0), (-1, -1), 24.0), ("BOTTOMPADDING", (0, 0), (-1, -1), 24.0),
] ]
cue_number_field = "%s<br /><font fontSize=7>%s</font>" % ( cue_number_field = (
line.cue_number, f"{line.cue_number}<br /><font fontSize=7>{line.character_name}</font>"
line.character_name,
) )
time_data = time_format(line.time_budget_mins) time_data = time_format(line.time_budget_mins)
if line.priority is not None: if line.priority is not None:
time_data = time_data + "<br />" + "P: " + line.priority time_data = time_data + "<br />" + "P: " + str(line.priority)
aux_data_field = build_aux_data_field(line) aux_data_field = build_aux_data_field(line)
@@ -96,7 +84,7 @@ def build_story(lines: List[ADRLine], tc_rate: TimecodeFormat, font_name="Helvet
[ [
Paragraph(cue_number_field, line_style), Paragraph(cue_number_field, line_style),
Paragraph(tc_data, line_style), Paragraph(tc_data, line_style),
Paragraph(line.prompt, line_style), Paragraph(line.prompt or "[No Prompt]", line_style),
Paragraph(time_data, line_style), Paragraph(time_data, line_style),
Paragraph(aux_data_field, line_style), Paragraph(aux_data_field, line_style),
] ]
@@ -135,11 +123,11 @@ def build_tc_data(line: ADRLine, tc_format: TimecodeFormat):
third_line = [] third_line = []
if line.reel is not None: if line.reel is not None:
if line.reel[0:1] == "R": if line.reel[0:1] == "R":
third_line.append("%s" % line.reel) third_line.append(f"{line.reel}")
else: else:
third_line.append("Reel %s" % line.reel) third_line.append(f"Reel {line.reel}")
if line.version is not None: if line.version is not None:
third_line.append("(%s)" % line.version) third_line.append(f"({line.version})")
if len(third_line) > 0: if len(third_line) > 0:
tc_data = tc_data + "<br/>" + " ".join(third_line) tc_data = tc_data + "<br/>" + " ".join(third_line)
return tc_data return tc_data
@@ -147,17 +135,17 @@ def build_tc_data(line: ADRLine, tc_format: TimecodeFormat):
def generate_report( def generate_report(
page_size, page_size,
lines: List[ADRLine], lines: list[ADRLine],
tc_rate: TimecodeFormat, tc_rate: TimecodeFormat,
character_number=None, character_number=None,
include_omitted=True, include_omitted=True,
): ):
if character_number is not None: if character_number is not None:
lines = [r for r in lines if r.character_id == character_number] lines = [r for r in lines if r.character_id == character_number]
title = "%s ADR Report (%s)" % (lines[0].title, lines[0].character_name) title = f"{lines[0].title} ADR Report ({lines[0].character_name})"
document_header = "%s ADR Report" % lines[0].character_name document_header = f"{lines[0].character_name} ADR Report"
else: else:
title = "%s ADR Report" % lines[0].title title = f"{lines[0].title} ADR Report"
document_header = "ADR Report" document_header = "ADR Report"
if not include_omitted: if not include_omitted:
@@ -172,9 +160,9 @@ def generate_report(
document_title=title, document_title=title,
document_header=document_header, document_header=document_header,
title=lines[0].title, title=lines[0].title,
supervisor=lines[0].supervisor, supervisor=lines[0].supervisor or "",
client=lines[0].client, client=lines[0].client or "",
document_subheader=lines[0].spot, document_subheader=lines[0].spot or "",
left_margin=0.75 * inch, left_margin=0.75 * inch,
) )
story = build_story(lines, tc_rate) story = build_story(lines, tc_rate)
@@ -182,13 +170,16 @@ def generate_report(
def output_report( def output_report(
lines: List[ADRLine], lines: list[ADRLine],
tc_display_format: TimecodeFormat, tc_display_format: TimecodeFormat,
page_size=portrait(letter), page_size: tuple[float, float] | None = None,
by_character=False, by_character: bool = False,
): ):
if page_size is None:
page_size = portrait(letter)
if by_character: if by_character:
character_numbers = set(r.character_id for r in lines) character_numbers = {r.character_id for r in lines}
for n in character_numbers: for n in character_numbers:
generate_report(page_size, lines, tc_display_format, n) generate_report(page_size, lines, tc_display_format, n)
else: else:
+10 -14
View File
@@ -24,7 +24,7 @@ def draw_header_block(canvas, rect, record: ADRLine):
def draw_character_row(canvas, rect, record: ADRLine): def draw_character_row(canvas, rect, record: ADRLine):
label_frame, value_frame = rect.split_x(1.25 * inch) label_frame, value_frame = rect.split_x(1.25 * inch)
label_frame.draw_text_cell(canvas, "CHARACTER", font_name, 10, force_baseline=9.0) label_frame.draw_text_cell(canvas, "CHARACTER", font_name, 10, force_baseline=9.0)
line = "%s / %s" % (record.character_id, record.character_name) line = f"{record.character_id} / {record.character_name}"
if record.actor_name is not None: if record.actor_name is not None:
line = line + " / " + record.actor_name line = line + " / " + record.actor_name
value_frame.draw_text_cell(canvas, line, font_name, 12, force_baseline=9.0) value_frame.draw_text_cell(canvas, line, font_name, 12, force_baseline=9.0)
@@ -61,9 +61,9 @@ def draw_cue_number_block(canvas, rect, record: ADRLine):
"omitted": "OMIT", "omitted": "OMIT",
} }
tag_field = "" tag_field = ""
for key in tags: for key, value in tags.items():
if getattr(record, key): if getattr(record, key):
tag_field = tag_field + tags[key] + " " tag_field = tag_field + value + " "
aux_frame.draw_text_cell( aux_frame.draw_text_cell(
canvas, tag_field, font_name, 10, inset_x=10.0, inset_y=2.0, vertical_align="t" canvas, tag_field, font_name, 10, inset_x=10.0, inset_y=2.0, vertical_align="t"
@@ -221,7 +221,7 @@ def draw_aux_block(canvas, rect, recording_time_sec_this_line, recording_time_se
rect.draw_border(canvas, "min_x") rect.draw_border(canvas, "min_x")
content_rect = rect.inset_xy(10.0, 10.0) content_rect = rect.inset_xy(10.0, 10.0)
lines, last_line = content_rect.divide_y( lines, _ = content_rect.divide_y(
[12.0, 12.0, 24.0, 24.0, 24.0, 24.0], direction="d" [12.0, 12.0, 24.0, 24.0, 24.0, 24.0], direction="d"
) )
@@ -255,7 +255,7 @@ def draw_footer(canvas, rect, record: ADRLine, report_date, line_no, total_lines
rect.draw_border(canvas, "max_y") rect.draw_border(canvas, "max_y")
report_date_s = [report_date.strftime("%c")] report_date_s = [report_date.strftime("%c")]
spotting_name = [record.spot] if record.spot is not None else [] spotting_name = [record.spot] if record.spot is not None else []
pages_s = ["Line %i of %i" % (line_no, total_lines)] pages_s = [f"Line {line_no} of {total_lines}"]
footer_s = " - ".join(report_date_s + spotting_name + pages_s) footer_s = " - ".join(report_date_s + spotting_name + pages_s)
rect.draw_text_cell( rect.draw_text_cell(
canvas, footer_s, font_name=font_name, font_size=10.0, inset_y=2.0 canvas, footer_s, font_name=font_name, font_size=10.0, inset_y=2.0
@@ -266,11 +266,8 @@ def create_report_for_character(
records, report_date, tc_display_format: TimecodeFormat records, report_date, tc_display_format: TimecodeFormat
): ):
outfile = "%s_%s_%s_Log.pdf" % ( outfile = f"{records[0].title}_{records[0].character_id}_{records[0].character_name}_Log.pdf"
records[0].title,
records[0].character_id,
records[0].character_name,
)
assert outfile is not None assert outfile is not None
assert outfile[-4:] == ".pdf", "Output file must have 'pdf' extension!" assert outfile[-4:] == ".pdf", "Output file must have 'pdf' extension!"
@@ -285,7 +282,7 @@ def create_report_for_character(
) )
) )
cue_header_block, title_header_block = header_row.split_x(4.0 * inch) cue_header_block, _title_header_block = header_row.split_x(4.0 * inch)
(cue_number_block, timecode_block), reason_block = data_row.divide_x( (cue_number_block, timecode_block), reason_block = data_row.divide_x(
[1.5 * inch, 1.5 * inch] [1.5 * inch, 1.5 * inch]
) )
@@ -297,8 +294,7 @@ def create_report_for_character(
) )
c.setTitle( c.setTitle(
"%s %s (%s) Supervisor's Log" f"{records[0].title} {records[0].character_name} ({records[0].character_id}) Supervisor's Log"
% (records[0].title, records[0].character_name, records[0].character_id)
) )
c.setAuthor(records[0].supervisor) c.setAuthor(records[0].supervisor)
@@ -339,7 +335,7 @@ def create_report_for_character(
def output_report(lines, tc_display_format: TimecodeFormat): def output_report(lines, tc_display_format: TimecodeFormat):
report_date = datetime.datetime.now() report_date = datetime.datetime.now()
events = sorted(lines, key=lambda x: x.start) events = sorted(lines, key=lambda x: x.start)
character_numbers = set([x.character_id for x in lines]) character_numbers = {x.character_id for x in lines}
for n in character_numbers: for n in character_numbers:
create_report_for_character( create_report_for_character(
+6 -8
View File
@@ -1,5 +1,3 @@
from typing import List
from reportlab.lib import colors from reportlab.lib import colors
from reportlab.lib.pagesizes import letter from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.styles import getSampleStyleSheet
@@ -14,9 +12,9 @@ from .__init__ import make_doc_template
def output_report( def output_report(
lines: List[ADRLine], tc_display_format: TimecodeFormat, font_name="Helvetica" lines: list[ADRLine], tc_display_format: TimecodeFormat, font_name="Helvetica"
): ):
character_numbers = set([n.character_id for n in lines]) character_numbers = {n.character_id for n in lines}
# pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc')) # pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc'))
for n in character_numbers: for n in character_numbers:
@@ -27,8 +25,8 @@ def output_report(
char_lines = sorted(char_lines, key=lambda line: line.start) char_lines = sorted(char_lines, key=lambda line: line.start)
title = "%s (%s) %s ADR Script" % (char_lines[0].title, character_name, n) title = f"{char_lines[0].title} ({character_name}) {n} ADR Script"
filename = "%s_%s_%s_ADR Script.pdf" % (char_lines[0].title, n, character_name) filename = f"{char_lines[0].title}_{n}_{character_name}_ADR Script.pdf"
doc = make_doc_template( doc = make_doc_template(
page_size=letter, page_size=letter,
@@ -64,7 +62,7 @@ def output_report(
finish_tc = tc_display_format.seconds_to_smpte(line.finish) finish_tc = tc_display_format.seconds_to_smpte(line.finish)
data_block = [ data_block = [
[ [
Paragraph(line.cue_number, number_style), Paragraph(line.cue_number or "[No QN]", number_style),
Paragraph(start_tc + " - " + finish_tc, number_style), Paragraph(start_tc + " - " + finish_tc, number_style),
] ]
] ]
@@ -80,7 +78,7 @@ def output_report(
colWidths=[1.5 * inch, 6.0 * inch], colWidths=[1.5 * inch, 6.0 * inch],
style=[("LEFTPADDING", (0, 0), (-1, -1), 0.0)], style=[("LEFTPADDING", (0, 0), (-1, -1), 0.0)],
), ),
Paragraph(line.prompt, prompt_style), Paragraph(line.prompt or "[No Prompt]", prompt_style),
Spacer(1.0, inch * 1.5), Spacer(1.0, inch * 1.5),
] ]
) )
+1 -3
View File
@@ -43,9 +43,7 @@ def print_advisory_tagging_error(
sys.stderr.write(" ! \033[33;1mTagging error: \033[0m") sys.stderr.write(" ! \033[33;1mTagging error: \033[0m")
ok_string = failed_string[:position] ok_string = failed_string[:position]
not_ok_string = failed_string[position:] not_ok_string = failed_string[position:]
sys.stderr.write( sys.stderr.write(f'\033[32m"{ok_string}\033[31;1m{not_ok_string}"\033[0m\n')
f'\033[32m"{ok_string}\033[31;1m{not_ok_string}"\033[0m\n'
)
if parent_track_name is not None: if parent_track_name is not None:
sys.stderr.write(f' ! > On track "{parent_track_name}"\n') sys.stderr.write(f' ! > On track "{parent_track_name}"\n')