<commit_before>
from __future__ import division, print_function, unicode_literals, absolute_import

import os
import sys
import json
import time

import sarge

from ordered_namespace import Struct

"""
This is a collection of classes and function for doing useful work with video files via ffmpeg.
"""

#------------------------------------------------
# Helper functions

def find_executable(executable, path=None):
    """
    This function was originally inspired by the public-domain function `find_executable`
    by Anatoly Techtonik <techtonik@gmail.com> at https://gist.github.com/4368898.

    Find if 'executable' can be run. Looks for it in 'path'
    (string that lists directories separated by 'os.pathsep';
    defaults to os.environ['PATH']).

    Checks for all executable extensions. Returns full path if found otherwise None.
    """
    if path is None:
        path = os.environ['PATH']

    paths = path.split(os.pathsep)

    extlist = ['']
    if sys.platform == 'win32':
        pathext = os.environ['PATHEXT'].lower().split(os.pathsep)
        (base, ext) = os.path.splitext(executable)
        if ext.lower() not in pathext:
            extlist = pathext

    for ext in extlist:
        execname = executable + ext
        if os.path.isfile(execname):
            return execname
        else:
            for p in paths:
                f = os.path.join(p, execname)
                if os.path.isfile(f):
                    return f
    else:
        return None

def safe_number(value):
    """Attempt to convert supplied object to an integer or float.
    Return original value if unsucessful.
    """
    try:
        try:
            return int(value)
        except ValueError:
            try:
                return float(value)
            except ValueError:
                return value
    except TypeError:
        return value

#------------------------------------------------

class Proc():
    """Base class for asynchronously running an external commandline application.
    """
    def __init__(self, fname_exe, verbose=False):
        """Initialize new Processor instance.
        """
        self.fname_exe = find_executable(fname_exe)

        if not os.path.isfile(self.fname_exe):
            raise ValueError('Executable not found: {}'.format(self.fname_exe))

        # if not path_work:
        #     path_work = os.path.curdir
        # self.path_work = os.path.normpath(os.path.abspath(path_work))

        self._proc = None
        self._stdout = []
        self._stderr = []
        self._results = None
        self.command = ''
        self.verbose = verbose

    def __repr__(self):
        if self.is_running:
            msg = 'Nutmeg process running with PID {}'.format(self.pid)
        else:
            msg = 'Nutmeg process not running'
        return str(msg)

    def run(self):
        """User should override this method to setup their application-specific work command string
        """
        raise NotImplementedError('Please override this method in your subclass implementation.')

    def _start(self):
        """Start work task running in the background.  User should not need to call this directly.
        Work with your subclasses `run` method instead.
        """
        self._results = None

        if not self.command:
            raise ValueError('command not defined')

        # Pre-existing process ID?
        if self.is_running:
            if self.verbose:
                print('\nProcess already running with PID: {:d}'.format(self.pid))
            return self.pid

        # Instantiate command and run it
        self._proc = sarge.Command(self.command, stdout=sarge.Capture(), stderr=sarge.Capture())
        self._proc.run(async=True)

        time.sleep(0.01)

        if not self.is_running:
           raise ValueError('Problem starting process: {}'.format(self.fname_exe))

    def wait(self):
        """Block until background process finishes
        """
        if self._proc:
            self._proc.wait()

    def stop(self):
        """Halt the process
        """
        if self.is_running:
            self._proc.kill()
            self._proc.wait()
            self._proc = None
            self._stdout = None
            self._stderr = None
            self._results = None

            if self.verbose:
                print('\nProcess stopped.')
        else:
            if self.verbose:
                print('\nProcess not running.  Nothing to stop.')

    @property
    def pid(self):
        """Process ID
        """
        if self._proc:
            return self._proc.process.pid
        else:
            return None

    @property
    def is_running(self):
        """Return True if process is running, otherwise False.
        """
        if self._proc:
            return self._proc.poll() is None
        else:
            return False

    @property
    def results(self):
        """Make available any post-processing results upon task completion.
        Block if process is still running in the background.
        """
        if self.is_running:
            self._proc.wait()

        if not self._proc:
            return None

        if not self._results:
            self._capture_stdout_stderr()

        return self._results

    def _capture_stdout_stderr(self):
        """Internal function to capture all lines of text from stdout and stderr.
        Method does nothing if process is still running.
        """
        if self.is_running:
            return

        lines_stdout = []
        for l in self._proc.stdout.text.split('\n'):
            lines_stdout.extend(l.split('\r'))

        lines_stderr = []
        for l in self._proc.stderr.text.split('\n'):
            lines_stderr.extend(l.split('\r'))

        self._stdout = lines_stdout
        self._stderr = lines_stderr
        self._results = self.post_process()

    def post_process(self):
        """do something with self._stdout and self._stderr
        """
        # raise NotImplementedError('Please override this method in your subclass implementation.')
        return True

#------------------------------------------------
#------------------------------------------------

class NutmegProbe(Proc):
    """Extract information from video using ffprobe command
    """
    def __init__(self, fname_in=None, fname_exe='ffprobe', verbose=False):
        """Initialize new Processor instance.
        """
        super().__init__(fname_exe, verbose=verbose)
        if fname_in:
            self.run(fname_in)

    def run(self, fname_in):
        """Query video file for detailed information
        """
        if not os.path.isfile(fname_in):
            raise ValueError('File not found: {}'.format(fname_in))

        parts = [self.fname_exe,
                 '-hide_banner',
                 '-show_format',
                 '-show_streams',
                 '-print_format json',
                 # '-unit -prefix -pretty',
                 '-i ' + fname_in]

        self.command = ' '.join(parts)
        self._start()

    def post_process(self):
        """Parse lines of JSON text from stdout, extract container and stream information.
        """
        text = ' '.join(self._stdout)
        parsed = json.loads(text)

        # Convert any string numerical values to int or float.  Safely.
        container = {k: safe_number(v) for k,v in parsed['format'].items()}
        streams =  [{k: safe_number(v) for k,v in s.items()} for s in parsed['streams']]

        # Store final results in handy namespace structures.  Same as a dict, but access items
        # via attributes.  Includes support for IPython tab-completion.
        results = Struct()
        results.container = Struct(container)
        results.num_streams = len(streams)
        results.streams = [Struct(s) for s in streams]

        return results

#------------------------------------------------

class NutmegIntra(Proc):
    """Convert a video file to an intermediate formate suitable for editing
    """
    def __init__(self, fname_in=None, fname_exe='ffmpeg', crf=23, verbose=True):
        """Initialize new Processor instance.
        """
        super().__init__(fname_exe, verbose=verbose)
        if fname_in:
            self.run(fname_in, crf=crf)

    def run(self, fname_in, crf=23, scale_factor=None, scale_size=None, block=True):
        """Convert video file to intra-frames only, more suitable for editing.

        Nice terse description of pseudo AVC-I via ffmpeg: https://vimeo.com/194400625

        intra stuff:
        - https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping
        - https://trac.ffmpeg.org/wiki/Encode/H.264
        """
        if not os.path.isfile(fname_in):
            raise ValueError('File not found: {}'.format(fname_in))

        self.fname_in = fname_in
        b, e = os.path.splitext(fname_in)
        self.fname_out = b + '.intra.mp4'

        # Filters
        # https://trac.ffmpeg.org/wiki/FilteringGuide
        if not scale_factor:
            scale_factor = 1

        if scale_factor != 1:
            filter_scale = '-vf scale=w=iw*{scale_factor:}:-2'.format(scale_factor=scale_factor)
        elif scale_size:
            filter_scale = '-vf scale=w={scale_size:}:-2'.format(scale_size=scale_size)
        else:
            filter_scale = ''

        # Command parts
        # https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping
        # https://ffmpeg.org/ffmpeg-formats.html#Options-8
        parts = [self.fname_exe,
                 '-y',
                 '-hide_banner',
                 '-loglevel info',
                 # '-report',   # lots of good debug info from ffmpeg
                 '-i {}'.format(self.fname_in),
                 filter_scale,
                 '-codec:a aac',
                 '-strict -2',       # enable experimental aac
                 '-codec:v libx264',
                 '-preset ultrafast',
                 '-tune fastdecode',
                 '-pix_fmt yuvj420p',
                 '-crf {}'.format(crf),
                 '-g 0 -keyint_min 0 -intra',
                 '-x264opts colormatrix=bt709 -x264opts force-cfr',
                 '-movflags +faststart+rtphint+disable_chpl+separate_moof+default_base_moof',
                 self.fname_out]

        self.command = ' '.join(parts)
        self._start()

        if block:
            self.wait()

    def post_process(self):
        """Return video processing results
        """
        if not os.path.isfile(self.fname_out):
            raise ValueError('Output file not found: {}'.format(self.fname_out))

        results = Struct()
        results.fname_in = self.fname_in
        results.probe_in = NutmegProbe(self.fname_in).results
        results.probe_out = NutmegProbe(self.fname_out).results
        results.fname_out = self.fname_out

        return results

class NutmegClip(Proc):
    """Extract clip from video file.
    """
    def __init__(self, fname_exe='ffmpeg', verbose=False):
        """Initialize new Processor instance.
        """
        super().__init__(fname_exe, verbose=verbose)

    def run(self, fname_in, time_start, time_stop=None, duration=None, block=True):
        """Extract clip from video file between two specified times.
        """
        if not time_stop and not duration:
            raise ValueError('Must specify at least one of `time_stop` or `duration`')

        if duration:
            time_stop = time_start + duration

        if not os.path.isfile(fname_in):
            raise ValueError('File not found: {}'.format(fname_in))

        self.fname_in = fname_in
        b, e = os.path.splitext(self.fname_in)
        self.fname_out = '{}.clip-{:.2f}-{:.2f}.mp4'.format(b, time_start, time_stop)

        # Command parts
        parts = [self.fname_exe,
                 '-y',
                 '-hide_banner',
                 '-loglevel warning',
                 # '-report',   # lots of good debug info from ffmpeg
                 '-i {}'.format(fname_in),
                 '-codec:a copy',
                 '-codec:v copy',
                 '-ss {}'.format(time_start),
                 '-to {}'.format(time_stop),
                 self.fname_out]

        self.command = ' '.join(parts)
        self._start()

        if block:
            self.wait()

    def post_process(self):
        """Return video processing results
        """
        if not os.path.isfile(self.fname_out):
            raise ValueError('Output file not found: {}'.format(self.fname_out))

        results = Struct()
        results.probe_in = NutmegProbe(self.fname_in).results
        results.probe_out = NutmegProbe(self.fname_out).results
        results.fname_out = self.fname_out

        return results

#################################################
# Convenience functions

def probe(fname_video):
    """Probe supplied video file for detailed information
    """
    p = NutmegProbe(fname_video)
    return p.results

def intra(fnames_video, scale_factor=1, scale_size=None):
    """Process supplied file(s) to intra frames for easier editing
    """
    if isinstance(fnames_video, str):
        fnames_video = [fnames_video]

    p = NutmegIntra()
    results = []
    for f in fnames_video:
        print('Processing: {}'.format(os.path.basename(f)))

        p.run(f, scale_factor=scale_factor, scale_size=scale_size, block=True)
        results.append(p.results)

    return results

#------------------------------------------------

if __name__ == '__main__':
    pass
<commit_msg>Fix a problem when the file path contains spaces<commit_after>
from __future__ import division, print_function, unicode_literals, absolute_import

