This commit is contained in:
Jamie Hardt
2023-07-21 14:03:05 -07:00
parent fd02d962d0
commit 5b36dcb5aa
7 changed files with 176 additions and 91 deletions

View File

@@ -4,7 +4,7 @@ from typing import Tuple, List
from reportlab.lib.pagesizes import portrait, letter from reportlab.lib.pagesizes import portrait, letter
from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch from reportlab.lib.units import inch
from reportlab.platypus import Paragraph, Table, Spacer from reportlab.platypus import Paragraph, Table
from ptulsconv.broadcast_timecode import TimecodeFormat from ptulsconv.broadcast_timecode import TimecodeFormat
from ptulsconv.pdf import make_doc_template from ptulsconv.pdf import make_doc_template
@@ -12,14 +12,15 @@ from ptulsconv.pdf import make_doc_template
# TODO: A Continuity # TODO: A Continuity
def table_for_scene(scene, tc_format, font_name = 'Helvetica'): def table_for_scene(scene, tc_format, font_name='Helvetica'):
scene_style = getSampleStyleSheet()['Normal'] scene_style = getSampleStyleSheet()['Normal']
scene_style.fontName = font_name scene_style.fontName = font_name
scene_style.leftIndent = 0. scene_style.leftIndent = 0.
scene_style.leftPadding = 0. scene_style.leftPadding = 0.
scene_style.spaceAfter = 18. scene_style.spaceAfter = 18.
tc_data = "<em>%s</em><br />%s" % (tc_format.seconds_to_smpte(scene[2]), tc_format.seconds_to_smpte(scene[3])) tc_data = "<em>%s</em><br />%s" % (tc_format.seconds_to_smpte(scene[2]),
tc_format.seconds_to_smpte(scene[3]))
row = [ row = [
Paragraph(tc_data, scene_style), Paragraph(tc_data, scene_style),
@@ -36,7 +37,7 @@ def table_for_scene(scene, tc_format, font_name = 'Helvetica'):
def output_report(scenes: List[Tuple[str, str, Fraction, Fraction]], def output_report(scenes: List[Tuple[str, str, Fraction, Fraction]],
tc_display_format: TimecodeFormat, tc_display_format: TimecodeFormat,
title: str, client: str, supervisor, paper_size = letter): title: str, client: str, supervisor, paper_size=letter):
filename = "%s Continuity.pdf" % title filename = "%s Continuity.pdf" % title
document_header = "Continuity" document_header = "Continuity"

View File

@@ -14,9 +14,12 @@ from .__init__ import time_format, make_doc_template
from ..docparser.adr_entity import ADRLine from ..docparser.adr_entity import ADRLine
def build_columns(lines: List[ADRLine], reel_list: Optional[List[str]], show_priorities=False, include_omitted=False): def build_columns(lines: List[ADRLine], reel_list: Optional[List[str]],
show_priorities=False, include_omitted=False):
columns = list() columns = list()
reel_numbers = reel_list or sorted(set([x.reel for x in lines if x.reel is not None])) reel_numbers = reel_list or sorted(
set([x.reel for x in lines if x.reel is not None])
)
num_column_width = 15. / 32. * inch num_column_width = 15. / 32. * inch
@@ -33,19 +36,26 @@ def build_columns(lines: List[ADRLine], reel_list: Optional[List[str]], show_pri
'heading': 'Role', 'heading': 'Role',
'value_getter': lambda recs: recs[0].character_name, 'value_getter': lambda recs: recs[0].character_name,
'value_getter2': lambda recs: recs[0].actor_name or "", 'value_getter2': lambda recs: recs[0].actor_name or "",
'style_getter': lambda col_index: [('LINEAFTER', (col_index, 0), (col_index, -1), 1.0, colors.black)], 'style_getter': lambda col_index: [('LINEAFTER',
(col_index, 0),
(col_index, -1),
1.0, colors.black)],
'width': 1.75 * inch, 'width': 1.75 * inch,
'summarize': False 'summarize': False
}) })
columns.append({ columns.append({
'heading': 'TV', 'heading': 'TV',
'value_getter': lambda recs: len([r for r in recs if r.tv]), 'value_getter': (lambda recs: len([r for r in recs if r.tv])),
'value_getter2': lambda recs: time_format(sum([r.time_budget_mins or 0. 'value_getter2': (lambda recs:
for r in recs if r.tv])), time_format(sum([r.time_budget_mins or 0.
'style_getter': lambda col_index: [('ALIGN', (col_index, 0), (col_index, -1), 'CENTER'), for r in recs if r.tv]))),
('LINEBEFORE', (col_index, 0), (col_index, -1), 1., colors.black), 'style_getter': (lambda col_index:
('LINEAFTER', (col_index, 0), (col_index, -1), .5, colors.gray)], [('ALIGN', (col_index, 0), (col_index, -1), 'CENTER'),
('LINEBEFORE', (col_index, 0), (col_index, -1),
1., colors.black),
('LINEAFTER', (col_index, 0), (col_index, -1),
.5, colors.gray)]),
'width': num_column_width 'width': num_column_width
}) })

