Added page count footer implementation

Added another validation
This commit is contained in:
Jamie Hardt
2021-05-19 13:25:51 -07:00
parent 6cb93ea75f
commit 67b785c2c2
6 changed files with 183 additions and 15 deletions

View File

@@ -15,6 +15,7 @@ from .validations import *
from ptulsconv.pdf.supervisor_1pg import output_report as output_supervisor_1pg from ptulsconv.pdf.supervisor_1pg import output_report as output_supervisor_1pg
from ptulsconv.pdf.line_count import output_report as output_line_count from ptulsconv.pdf.line_count import output_report as output_line_count
from ptulsconv.pdf.talent_sides import output_report as output_talent_sides from ptulsconv.pdf.talent_sides import output_report as output_talent_sides
from ptulsconv.pdf.summary_log import output_report as output_summary
# field_map maps tags in the text export to fields in FMPXMLRESULT # field_map maps tags in the text export to fields in FMPXMLRESULT
# - tuple field 0 is a list of tags, the first tag with contents will be used as source # - tuple field 0 is a list of tags, the first tag with contents will be used as source
@@ -208,10 +209,14 @@ def convert(input_file, output_format='fmpxml', start=None, end=None, select_ree
validate_non_empty_field(parsed, field='QN'), validate_non_empty_field(parsed, field='QN'),
validate_non_empty_field(parsed, field='CN'), validate_non_empty_field(parsed, field='CN'),
validate_non_empty_field(parsed, field='Char'), validate_non_empty_field(parsed, field='Char'),
validate_non_empty_field(parsed, field='Title'),
validate_dependent_value(parsed, key_field='CN', validate_dependent_value(parsed, key_field='CN',
dependent_field='Char'), dependent_field='Char'),
validate_dependent_value(parsed, key_field='CN', validate_dependent_value(parsed, key_field='CN',
dependent_field='Actor'),): dependent_field='Actor'),
validate_unique_count(parsed, field='Title', count=1),
validate_unique_count(parsed, field='Spotting', count=1),
validate_unique_count(parsed, field='Supervisor', count=1)):
print_warning(warning.report_message()) print_warning(warning.report_message())
@@ -220,9 +225,12 @@ def convert(input_file, output_format='fmpxml', start=None, end=None, select_ree
elif output_format == 'full': elif output_format == 'full':
print("Sorry, the `full` output type is not yet supported.") print("Sorry, the `full` output type is not yet supported.")
normalized_records = normalize_record_keys(parsed) normalized_records = normalize_record_keys(parsed)
output_supervisor_1pg(normalized_records) output_supervisor_1pg(normalized_records)
output_talent_sides(normalized_records) output_talent_sides(normalized_records)
output_line_count(normalized_records) output_line_count(normalized_records)
output_summary(normalized_records)
elif output_format == 'fmpxml': elif output_format == 'fmpxml':
if xsl is None: if xsl is None:
fmp_dump(parsed, input_file, output) fmp_dump(parsed, input_file, output)

View File

@@ -1,6 +1,51 @@
from reportlab.pdfbase.pdfmetrics import (getAscent, getDescent) from reportlab.pdfbase.pdfmetrics import (getAscent, getDescent)
from reportlab.lib.units import inch
from reportlab.pdfgen import canvas
import datetime
# This is from https://code.activestate.com/recipes/576832/ for
# generating page count messages
class NumberedCanvas(canvas.Canvas):
def __init__(self, *args, **kwargs):
canvas.Canvas.__init__(self, *args, **kwargs)
self._saved_page_states = []
self._report_date = datetime.datetime.now()
def showPage(self):
self._saved_page_states.append(dict(self.__dict__))
self._startPage()
def save(self):
"""add page info to each page (page x of y)"""
num_pages = len(self._saved_page_states)
for state in self._saved_page_states:
self.__dict__.update(state)
self.draw_page_number(num_pages)
canvas.Canvas.showPage(self)
canvas.Canvas.save(self)
def draw_page_number(self, page_count):
self.saveState()
self.setFont("Futura", 10)
self.drawString(0.5 * inch, 0.5 * inch, "Page %d of %d" % (self._pageNumber, page_count))
right_edge = self._pagesize[0] - 0.5 * inch
self.drawRightString(right_edge, 0.5 * inch, self._report_date.strftime("%c"))
topline = self.beginPath()
topline.moveTo(0.5 * inch, 0.75 * inch)
topline.lineTo(right_edge, 0.75 * inch)
self.setLineWidth(0.5)
self.drawPath(topline)
self.restoreState()
def time_format(mins):
if mins < 60.:
return "%im" % round(mins)
else:
m = round(mins)
hh, mm = divmod(m, 60)
return "%ih%im" % (hh, mm)
## draws the title block inside the given rect
def draw_title_block(canvas, rect, record): def draw_title_block(canvas, rect, record):
(supervisor, client,), title = rect.divide_y([16., 16., ]) (supervisor, client,), title = rect.divide_y([16., 16., ])
title.draw_text_cell(canvas, record['Title'], "Futura", 18, inset_y=2.) title.draw_text_cell(canvas, record['Title'], "Futura", 18, inset_y=2.)
@@ -56,8 +101,12 @@ class GRect:
elif at <= 0: elif at <= 0:
return self, None return self, None
else: else:
return (GRect(self.min_x, self.min_y, at, self.height), if direction == 'l':
GRect(self.min_x + at, self.y, self.width - at, self.height)) return (GRect(self.min_x, self.min_y, at, self.height),
GRect(self.min_x + at, self.y, self.width - at, self.height))
else:
return (GRect(self.max_x - at, self.y, at, self.height),
GRect(self.min_x, self.y, self.width - at, self.height))
def split_y(self, at, direction='u'): def split_y(self, at, direction='u'):
if at >= self.height: if at >= self.height:

View File

@@ -10,7 +10,7 @@ from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet from reportlab.lib.styles import getSampleStyleSheet
from reportlab.platypus import Paragraph, Table, TableStyle from reportlab.platypus import Paragraph, Table, TableStyle
from .common import GRect from .common import GRect, time_format, NumberedCanvas
import datetime import datetime
@@ -26,14 +26,6 @@ def build_columns(records, show_priorities = False):
else: else:
return str(l) return str(l)
def time_format(mins):
if mins < 60.:
return "%im" % round(mins)
else:
m = round(mins)
hh, mm = divmod(m, 60)
return "%ih%im" % (hh, mm)
columns.append({ columns.append({
'heading': '#', 'heading': '#',
'value_getter': lambda recs: recs[0]['Character Number'], 'value_getter': lambda recs: recs[0]['Character Number'],
@@ -180,7 +172,7 @@ def output_report(records):
page = page.inset(inch * 0.5) page = page.inset(inch * 0.5)
title_box, table_box = page.split_y(inch, 'd') title_box, table_box = page.split_y(inch, 'd')
c = Canvas('Line Count.pdf', pagesize=(letter[1], letter[0])) c = NumberedCanvas('Line Count.pdf', pagesize=(letter[1], letter[0]))
c.setFont('Futura', 18.) c.setFont('Futura', 18.)
c.drawCentredString(title_box.center_x, title_box.center_y, "Line Count") c.drawCentredString(title_box.center_x, title_box.center_y, "Line Count")

View File

@@ -0,0 +1,114 @@
# -*- coding: utf-8 -*-
from .common import GRect, draw_title_block, time_format, NumberedCanvas
from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter, landscape, portrait
from reportlab.platypus import BaseDocTemplate, Paragraph, Spacer, \
KeepTogether, Table, HRFlowable, PageTemplate, Frame
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib import colors
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
def build_aux_data_field(line):
entries = list()
if 'Reason' in line.keys():
entries.append("Reason: " + line["Reason"])
if 'Note' in line.keys():
entries.append("Note: " + line["Note"])
if 'Requested by' in line.keys():
entries.append("Requested by: " + line["Requested by"])
if 'Shot' in line.keys():
entries.append("Shot: " + line["Shot"])
tag_field = ""
for tag in line.keys():
if line[tag] == tag and tag != 'ADR':
tag_field += "<font backColor=black textColor=white>" + tag + "</font> "
entries.append(tag_field)
return "<br />".join(entries)
def build_story(lines):
story = list()
this_scene = None
scene_style = getSampleStyleSheet()['Normal']
scene_style.fontName = 'Futura'
scene_style.leftIndent = 0.
line_style = getSampleStyleSheet()['Normal']
line_style.fontName = 'Futura'
for line in lines:
table_style = [('VALIGN', (0, 0), (-1, -1), 'TOP'), ('LEFTPADDING', (0, 0), (0, 0), 0.0)]
if 'Omitted' in line.keys():
cue_number_field = "<s>" + line['Cue Number'] + "</s><br /><font fontSize=9>" + \
line['Character Name'] + "</font>"
table_style.append(('BACKGROUND', (0, 0), (-1, 0), colors.lightpink))
else:
cue_number_field = line['Cue Number'] + "<br /><font fontSize=9>" + line['Character Name'] + "</font>"
time_data = time_format(line.get('Time Budget Mins', 0.))
if 'Priority' in line.keys():
time_data = time_data + "<br />" + "P:" + int(line['Priority'])
aux_data_field = build_aux_data_field(line)
line_table_data = [[Paragraph(cue_number_field, line_style),
Paragraph(line['PT.Clip.Start'] + "<br />" + line['PT.Clip.Finish'], line_style),
Paragraph(line['Line'], line_style),
Paragraph(time_data, line_style),
Paragraph(aux_data_field, line_style)
]]
line_table = Table(data=line_table_data,
colWidths=[inch, inch, inch * 3., 0.5 * inch, inch * 2.],
style=table_style)
if line['Scene'] != this_scene:
this_scene = line['Scene']
story.append(KeepTogether([
Spacer(1., 0.25 * inch),
Paragraph("<u>" + this_scene + "</u>", scene_style),
line_table]))
else:
line_table.setStyle(table_style + [('LINEABOVE', (0, 0), (-1,0), .5, colors.gray)])
story.append(KeepTogether([line_table]))
return story
def output_report(records):
page_size = portrait(letter)
page_box = GRect(inch * 0.5, inch * 0.5, page_size[0] - inch, page_size[1] - inch)
title_box, page_box = page_box.split_y(0.875 * inch, 'd')
footer_box, page_box = page_box.split_y(0.25 * inch, 'u')
title_block, header_block = title_box.split_x(inch * 4., direction='r')
pdfmetrics.registerFont(TTFont('Futura', 'Futura.ttc'))
page_template = PageTemplate(id="Main",
frames=[Frame(page_box.min_x, page_box.min_y, page_box.width, page_box.height)],
onPage=lambda canvas, _: draw_title_block(canvas, title_block, lines[0]))
lines = sorted(records['events'], key=lambda line: line['PT.Clip.Start_Seconds'])
doc = BaseDocTemplate("Summary.pdf",
pagesize=page_size, leftMargin=0.5 * inch,
rightMargin=0.5 * inch, topMargin=0.5 * inch,
bottomMargin=0.5 * inch)
doc.addPageTemplates([page_template])
story = build_story(lines)
doc.build(story, canvasmaker=NumberedCanvas)

View File

@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from .common import GRect, draw_title_block from .common import GRect, draw_title_block, NumberedCanvas
from reportlab.lib.units import inch from reportlab.lib.units import inch
from reportlab.lib.pagesizes import letter from reportlab.lib.pagesizes import letter
@@ -69,4 +69,4 @@ def output_report(records):
) )
) )
doc.build(story) doc.build(story, canvasmaker=NumberedCanvas)

View File

@@ -10,6 +10,11 @@ class ValidationError:
def report_message(self): def report_message(self):
return f"{self.message}: event at {self.event['PT.Clip.Start']} on track {self.event['PT.Track.Name']}" return f"{self.message}: event at {self.event['PT.Clip.Start']} on track {self.event['PT.Track.Name']}"
def validate_unique_count(input_dict, field='Title', count=1):
values = set(list(map(lambda e: e[field], input_dict['events'])))
if len(values) > count:
yield ValidationError(message="Field {} has too many values (max={}): {}".format(field, count, values))
def validate_value(input_dict, key_field, predicate): def validate_value(input_dict, key_field, predicate):
for event in input_dict['events']: for event in input_dict['events']:
val = event[key_field] val = event[key_field]