import os
import sys
import json
import time

import sarge

from ordered_namespace import Struct

"""
This is a collection of classes and function for doing useful work with video files via ffmpeg.
"""

#------------------------------------------------
# Helper functions

def find_executable(executable, path=None):
    """
    This function was originally inspired by the public-domain function `find_executable`
    by Anatoly Techtonik <techtonik@gmail.com> at https://gist.github.com/4368898.

    Find if 'executable' can be run. Looks for it in 'path'
    (string that lists directories separated by 'os.pathsep';
    defaults to os.environ['PATH']).

    Checks for all executable extensions. Returns full path if found otherwise None.
    """
    if path is None:
        path = os.environ['PATH']

    paths = path.split(os.pathsep)

    extlist = ['']
    if sys.platform == 'win32':
        pathext = os.environ['PATHEXT'].lower().split(os.pathsep)
        (base, ext) = os.path.splitext(executable)
        if ext.lower() not in pathext:
            extlist = pathext

    for ext in extlist:
        execname = executable + ext
        if os.path.isfile(execname):
            return execname
        else:
            for p in paths:
                f = os.path.join(p, execname)
                if os.path.isfile(f):
                    return f
    else:
        return None

def safe_number(value):
    """Attempt to convert supplied object to an integer or float.
    Return original value if unsucessful.
    """
    try:
        try:
            return int(value)
        except ValueError:
            try:
                return float(value)
            except ValueError:
                return value
    except TypeError:
        return value

#------------------------------------------------

class Proc():
    """Base class for asynchronously running an external commandline application.
    """
    def __init__(self, fname_exe, verbose=False):
        """Initialize new Processor instance.
        """
        self.fname_exe = find_executable(fname_exe)

        if not os.path.isfile(self.fname_exe):
            raise ValueError('Executable not found: {}'.format(self.fname_exe))

        # if not path_work:
        #     path_work = os.path.curdir
        # self.path_work = os.path.normpath(os.path.abspath(path_work))

        self._proc = None
        self._stdout = []
        self._stderr = []
        self._results = None
        self.command = ''
        self.verbose = verbose

    def __repr__(self):
        if self.is_running:
            msg = 'Nutmeg process running with PID {}'.format(self.pid)
        else:
            msg = 'Nutmeg process not running'
        return str(msg)

    def run(self):
        """User should override this method to setup their application-specific work command string
        """
        raise NotImplementedError('Please override this method in your subclass implementation.')

    def _start(self):
        """Start work task running in the background.  User should not need to call this directly.
        Work with your subclasses `run` method instead.
        """
        self._results = None

        if not self.command:
            raise ValueError('command not defined')

        # Pre-existing process ID?
        if self.is_running:
            if self.verbose:
                print('\nProcess already running with PID: {:d}'.format(self.pid))
            return self.pid

        # Instantiate command and run it
        self._proc = sarge.Command(self.command, stdout=sarge.Capture(), stderr=sarge.Capture(), shell=True)
        self._proc.run(async=True)

        time.sleep(0.01)

        if not self.is_running:
           raise ValueError('Problem starting process: {}'.format(self.fname_exe))

    def wait(self):
        """Block until background process finishes
        """
        if self._proc:
            self._proc.wait()

    def stop(self):
        """Halt the process
        """
        if self.is_running:
            self._proc.kill()
            self._proc.wait()
            self._proc = None
            self._stdout = None
            self._stderr = None
            self._results = None

            if self.verbose:
                print('\nProcess stopped.')
        else:
            if self.verbose:
                print('\nProcess not running.  Nothing to stop.')

    @property
    def pid(self):
        """Process ID
        """
        if self._proc:
            return self._proc.process.pid
        else:
            return None

    @property
    def is_running(self):
        """Return True if process is running, otherwise False.
        """
        if self._proc:
            return self._proc.poll() is None
        else:
            return False

    @property
    def results(self):
        """Make available any post-processing results upon task completion.
        Block if process is still running in the background.
        """
        if self.is_running:
            self._proc.wait()

        if not self._proc:
            return None

        if not self._results:
            self._capture_stdout_stderr()

        return self._results

    def _capture_stdout_stderr(self):
        """Internal function to capture all lines of text from stdout and stderr.
        Method does nothing if process is still running.
        """
        if self.is_running:
            return

        lines_stdout = []
        for l in self._proc.stdout.text.split('\n'):
            lines_stdout.extend(l.split('\r'))

        lines_stderr = []
        for l in self._proc.stderr.text.split('\n'):
            lines_stderr.extend(l.split('\r'))

        self._stdout = lines_stdout
        self._stderr = lines_stderr
        self._results = self.post_process()

    def post_process(self):
        """do something with self._stdout and self._stderr
        """
        # raise NotImplementedError('Please override this method in your subclass implementation.')
        return True

#------------------------------------------------
#------------------------------------------------

class NutmegProbe(Proc):
    """Extract information from video using ffprobe command
    """
    def __init__(self, fname_in=None, fname_exe='ffprobe', verbose=False):
        """Initialize new Processor instance.
        """
        super().__init__(fname_exe, verbose=verbose)
        if fname_in:
            self.run(fname_in)

    def run(self, fname_in):
        """Query video file for detailed information
        """
        if not os.path.isfile(fname_in):
            raise ValueError('File not found: {}'.format(fname_in))

        parts = [self.fname_exe,
                 '-hide_banner',
                 '-show_format',
                 '-show_streams',
                 '-print_format json',
                 # '-unit -prefix -pretty',
                 '-i "' + fname_in + '"']

        self.command = ' '.join(parts)
        self._start()

    def post_process(self):
        """Parse lines of JSON text from stdout, extract container and stream information.
        """
        text = ' '.join(self._stdout)
        parsed = json.loads(text)

        # Convert any string numerical values to int or float.  Safely.
        container = {k: safe_number(v) for k,v in parsed['format'].items()}
        streams =  [{k: safe_number(v) for k,v in s.items()} for s in parsed['streams']]

        # Store final results in handy namespace structures.  Same as a dict, but access items
        # via attributes.  Includes support for IPython tab-completion.
        results = Struct()
        results.container = Struct(container)
        results.num_streams = len(streams)
        results.streams = [Struct(s) for s in streams]

        return results

#------------------------------------------------

class NutmegIntra(Proc):
    """Convert a video file to an intermediate formate suitable for editing
    """
    def __init__(self, fname_in=None, fname_exe='ffmpeg', crf=23, verbose=True):
        """Initialize new Processor instance.
        """
        super().__init__(fname_exe, verbose=verbose)
        if fname_in:
            self.run(fname_in, crf=crf)

    def run(self, fname_in, crf=23, scale_factor=None, scale_size=None, block=True):
        """Convert video file to intra-frames only, more suitable for editing.

        Nice terse description of pseudo AVC-I via ffmpeg: https://vimeo.com/194400625

        intra stuff:
        - https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping
        - https://trac.ffmpeg.org/wiki/Encode/H.264
        """
        if not os.path.isfile(fname_in):
            raise ValueError('File not found: {}'.format(fname_in))

        self.fname_in = fname_in
        b, e = os.path.splitext(fname_in)
        self.fname_out = b + '.intra.mp4'

        # Filters
        # https://trac.ffmpeg.org/wiki/FilteringGuide
        if not scale_factor:
            scale_factor = 1

        if scale_factor != 1:
            filter_scale = '-vf scale=w=iw*{scale_factor:}:-2'.format(scale_factor=scale_factor)
        elif scale_size:
            filter_scale = '-vf scale=w={scale_size:}:-2'.format(scale_size=scale_size)
        else:
            filter_scale = ''

        # Command parts
        # https://sites.google.com/site/linuxencoding/x264-ffmpeg-mapping
        # https://ffmpeg.org/ffmpeg-formats.html#Options-8
        parts = [self.fname_exe,
                 '-y',
                 '-hide_banner',
                 '-loglevel info',
                 # '-report',   # lots of good debug info from ffmpeg
                 '-i {}'.format(self.fname_in),
                 filter_scale,
                 '-codec:a aac',
                 '-strict -2',       # enable experimental aac
                 '-codec:v libx264',
                 '-preset ultrafast',
                 '-tune fastdecode',
                 '-pix_fmt yuvj420p',
                 '-crf {}'.format(crf),
                 '-g 0 -keyint_min 0 -intra',
                 '-x264opts colormatrix=bt709 -x264opts force-cfr',
                 '-movflags +faststart+rtphint+disable_chpl+separate_moof+default_base_moof',
                 self.fname_out]

        self.command = ' '.join(parts)
        self._start()

        if block:
            self.wait()

    def post_process(self):
        """Return video processing results
        """
        if not os.path.isfile(self.fname_out):
            raise ValueError('Output file not found: {}'.format(self.fname_out))

        results = Struct()
        results.fname_in = self.fname_in
        results.probe_in = NutmegProbe(self.fname_in).results
        results.probe_out = NutmegProbe(self.fname_out).results
        results.fname_out = self.fname_out

        return results

class NutmegClip(Proc):
    """Extract clip from video file.
    """
    def __init__(self, fname_exe='ffmpeg', verbose=False):
        """Initialize new Processor instance.
        """
        super().__init__(fname_exe, verbose=verbose)

    def run(self, fname_in, time_start, time_stop=None, duration=None, block=True):
        """Extract clip from video file between two specified times.
        """
        if not time_stop and not duration:
            raise ValueError('Must specify at least one of `time_stop` or `duration`')

        if duration:
            time_stop = time_start + duration

        if not os.path.isfile(fname_in):
            raise ValueError('File not found: {}'.format(fname_in))

        self.fname_in = fname_in
        b, e = os.path.splitext(self.fname_in)
        self.fname_out = '{}.clip-{:.2f}-{:.2f}.mp4'.format(b, time_start, time_stop)

        # Command parts
        parts = [self.fname_exe,
                 '-y',
                 '-hide_banner',
                 '-loglevel warning',
                 # '-report',   # lots of good debug info from ffmpeg
                 '-i {}'.format(fname_in),
                 '-codec:a copy',
                 '-codec:v copy',
                 '-ss {}'.format(time_start),
                 '-to {}'.format(time_stop),
                 self.fname_out]

        self.command = ' '.join(parts)
        self._start()

        if block:
            self.wait()

    def post_process(self):
        """Return video processing results
        """
        if not os.path.isfile(self.fname_out):
            raise ValueError('Output file not found: {}'.format(self.fname_out))

        results = Struct()
        results.probe_in = NutmegProbe(self.fname_in).results
        results.probe_out = NutmegProbe(self.fname_out).results
        results.fname_out = self.fname_out

        return results

#################################################
# Convenience functions

def probe(fname_video):
    """Probe supplied video file for detailed information
    """
    p = NutmegProbe(fname_video)
    return p.results

def intra(fnames_video, scale_factor=1, scale_size=None):
    """Process supplied file(s) to intra frames for easier editing
    """
    if isinstance(fnames_video, str):
        fnames_video = [fnames_video]

    p = NutmegIntra()
    results = []
    for f in fnames_video:
        print('Processing: {}'.format(os.path.basename(f)))

        p.run(f, scale_factor=scale_factor, scale_size=scale_size, block=True)
        results.append(p.results)

    return results

#------------------------------------------------

if __name__ == '__main__':
    pass
