mirror of
https://github.com/iluvcapra/ptulsconv.git
synced 2026-01-02 18:00:48 +00:00
Added page count footer implementation
Added another validation
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
if direction == 'l':
|
||||||
return (GRect(self.min_x, self.min_y, 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))
|
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:
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|
||||||
|
|||||||
114
ptulsconv/pdf/summary_log.py
Normal file
114
ptulsconv/pdf/summary_log.py
Normal 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)
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
Reference in New Issue
Block a user