View File

@@ -1,7 +1,7 @@
from reportlab.pdfgen.canvas import Canvas from reportlab.pdfgen.canvas import Canvas
from reportlab.pdfbase import pdfmetrics # from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont # from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.units import inch from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter from reportlab.lib.pagesizes import letter
@@ -11,20 +11,23 @@ from reportlab.platypus import Paragraph
from .__init__ import GRect from .__init__ import GRect
from ptulsconv.broadcast_timecode import TimecodeFormat, footage_to_frame_count from ptulsconv.broadcast_timecode import TimecodeFormat
from ptulsconv.docparser.adr_entity import ADRLine from ptulsconv.docparser.adr_entity import ADRLine
import datetime import datetime
font_name = 'Helvetica' font_name = 'Helvetica'
def draw_header_block(canvas, rect, record: ADRLine): def draw_header_block(canvas, rect, record: ADRLine):
rect.draw_text_cell(canvas, record.cue_number, "Helvetica", 44, vertical_align='m') rect.draw_text_cell(canvas, record.cue_number, "Helvetica", 44,
vertical_align='m')
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.) label_frame.draw_text_cell(canvas, "CHARACTER", font_name, 10,
force_baseline=9.)
line = "%s / %s" % (record.character_id, record.character_name) line = "%s / %s" % (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
@@ -33,7 +36,8 @@ def draw_character_row(canvas, rect, record: ADRLine):
def draw_cue_number_block(canvas, rect, record: ADRLine): def draw_cue_number_block(canvas, rect, record: ADRLine):
(label_frame, number_frame,), aux_frame = rect.divide_y([0.20 * inch, 0.375 * inch], direction='d') (label_frame, number_frame,), aux_frame = \
rect.divide_y([0.20 * inch, 0.375 * inch], direction='d')
label_frame.draw_text_cell(canvas, "CUE NUMBER", font_name, 10, label_frame.draw_text_cell(canvas, "CUE NUMBER", font_name, 10,
inset_y=5., vertical_align='t') inset_y=5., vertical_align='t')
number_frame.draw_text_cell(canvas, record.cue_number, font_name, 14, number_frame.draw_text_cell(canvas, record.cue_number, font_name, 14,
@@ -55,18 +59,25 @@ def draw_cue_number_block(canvas, rect, record: ADRLine):
rect.draw_border(canvas, 'max_x') rect.draw_border(canvas, 'max_x')
def draw_timecode_block(canvas, rect, record: ADRLine, tc_display_format: TimecodeFormat): def draw_timecode_block(canvas, rect, record: ADRLine,
tc_display_format: TimecodeFormat):
(in_label_frame, in_frame, out_label_frame, out_frame), _ = rect.divide_y( (in_label_frame, in_frame, out_label_frame, out_frame), _ = rect.divide_y(
[0.20 * inch, 0.25 * inch, 0.20 * inch, 0.25 * inch], direction='d') [0.20 * inch, 0.25 * inch, 0.20 * inch, 0.25 * inch], direction='d')
in_label_frame.draw_text_cell(canvas, "IN", font_name, 10, in_label_frame.draw_text_cell(canvas, "IN", font_name, 10,
vertical_align='t', inset_y=5., inset_x=5.) vertical_align='t', inset_y=5., inset_x=5.)
in_frame.draw_text_cell(canvas, tc_display_format.seconds_to_smpte(record.start), font_name, 14, in_frame.draw_text_cell(canvas,
inset_x=10., inset_y=2., draw_baseline=True) tc_display_format.seconds_to_smpte(record.start),
font_name, 14,
inset_x=10., inset_y=2.,
draw_baseline=True)
out_label_frame.draw_text_cell(canvas, "OUT", font_name, 10, out_label_frame.draw_text_cell(canvas, "OUT", font_name, 10,
vertical_align='t', inset_y=5., inset_x=5.) vertical_align='t', inset_y=5., inset_x=5.)
out_frame.draw_text_cell(canvas, tc_display_format.seconds_to_smpte(record.finish), font_name, 14, out_frame.draw_text_cell(canvas,
inset_x=10., inset_y=2., draw_baseline=True) tc_display_format.seconds_to_smpte(record.finish),
font_name, 14,
inset_x=10., inset_y=2.,
draw_baseline=True)
rect.draw_border(canvas, 'max_x') rect.draw_border(canvas, 'max_x')
@@ -91,13 +102,15 @@ def draw_reason_block(canvas, rect, record: ADRLine):
p = Paragraph(record.note or "", style) p = Paragraph(record.note or "", style)
notes_value.draw_flowable(canvas, p, draw_baselines=True, inset_x=5., inset_y=5.) notes_value.draw_flowable(canvas, p, draw_baselines=True,
inset_x=5., inset_y=5.)
def draw_prompt(canvas, rect, prompt=""): def draw_prompt(canvas, rect, prompt=""):
label, block = rect.split_y(0.20 * inch, direction='d') label, block = rect.split_y(0.20 * inch, direction='d')
label.draw_text_cell(canvas, "PROMPT", font_name, 10, vertical_align='t', inset_y=5., inset_x=0.) label.draw_text_cell(canvas, "PROMPT", font_name, 10, vertical_align='t',
inset_y=5., inset_x=0.)
style = getSampleStyleSheet()['BodyText'] style = getSampleStyleSheet()['BodyText']
style.fontName = font_name style.fontName = font_name
@@ -117,7 +130,8 @@ def draw_prompt(canvas, rect, prompt=""):
def draw_notes(canvas, rect, note=""): def draw_notes(canvas, rect, note=""):
label, block = rect.split_y(0.20 * inch, direction='d') label, block = rect.split_y(0.20 * inch, direction='d')
label.draw_text_cell(canvas, "NOTES", font_name, 10, vertical_align='t', inset_y=5., inset_x=0.) label.draw_text_cell(canvas, "NOTES", font_name, 10, vertical_align='t',
inset_y=5., inset_x=0.)
style = getSampleStyleSheet()['BodyText'] style = getSampleStyleSheet()['BodyText']
style.fontName = font_name style.fontName = font_name
@@ -169,31 +183,43 @@ def draw_take_grid(canvas, rect):
canvas.restoreState() canvas.restoreState()
def draw_aux_block(canvas, rect, recording_time_sec_this_line, recording_time_sec): def draw_aux_block(canvas, rect, recording_time_sec_this_line,
recording_time_sec):
rect.draw_border(canvas, 'min_x') rect.draw_border(canvas, 'min_x')
content_rect = rect.inset_xy(10., 10.) content_rect = rect.inset_xy(10., 10.)
lines, last_line = content_rect.divide_y([12., 12., 24., 24., 24., 24.], direction='d') lines, last_line = content_rect.divide_y([12., 12., 24., 24., 24., 24.],
direction='d')
lines[0].draw_text_cell(canvas, lines[0].draw_text_cell(canvas,
"Time for this line: %.1f mins" % (recording_time_sec_this_line / 60.), font_name, 9.) "Time for this line: %.1f mins" %
lines[1].draw_text_cell(canvas, "Running time: %03.1f mins" % (recording_time_sec / 60.), font_name, 9.) (recording_time_sec_this_line / 60.),
lines[2].draw_text_cell(canvas, "Actual Start: ______________", font_name, 9., vertical_align='b') font_name, 9.)
lines[3].draw_text_cell(canvas, "Record Date: ______________", font_name, 9., vertical_align='b') lines[1].draw_text_cell(canvas, "Running time: %03.1f mins" %
lines[4].draw_text_cell(canvas, "Engineer: ______________", font_name, 9., vertical_align='b') (recording_time_sec / 60.), font_name, 9.)
lines[5].draw_text_cell(canvas, "Location: ______________", font_name, 9., vertical_align='b') lines[2].draw_text_cell(canvas, "Actual Start: ______________",
font_name, 9., vertical_align='b')
lines[3].draw_text_cell(canvas, "Record Date: ______________",
font_name, 9., vertical_align='b')
lines[4].draw_text_cell(canvas, "Engineer: ______________",
font_name, 9., vertical_align='b')
lines[5].draw_text_cell(canvas, "Location: ______________",
font_name, 9., vertical_align='b')
def draw_footer(canvas, rect, record: ADRLine, report_date, line_no, total_lines): 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 = ["Line %i of %i" % (line_no, 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(canvas, footer_s, font_name=font_name, font_size=10., inset_y=2.) rect.draw_text_cell(canvas, footer_s, font_name=font_name, font_size=10.,
inset_y=2.)
def create_report_for_character(records, report_date, tc_display_format: TimecodeFormat): def create_report_for_character(records, report_date,
tc_display_format: TimecodeFormat):
outfile = "%s_%s_%s_Log.pdf" % (records[0].title, outfile = "%s_%s_%s_Log.pdf" % (records[0].title,
records[0].character_id, records[0].character_id,
@@ -201,20 +227,24 @@ def create_report_for_character(records, report_date, tc_display_format: Timecod
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!"
#pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc')) # pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc'))
page: GRect = GRect(0, 0, letter[0], letter[1]) page: GRect = GRect(0, 0, letter[0], letter[1])
page = page.inset(inch * 0.5) page = page.inset(inch * 0.5)
(header_row, char_row, data_row, prompt_row, notes_row, takes_row), footer = \ (header_row, char_row, data_row,
page.divide_y([0.875 * inch, 0.375 * inch, inch, 3.0 * inch, 1.5 * inch, 3 * inch], direction='d') prompt_row, notes_row, takes_row), footer = \
page.divide_y([0.875 * inch, 0.375 * inch, inch,
3.0 * inch, 1.5 * inch, 3 * inch], direction='d')
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([1.5 * inch, 1.5 * inch]) (cue_number_block, timecode_block), reason_block = \
data_row.divide_x([1.5 * inch, 1.5 * inch])
(take_grid_block), aux_block = takes_row.split_x(5.25 * inch) (take_grid_block), aux_block = takes_row.split_x(5.25 * inch)
c = Canvas(outfile, pagesize=letter,) c = Canvas(outfile, pagesize=letter,)
c.setTitle("%s %s (%s) Supervisor's Log" % (records[0].title, records[0].character_name, c.setTitle("%s %s (%s) Supervisor's Log" % (records[0].title,
records[0].character_name,
records[0].character_id)) records[0].character_id))
c.setAuthor(records[0].supervisor) c.setAuthor(records[0].supervisor)
@@ -223,7 +253,8 @@ def create_report_for_character(records, report_date, tc_display_format: Timecod
line_n = 1 line_n = 1
for record in records: for record in records:
record: ADRLine record: ADRLine
recording_time_sec_this_line: float = (record.time_budget_mins or 6.0) * 60.0 recording_time_sec_this_line: float = (
record.time_budget_mins or 6.0) * 60.0
recording_time_sec = recording_time_sec + recording_time_sec_this_line recording_time_sec = recording_time_sec + recording_time_sec_this_line
draw_header_block(c, cue_header_block, record) draw_header_block(c, cue_header_block, record)
@@ -233,14 +264,17 @@ def create_report_for_character(records, report_date, tc_display_format: Timecod
# draw_title_box(c, title_header_block, record) # draw_title_box(c, title_header_block, record)
draw_character_row(c, char_row, record) draw_character_row(c, char_row, record)
draw_cue_number_block(c, cue_number_block, record) draw_cue_number_block(c, cue_number_block, record)
draw_timecode_block(c, timecode_block, record, tc_display_format=tc_display_format) draw_timecode_block(c, timecode_block, record,
tc_display_format=tc_display_format)
draw_reason_block(c, reason_block, record) draw_reason_block(c, reason_block, record)
draw_prompt(c, prompt_row, prompt=record.prompt) draw_prompt(c, prompt_row, prompt=record.prompt or "")
draw_notes(c, notes_row, note="") draw_notes(c, notes_row, note="")
draw_take_grid(c, take_grid_block) draw_take_grid(c, take_grid_block)
draw_aux_block(c, aux_block, recording_time_sec_this_line, recording_time_sec) draw_aux_block(c, aux_block, recording_time_sec_this_line,
recording_time_sec)
draw_footer(c, footer, record, report_date, line_no=line_n, total_lines=total_lines) draw_footer(c, footer, record, report_date, line_no=line_n,
total_lines=total_lines)
line_n = line_n + 1 line_n = line_n + 1
c.showPage() c.showPage()
@@ -254,5 +288,6 @@ def output_report(lines, tc_display_format: TimecodeFormat):
character_numbers = set([x.character_id for x in lines]) character_numbers = set([x.character_id for x in lines])
for n in character_numbers: for n in character_numbers:
create_report_for_character([e for e in events if e.character_id == n], report_date, create_report_for_character([e for e in events if e.character_id == n],
report_date,
tc_display_format=tc_display_format) tc_display_format=tc_display_format)

View File

@@ -5,36 +5,42 @@ from .__init__ import make_doc_template
from reportlab.lib.units import inch from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter from reportlab.lib.pagesizes import letter
from reportlab.platypus import Paragraph, Spacer, KeepTogether, Table, HRFlowable from reportlab.platypus import Paragraph, Spacer, KeepTogether, Table,\
HRFlowable
from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib import colors from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics # from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont # from reportlab.pdfbase.ttfonts import TTFont
from ..broadcast_timecode import TimecodeFormat from ..broadcast_timecode import TimecodeFormat
from ..docparser.adr_entity import ADRLine from ..docparser.adr_entity import ADRLine
def output_report(lines: List[ADRLine], tc_display_format: TimecodeFormat, font_name="Helvetica"): def output_report(lines: List[ADRLine], tc_display_format: TimecodeFormat,
font_name="Helvetica"):
character_numbers = set([n.character_id for n in lines]) character_numbers = set([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:
char_lines = [line for line in lines if not line.omitted and line.character_id == n] char_lines = [line for line in lines
if not line.omitted and line.character_id == n]
character_name = char_lines[0].character_name character_name = char_lines[0].character_name
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 = "%s (%s) %s ADR Script" % (char_lines[0].title,
filename = "%s_%s_%s_ADR Script.pdf" % (char_lines[0].title, n, character_name) character_name, n)
filename = "%s_%s_%s_ADR Script.pdf" % (char_lines[0].title,
n, character_name)
doc = make_doc_template(page_size=letter, filename=filename, document_title=title, doc = make_doc_template(page_size=letter, filename=filename,
document_title=title,
title=char_lines[0].title, title=char_lines[0].title,
document_subheader=char_lines[0].spot, document_subheader=char_lines[0].spot or "",
supervisor=char_lines[0].supervisor, supervisor=char_lines[0].supervisor or "",
client=char_lines[0].client, client=char_lines[0].client or "",
document_header=character_name) document_header=character_name or "")
story = [] story = []
@@ -58,7 +64,8 @@ def output_report(lines: List[ADRLine], tc_display_format: TimecodeFormat, font_
start_tc = tc_display_format.seconds_to_smpte(line.start) start_tc = tc_display_format.seconds_to_smpte(line.start)
finish_tc = tc_display_format.seconds_to_smpte(line.finish) finish_tc = tc_display_format.seconds_to_smpte(line.finish)
data_block = [[Paragraph(line.cue_number, number_style), data_block = [[Paragraph(line.cue_number, number_style),
Paragraph(start_tc + " - " + finish_tc, number_style) Paragraph(start_tc + " - " + finish_tc,
number_style)
]] ]]
# RIGHTWARDS ARROW → # RIGHTWARDS ARROW →

View File

@@ -35,13 +35,15 @@ def print_warning(warning_string):
sys.stderr.write(" - %s\n" % warning_string) sys.stderr.write(" - %s\n" % warning_string)
def print_advisory_tagging_error(failed_string, position, parent_track_name=None, clip_time=None): def print_advisory_tagging_error(failed_string, position,
parent_track_name=None, clip_time=None):
if sys.stderr.isatty(): if sys.stderr.isatty():
sys.stderr.write("\n") sys.stderr.write("\n")
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("\033[32m\"%s\033[31;1m%s\"\033[0m\n" % (ok_string, not_ok_string)) sys.stderr.write("\033[32m\"%s\033[31;1m%s\"\033[0m\n" %
(ok_string, not_ok_string))
if parent_track_name is not None: if parent_track_name is not None:
sys.stderr.write(" ! > On track \"%s\"\n" % parent_track_name) sys.stderr.write(" ! > On track \"%s\"\n" % parent_track_name)

View File

@@ -14,15 +14,20 @@ class ValidationError:
def report_message(self): def report_message(self):
if self.event is not None: if self.event is not None:
return f"{self.message}: event at {self.event.start} with number {self.event.cue_number}" return (f"{self.message}: event at {self.event.start} with number"
"{self.event.cue_number}")
else: else:
return self.message return self.message
def validate_unique_count(input_lines: Iterator[ADRLine], field='title', count=1): def validate_unique_count(input_lines: Iterator[ADRLine], field='title',
count=1):
values = set(list(map(lambda e: getattr(e, field), input_lines))) values = set(list(map(lambda e: getattr(e, field), input_lines)))
if len(values) > count: if len(values) > count:
yield ValidationError(message="Field {} has too many values (max={}): {}".format(field, count, values)) yield ValidationError(
message="Field {} has too many values (max={}): {}"
.format(field, count, values)
)
def validate_value(input_lines: Iterator[ADRLine], key_field, predicate): def validate_value(input_lines: Iterator[ADRLine], key_field, predicate):
@@ -33,7 +38,8 @@ def validate_value(input_lines: Iterator[ADRLine], key_field, predicate):
event=event) event=event)
def validate_unique_field(input_lines: Iterator[ADRLine], field='cue_number', scope=None): def validate_unique_field(input_lines: Iterator[ADRLine], field='cue_number',
scope=None):
values = dict() values = dict()
for event in input_lines: for event in input_lines:
this = getattr(event, field) this = getattr(event, field)
@@ -44,26 +50,31 @@ def validate_unique_field(input_lines: Iterator[ADRLine], field='cue_number', sc
values.setdefault(key, set()) values.setdefault(key, set())
if this in values[key]: if this in values[key]:
yield ValidationError(message='Re-used {}'.format(field), event=event) yield ValidationError(message='Re-used {}'.format(field),
event=event)
else: else:
values[key].update(this) values[key].update(this)
def validate_non_empty_field(input_lines: Iterator[ADRLine], field='cue_number'): def validate_non_empty_field(input_lines: Iterator[ADRLine],
field='cue_number'):
for event in input_lines: for event in input_lines:
if getattr(event, field, None) is None: if getattr(event, field, None) is None:
yield ValidationError(message='Empty field {}'.format(field), event=event) yield ValidationError(message='Empty field {}'.format(field),
event=event)
def validate_dependent_value(input_lines: Iterator[ADRLine], key_field, dependent_field): def validate_dependent_value(input_lines: Iterator[ADRLine], key_field,
dependent_field):
""" """
Validates that two events with the same value in `key_field` always have the Validates that two events with the same value in `key_field` always have
same value in `dependent_field` the same value in `dependent_field`
""" """
key_values = set((getattr(x, key_field) for x in input_lines)) key_values = set((getattr(x, key_field) for x in input_lines))
for key_value in key_values: for key_value in key_values:
rows = [(getattr(x, key_field), getattr(x, dependent_field)) for x in input_lines rows = [(getattr(x, key_field), getattr(x, dependent_field))
for x in input_lines
if getattr(x, key_field) == key_value] if getattr(x, key_field) == key_value]
unique_rows = set(rows) unique_rows = set(rows)
if len(unique_rows) > 1: if len(unique_rows) > 1:

View File

@@ -12,7 +12,10 @@ import ptulsconv
from ptulsconv.docparser.adr_entity import ADRLine from ptulsconv.docparser.adr_entity import ADRLine
# TODO Get a third-party test for Avid Marker lists # TODO Get a third-party test for Avid Marker lists
def avid_marker_list(lines: List[ADRLine], report_date=datetime.datetime.now(), reel_start_frame=0, fps=24):
def avid_marker_list(lines: List[ADRLine], report_date=datetime.datetime.now(),
reel_start_frame=0, fps=24):
doc = TreeBuilder(element_factory=None) doc = TreeBuilder(element_factory=None)
doc.start('Avid:StreamItems', {'xmlns:Avid': 'http://www.avid.com'}) doc.start('Avid:StreamItems', {'xmlns:Avid': 'http://www.avid.com'})
@@ -48,26 +51,35 @@ def avid_marker_list(lines: List[ADRLine], report_date=datetime.datetime.now(),
for line in lines: for line in lines:
doc.start('AvClass', {'id': 'ATTR'}) doc.start('AvClass', {'id': 'ATTR'})
doc.start('AvProp', {'id': 'ATTR', 'name': '__OMFI:ATTR:NumItems', 'type': 'int32'}) doc.start('AvProp', {'id': 'ATTR',
'name': '__OMFI:ATTR:NumItems',
'type': 'int32'})
doc.data('7') doc.data('7')
doc.end('AvProp') doc.end('AvProp')
doc.start('List', {'id': 'OMFI:ATTR:AttrRefs'}) doc.start('List', {'id': 'OMFI:ATTR:AttrRefs'})
insert_elem('1', 'OMFI:ATTB:IntAttribute', 'int32', '_ATN_CRM_LONG_CREATE_DATE', report_date.strftime("%s")) insert_elem('1', 'OMFI:ATTB:IntAttribute', 'int32',
insert_elem('2', 'OMFI:ATTB:StringAttribute', 'string', '_ATN_CRM_COLOR', 'yellow') '_ATN_CRM_LONG_CREATE_DATE', report_date.strftime("%s"))
insert_elem('2', 'OMFI:ATTB:StringAttribute', 'string', '_ATN_CRM_USER', line.supervisor or "") insert_elem('2', 'OMFI:ATTB:StringAttribute', 'string',
'_ATN_CRM_COLOR', 'yellow')
insert_elem('2', 'OMFI:ATTB:StringAttribute', 'string',
'_ATN_CRM_USER', line.supervisor or "")
marker_name = "%s: %s" % (line.cue_number, line.prompt) marker_name = "%s: %s" % (line.cue_number, line.prompt)
insert_elem('2', 'OMFI:ATTB:StringAttribute', 'string', '_ATN_CRM_COM', marker_name) insert_elem('2', 'OMFI:ATTB:StringAttribute', 'string',
'_ATN_CRM_COM', marker_name)
start_frame = int(line.start * fps) start_frame = int(line.start * fps)
insert_elem('2', "OMFI:ATTB:StringAttribute", 'string', '_ATN_CRM_TC', insert_elem('2', "OMFI:ATTB:StringAttribute", 'string',
'_ATN_CRM_TC',
str(start_frame - reel_start_frame)) str(start_frame - reel_start_frame))
insert_elem('2', "OMFI:ATTB:StringAttribute", 'string', '_ATN_CRM_TRK', 'V1') insert_elem('2', "OMFI:ATTB:StringAttribute", 'string',
insert_elem('1', "OMFI:ATTB:IntAttribute", 'int32', '_ATN_CRM_LENGTH', '1') '_ATN_CRM_TRK', 'V1')
insert_elem('1', "OMFI:ATTB:IntAttribute", 'int32',
'_ATN_CRM_LENGTH', '1')
doc.start('ListElem', {}) doc.start('ListElem', {})
doc.end('ListElem') doc.end('ListElem')
@@ -82,17 +94,22 @@ def avid_marker_list(lines: List[ADRLine], report_date=datetime.datetime.now(),
def dump_fmpxml(data, input_file_name, output, adr_field_map): def dump_fmpxml(data, input_file_name, output, adr_field_map):
doc = TreeBuilder(element_factory=None) doc = TreeBuilder(element_factory=None)
doc.start('FMPXMLRESULT', {'xmlns': 'http://www.filemaker.com/fmpxmlresult'}) doc.start('FMPXMLRESULT', {'xmlns':
'http://www.filemaker.com/fmpxmlresult'})
doc.start('ERRORCODE', {}) doc.start('ERRORCODE', {})
doc.data('0') doc.data('0')
doc.end('ERRORCODE') doc.end('ERRORCODE')
doc.start('PRODUCT', {'NAME': ptulsconv.__name__, 'VERSION': ptulsconv.__version__}) doc.start('PRODUCT', {'NAME': ptulsconv.__name__,
'VERSION': ptulsconv.__version__})
doc.end('PRODUCT') doc.end('PRODUCT')
doc.start('DATABASE', {'DATEFORMAT': 'MM/dd/yy', 'LAYOUT': 'summary', 'TIMEFORMAT': 'hh:mm:ss', doc.start('DATABASE', {'DATEFORMAT': 'MM/dd/yy',
'RECORDS': str(len(data['events'])), 'NAME': os.path.basename(input_file_name)}) 'LAYOUT': 'summary',
'TIMEFORMAT': 'hh:mm:ss',
'RECORDS': str(len(data['events'])),
'NAME': os.path.basename(input_file_name)})
doc.end('DATABASE') doc.end('DATABASE')
doc.start('METADATA', {}) doc.start('METADATA', {})
@@ -102,7 +119,8 @@ def dump_fmpxml(data, input_file_name, output, adr_field_map):
if tp is int or tp is float: if tp is int or tp is float:
ft = 'NUMBER' ft = 'NUMBER'
doc.start('FIELD', {'EMPTYOK': 'YES', 'MAXREPEAT': '1', 'NAME': field[1], 'TYPE': ft}) doc.start('FIELD', {'EMPTYOK': 'YES', 'MAXREPEAT': '1',
'NAME': field[1], 'TYPE': ft})
doc.end('FIELD') doc.end('FIELD')
doc.end('METADATA') doc.end('METADATA')
@@ -157,7 +175,8 @@ def fmp_transformed_dump(data, input_file, xsl_name, output, adr_field_map):
print_status_style("Running xsltproc") print_status_style("Running xsltproc")
xsl_path = os.path.join(pathlib.Path(__file__).parent.absolute(), 'xslt', xsl_name + ".xsl") xsl_path = os.path.join(pathlib.Path(__file__).parent.absolute(), 'xslt',
xsl_name + ".xsl")
print_status_style("Using xsl: %s" % xsl_path) print_status_style("Using xsl: %s" % xsl_path)
subprocess.run(['xsltproc', xsl_path, '-'], subprocess.run(['xsltproc', xsl_path, '-'],
input=str_data, text=True, input=str_data, text=True,