<|endoftext|>


<commit_before># This file is part of Workout Manager.
#
# Workout Manager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Workout Manager is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Workout Manager.  If not, see <http://www.gnu.org/licenses/>.

from django.db import models

from exercises.models import Language

from django.contrib.auth.models import User
from django.utils.translation import ugettext as _

class NutritionPlan(models.Model):
    """ A nutrition plan
    """

    user = models.ForeignKey(User, verbose_name = _('User'))
    language = models.ForeignKey(Language, verbose_name = _('Language'))
    creation_date = models.DateField(_('Creation date'), auto_now_add=True)
    description = models.TextField(max_length = 2000,
                                   blank = True,
                                   verbose_name = _('Description'),
                                   help_text = _('A description of the goal of the plan, e.g. "Gain mass" or "Prepare for summer"'))

    def __unicode__(self):
        """Return a more human-readable representation
        """
        return "Nutrition plan for %s, %s" % (self.user, self.creation_date)

class Ingredient(models.Model):
    """ An ingredient, with some approximate nutrition values
    """

    name = models.CharField(max_length=100,
                            verbose_name = _('Name'),)

    language = models.ForeignKey(Language, verbose_name = _('Language'))

    energy = models.IntegerField(
                                 verbose_name = _('Energy'),
                                 help_text = _('In kcal per 100g'))

    protein = models.FloatField(
                                verbose_name = _('Protein'),
                                help_text = _('In g per 100g of product'))

    carbohydrates = models.FloatField(
                                        verbose_name = _('Carbohydrates'),
                                        help_text = _('In g per 100g of product'))

    fat = models.FloatField(blank = True,
                              verbose_name = _('Fat'),
                              help_text = _('In g per 100g of product'))

    fibres = models.FloatField(blank = True,
                               null = True,
                               verbose_name = _('Fibres'),
                               help_text = _('In g per 100g of product'))

    natrium = models.FloatField(blank = True,
                                null = True,
                                verbose_name = _('Natrium'),
                                help_text = _('In g per 100g of product'))

    def __unicode__(self):
        """Return a more human-readable representation
        """
        return "%s" % (self.name, )

class Meal(models.Model):
    """ A meal
    """

    plan = models.ForeignKey(NutritionPlan, verbose_name = _('Nutrition plan'))
    order = models.IntegerField(max_length = 1, blank = True, verbose_name = _('Order'))
    time = models.TimeField(null = True, blank = True, verbose_name = _('Time (approx)'))

    def __unicode__(self):
        """Return a more human-readable representation
        """
        return "%s Meal" % (self.order,)

class MealItem(models.Model):
    """ An item (component) of a meal
    """

    meal = models.ForeignKey(Meal, verbose_name = _('Nutrition plan'))
    order = models.IntegerField(max_length = 1, blank = True, verbose_name = _('Order'))
    amount_gramm = models.IntegerField(max_length=4, blank = True, verbose_name = _('Amount in gramms'))
    amount_freetext = models.CharField(max_length=100,
                                       blank = True,
                                       verbose_name = _('Amount (freetext)'),
                                       help_text = _('The amount in another unit, e.g. "a spoonfull" or "3 pieces"'))
    ingredient_freetext = models.CharField(max_length=100,
                                           blank = True,
                                           verbose_name = _('Ingredient'),
                                           help_text = _('If the ingredient is not in the system, enter the name here'))
    ingredient = models.ForeignKey(Ingredient, verbose_name = _('Ingredient'))

    def __unicode__(self):
        """Return a more human-readable representation
        """
        return "%s %s %s" % (self.order, self.amount_gramm or self.amount_freetext, self.ingredient)<commit_msg>Set default order of model ingredient (name, ascending)<commit_after># This file is part of Workout Manager.
#
# Workout Manager is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Workout Manager is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Workout Manager.  If not, see <http://www.gnu.org/licenses/>.

from django.db import models

from exercises.models import Language

from django.contrib.auth.models import User
from django.utils.translation import ugettext as _

class NutritionPlan(models.Model):
    """ A nutrition plan
    """

    user = models.ForeignKey(User, verbose_name = _('User'))
    language = models.ForeignKey(Language, verbose_name = _('Language'))
    creation_date = models.DateField(_('Creation date'), auto_now_add=True)
    description = models.TextField(max_length = 2000,
                                   blank = True,
                                   verbose_name = _('Description'),
                                   help_text = _('A description of the goal of the plan, e.g. "Gain mass" or "Prepare for summer"'))

    def __unicode__(self):
        """Return a more human-readable representation
        """
        return "Nutrition plan for %s, %s" % (self.user, self.creation_date)

class Ingredient(models.Model):
    """ An ingredient, with some approximate nutrition values
    """

    # Metaclass to set some other properties
    class Meta:
        ordering = ["name", ]

    name = models.CharField(max_length=100,
                            verbose_name = _('Name'),)

    language = models.ForeignKey(Language, verbose_name = _('Language'))

    energy = models.IntegerField(
                                 verbose_name = _('Energy'),
                                 help_text = _('In kcal per 100g'))

    protein = models.FloatField(
                                verbose_name = _('Protein'),
                                help_text = _('In g per 100g of product'))

    carbohydrates = models.FloatField(
                                        verbose_name = _('Carbohydrates'),
                                        help_text = _('In g per 100g of product'))

    fat = models.FloatField(blank = True,
                              verbose_name = _('Fat'),
                              help_text = _('In g per 100g of product'))

    fibres = models.FloatField(blank = True,
                               null = True,
                               verbose_name = _('Fibres'),
                               help_text = _('In g per 100g of product'))

    natrium = models.FloatField(blank = True,
                                null = True,
                                verbose_name = _('Natrium'),
                                help_text = _('In g per 100g of product'))

    def __unicode__(self):
        """Return a more human-readable representation
        """
        return "%s" % (self.name, )

class Meal(models.Model):
    """ A meal
    """

    plan = models.ForeignKey(NutritionPlan, verbose_name = _('Nutrition plan'))
    order = models.IntegerField(max_length = 1, blank = True, verbose_name = _('Order'))
    time = models.TimeField(null = True, blank = True, verbose_name = _('Time (approx)'))

    def __unicode__(self):
        """Return a more human-readable representation
        """
        return "%s Meal" % (self.order,)

class MealItem(models.Model):
    """ An item (component) of a meal
    """

    meal = models.ForeignKey(Meal, verbose_name = _('Nutrition plan'))
    order = models.IntegerField(max_length = 1, blank = True, verbose_name = _('Order'))
    amount_gramm = models.IntegerField(max_length=4, blank = True, verbose_name = _('Amount in gramms'))
    amount_freetext = models.CharField(max_length=100,
                                       blank = True,
                                       verbose_name = _('Amount (freetext)'),
                                       help_text = _('The amount in another unit, e.g. "a spoonfull" or "3 pieces"'))
    ingredient_freetext = models.CharField(max_length=100,
                                           blank = True,
                                           verbose_name = _('Ingredient'),
                                           help_text = _('If the ingredient is not in the system, enter the name here'))
    ingredient = models.ForeignKey(Ingredient, verbose_name = _('Ingredient'))

    def __unicode__(self):
        """Return a more human-readable representation
        """
        return "%s %s %s" % (self.order, self.amount_gramm or self.amount_freetext, self.ingredient)
<|endoftext|>


<commit_before>from ete2 import Nexml, TreeStyle, NodeStyle

import argparse
import tinycss
import sys
import os

argparser = argparse.ArgumentParser(
    description='Produce publication-quality phylogenetic trees using NexSS stylesheets'
)
argparser.add_argument(
    'source',
    nargs=1,
    help='Input NeXML file to process'
)
argparser.add_argument(
    'stylesheet',
    nargs='*',
    help='NexSS stylesheet to format the NeXML file with'
)
argparser.add_argument(
    '-o', '--output',
    nargs='?',
    default='output.svg',
    help='The name of the output file. Must have a .svg, .png or .pdf extension.'
)
args = argparser.parse_args()

nexml = Nexml()
nexml.build_from_file(sys.argv[1])

def build_tree_style(tree):
    # use our simple TSS cascade to prepare an ETE TreeStyle object
    sheets = gather_tss_stylesheets(tree)
    if len(sheets) == 0:
        return None

    # Some styles can be applied to the entire tree
    ts = TreeStyle()
    # For nodes (and other elements?), build an ordered set of TSS rules
    # to apply to each element in our layout function
    node_rules = []

    for s in sheets:
        ts, tss_cascade = apply_stylesheet(
            stylesheet=s,
            tree_style=ts,
            node_rules=node_rules)

    # Use a layout function to test each node against TSS selectors?
    def apply_tss(node):
        node_style = NodeStyle()
        for rule in node_rules:
            # Test this node against each selector
            if test_node_against_selector(node, rule.selector):
                node_style, node = apply_node_rule(rule, node_style, node)
        node.set_style(node_style);
        return node

    # apply this layout function to each node as it's rendered
    ts.layout_fn = apply_tss
    return ts

def test_node_against_selector(node, selector):
    # TODO: Here we can interpret our selectors
    # in the context of a live tree (vs. a DOM)
    return True

TREE_STYLE_PROPERTIES = ("layout",)
# See the full list at
# http://pythonhosted.org/ete2/reference/reference_treeview.html#treestyle

NODE_STYLE_PROPERTIES = ("color", "background-color", "size", "shape",)
# See the full list at
# http://pythonhosted.org/ete2/reference/reference_treeview.html#ete2.NodeStyle

# Report unsupported style properties *just once*
unsupported_tree_styles = []
def report_unsupported_tree_style(name):
    if name not in unsupported_tree_styles:
        print("ETE does not support '%s' in TreeStyle" % name)
        unsupported_tree_styles.append(name)

unsupported_node_styles = []
def report_unsupported_node_style(name):
    if name not in unsupported_node_styles:
        print("ETE does not support '%s' in NodeStyle" % name)
        unsupported_node_styles.append(name)

def apply_node_rule(rule, node_style, node):
    for style in rule.declarations:
        # N.B. name is always normalized lower-case
        # Translate TSS/CSS property names into ETE properties
        if style.name not in NODE_STYLE_PROPERTIES:
            report_unsupported_node_style(style.name)
            continue

        # TODO: handle dynamic (data-driven) values in all cases!
        if style.name == "color":
            node_style["fgcolor"] = style.value.as_css()
        elif style.name == "background-color":
            node_style["bgcolor"] = style.value.as_css()
        else:
            # by default, use the same name as in TSS
            try:
                setattr(node_style, style.name, style.value.as_css())
            except:
                print("Invalid property for node: %s" % style.name);
                pass

        # TODO: consider style.priority? ('important')

    # node_style["shape"] = "sphere"
    # node_style["hz_line_type"] = 2
    # node_style["hz_line_color"] = "#cc99cc"
    # node_style["size"] = 10

    return node_style, node

def gather_tss_stylesheets(tree):
    sheets = []
    # if a stylesheet was provided, this is all we should use
    if args.stylesheet:
        sheets.extend(args.stylesheet)
        return sheets

    # TODO: add any default stylesheet for this tool?

    # add any linked stylesheets in the NeXML file
    # add any embedded stylesheets in the NeXML file
    nexml_doc = tree.nexml_project
    if nexml_doc:
        # TODO: can we retrieve <?xml-stylesheet ... ?> elements?
        pass

    # TODO: add any linked stylesheets just for this tree

    # TODO: add any embedded stylesheets in this tree

    return sheets

