Skip to content

Commit

Permalink
[medialibrary-118] Validation tool should work at the media level
Browse files Browse the repository at this point in the history
  • Loading branch information
cjcodeproj committed Nov 5, 2023
1 parent 539f71c commit ec9315b
Show file tree
Hide file tree
Showing 25 changed files with 2,006 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ medialibrary CHANGELOG
======================

## CURRENT
- [medialibrary-118](https://github.com/cjcodeproj/medialibrary/issues/118) Validation tool should work at the media level
- [medialibrary-109](https://github.com/cjcodeproj/medialibrary/issues/109) README.md should reflect location of distribution on PyPi
- [medialibrary-106](https://github.com/cjcodeproj/medialibrary/issues/106) Repo: Skip directories based on presence of ignore file

Expand Down
210 changes: 210 additions & 0 deletions src/media/tools/media/validate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#!/usr/bin/env python

#
# Copyright 2023 Chris Josephes
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#

'''
Run simple validation tests against the movies, report any incomplete data.
'''

# pylint: disable=R0801
# pylint: disable=too-few-public-methods

import argparse
import os
from datetime import datetime
from media.data.media.medium.release import FormalType
from media.generic.sorting.lists import Organizer
from media.tools.common import load_media_dev

from media.validation.core.validator import Validator
from media.validation.core.status import Tally


def filter_output_set(in_results, in_args):
'''
Output a random sample of records.
'''
outset = []
# final = []
for result in in_results:
if in_args.filter == 'none':
outset.append(result)
elif in_args.filter == 'passing':
if result.has_passed():
outset.append(result)
else:
if not result.has_passed():
outset.append(result)
if in_args.random:
# return random_sample(output, args.random)
return Organizer.get_random_sample(outset, in_args.random)
return outset


def output_list(filtered_list):
'''
Output a simple list of results.
'''
print(Tally.header(), end='')
for sample_i in filtered_list:
mtype = FormalType.formal_convert(sample_i.media.medium.release.type)
status_o = sample_i.status
print(f"{sample_i.media!s:40.39s} {mtype:10s} {status_o.tally!s}")


def output_details(filtered_list):
'''
Output the full status report of every piece of media.
'''
for sample_i in filtered_list:
print(sample_i.status.report())


def output_timestamp(in_start_time):
'''
Output program run timestamp data.
'''
end_time = datetime.now()
out = f"\n {'Program Start Time'} : {in_start_time}\n" + \
f" {'Program End Time'} : {end_time}\n" + \
f" {'Duration'} : {end_time - start_time}\n"
return out


def single_column_stats(in_results):
'''
Output a single column of stats.
'''
output = "\n"
all_passed = 0
all_failed = 0
all_total = len(in_results)
for result in in_results:
if result.has_passed():
all_passed += 1
else:
all_failed += 1
all_perc = all_passed / all_total * 100
output += f"{' ':20s} {'Entire Set'}\n" + \
f"{' ':20s} {'-'*10}\n" + \
f"{'Total Media:':20s} {all_total:10d}\n" + \
f"{'Perfect:':20s} {all_passed:10d}\n" + \
f"{'Failed:':20s} {all_failed:10d}\n\n" + \
f"{'Overall Percentage:':20s} {all_perc:9.2f}%\n"
return output


def double_column_stats(in_results, in_filtered_results):
'''
Output two columns of stats, comparing all results
and the filtered results.
'''
output = "\n"
all_total = len(in_results)
sample_total = len(in_filtered_results)
all_passed = 0
all_failed = 0
sample_passed = 0
sample_failed = 0
for result in in_results:
if result.has_passed():
all_passed += 1
else:
all_failed += 1
for result in in_filtered_results:
if result.has_passed():
sample_passed += 1
else:
sample_failed += 1
all_perc = all_passed / all_total * 100
sample_perc = sample_passed / sample_total * 100
output += f"{' ':20s} {'Entire Set'} {'Sample Set'}\n" + \
f"{' ':20s} {'-'*10} {'-'*10}\n" + \
f"{'Total Media:':20s} {all_total:10d} {sample_total:10d}\n" + \
f"{'Passed:':20s} {all_passed:10d} {sample_passed:10d}\n" + \
f"{'Failed:':20s} {all_failed:10d} {sample_failed:10d}\n\n" + \
f"{'Overall Percentage:':20s} {all_perc:9.2f}% " +\
f"{sample_perc:9.2f}%\n"
return output


def output_stats(in_start_time, in_results, in_filtered):
'''
Output overall statistics, including time metrics.
'''
o_total = len(results)
f_total = len(filtered_results)
output = ''
if f_total == o_total:
output = single_column_stats(in_results)
else:
output = double_column_stats(results, in_filtered)
output += output_timestamp(in_start_time)
print(output)


if __name__ == '__main__':
start_time = datetime.now()
parser = argparse.ArgumentParser(description='Simple movie list.')
parser.add_argument('--mediapath',
help='path of media library')
parser.add_argument('--level',
type=int,
default=4,
choices=range(1, 6),
help='diagnostic level')
parser.add_argument('--filter',
choices=['passing', 'failed', 'none'],
default='none',
help='records to output')
parser.add_argument('--random', type=int, help='show X random entries')
parser.add_argument('--list',
action=argparse.BooleanOptionalAction,
default=True,
help='list records')
parser.add_argument('--details',
action=argparse.BooleanOptionalAction,
default=False,
help='show record details')
parser.add_argument('--stats', action='store_true',
help='report statistics')
args = parser.parse_args()
mediapath = args.mediapath or os.environ['MEDIAPATH']
if not mediapath:
parser.print_help()
media = load_media_dev(mediapath)
sample = media
validator = Validator()
validator.set_suite()
validator.load_media(sample, args.level)
results = validator.run_tests()
if args.filter:
filtered_results = filter_output_set(results, args)
else:
filtered_results = results
if args.list:
output_list(filtered_results)
if args.details:
output_details(filtered_results)
if args.stats:
output_stats(start_time, results, filtered_results)
23 changes: 23 additions & 0 deletions src/media/validation/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env python

#
# Copyright 2023 Chris Josephes
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
23 changes: 23 additions & 0 deletions src/media/validation/core/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env python

#
# Copyright 2023 Chris Josephes
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
65 changes: 65 additions & 0 deletions src/media/validation/core/faults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env python

#
# Copyright 2023 Chris Josephes
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#

'''
Objects pertaining to validation test faults.
'''

# pylint: disable=R0903


class FaultLevel():
'''
Fault levels.
'''
NOTICE = 1
WARNING = 2
CRITICAL = 3


class Fault():
'''
A fault is the equivelant of an exception
when validating media records.
'''
def __init__(self, in_level, in_message, in_text=None):
self.level = in_level
self.message = in_message
self.text = in_text

def lvl_str(self):
'''
Return the fault level as a string.
'''
matrix = {
FaultLevel.NOTICE: 'NOTICE',
FaultLevel.WARNING: 'WARNING',
FaultLevel.CRITICAL: 'CRITICAL'
}
return matrix[self.level]

def __str__(self):
if self.text:
return f"{self.lvl_str()}: {self.message} ({self.text})"
return f"{self.lvl_str()}: {self.message}"
58 changes: 58 additions & 0 deletions src/media/validation/core/results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env python

#
# Copyright 2023 Chris Josephes
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#


'''
Status objects.
'''

# pylint: disable=R0903


class StatusCode():
'''
Simple status code values.
'''
PASS = 0
FAIL = 1


class ResultSet():
'''
Simple pairing of media and results.
'''
def __init__(self, in_media):
self.media = in_media
self.results = []


class Result():
'''
Simple result.
'''
def __init__(self):
self.status = 0
self.severity = 5
self.message = ""
self.area = ""
Loading

0 comments on commit ec9315b

Please sign in to comment.