Implementing validation feature

This commit is contained in:
Jamie Hardt
2020-10-22 14:10:28 -07:00
parent 6ad29ccf8b
commit d17f6951d6
6 changed files with 103 additions and 5 deletions

View File

@@ -2,6 +2,6 @@ from .ptuls_grammar import protools_text_export_grammar
from .ptuls_parser_visitor import DictionaryParserVisitor
from .transformations import TimecodeInterpreter
__version__ = '0.5.3'
__version__ = '0.6.0'
__author__ = 'Jamie Hardt'
__license__ = 'MIT'

View File

@@ -29,6 +29,15 @@ def main():
parser.add_option_group(filter_opts)
warn_options = OptionGroup(title="Warning and Validation Options", parser=parser)
warn_options.add_option('-W', action='store_true', dest='warnings',
help='Generate warnings for common errors (missing code numbers etc.)')
warn_options.add_option('-S', action='store_true', dest='spelling',
help='Check spelling and warn on misspellings.')
parser.add_option_group(warn_options)
output_opts = OptionGroup(title="Output Options", parser=parser)
output_opts.add_option('--json', default=False, action='store_true', dest='write_json',
help='Output a JSON document instead of XML. If this option is enabled, --xform will have '
@@ -90,7 +99,8 @@ def main():
convert(input_file=args[1], output_format=output_format, start=options.in_time, end=options.out_time,
include_muted=options.include_muted, xsl=options.xslt, select_reel=options.select_reel,
progress=False, output=sys.stdout, log_output=sys.stderr)
progress=False, output=sys.stdout, log_output=sys.stderr,
warnings=options.warnings, spelling=options.spelling)
except FileNotFoundError as e:
print_fatal_error("Error trying to read input file")
raise e

View File

@@ -7,8 +7,10 @@ from xml.etree.ElementTree import TreeBuilder, tostring
import subprocess
import pathlib
import ptulsconv
from itertools import chain
from .reporting import print_section_header_style, print_status_style
from .reporting import print_section_header_style, print_status_style, print_warning
from .validations import *
# 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
@@ -148,7 +150,7 @@ def fmp_transformed_dump(data, input_file, xsl_name, output):
def convert(input_file, output_format='fmpxml', start=None, end=None, select_reel=None,
progress=False, include_muted=False, xsl=None,
output=sys.stdout, log_output=sys.stderr):
output=sys.stdout, log_output=sys.stderr, warnings=False, spelling=False):
with open(input_file, 'r') as file:
print_section_header_style('Parsing')
ast = ptulsconv.protools_text_export_grammar.parse(file.read())
@@ -184,6 +186,16 @@ def convert(input_file, output_format='fmpxml', start=None, end=None, select_ree
parsed = reel_xform.transform(parsed)
if warnings:
for warning in chain(validate_unique_field(parsed, field='QN'),
validate_non_empty_field(parsed, field='QN'),
validate_non_empty_field(parsed, field='CN'),
validate_non_empty_field(parsed, field='Char'),
validate_dependent_value(parsed, key_field='CN', dependent_field='Char'),
validate_dependent_value(parsed, key_field='CN', dependent_field='Actor'),):
print_warning(warning.report_message())
if output_format == 'json':
json.dump(parsed, output)
elif output_format == 'fmpxml':

13
ptulsconv/movie_export.py Normal file
View File

@@ -0,0 +1,13 @@
import ffmpeg # ffmpeg-python
def create_movie(event):
start = event['Movie.Start_Offset_Seconds']
duration = event['PT.Clip.Finish_Seconds'] - event['PT.Clip.Start_Seconds']
input_movie = event['Movie.Filename']
print("Will make movie starting at {}, dur {} from movie {}".format(start, duration, input_movie))
def export_movies(events):
for event in events:
create_movie(event)

View File

@@ -12,12 +12,21 @@ def print_section_header_style(str):
else:
sys.stderr.write("%s\n\n" % str)
def print_status_style(str):
if sys.stderr.isatty():
sys.stderr.write("\033[3m - %s\033[0m\n" % str)
else:
sys.stderr.write(" - %s\n" % str)
def print_warning(warning_string):
if sys.stderr.isatty():
sys.stderr.write("\033[3m - %s\033[0m\n" % warning_string)
else:
sys.stderr.write(" - %s\n" % warning_string)
def print_advisory_tagging_error(failed_string, position, parent_track_name=None, clip_time=None):
if sys.stderr.isatty():
sys.stderr.write("\n")

54
ptulsconv/validations.py Normal file
View File

@@ -0,0 +1,54 @@
from dataclasses import dataclass
from sys import stderr
@dataclass
class ValidationError:
message: str
event: dict
def report_message(self):
return f"{self.message}: event at {self.event['PT.Clip.Start']} on track {self.event['PT.Track.Name']}"
def validate_value(input_dict, key_field, predicate):
for event in input_dict['events']:
val = event[key_field]
if not predicate(val):
yield ValidationError(message='Field {} not in range'.format(val),
event=event)
def validate_unique_field(input_dict, field='QN'):
values = set()
for event in input_dict['events']:
if event[field] in values:
yield ValidationError(message='Re-used {}'.format(field), event=event)
def validate_non_empty_field(input_dict, field='QN'):
for event in input_dict['events']:
if field not in event.keys() or len(event[field]) == 0:
yield ValidationError(message='Empty field {}'.format(field), event=event)
def validate_dependent_value(input_dict, key_field, dependent_field):
"""
Validates that two events with the same value in `key_field` always have the
same value in `dependent_field`
"""
value_map = dict()
for event in input_dict['events']:
if key_field not in event.keys():
continue
if event[key_field] not in value_map.keys():
value_map[event[key_field]] = event.get(dependent_field, None)
else:
if value_map[event[key_field]] != event.get(dependent_field, None):
yield ValidationError(message='Field {} depends on key field {} (value={}), expected {}, was {}'
.format(dependent_field, key_field, event[key_field], value_map[key_field],
event.get(dependent_field, None)), event=event)