# Apply styles to an existing TreeStyle object and return it
def apply_stylesheet(stylesheet, tree_style, node_rules):
    if (not stylesheet):
        print("Missing stylesheet!")
        return tree_style, node_rules
    if (not tree_style):
        print("Missing tree_style!")
        return None, None

    # parse the TSS from its CSS-style syntax
    parser = tinycss.make_parser('page3')
    # load the stylesheet using its path+filename
    stylesheet = os.path.abspath(stylesheet)
    style = parser.parse_stylesheet_file(css_file=stylesheet)
    print("Found %i rules, %i errors" %
       (len(style.rules),len(style.errors)))
    if len(style.errors) > 0:
        for e in style.errors:
            print(e)

    # walk the TSS rules and translate them into TreeStyle properties
    # or add them to the node_rules collection
    for r in style.rules:
        if r.at_keyword:
            # this is an ImportRule, MediaRule, or the like
            # TODO: support @import, etc?
            print("Unsupported at-rule:")
            print(r)
        else:
            # it's a normal CSS RuleSet
            #print("TSS RuleSet:")
            #print("  selector: %s" % r.selector.as_css())
            #print("  as list:  %s" % repr(r.selector))
            #print("  declarations: %s" % r.declarations)

            # add every rule to node tests
            node_rules.append(r)

            # TODO: interpret its selector to find targets
            # see https://pythonhosted.org/tinycss/parsing.html

            # Some rules should modify the current TreeStyle
            if r.selector.as_css() in ("canvas", "tree", "scale"):
                for style in r.declarations:
                    if style.name not in TREE_STYLE_PROPERTIES:
                        report_unsupported_tree_style(style.name)
                        continue
                    if style.name == "layout":
                        its_value = style.value.as_css()
                        if its_value == "rectangular":
                            tree_style.mode = "r"
                        elif its_value == "circular":
                            tree_style.mode = "c"
                    else:
                        setattr(tree_style, style.name, style.value.as_css())

            #tree_style.mode = 'c'  # circular
            #tree_style.show_leaf_name = False
            #tree_style.show_branch_length = True

    return tree_style, node_rules

# Figure out the file basename in case we have multiple trees.
(output_basename, output_extension) = os.path.splitext(args.output)

# render a series of output files (one for each tree)
tree_index = 0
for trees in nexml.get_trees():
    for tree in trees.get_tree():
        tree_index += 1
        ts = build_tree_style(tree)

        # Only use suffixes if there is more than one tree.
        output_filename = "%s%s" % (output_basename, output_extension)
        if tree_index > 1:
            output_filename = "%s%d%s" % (output_basename, tree_index, output_extension)

        tree.render(output_filename, tree_style=ts)

        # let's try the interactive QT viewer
        tree.show(tree_style=ts)

<commit_msg>Added support for output width and height (ow, oh) and dpi.<commit_after>from ete2 import Nexml, TreeStyle, NodeStyle

import argparse
import tinycss
import sys
import os

argparser = argparse.ArgumentParser(
    description='Produce publication-quality phylogenetic trees using NexSS stylesheets'
)
argparser.add_argument(
    'source',
    nargs=1,
    help='Input NeXML file to process'
)
argparser.add_argument(
    'stylesheet',
    nargs='*',
    help='NexSS stylesheet to format the NeXML file with'
)
argparser.add_argument(
    '-o', '--output',
    nargs=1,
    default='output.svg',
    help='The name of the output file. Must have a .svg, .png or .pdf extension.'
)
argparser.add_argument(
    '-ow', '--width',
    nargs=1,
    default=[None],
    type=int,
    help='The width of the output image'
)
argparser.add_argument(
    '-oh', '--height',
    nargs=1,
    default=[None],
    type=int,
    help='The height of the output image'
)
argparser.add_argument(
    '--dpi',
    nargs=1,
    default=[300],
    type=int,
    help='The dots-per-inch (DPI) in the output file.'
)
args = argparser.parse_args()

# Change single-list items into single items.
args.output = args.output[0]
args.dpi = args.dpi[0]
args.width = args.width[0]
args.height = args.height[0]

nexml = Nexml()
nexml.build_from_file(sys.argv[1])

def build_tree_style(tree):
    # use our simple TSS cascade to prepare an ETE TreeStyle object
    sheets = gather_tss_stylesheets(tree)
    if len(sheets) == 0:
        return None

    # Some styles can be applied to the entire tree
    ts = TreeStyle()
    # For nodes (and other elements?), build an ordered set of TSS rules
    # to apply to each element in our layout function
    node_rules = []

    for s in sheets:
        ts, tss_cascade = apply_stylesheet(
            stylesheet=s,
            tree_style=ts,
            node_rules=node_rules)

    # Use a layout function to test each node against TSS selectors?
    def apply_tss(node):
        node_style = NodeStyle()
        for rule in node_rules:
            # Test this node against each selector
            if test_node_against_selector(node, rule.selector):
                node_style, node = apply_node_rule(rule, node_style, node)
        node.set_style(node_style);
        return node

    # apply this layout function to each node as it's rendered
    ts.layout_fn = apply_tss
    return ts

def test_node_against_selector(node, selector):
    # TODO: Here we can interpret our selectors
    # in the context of a live tree (vs. a DOM)
    return True

TREE_STYLE_PROPERTIES = ("layout",)
# See the full list at
# http://pythonhosted.org/ete2/reference/reference_treeview.html#treestyle

NODE_STYLE_PROPERTIES = ("color", "background-color", "size", "shape",)
# See the full list at
# http://pythonhosted.org/ete2/reference/reference_treeview.html#ete2.NodeStyle

# Report unsupported style properties *just once*
unsupported_tree_styles = []
def report_unsupported_tree_style(name):
    if name not in unsupported_tree_styles:
        print("ETE does not support '%s' in TreeStyle" % name)
        unsupported_tree_styles.append(name)

unsupported_node_styles = []
def report_unsupported_node_style(name):
    if name not in unsupported_node_styles:
        print("ETE does not support '%s' in NodeStyle" % name)
        unsupported_node_styles.append(name)

def apply_node_rule(rule, node_style, node):
    for style in rule.declarations:
        # N.B. name is always normalized lower-case
        # Translate TSS/CSS property names into ETE properties
        if style.name not in NODE_STYLE_PROPERTIES:
            report_unsupported_node_style(style.name)
            continue

        # TODO: handle dynamic (data-driven) values in all cases!
        if style.name == "color":
            node_style["fgcolor"] = style.value.as_css()
        elif style.name == "background-color":
            node_style["bgcolor"] = style.value.as_css()
        else:
            # by default, use the same name as in TSS
            try:
                setattr(node_style, style.name, style.value.as_css())
            except:
                print("Invalid property for node: %s" % style.name);
                pass

        # TODO: consider style.priority? ('important')

    # node_style["shape"] = "sphere"
    # node_style["hz_line_type"] = 2
    # node_style["hz_line_color"] = "#cc99cc"
    # node_style["size"] = 10

    return node_style, node

def gather_tss_stylesheets(tree):
    sheets = []
    # if a stylesheet was provided, this is all we should use
    if args.stylesheet:
        sheets.extend(args.stylesheet)
        return sheets

    # TODO: add any default stylesheet for this tool?

    # add any linked stylesheets in the NeXML file
    # add any embedded stylesheets in the NeXML file
    nexml_doc = tree.nexml_project
    if nexml_doc:
        # TODO: can we retrieve <?xml-stylesheet ... ?> elements?
        pass

    # TODO: add any linked stylesheets just for this tree

    # TODO: add any embedded stylesheets in this tree

    return sheets

# Apply styles to an existing TreeStyle object and return it
def apply_stylesheet(stylesheet, tree_style, node_rules):
    if (not stylesheet):
        print("Missing stylesheet!")
        return tree_style, node_rules
    if (not tree_style):
        print("Missing tree_style!")
        return None, None

    # parse the TSS from its CSS-style syntax
    parser = tinycss.make_parser('page3')
    # load the stylesheet using its path+filename
    stylesheet = os.path.abspath(stylesheet)
    style = parser.parse_stylesheet_file(css_file=stylesheet)
    print("Found %i rules, %i errors" %
       (len(style.rules),len(style.errors)))
    if len(style.errors) > 0:
        for e in style.errors:
            print(e)

    # walk the TSS rules and translate them into TreeStyle properties
    # or add them to the node_rules collection
    for r in style.rules:
        if r.at_keyword:
            # this is an ImportRule, MediaRule, or the like
            # TODO: support @import, etc?
            print("Unsupported at-rule:")
            print(r)
        else:
            # it's a normal CSS RuleSet
            #print("TSS RuleSet:")
            #print("  selector: %s" % r.selector.as_css())
            #print("  as list:  %s" % repr(r.selector))
            #print("  declarations: %s" % r.declarations)

            # add every rule to node tests
            node_rules.append(r)

            # TODO: interpret its selector to find targets
            # see https://pythonhosted.org/tinycss/parsing.html

            # Some rules should modify the current TreeStyle
            if r.selector.as_css() in ("canvas", "tree", "scale"):
                for style in r.declarations:
                    if style.name not in TREE_STYLE_PROPERTIES:
                        report_unsupported_tree_style(style.name)
                        continue
                    if style.name == "layout":
                        its_value = style.value.as_css()
                        if its_value == "rectangular":
                            tree_style.mode = "r"
                        elif its_value == "circular":
                            tree_style.mode = "c"
                    else:
                        setattr(tree_style, style.name, style.value.as_css())

            #tree_style.mode = 'c'  # circular
            #tree_style.show_leaf_name = False
            #tree_style.show_branch_length = True

    return tree_style, node_rules

# Figure out the file basename in case we have multiple trees.
(output_basename, output_extension) = os.path.splitext(args.output)

# render a series of output files (one for each tree)
tree_index = 0
for trees in nexml.get_trees():
    for tree in trees.get_tree():
        tree_index += 1
        ts = build_tree_style(tree)

        # Only use suffixes if there is more than one tree.
        output_filename = "%s%s" % (output_basename, output_extension)
        if tree_index > 1:
            output_filename = "%s%d%s" % (output_basename, tree_index, output_extension)

        if args.width and args.height:
            tree.render(output_filename, tree_style=ts, w=args.width, h=args.height, dpi=args.dpi)
        elif args.width:
            tree.render(output_filename, tree_style=ts, w=args.width, dpi=args.dpi)
        elif args.height:
            tree.render(output_filename, tree_style=ts, h=args.height, dpi=args.dpi)
        else:
            tree.render(output_filename, tree_style=ts, dpi=args.dpi)

        # let's try the interactive QT viewer
        tree.show(tree_style=ts)

<|endoftext|>


<commit_before># About this plugin:
#   This plugin uses collectd's Python plugin to record PuppetDB informations.
#
# Authors:
#   Laurent Apollis
#
# collectd:
#   http://collectd.org
# PuppetDB:
#   http://docs.puppetlabs.com/puppetdb/
# collectd-python:
#   http://collectd.org/documentation/manpages/collectd-python.5.shtml

import pypuppetdb
import collectd

from pypuppetdb import connect

