functional

pull/128/head
Christopher Usher 5 years ago
parent 223af52265
commit ac72f775c9

@ -1,7 +1,7 @@
FROM alpine:3.7 FROM alpine:3.7
# dependencies needed for compiling c extensions # dependencies needed for compiling c extensions
# also busybox-extras for telnet for easier use of backdoor # also busybox-extras for telnet for easier use of backdoor
RUN apk --update add py2-pip gcc python-dev musl-dev busybox-extras RUN apk --update add py2-pip gcc python-dev musl-dev busybox-extras libpng
# Install common lib first as it changes less # Install common lib first as it changes less
COPY common /tmp/common COPY common /tmp/common

@ -1,10 +1,13 @@
import datetime import datetime
import itertools
import logging import logging
import os import os
import signal import signal
import argh import argh
import gevent.backdoor import gevent.backdoor
import matplotlib.pyplot as plt
import numpy as np
import prometheus_client as prom import prometheus_client as prom
import common import common
@ -15,6 +18,7 @@ class CoverageChecker(object):
"""Checks the segment coverage for a given channel in a a given directoy.""" """Checks the segment coverage for a given channel in a a given directoy."""
CHECK_INTERVAL = 60 #seconds between checking coverage CHECK_INTERVAL = 60 #seconds between checking coverage
CHECK_INTERVAL = 6 #seconds between checking coverage
def __init__(self, channel, qualities, base_dir): def __init__(self, channel, qualities, base_dir):
"""Constructor for CoverageChecker. """Constructor for CoverageChecker.
@ -45,73 +49,121 @@ class CoverageChecker(object):
path = os.path.join(self.base_dir, self.channel, quality) path = os.path.join(self.base_dir, self.channel, quality)
hours = [name for name in os.listdir(path) if not name.startswith('.')] hours = [name for name in os.listdir(path) if not name.startswith('.')]
hours.sort() hours.sort()
previous_hour_segments = None
for hour in hours: for hour in hours:
if self.stopping.is_set(): if self.stopping.is_set():
break break
self.logger.info('Checking {}/{}'.format(quality, hour)) self.logger.info('Checking {}/{}'.format(quality, hour))
path = os.path.join(self.base_dir, self.channel, quality, hour)
segment_names = [name for name in os.listdir(path) if not name.startswith('.')]
segment_names.sort()
segments = []
for name in segment_names:
path = os.path.join(hour, name)
try:
segments.append(common.parse_segment_path(path))
except ValueError:
self.logger.warning('Skipping segment {} with invalid format'.format(path))
# based on common.segments.best_segments_by_start
# but more complicated to capture more detailed metrics
hour_path = os.path.join(self.base_dir, self.channel, quality, hour)
segment_names = [name for name in os.listdir(hour_path) if not name.startswith('.')]
segment_names.sort()
parsed = (common.parse_segment_path(os.path.join(hour_path, name)) for name in segment_names)
full_segment_count = 0
partial_segment_count = 0
full_segment_duration = datetime.timedelta(0)
partial_segment_duration = datetime.timedelta(0)
full_repeats = 0
full_repeat_duration = datetime.timedelta(0)
partial_repeats = 0
partial_repeat_duration = datetime.timedelta(0)
best_segments = []
holes = []
editable_holes = []
overlap_count = 0
overlap_duration = datetime.timedelta(0)
previous = None
previous_editable = None
coverage = datetime.timedelta(0)
editable_coverage = datetime.timedelta(0)
only_partials = []
for start_time, segments in itertools.groupby(parsed, key=lambda segment: segment.start):
full_segments = [] full_segments = []
partial_segments = [] partial_segments = []
useful_segments = []
for segment in segments: for segment in segments:
if segment.type == 'full': if segment.type == 'full':
full_segments.append(segment) full_segments.append(segment)
useful_segments.append(segment) full_segment_count += 1
full_segment_duration += segment.duration
elif segment.type == 'partial': elif segment.type == 'partial':
partial_segments.append(segment) partial_segments.append(segment)
useful_segments.append(segment) partial_segment_count += 1
partial_segment_duration += segment.duration
full_segments_duration = sum([segment.duration.seconds for segment in full_segments]) if full_segments:
partial_segments_duration = sum([segment.duration.seconds for segment in partial_segments]) if len(full_segments) == 1:
useful_segments_duration = sum([segment.duration.seconds for segment in useful_segments]) best_segment = full_segments[0]
self.logger.info('{}/{}: {} full segments totalling {} s'.format(quality, hour, len(full_segments), full_segments_duration)) else:
self.logger.info('{}/{}: {} partial segments totalling {} s'.format(quality, hour, len(partial_segments), partial_segments_duration)) full_segments.sort(key=lambda segment: (segment.duration))
self.logger.info('{}/{}: {} useful segments totalling {} s'.format(quality, hour, len(useful_segments), useful_segments_duration)) best_segment = full_segments[-1]
for segment in full_segments[:-1]:
full_repeats += 1
full_repeat_duration += segment.duration
if partial_segments:
for segment in partial_segments:
partial_repeats += 1
partial_repeat_duration += segment.duration
else:
partial_segments.sort(key=lambda segment: (segment.duration))
best_segment = partial_segments[-1]
only_partials.append((best_segment.start, best_segment.start + best_segment.duration))
for segment in partial_segments[:-1]:
partial_repeats += 1
partial_repeat_duration += segment.duration
self.logger.debug(best_segment.path.split('/')[-1])
best_segments.append(best_segment)
if previous is None:
coverage += best_segment.duration
editable_coverage += best_segment.duration
previous_editable = best_segment
holes = [] else:
editiable_holes = []
overlaps = []
previous = full_segments[0]
coverage = previous.duration
editable_coverage = previous.duration
total_duration = previous.duration
for segment in full_segments[1:]:
total_duration += previous.duration
previous_end = previous.start + previous.duration previous_end = previous.start + previous.duration
if segment.start < previous_end: if segment.start < previous_end:
overlaps.append(segment) overlap_count += 1
overlap_duration += previous_end - segment.start
coverage += segment.start - previous_end + segment.duration coverage += segment.start - previous_end + segment.duration
if segment.start == previous_end:
coverage += segment.duration
editable_coverage += segment.duration
else: else:
holes.append((previous_end, segment.start))
coverage += segment.duration coverage += segment.duration
editable_coverage += segment.duration editable_coverage += segment.duration
previous = segment
if segment.start > previous_end:
holes.append((previous_end, segment.start))
previous_editable_end = previous_editable.start + previous_editable.duration
if segment.start > previous_editable_end:
editable_holes.append((previous_editable_end, segment.start))
hole_duration = datetime.timedelta(hours=1) - coverage previous_editable = best_segment
overlap_duration = total_duration - coverage
previous = best_segment
start = best_segments[0].start
end = best_segments[-1].start + best_segments[-1].duration
hole_duration = end - start - coverage
editable_hole_duration = end - start - editable_coverage
self.logger.info('{}/{}: Start: {} End: {} ({} s)'.format(quality, hour, start, end, (end - start).seconds))
self.logger.info('{}/{}: {} full segments totalling {} s'.format(quality, hour, full_segment_count, full_segment_duration.seconds))
self.logger.info('{}/{}: {} full segment repeats totalling {} s'.format(quality, hour, full_repeats, full_repeat_duration.seconds))
self.logger.info('{}/{}: {} partial segments totalling {} s'.format(quality, hour, partial_segment_count, partial_segment_duration.seconds))
self.logger.info('{}/{}: {} partial segment repeats totalling {} s'.format(quality, hour, partial_repeats, partial_repeat_duration.seconds))
self.logger.info('{}/{}: {} full segments totalling {} s'.format(quality, hour, len(full_segments), total_duration.seconds))
self.logger.info('{}/{}: covering {} s, {} s editable'.format(quality, hour, coverage.seconds, editable_coverage.seconds)) self.logger.info('{}/{}: covering {} s, {} s editable'.format(quality, hour, coverage.seconds, editable_coverage.seconds))
self.logger.info('{}/{}: {} holes totalling {} s '.format(quality, hour, len(holes), hole_duration.seconds)) self.logger.info('{}/{}: {} holes totalling {} s '.format(quality, hour, len(holes), hole_duration.seconds))
self.logger.info('{}/{}: {} overlapping segments, {} s overlapping'.format(quality, hour, len(overlaps), overlap_duration.seconds)) self.logger.info('{}/{}: {} editable holes totalling {} s '.format(quality, hour, len(editable_holes), editable_hole_duration.seconds))
self.logger.info('{}/{}: {} overlapping segments, {} s overlapping'.format(quality, hour, overlap_count, overlap_duration.seconds))
self.logger.info(holes)
self.logger.info(only_partials)
previous_hour_segments = best_segments
self.stopping.wait(common.jitter(self.CHECK_INTERVAL)) self.stopping.wait(common.jitter(self.CHECK_INTERVAL))

@ -7,6 +7,8 @@ setup(
install_requires = [ install_requires = [
'argh', 'argh',
'gevent', 'gevent',
'matplotlib',
'numpy',
'prometheus-client', 'prometheus-client',
'python-dateutil', 'python-dateutil',
'wubloader-common', 'wubloader-common',

Loading…
Cancel
Save