# Host to connect to. Override in config by specifying 'Host'.
PUPPETDB_HOST    = 'localhost'
# Port to connect to. Override in config by specifying 'Port'.
PUPPETDB_PORT    = '8080'
# Use ssl. Override in config by specifying 'SSL_VERIFY'.
PUPPETDB_SSL     = None
# Key used to connect to ('/path/to/private.pem'). Override in config by specifying 'Key'.
PUPPETDB_KEY     = None
# CERT used to connect to ('/path/to/public.pem'). Override in config by specifying 'CERT'.
PUPPETDB_CERT    = None
#Connect timeout. Override in config by specifying 'Timeout'.
PUPPETDB_TIMEOUT = '20'
#Time to consider unreported nodes. Override in config by specifying 'UnreportTime'.
UNREPORTED_TIME = 25
# Verbose logging on/off. Override in config by specifying 'Verbose'.
VERBOSE_LOGGING  = False

def get_infos(func, *args, **kwargs):
    try:
        return func(*args, **kwargs)
    except HTTPError as e:
        abort(e.response.status_code)
    except ConnectionError:
        abort(500)
    except EmptyResponseError:
        abort(204)

def dispatch_value(value, key, type, type_instance=None):
    if not type_instance:
        type_instance = key

    log_verbose('Sending value: %s=%s' % (type_instance, value))

    val = collectd.Values(plugin='puppetdb')
    val.type = type
    val.type_instance = type_instance
    val.values = [value]
    val.dispatch()

def read_callback():
    puppetdb = connect(
        api_version= 3,
        host=PUPPETDB_HOST,
        port=PUPPETDB_PORT,
        ssl_verify=PUPPETDB_SSL,
        ssl_key=PUPPETDB_KEY,
        ssl_cert=PUPPETDB_CERT,
        timeout=PUPPETDB_TIMEOUT,
    )

    prefix = 'com.puppetlabs.puppetdb.query.population'
    num_nodes = get_infos(
        puppetdb.metric,
        "{0}{1}".format(prefix, ':type=default,name=num-nodes'))
    num_resources = get_infos(
        puppetdb.metric,
        "{0}{1}".format(prefix, ':type=default,name=num-resources'))
    avg_resources_node = get_infos(
        puppetdb.metric,
        "{0}{1}".format(prefix, ':type=default,name=avg-resources-per-node'))

    # Ftech nodes
    nodes = puppetdb.nodes(
        unreported=UNREPORTED_TIME,
        with_status=True)

    #Init stats
    stats = {
        'changed': 0,
        'unchanged': 0,
        'failed': 0,
        'unreported': 0,
        'noop': 0
        }

    for node in nodes:
        if node.status == 'unreported':
            stats['unreported'] += 1
        elif node.status == 'changed':
            stats['changed'] += 1
        elif node.status == 'failed':
            stats['failed'] += 1
        elif node.status == 'noop':
            stats['noop'] += 1
        else:
            stats['unchanged'] += 1

    log_verbose('population: %s\n' % num_nodes['Value'])
    dispatch_value(num_nodes['Value'], 'population','gauge')

    log_verbose('unreported: %s\n' % stats['unreported'])
    dispatch_value(stats['unreported'], 'unreported','gauge')

    log_verbose('changed: %s\n' % stats['changed'])
    dispatch_value(stats['changed'], 'changed','gauge')

    log_verbose('failed: %s\n' % stats['failed'])
    dispatch_value(stats['failed'], 'failed','gauge')

    log_verbose('noop: %s\n' % stats['noop'])
    dispatch_value(stats['noop'], 'noop','gauge')

    log_verbose('unchanged: %s\n' % stats['unchanged'])
    dispatch_value(stats['unchanged'], 'unchanged','gauge')

def log_verbose(msg):
    if not VERBOSE_LOGGING:
        return
    collectd.info('puppetdb plugin [verbose]: %s' % msg)

def configure_callback(conf):
    """Receive configuration block"""
    global PUPPETDB_HOST, PUPPETDB_PORT, PUPPETDB_SSL, PUPPETDB_KEY, PUPPETDB_CERT, PUPPETDB_TIMEOUT, UNREPORTED_TIME, VERBOSE_LOGGING
    for node in conf.children:
        if node.key == 'Host':
            PUPPETDB_HOST = node.values[0]
        elif node.key == 'Port':
            PUPPETDB_PORT = node.values[0]
        elif node.key == 'SSL_VERIFY':
            PUPPETDB_SSL = node.values[0]
        elif node.key == 'Key':
            PUPPETDB_KEY = node.values[0]
        elif node.key == 'CERT':
            PUPPETDB_CERT = node.values[0]
        elif node.key == 'Timeout':
            PUPPETDB_TIMEOUT = int(node.values[0])
        elif node.key == 'UnreportTime':
            UNREPORTED_TIME = int(node.values[0])
        elif node.key == 'Verbose':
            VERBOSE_LOGGING = bool(node.values[0])
        else:
            collectd.warning('puppetdb plugin: Unknown config key: %s.'
                             % node.key)
    log_verbose('Configured with host=%s, port=%s, ssl=%s, key=%s, cert=%s, timeout=%s' % (PUPPETDB_HOST, PUPPETDB_PORT, PUPPETDB_SSL, PUPPETDB_KEY, PUPPETDB_CERT, PUPPETDB_TIMEOUT))

collectd.register_config(configure_callback)
collectd.register_read(read_callback)<commit_msg>Ensure port and timeout are ints<commit_after># About this plugin:
#   This plugin uses collectd's Python plugin to record PuppetDB informations.
#
# Authors:
#   Laurent Apollis
#
# collectd:
#   http://collectd.org
# PuppetDB:
#   http://docs.puppetlabs.com/puppetdb/
# collectd-python:
#   http://collectd.org/documentation/manpages/collectd-python.5.shtml

import pypuppetdb
import collectd

from pypuppetdb import connect

# Host to connect to. Override in config by specifying 'Host'.
PUPPETDB_HOST    = 'localhost'
# Port to connect to. Override in config by specifying 'Port'.
PUPPETDB_PORT    = 8080
# Use ssl. Override in config by specifying 'SSL_VERIFY'.
PUPPETDB_SSL     = None
# Key used to connect to ('/path/to/private.pem'). Override in config by specifying 'Key'.
PUPPETDB_KEY     = None
# CERT used to connect to ('/path/to/public.pem'). Override in config by specifying 'CERT'.
PUPPETDB_CERT    = None
#Connect timeout. Override in config by specifying 'Timeout'.
PUPPETDB_TIMEOUT = 20
#Time to consider unreported nodes. Override in config by specifying 'UnreportTime'.
UNREPORTED_TIME = 25
# Verbose logging on/off. Override in config by specifying 'Verbose'.
VERBOSE_LOGGING  = False

def get_infos(func, *args, **kwargs):
    try:
        return func(*args, **kwargs)
    except HTTPError as e:
        abort(e.response.status_code)
    except ConnectionError:
        abort(500)
    except EmptyResponseError:
        abort(204)

def dispatch_value(value, key, type, type_instance=None):
    if not type_instance:
        type_instance = key

    log_verbose('Sending value: %s=%s' % (type_instance, value))

    val = collectd.Values(plugin='puppetdb')
    val.type = type
    val.type_instance = type_instance
    val.values = [value]
    val.dispatch()

def read_callback():
    puppetdb = connect(
        api_version= 3,
        host=PUPPETDB_HOST,
        port=PUPPETDB_PORT,
        ssl_verify=PUPPETDB_SSL,
        ssl_key=PUPPETDB_KEY,
        ssl_cert=PUPPETDB_CERT,
        timeout=PUPPETDB_TIMEOUT,
    )

    prefix = 'com.puppetlabs.puppetdb.query.population'
    num_nodes = get_infos(
        puppetdb.metric,
        "{0}{1}".format(prefix, ':type=default,name=num-nodes'))
    num_resources = get_infos(
        puppetdb.metric,
        "{0}{1}".format(prefix, ':type=default,name=num-resources'))
    avg_resources_node = get_infos(
        puppetdb.metric,
        "{0}{1}".format(prefix, ':type=default,name=avg-resources-per-node'))

    # Ftech nodes
    nodes = puppetdb.nodes(
        unreported=UNREPORTED_TIME,
        with_status=True)

    #Init stats
    stats = {
        'changed': 0,
        'unchanged': 0,
        'failed': 0,
        'unreported': 0,
        'noop': 0
        }

    for node in nodes:
        if node.status == 'unreported':
            stats['unreported'] += 1
        elif node.status == 'changed':
            stats['changed'] += 1
        elif node.status == 'failed':
            stats['failed'] += 1
        elif node.status == 'noop':
            stats['noop'] += 1
        else:
            stats['unchanged'] += 1

    log_verbose('population: %s\n' % num_nodes['Value'])
    dispatch_value(num_nodes['Value'], 'population','gauge')

    log_verbose('unreported: %s\n' % stats['unreported'])
    dispatch_value(stats['unreported'], 'unreported','gauge')

    log_verbose('changed: %s\n' % stats['changed'])
    dispatch_value(stats['changed'], 'changed','gauge')

    log_verbose('failed: %s\n' % stats['failed'])
    dispatch_value(stats['failed'], 'failed','gauge')

    log_verbose('noop: %s\n' % stats['noop'])
    dispatch_value(stats['noop'], 'noop','gauge')

    log_verbose('unchanged: %s\n' % stats['unchanged'])
    dispatch_value(stats['unchanged'], 'unchanged','gauge')

def log_verbose(msg):
    if not VERBOSE_LOGGING:
        return
    collectd.info('puppetdb plugin [verbose]: %s' % msg)

def configure_callback(conf):
    """Receive configuration block"""
    global PUPPETDB_HOST, PUPPETDB_PORT, PUPPETDB_SSL, PUPPETDB_KEY, PUPPETDB_CERT, PUPPETDB_TIMEOUT, UNREPORTED_TIME, VERBOSE_LOGGING
    for node in conf.children:
        if node.key == 'Host':
            PUPPETDB_HOST = node.values[0]
        elif node.key == 'Port':
            PUPPETDB_PORT = int(node.values[0])
        elif node.key == 'SSL_VERIFY':
            PUPPETDB_SSL = node.values[0]
        elif node.key == 'Key':
            PUPPETDB_KEY = node.values[0]
        elif node.key == 'CERT':
            PUPPETDB_CERT = node.values[0]
        elif node.key == 'Timeout':
            PUPPETDB_TIMEOUT = int(node.values[0])
        elif node.key == 'UnreportTime':
            UNREPORTED_TIME = int(node.values[0])
        elif node.key == 'Verbose':
            VERBOSE_LOGGING = bool(node.values[0])
        else:
            collectd.warning('puppetdb plugin: Unknown config key: %s.'
                             % node.key)
    log_verbose('Configured with host=%s, port=%s, ssl=%s, key=%s, cert=%s, timeout=%s' % (PUPPETDB_HOST, PUPPETDB_PORT, PUPPETDB_SSL, PUPPETDB_KEY, PUPPETDB_CERT, PUPPETDB_TIMEOUT))

collectd.register_config(configure_callback)
collectd.register_read(read_callback)<|endoftext|>


<commit_before>#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2017 bily     Huazhong University of Science and Technology
#
# Distributed under terms of the MIT license.

"""Figure with horizontal scrollbar and play capabilities

For latest version, go to https://github.com/bilylee/videofig

Basic usage
-----------
Creates a figure with a horizontal scrollbar and shortcuts to scroll automatically.
The scroll range is 0 to NUM_FRAMES - 1. The function REDRAW_FUN(F, AXES) is called to
redraw at scroll position F (for example, REDRAW_FUNC can show the frame F of a video)
using AXES for drawing. F is an integer, AXES is a instance of [Axes class](https://matplotlib.org/api/axes_api.html)

This can be used not only to play and analyze standard videos, but it also lets you place
any custom Matplotlib plots and graphics on top.

The keyboard shortcuts are:
  Enter(Return) -- play/pause video (25 frames-per-second default).
  Backspace -- play/pause video 5 times slower.
  Right/left arrow keys -- advance/go back one frame.
  Page down/page up -- advance/go back 30 frames.
  Home/end -- go to first/last frame of video.

Advanced usage
--------------
videofig(NUM_FRAMES, REDRAW_FUNC, FPS, BIG_SCROLL)
Also specifies the speed of the play function (frames-per-second) and
the frame step of page up/page down (or empty for defaults).

videofig(NUM_FRAMES, REDRAW_FUNC, FPS, BIG_SCROLL, KEY_FUNC)
Also calls KEY_FUNC(KEY) with any keys that weren't processed, so you
can add more shortcut keys (or empty for none).

Example 1: Plot a dynamic sine wave
---------
  import numpy as np

  def redraw_fn(f, axes):
    amp = float(f) / 3000
    f0 = 3
    t = np.arange(0.0, 1.0, 0.001)
    s = amp * np.sin(2 * np.pi * f0 * t)
    if not redraw_fn.initialized:
      redraw_fn.l, = axes.plot(t, s, lw=2, color='red')
      redraw_fn.initialized = True
    else:
      redraw_fn.l.set_ydata(s)

  redraw_fn.initialized = False

  videofig(100, redraw_fn)

Example 2: Show images in a custom directory
---------
  import os
  import glob
  from scipy.misc import imread

  img_dir = 'YOUR-IMAGE-DIRECTORY'
  img_files = glob.glob(os.path.join(video_dir, '*.jpg'))

  def redraw_fn(f, axes):
    img_file = img_files[f]
    img = imread(img_file)
    if not redraw_fn.initialized:
      redraw_fn.im = axes.imshow(img, animated=True)
      redraw_fn.initialized = True
    else:
      redraw_fn.im.set_array(img)
  redraw_fn.initialized = False

  videofig(len(img_files), redraw_fn, play_fps=30)

Example 3: Show images together with object bounding boxes
----------
  import os
  import glob
  from scipy.misc import imread
  from matplotlib.pyplot import Rectangle

  video_dir = 'YOUR-VIDEO-DIRECTORY'

  img_files = glob.glob(os.path.join(video_dir, '*.jpg'))
  box_files = glob.glob(os.path.join(video_dir, '*.txt'))

  def redraw_fn(f, axes):
    img = imread(img_files[f])
    box = bbread(box_files[f])  # Define your own bounding box reading utility
    x, y, w, h = box
    if not redraw_fn.initialized:
      im = axes.imshow(img, animated=True)
      bb = Rectangle((x, y), w, h,
                     fill=False,  # remove background
                     edgecolor="red")
      axes.add_patch(bb)
      redraw_fn.im = im
      redraw_fn.bb = bb
      redraw_fn.initialized = True
    else:
      redraw_fn.im.set_array(img)
      redraw_fn.bb.set_xy((x, y))
      redraw_fn.bb.set_width(w)
      redraw_fn.bb.set_height(h)
  redraw_fn.initialized = False

  videofig(len(img_files), redraw_fn, play_fps=30)
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Slider

def videofig(num_frames, redraw_func, play_fps=25, big_scroll=30, key_func=None, *args):
  """Figure with horizontal scrollbar and play capabilities

  This script is mainly inspired by the elegant work of João Filipe Henriques
    https://www.mathworks.com/matlabcentral/fileexchange/29544-figure-to-play-and-analyze-videos-with-custom-plots-on-top?focused=5172704&tab=function

  :param num_frames: an integer, number of frames in a sequence
  :param redraw_func: callable with signature redraw_func(f, axes)
                      used to draw a new frame at position f using axes, which is a instance of Axes class in matplotlib
  :param play_fps: an integer, number of frames per second, used to control the play speed
  :param big_scroll: an integer, big scroll number used when pressed page down or page up keys.
  :param key_func: optional callable which signature key_func(key), used to provide custom key shortcuts.
  :param args: other optional arguments
  :return: None
  """
  # Check arguments
  check_int_scalar(num_frames, 'num_frames')
  check_callback(redraw_func, 'redraw_func')
  check_int_scalar(play_fps, 'play_fps')
  check_int_scalar(big_scroll, 'big_scroll')
  if key_func:
    check_callback(key_func, 'key_func')

  # Initialize figure
  fig_handle = plt.figure()

  # main drawing axes for video display
  axes_handle = plt.axes([0, 0.03, 1, 0.97])
  axes_handle.set_axis_off()

  # Build scrollbar
  scroll_axes_handle = plt.axes([0, 0, 1, 0.03], facecolor='lightgoldenrodyellow')
  scroll_handle = Slider(scroll_axes_handle, '', 0.0, num_frames - 1, valinit=0.0)

  def draw_new(_):
    # Set to the right axes and call the custom redraw function
    plt.sca(axes_handle)
    redraw_func(int(scroll_handle.val), axes_handle)
    fig_handle.canvas.draw_idle()

  def scroll(new_f):
    new_f = min(max(new_f, 0), num_frames - 1)  # clip in the range of [0, num_frames - 1]
    cur_f = scroll_handle.val

    # Stop player at the end of the sequence
    if new_f == (num_frames - 1):
      play.running = False

    if cur_f != new_f:
      # move scroll bar to new position
      scroll_handle.set_val(new_f)

    return axes_handle

  def play(period):
    play.running ^= True  # Toggle state
    if play.running:
      frame_idxs = range(int(scroll_handle.val), num_frames)
      play.anim = FuncAnimation(fig_handle, scroll, frame_idxs,
                                interval=1000 * period, repeat=False)
      plt.draw()
    else:
      play.anim.event_source.stop()

  # Set initial player state
  play.running = False

  def key_press(event):
    key = event.key
    f = scroll_handle.val
    if key == 'left':
      scroll(f - 1)
    elif key == 'right':
      scroll(f + 1)
    elif key == 'pageup':
      scroll(f - big_scroll)
    elif key == 'pagedown':
      scroll(f + big_scroll)
    elif key == 'home':
      scroll(0)
    elif key == 'end':
      scroll(num_frames - 1)
    elif key == 'enter':
      play(1 / play_fps)
    elif key == 'backspace':
      play(5 / play_fps)
    else:
      if key_func:
        key_func(key)

  # Register events
  scroll_handle.on_changed(draw_new)
  fig_handle.canvas.mpl_connect('key_press_event', key_press)

  # Draw initial frame
  redraw_func(0, axes_handle)

  # Start playing
  play(1 / play_fps)

  # plt.show() has to be put in the end of the function,
  # otherwise, the program simply won't work, weird...
  plt.show()

def check_int_scalar(a, name):
  assert isinstance(a, int), '{} must be a int scalar, instead of {}'.format(name, type(name))

def check_callback(a, name):
  # Check http://stackoverflow.com/questions/624926/how-to-detect-whether-a-python-variable-is-a-function
  # for more details about python function type detection.
  assert callable(a), '{} must be callable, instead of {}'.format(name, type(name))

if __name__ == '__main__':
  import numpy as np

  def redraw_fn(f, axes):
    amp = float(f) / 3000
    f0 = 3
    t = np.arange(0.0, 1.0, 0.001)
    s = amp * np.sin(2 * np.pi * f0 * t)
    if not redraw_fn.initialized:
      redraw_fn.l, = axes.plot(t, s, lw=2, color='red')
      redraw_fn.initialized = True
    else:
      redraw_fn.l.set_ydata(s)

  redraw_fn.initialized = False

  videofig(100, redraw_fn)
<commit_msg>add multiple new features<commit_after>#! /usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright © 2017-2020 bily     Huazhong University of Science and Technology
#
# Distributed under terms of the MIT license.

"""Figure with horizontal scrollbar and play capabilities

For latest version, go to https://github.com/bilylee/videofig

Basic usage
-----------
Creates a figure with a horizontal scrollbar and shortcuts to scroll automatically.
The scroll range is 0 to NUM_FRAMES - 1. The function REDRAW_FUN(F, AXES) is called to
redraw at scroll position F (for example, REDRAW_FUNC can show the frame F of a video)
using AXES for drawing. F is an integer, AXES is a instance of [Axes class](https://matplotlib.org/api/axes_api.html)

This can be used not only to play and analyze standard videos, but it also lets you place
any custom Matplotlib plots and graphics on top.

The keyboard shortcuts are:
  Enter(Return) -- play/pause video (25 frames-per-second default).
  Backspace -- play/pause video 5 times slower.
  Right/left arrow keys -- advance/go back one frame.
  Page down/page up -- advance/go back 30 frames.
  Home/end -- go to first/last frame of video.

Advanced usage
--------------
videofig(NUM_FRAMES, REDRAW_FUNC, FPS, BIG_SCROLL)
Also specifies the speed of the play function (frames-per-second) and
the frame step of page up/page down (or empty for defaults).

videofig(NUM_FRAMES, REDRAW_FUNC, FPS, BIG_SCROLL, KEY_FUNC)
Also calls KEY_FUNC(KEY) with any keys that weren't processed, so you
can add more shortcut keys (or empty for none).

Example 1: Plot a dynamic sine wave
---------
  import numpy as np

  def redraw_fn(f, axes):
    amp = float(f) / 3000
    f0 = 3
    t = np.arange(0.0, 1.0, 0.001)
    s = amp * np.sin(2 * np.pi * f0 * t)
    if not redraw_fn.initialized:
      redraw_fn.l, = axes.plot(t, s, lw=2, color='red')
      redraw_fn.initialized = True
    else:
      redraw_fn.l.set_ydata(s)

  redraw_fn.initialized = False

  videofig(100, redraw_fn)

Example 2: Show images in a custom directory
---------
  import os
  import glob
  from scipy.misc import imread

  img_dir = 'YOUR-IMAGE-DIRECTORY'
  img_files = glob.glob(os.path.join(video_dir, '*.jpg'))

  def redraw_fn(f, axes):
    img_file = img_files[f]
    img = imread(img_file)
    if not redraw_fn.initialized:
      redraw_fn.im = axes.imshow(img, animated=True)
      redraw_fn.initialized = True
    else:
      redraw_fn.im.set_array(img)
  redraw_fn.initialized = False

  videofig(len(img_files), redraw_fn, play_fps=30)

Example 3: Show images together with object bounding boxes
----------
  import os
  import glob
  from scipy.misc import imread
  from matplotlib.pyplot import Rectangle

  video_dir = 'YOUR-VIDEO-DIRECTORY'

  img_files = glob.glob(os.path.join(video_dir, '*.jpg'))
  box_files = glob.glob(os.path.join(video_dir, '*.txt'))

  def redraw_fn(f, axes):
    img = imread(img_files[f])
    box = bbread(box_files[f])  # Define your own bounding box reading utility
    x, y, w, h = box
    if not redraw_fn.initialized:
      im = axes.imshow(img, animated=True)
      bb = Rectangle((x, y), w, h,
                     fill=False,  # remove background
                     edgecolor="red")
      axes.add_patch(bb)
      redraw_fn.im = im
      redraw_fn.bb = bb
      redraw_fn.initialized = True
    else:
      redraw_fn.im.set_array(img)
      redraw_fn.bb.set_xy((x, y))
      redraw_fn.bb.set_width(w)
      redraw_fn.bb.set_height(h)
  redraw_fn.initialized = False

  videofig(len(img_files), redraw_fn, play_fps=30)
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os

import matplotlib
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Slider
import matplotlib.gridspec as gridspec
import matplotlib.animation as animation

__all__ = ['videofig']

def videofig(num_frames, redraw_func, play_fps=25, big_scroll=30, key_func=None,
             grid_specs=None, layout_specs=None, figname=None, save_dir=None, *args):
  """Figure with horizontal scrollbar and play capabilities

  This script is mainly inspired by the elegant work of João Filipe Henriques
    https://www.mathworks.com/matlabcentral/fileexchange/29544-figure-to-play-and-analyze-videos-with-custom-plots-on-top?focused=5172704&tab=function

  :param num_frames: an integer, number of frames in a sequence
  :param redraw_func: callable with signature redraw_func(f, axes)
                      used to draw a new frame at position f using axes, which is a instance of Axes class in matplotlib
  :param play_fps: an integer, number of frames per second, used to control the play speed
  :param big_scroll: an integer, big scroll number used when pressed page down or page up keys.
  :param key_func: optional callable which signature key_func(key), used to provide custom key shortcuts.
  :param grid_specs: optional dictionary, used to specify the gridspec of the main drawing pane.
                     For example, grid_specs = {'nrows': 2, 'ncols': 2} will create a gridspec with 2 rows and 2 cols.
  :param layout_specs: optional list, used to specify the layout of the gridspec of the main drawing pane.
                     For example, layout_specs = ['[:, 0]', '[:, 1]'] means:
                        gs = ... Some code to create the main drawing pane gridspec ...
                        ax1 = plt.subplot(gs[:, 0])
                        ax2 = plt.subplot(gs[:, 1])
  :param save_dir: a string, used to specify the directory to which figures will be saved.
  :param args: other optional arguments
  :return: None
  """
  # Check arguments
  check_int_scalar(num_frames, 'num_frames')
  check_callback(redraw_func, 'redraw_func')
  check_int_scalar(play_fps, 'play_fps')
  check_int_scalar(big_scroll, 'big_scroll')
  if key_func:
    check_callback(key_func, 'key_func')

  # Initialize figure
  if not figname:
    figname = 'VideoPlayer'
  fig_handle = plt.figure(num=figname)

  # We do not want to show the slider when saving figures
  slider_ratio = 3 if save_dir is None else 1e-3

  # We use GridSpec to support embedding multiple plots in the main drawing pane.
  # A nested grid demo can be found at https://matplotlib.org/users/plotting/examples/demo_gridspec06.py
  # Construct outer grid, which contains the main drawing pane and slider ball
  outer_grid = gridspec.GridSpec(2, 1,
                                 left=0, bottom=0, right=1, top=1,
                                 height_ratios=[97, slider_ratio], wspace=0.0, hspace=0.0)

  # Construct inner grid in main drawing pane
  if grid_specs is None:
    grid_specs = {'nrows': 1, 'ncols': 1}  # We will have only one axes by default

  inner_grid = gridspec.GridSpecFromSubplotSpec(subplot_spec=outer_grid[0], **grid_specs)
  if layout_specs:
    # eval() can't work properly in list comprehension which is inside a function.
    # Refer to: http://bugs.python.org/issue5242
    # Maybe I should find another way to implement layout_specs without using eval()...
    axes_handle = []
    for spec in layout_specs:
      axes_handle.append(plt.Subplot(fig_handle, eval('inner_grid' + spec)))
  else:
    num_inner_plots = grid_specs['nrows'] * grid_specs['ncols']
    axes_handle = [plt.Subplot(fig_handle, inner_grid[i]) for i in range(num_inner_plots)]

  for ax in axes_handle: fig_handle.add_subplot(ax)

  if len(axes_handle) == 1:
    axes_handle = axes_handle[0]
    axes_handle.set_axis_off()

  # Build scrollbar
  scroll_axes_handle = plt.Subplot(fig_handle, outer_grid[1])
  scroll_axes_handle.set_facecolor('lightgoldenrodyellow')
  fig_handle.add_subplot(scroll_axes_handle)
  scroll_handle = Slider(scroll_axes_handle, '', 0.0, num_frames - 1, valinit=0.0)

  def draw_new(_):
    redraw_func(int(scroll_handle.val), axes_handle)
    fig_handle.canvas.draw_idle()

  def scroll(new_f):
    new_f = min(max(new_f, 0), num_frames - 1)  # clip in the range of [0, num_frames - 1]
    cur_f = scroll_handle.val

    # Stop player at the end of the sequence
    if new_f == (num_frames - 1):
      play.running = False

    if cur_f != new_f:
      # move scroll bar to new position
      scroll_handle.set_val(new_f)

    return axes_handle

  def play(period):
    play.running ^= True  # Toggle state
    if play.running:
      frame_idxs = range(int(scroll_handle.val), num_frames)
      play.anim = FuncAnimation(fig_handle, scroll, frame_idxs,
                                interval=1000 * period, repeat=False, blit=False)
      plt.draw()
    else:
      play.anim.event_source.stop()

  # Set initial player state
  play.running = False

  def key_press(event):
    key = event.key
    f = scroll_handle.val
    if key == 'left':
      scroll(f - 1)
    elif key == 'right':
      scroll(f + 1)
    elif key in ['pageup', 'up']:
      scroll(f - big_scroll)
    elif key in ['pagedown', 'down']:
      scroll(f + big_scroll)
    elif key == 'home':
      scroll(0)
    elif key == 'end':
      scroll(num_frames - 1)
    elif key == 'enter':
      play(1 / play_fps)
    elif key == 'backspace':
      play(5 / play_fps)
    else:
      if key_func:
        key_func(key)

  # Register events
  scroll_handle.on_changed(draw_new)
  fig_handle.canvas.mpl_connect('key_press_event', key_press)

  if save_dir is None:
    # Draw initial frame
    redraw_func(0, axes_handle)

    # Start playing
    play(1 / play_fps)

    # plt.show() has to be put in the end of the function,
    # otherwise, the program simply won't work, weird...
    plt.show()
  else:
    # Due to the issue of https://github.com/matplotlib/matplotlib/issues/7614
    # we need to save the first image twice. Otherwise, the first saved image will be distorted.
    for index in [0] + list(range(0, num_frames)):
      redraw_func(index, axes_handle)
      fig_handle.canvas.draw_idle()
      if not isinstance(axes_handle, list):
        if matplotlib.get_backend() == 'nbAgg':
          print('Warning: remove "%matplotlib notebook", otherwise saved images will be distorted!')
        extent = axes_handle.get_window_extent().transformed(fig_handle.dpi_scale_trans.inverted())
      else:
        extent = fig_handle.bbox
      fig_handle.savefig(os.path.join(save_dir, '{:04d}.jpg'.format(index)), bbox_inches=extent)

def check_int_scalar(a, name):
  assert isinstance(a, int), '{} must be a int scalar, instead of {}'.format(name, type(name))

def check_callback(a, name):
  # Check http://stackoverflow.com/questions/624926/how-to-detect-whether-a-python-variable-is-a-function
  # for more details about python function type detection.
  assert callable(a), '{} must be callable, instead of {}'.format(name, type(name))

if __name__ == '__main__':
  import argparse
  parser = argparse.ArgumentParser()
  parser.add_argument("-i", "--imgdir", type=str, default=None, help="directory containing JPEG images")
  args = parser.parse_args()

  if args.imgdir:
    import os
    import glob
    from scipy.misc import imread
    img_files = sorted(glob.glob(os.path.join(args.imgdir, '*.jpg')))
    def redraw_fn(f, axes):
      img_file = img_files[f]
      img = imread(img_file)
      if not redraw_fn.initialized:
        redraw_fn.im = axes.imshow(img, animated=True)
        redraw_fn.initialized = True
      else:
        redraw_fn.im.set_array(img)
    redraw_fn.initialized = False
    videofig(len(img_files), redraw_fn, play_fps=30)

  else:
    import numpy as np
    def redraw_fn(f, axes):
      amp = float(f) / 3000
      f0 = 3
      t = np.arange(0.0, 1.0, 0.001)
      s = amp * np.sin(2 * np.pi * f0 * t)
      if not redraw_fn.initialized:
        redraw_fn.l, = axes.plot(t, s, lw=2, color='red')
        redraw_fn.initialized = True
      else:
        redraw_fn.l.set_ydata(s)

    redraw_fn.initialized = False
    videofig(100, redraw_fn)
<|endoftext|>


<commit_before>#!/usr/bin/env python
# This is a wrapper around the 'post-review' tool provided by
# Review Board. This is currently used by Apache Mesos development.
#
# What does this do?
# It provides the ability to send a review for each commit on the
# current branch.
#
# Why is that useful?
# No one likes a 5000 line review request. Using this tool forces one
# to create logical commits which can be reviewed independently.
#
# How do I use it?
# First install 'post-review' from Review Board.
# http://www.reviewboard.org/docs/manual/dev/users/tools/post-review/
#
# $ cd /path/to/mesos
# $ [ do some work on your branch off of master, make commit(s) ]
# $ ./support/post-reviews.py --server=https://reviews.apache.org \
#   --tracking-branch=origin/master --target-groups=mesos --open
#
# NOTE: post-reviews is currently specific to Mesos development,
# but can easily be adapted for other projects.

import atexit
import os
import sys

from subprocess import *

def readline(prompt):
    try:
        return raw_input(prompt)
    except KeyboardInterrupt:
        sys.exit(1)

def execute(command, ignore_errors=False):
    process = Popen(command,
                    stdin=PIPE,
                    stdout=PIPE,
                    stderr=STDOUT,
                    shell=False)
    data = process.stdout.read()
    status = process.wait()
    if status != 0 and not ignore_errors:
        cmdline = ' '.join(command) if isinstance(command, list) else command
        print 'Failed to execute: \'' + cmdline + '\':'
        print data
        sys.exit(1)
    elif status != 0:
        return None
    return data

# TODO(benh): Make sure this is a git repository, apologize if not.

# Don't do anything if people have uncommitted changes.
diff_stat = execute(['git', 'diff', '--shortstat']).strip()

if diff_stat:
  print 'Please commit or stash any changes before using post-reviews!'
  sys.exit(1)

top_level_dir = execute(['git', 'rev-parse', '--show-toplevel']).strip()

repository = 'git://git.apache.org/incubator-mesos.git'

parent_branch = 'master'

branch_ref = execute(['git', 'symbolic-ref', 'HEAD']).strip()
branch = branch_ref.replace('refs/heads/', '', 1)

temporary_branch = '_post-reviews_' + branch

# Always delete the temporary branch.
atexit.register(lambda: execute(['git', 'branch', '-D', temporary_branch], True))

# Always put us back on the original branch.
atexit.register(lambda: execute(['git', 'checkout', branch]))

merge_base = execute(['git', 'merge-base', parent_branch, branch_ref]).strip()

print 'Running post-review across all of ...'

call(['git',
      '--no-pager',
      'log',
      '--pretty=format:%Cred%H%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset',
      merge_base + '..HEAD'])

log = execute(['git',
	       '--no-pager',
               'log',
               '--pretty=oneline',
               '--reverse',
               merge_base + '..HEAD']).strip()

shas = []

for line in log.split('\n'):
    sha = line.split()[0]
    shas.append(sha)

previous = 'master'
for i in range(len(shas)):
    sha = shas[i]

    execute(['git', 'branch', '-D', temporary_branch], True)

    message = execute(['git',
	               '--no-pager',
                       'log',
                       '--pretty=format:%B',
                       previous + '..' + sha])

    review_request_id = None

    if message.find('Review: ') != -1:
        url = message[(message.index('Review: ') + len('Review: ')):].strip()
        # TODO(benh): Handle bad (or not Review Board) URLs.
        review_request_id = os.path.basename(url.strip('/'))

    # Show the commit.
    if review_request_id is None:
        print '\nCreating diff of:\n'
        call(['git',
	      '--no-pager',
              'log',
              '--pretty=format:%Cred%H%Creset -%C(yellow)%d%Creset %s',
              previous + '..' + sha])
    else:
        print '\nUpdating diff of:\n'
        call(['git',
	      '--no-pager',
              'log',
              '--pretty=format:%Cred%H%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset',
              previous + '..' + sha])

    # Show the "parent" commit(s).
    print '\n... with parent diff created from:\n'
    call(['git',
	  '--no-pager',
          'log',
          '--pretty=format:%Cred%H%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset',
          parent_branch + '..' + previous])

    try:
        raw_input('\nPress enter to continue or \'Ctrl-C\' to skip.\n')
    except KeyboardInterrupt:
        i = i + 1
        previous = sha
        continue

    revision_range = previous + ':' + sha

    if review_request_id is None:
        output = execute(['post-review',
                          '--repository-url=' + repository,
                          '--tracking-branch=incubator-mesos/master',
                          '--revision-range=' + revision_range] + sys.argv[1:]).strip()
    else:
        output = execute(['post-review',
                          '--review-request-id=' + review_request_id,
                          '--repository-url=' + repository,
                          '--tracking-branch=incubator-mesos/master',
                          '--revision-range=' + revision_range] + sys.argv[1:]).strip()

    print output

    if review_request_id is not None:
        i = i + 1
        previous = sha
        continue

    lines = output.split('\n')
    url = lines[len(lines) - 1]
    url = url.strip('/')

    # Construct new commit message.
    message = message + '\n' + 'Review: ' + url + '\n'

    execute(['git', 'checkout', '-b', temporary_branch])
    execute(['git', 'reset', '--hard', sha])
    execute(['git', 'commit', '--amend', '-m', message])

    # Now rebase all remaining shas on top of this amended commit.
    j = i + 1
    old_sha = execute(['cat', os.path.join(top_level_dir, '.git/refs/heads', temporary_branch)]).strip()
    previous = old_sha
    while j < len(shas):
        execute(['git', 'checkout', shas[j]])
        execute(['git', 'rebase', temporary_branch])
        # Get the sha for our detached HEAD.
        new_sha = execute(['git', '--no-pager', 'log', '--pretty=format:%H', '-n', '1', 'HEAD']).strip()
        execute(['git',
                 'update-ref',
                 'refs/heads/' + temporary_branch,
                 new_sha,
                 old_sha])
        old_sha = new_sha
        shas[j] = new_sha
        j = j + 1

    # Okay, now update the actual branch to our temporary branch.
    new_sha = old_sha
    old_sha = execute(['cat', os.path.join(top_level_dir, '.git/refs/heads', branch)]).strip()
    execute(['git', 'update-ref', 'refs/heads/' + branch, new_sha, old_sha])

    i = i + 1
<commit_msg>More sophisticated hack for post-reviews.py.<commit_after>#!/usr/bin/env python
# This is a wrapper around the 'post-review' tool provided by
# Review Board. This is currently used by Apache Mesos development.
#
# What does this do?
# It provides the ability to send a review for each commit on the
# current branch.
#
# Why is that useful?
# No one likes a 5000 line review request. Using this tool forces one
# to create logical commits which can be reviewed independently.
#
# How do I use it?
# First install 'post-review' from Review Board.
# http://www.reviewboard.org/docs/manual/dev/users/tools/post-review/
#
# $ cd /path/to/mesos
# $ [ do some work on your branch off of master, make commit(s) ]
# $ ./support/post-reviews.py --server=https://reviews.apache.org \
#   --tracking-branch=origin/master --target-groups=mesos --open
#
# NOTE: post-reviews is currently specific to Mesos development,
# but can easily be adapted for other projects.

import atexit
import os
import sys

from subprocess import *

def readline(prompt):
    try:
        return raw_input(prompt)
    except KeyboardInterrupt:
        sys.exit(1)

def execute(command, ignore_errors=False):
    process = Popen(command,
                    stdin=PIPE,
                    stdout=PIPE,
                    stderr=STDOUT,
                    shell=False)
    data = process.stdout.read()
    status = process.wait()
    if status != 0 and not ignore_errors:
        cmdline = ' '.join(command) if isinstance(command, list) else command
        print 'Failed to execute: \'' + cmdline + '\':'
        print data
        sys.exit(1)
    elif status != 0:
        return None
    return data

# TODO(benh): Make sure this is a git repository, apologize if not.

# Don't do anything if people have uncommitted changes.
diff_stat = execute(['git', 'diff', '--shortstat']).strip()

if diff_stat:
  print 'Please commit or stash any changes before using post-reviews!'
  sys.exit(1)

top_level_dir = execute(['git', 'rev-parse', '--show-toplevel']).strip()

repository = 'git://git.apache.org/incubator-mesos.git'

parent_branch = 'master'

branch_ref = execute(['git', 'symbolic-ref', 'HEAD']).strip()
branch = branch_ref.replace('refs/heads/', '', 1)

temporary_branch = '_post-reviews_' + branch

# Always delete the temporary branch.
atexit.register(lambda: execute(['git', 'branch', '-D', temporary_branch], True))

# Always put us back on the original branch.
atexit.register(lambda: execute(['git', 'checkout', branch]))

merge_base = execute(['git', 'merge-base', parent_branch, branch_ref]).strip()

print 'Running post-review across all of ...'

call(['git',
      '--no-pager',
      'log',
      '--pretty=format:%Cred%H%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset',
      merge_base + '..HEAD'])

log = execute(['git',
	       '--no-pager',
               'log',
               '--pretty=oneline',
               '--reverse',
               merge_base + '..HEAD']).strip()

shas = []

for line in log.split('\n'):
    sha = line.split()[0]
    shas.append(sha)

previous = 'master'
for i in range(len(shas)):
    sha = shas[i]

    execute(['git', 'branch', '-D', temporary_branch], True)

    message = execute(['git',
	               '--no-pager',
                       'log',
                       '--pretty=format:%B',
                       previous + '..' + sha])

    review_request_id = None

    if message.find('Review: ') != -1:
        url = message[(message.index('Review: ') + len('Review: ')):].strip()
        # TODO(benh): Handle bad (or not Review Board) URLs.
        review_request_id = os.path.basename(url.strip('/'))

    # Show the commit.
    if review_request_id is None:
        print '\nCreating diff of:\n'
        call(['git',
	      '--no-pager',
              'log',
              '--pretty=format:%Cred%H%Creset -%C(yellow)%d%Creset %s',
              previous + '..' + sha])
    else:
        print '\nUpdating diff of:\n'
        call(['git',
	      '--no-pager',
              'log',
              '--pretty=format:%Cred%H%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset',
              previous + '..' + sha])

    # Show the "parent" commit(s).
    print '\n... with parent diff created from:\n'
    call(['git',
	  '--no-pager',
          'log',
          '--pretty=format:%Cred%H%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset',
          parent_branch + '..' + previous])

    try:
        raw_input('\nPress enter to continue or \'Ctrl-C\' to skip.\n')
    except KeyboardInterrupt:
        i = i + 1
        previous = sha
        continue

    revision_range = previous + ':' + sha

    #################################################################
    # THIS IS A GIANT HACK FOR GETTING POST-REVIEWS TO WORK UNTIL
    # APACHE INFRASTRUCTURE MANAGES TO FIX REVIEW BOARD!

    # Create a temporary branch from incubator-mesos/master.
    hack_temporary_branch = '_post-reviews_' + branch + '_' + str(i)
    execute(['git', 'checkout', '--track', '-b', hack_temporary_branch, 'incubator-mesos/master'])
    atexit.register(lambda: execute(['git', 'branch', '-D', hack_temporary_branch], True))

    # Then 'cherry-pick' from 'git merge-base incubator-mesos/master
    # master' to 'sha'. This assumes your branch's parent is 'master'.
    hack_merge_base = execute(['git', 'merge-base', hack_temporary_branch, 'master']).strip()
    execute(['git', 'cherry-pick', hack_merge_base + '..' + sha])

    # Then post-review with '--tracking-branch=incubator-mesos/master'
    # and '--revision-range=HEAD~1:HEAD'
    if review_request_id is None:
        output = execute(['post-review',
                          '--repository-url=' + repository,
                          '--tracking-branch=incubator-mesos/master',
                          '--revision-range=HEAD~1:HEAD'] + sys.argv[1:]).strip()
    else:
        output = execute(['post-review',
                          '--review-request-id=' + review_request_id,
                          '--repository-url=' + repository,
                          '--tracking-branch=incubator-mesos/master',
                          '--revision-range=HEAD~1:HEAD'] + sys.argv[1:]).strip()
    execute(['git', 'checkout', branch])
    execute(['git', 'branch', '-D', hack_temporary_branch])
    #################################################################

    print output

    if review_request_id is not None:
        i = i + 1
        previous = sha
        continue

    lines = output.split('\n')
    url = lines[len(lines) - 1]
    url = url.strip('/')

    # Construct new commit message.
    message = message + '\n' + 'Review: ' + url + '\n'

    execute(['git', 'checkout', '-b', temporary_branch])
    execute(['git', 'reset', '--hard', sha])
    execute(['git', 'commit', '--amend', '-m', message])

    # Now rebase all remaining shas on top of this amended commit.
    j = i + 1
    old_sha = execute(['cat', os.path.join(top_level_dir, '.git/refs/heads', temporary_branch)]).strip()
    previous = old_sha
    while j < len(shas):
        execute(['git', 'checkout', shas[j]])
        execute(['git', 'rebase', temporary_branch])
        # Get the sha for our detached HEAD.
        new_sha = execute(['git', '--no-pager', 'log', '--pretty=format:%H', '-n', '1', 'HEAD']).strip()
        execute(['git',
                 'update-ref',
                 'refs/heads/' + temporary_branch,
                 new_sha,
                 old_sha])
        old_sha = new_sha
        shas[j] = new_sha
        j = j + 1

    # Okay, now update the actual branch to our temporary branch.
    new_sha = old_sha
    old_sha = execute(['cat', os.path.join(top_level_dir, '.git/refs/heads', branch)]).strip()
    execute(['git', 'update-ref', 'refs/heads/' + branch, new_sha, old_sha])

    i = i + 1
<|endoftext|>