<commit_before>from argparse import ArgumentParser
from os import environ

import numpy as np
import xarray as xr

from parcels import Field
from parcels.plotting import cartopy_colorbar
from parcels.plotting import create_parcelsfig_axis
from parcels.plotting import plotfield
try:
    import matplotlib.animation as animation
    from matplotlib import rc
except:
    anim = None

def plotTrajectoriesFile(filename, mode='2d', tracerfile=None, tracerfield='P',
                         tracerlon='x', tracerlat='y', recordedvar=None, movie_forward=True,
                         bins=20, show_plt=True):
    """Quick and simple plotting of Parcels trajectories

    :param filename: Name of Parcels-generated NetCDF file with particle positions
    :param mode: Type of plot to show. Supported are '2d', '3d', 'hist2d',
                'movie2d' and 'movie2d_notebook'. The latter two give animations,
                with 'movie2d_notebook' specifically designed for jupyter notebooks
    :param tracerfile: Name of NetCDF file to show as background
    :param tracerfield: Name of variable to show as background
    :param tracerlon: Name of longitude dimension of variable to show as background
    :param tracerlat: Name of latitude dimension of variable to show as background
    :param recordedvar: Name of variable used to color particles in scatter-plot.
                Only works in 'movie2d' or 'movie2d_notebook' mode.
    :param movie_forward: Boolean whether to show movie in forward or backward mode (default True)
    :param bins: Number of bins to use in `hist2d` mode. See also https://matplotlib.org/api/_as_gen/matplotlib.pyplot.hist2d.html
    :param show_plt: Boolean whether plot should directly be show (for py.test)
    """

    environ["HDF5_USE_FILE_LOCKING"] = "FALSE"
    try:
        pfile = xr.open_dataset(str(filename), decode_cf=True)
    except:
        pfile = xr.open_dataset(str(filename), decode_cf=False)
    lon = np.ma.filled(pfile.variables['lon'], np.nan)
    lat = np.ma.filled(pfile.variables['lat'], np.nan)
    time = np.ma.filled(pfile.variables['time'], np.nan)
    z = np.ma.filled(pfile.variables['z'], np.nan)
    mesh = pfile.attrs['parcels_mesh'] if 'parcels_mesh' in pfile.attrs else 'spherical'

    if(recordedvar is not None):
        record = np.ma.filled(pfile.variables[recordedvar], np.nan)
    pfile.close()

    if tracerfile is not None and mode != 'hist2d':
        tracerfld = Field.from_netcdf(tracerfile, tracerfield, {'lon': tracerlon, 'lat': tracerlat})
        plt, fig, ax, cartopy = plotfield(tracerfld)
        if plt is None:
            return  # creating axes was not possible
        titlestr = ' and ' + tracerfield
    else:
        spherical = False if mode == '3d' or mesh == 'flat' else True
        plt, fig, ax, cartopy = create_parcelsfig_axis(spherical=spherical)
        if plt is None:
            return  # creating axes was not possible
        titlestr = ''

    if cartopy:
        for p in range(lon.shape[1]):
            lon[:, p] = [ln if ln < 180 else ln - 360 for ln in lon[:, p]]

    if mode == '3d':
        from mpl_toolkits.mplot3d import Axes3D  # noqa
        plt.clf()  # clear the figure
        ax = fig.gca(projection='3d')
        for p in range(len(lon)):
            ax.plot(lon[p, :], lat[p, :], z[p, :], '.-')
        ax.set_xlabel('Longitude')
        ax.set_ylabel('Latitude')
        ax.set_zlabel('Depth')
        ax.set_title('Particle trajectories')
    elif mode == '2d':
        if cartopy:
            ax.plot(np.transpose(lon), np.transpose(lat), '.-', transform=cartopy.crs.Geodetic())
        else:
            ax.plot(np.transpose(lon), np.transpose(lat), '.-')
        ax.set_title('Particle trajectories' + titlestr)
    elif mode == 'hist2d':
        _, _, _, cs = plt.hist2d(lon[~np.isnan(lon)], lat[~np.isnan(lat)], bins=bins)
        cartopy_colorbar(cs, plt, fig, ax)
        ax.set_title('Particle histogram')
    elif mode in ('movie2d', 'movie2d_notebook'):
        ax.set_xlim(np.nanmin(lon), np.nanmax(lon))
        ax.set_ylim(np.nanmin(lat), np.nanmax(lat))
        plottimes = np.unique(time)
        if not movie_forward:
            plottimes = np.flip(plottimes, 0)
        if isinstance(plottimes[0], (np.datetime64, np.timedelta64)):
            plottimes = plottimes[~np.isnat(plottimes)]
        else:
            try:
                plottimes = plottimes[~np.isnan(plottimes)]
            except:
                pass
        b = time == plottimes[0]
        if cartopy:
            scat = ax.scatter(lon[b], lat[b], s=20, color='k', transform=cartopy.crs.Geodetic())
        else:
            scat = ax.scatter(lon[b], lat[b], s=20, color='k')
        ttl = ax.set_title('Particles' + titlestr + ' at time ' + str(plottimes[0]))
        frames = np.arange(0, len(plottimes))

        def animate(t):
            b = time == plottimes[t]
            scat.set_offsets(np.vstack((lon[b], lat[b])).transpose())
            ttl.set_text('Particle' + titlestr + ' at time ' + str(plottimes[t]))
            if recordedvar is not None:
                scat.set_array(record[b])
            return scat,

        rc('animation', html='html5')
        anim = animation.FuncAnimation(fig, animate, frames=frames, interval=100, blit=False)
    else:
        raise RuntimeError('mode %s not known' % mode)

    if mode == 'movie2d_notebook':
        plt.close()
        return anim
    else:
        if show_plt:
            plt.show()
        return plt

if __name__ == "__main__":
    p = ArgumentParser(description="""Quick and simple plotting of Parcels trajectories""")
    p.add_argument('mode', choices=('2d', '3d', 'hist2d', 'movie2d', 'movie2d_notebook'), nargs='?',
                   default='movie2d', help='Type of display')
    p.add_argument('-p', '--particlefile', type=str, default='MyParticle.nc',
                   help='Name of particle file')
    p.add_argument('-f', '--tracerfile', type=str, default=None,
                   help='Name of tracer file to display underneath particle trajectories')
    p.add_argument('-flon', '--tracerfilelon', type=str, default='x',
                   help='Name of longitude dimension in tracer file')
    p.add_argument('-flat', '--tracerfilelat', type=str, default='y',
                   help='Name of latitude dimension in tracer file')
    p.add_argument('-ffld', '--tracerfilefield', type=str, default='P',
                   help='Name of field in tracer file')
    p.add_argument('-r', '--recordedvar', type=str, default=None,
                   help='Name of a variable recorded along trajectory')
    p.add_argument('-bins', type=int, default=20,
                   help='Number of bins for mode=hist2d')
    args = p.parse_args()

    plotTrajectoriesFile(args.particlefile, mode=args.mode, tracerfile=args.tracerfile,
                         tracerfield=args.tracerfilefield, tracerlon=args.tracerfilelon,
                         tracerlat=args.tracerfilelat, recordedvar=args.recordedvar,
                         bins=args.bins, show_plt=True)
<commit_msg>Improving representation of timedelta64 times in animations with plotTrajectoryFile<commit_after>from argparse import ArgumentParser
from os import environ

import numpy as np
import xarray as xr

from parcels import Field
from parcels.plotting import cartopy_colorbar
from parcels.plotting import create_parcelsfig_axis
from parcels.plotting import plotfield
try:
    import matplotlib.animation as animation
    from matplotlib import rc
except:
    anim = None

def plotTrajectoriesFile(filename, mode='2d', tracerfile=None, tracerfield='P',
                         tracerlon='x', tracerlat='y', recordedvar=None, movie_forward=True,
                         bins=20, show_plt=True):
    """Quick and simple plotting of Parcels trajectories

    :param filename: Name of Parcels-generated NetCDF file with particle positions
    :param mode: Type of plot to show. Supported are '2d', '3d', 'hist2d',
                'movie2d' and 'movie2d_notebook'. The latter two give animations,
                with 'movie2d_notebook' specifically designed for jupyter notebooks
    :param tracerfile: Name of NetCDF file to show as background
    :param tracerfield: Name of variable to show as background
    :param tracerlon: Name of longitude dimension of variable to show as background
    :param tracerlat: Name of latitude dimension of variable to show as background
    :param recordedvar: Name of variable used to color particles in scatter-plot.
                Only works in 'movie2d' or 'movie2d_notebook' mode.
    :param movie_forward: Boolean whether to show movie in forward or backward mode (default True)
    :param bins: Number of bins to use in `hist2d` mode. See also https://matplotlib.org/api/_as_gen/matplotlib.pyplot.hist2d.html
    :param show_plt: Boolean whether plot should directly be show (for py.test)
    """

    environ["HDF5_USE_FILE_LOCKING"] = "FALSE"
    try:
        pfile = xr.open_dataset(str(filename), decode_cf=True)
    except:
        pfile = xr.open_dataset(str(filename), decode_cf=False)
    lon = np.ma.filled(pfile.variables['lon'], np.nan)
    lat = np.ma.filled(pfile.variables['lat'], np.nan)
    time = np.ma.filled(pfile.variables['time'], np.nan)
    z = np.ma.filled(pfile.variables['z'], np.nan)
    mesh = pfile.attrs['parcels_mesh'] if 'parcels_mesh' in pfile.attrs else 'spherical'

    if(recordedvar is not None):
        record = np.ma.filled(pfile.variables[recordedvar], np.nan)
    pfile.close()

    if tracerfile is not None and mode != 'hist2d':
        tracerfld = Field.from_netcdf(tracerfile, tracerfield, {'lon': tracerlon, 'lat': tracerlat})
        plt, fig, ax, cartopy = plotfield(tracerfld)
        if plt is None:
            return  # creating axes was not possible
        titlestr = ' and ' + tracerfield
    else:
        spherical = False if mode == '3d' or mesh == 'flat' else True
        plt, fig, ax, cartopy = create_parcelsfig_axis(spherical=spherical)
        if plt is None:
            return  # creating axes was not possible
        titlestr = ''

    if cartopy:
        for p in range(lon.shape[1]):
            lon[:, p] = [ln if ln < 180 else ln - 360 for ln in lon[:, p]]

    if mode == '3d':
        from mpl_toolkits.mplot3d import Axes3D  # noqa
        plt.clf()  # clear the figure
        ax = fig.gca(projection='3d')
        for p in range(len(lon)):
            ax.plot(lon[p, :], lat[p, :], z[p, :], '.-')
        ax.set_xlabel('Longitude')
        ax.set_ylabel('Latitude')
        ax.set_zlabel('Depth')
        ax.set_title('Particle trajectories')
    elif mode == '2d':
        if cartopy:
            ax.plot(np.transpose(lon), np.transpose(lat), '.-', transform=cartopy.crs.Geodetic())
        else:
            ax.plot(np.transpose(lon), np.transpose(lat), '.-')
        ax.set_title('Particle trajectories' + titlestr)
    elif mode == 'hist2d':
        _, _, _, cs = plt.hist2d(lon[~np.isnan(lon)], lat[~np.isnan(lat)], bins=bins)
        cartopy_colorbar(cs, plt, fig, ax)
        ax.set_title('Particle histogram')
    elif mode in ('movie2d', 'movie2d_notebook'):
        ax.set_xlim(np.nanmin(lon), np.nanmax(lon))
        ax.set_ylim(np.nanmin(lat), np.nanmax(lat))
        plottimes = np.unique(time)
        if not movie_forward:
            plottimes = np.flip(plottimes, 0)
        if isinstance(plottimes[0], (np.datetime64, np.timedelta64)):
            plottimes = plottimes[~np.isnat(plottimes)]
        else:
            try:
                plottimes = plottimes[~np.isnan(plottimes)]
            except:
                pass
        b = time == plottimes[0]

        def timestr(plottimes, index):
            if isinstance(plottimes[index], np.timedelta64):
                if plottimes[-1] > np.timedelta64(1, 'h'):
                    return str(plottimes[index].astype('timedelta64[h]'))
                elif plottimes[-1] > np.timedelta64(1, 's'):
                    return str(plottimes[index].astype('timedelta64[s]'))
            else:
                return str(plottimes[index])

        if cartopy:
            scat = ax.scatter(lon[b], lat[b], s=20, color='k', transform=cartopy.crs.Geodetic())
        else:
            scat = ax.scatter(lon[b], lat[b], s=20, color='k')
        ttl = ax.set_title('Particles' + titlestr + ' at time ' + timestr(plottimes, 0))
        frames = np.arange(0, len(plottimes))

        def animate(t):
            b = time == plottimes[t]
            scat.set_offsets(np.vstack((lon[b], lat[b])).transpose())
            ttl.set_text('Particle' + titlestr + ' at time ' + timestr(plottimes, t))
            if recordedvar is not None:
                scat.set_array(record[b])
            return scat,

        rc('animation', html='html5')
        anim = animation.FuncAnimation(fig, animate, frames=frames, interval=100, blit=False)
    else:
        raise RuntimeError('mode %s not known' % mode)

    if mode == 'movie2d_notebook':
        plt.close()
        return anim
    else:
        if show_plt:
            plt.show()
        return plt

if __name__ == "__main__":
    p = ArgumentParser(description="""Quick and simple plotting of Parcels trajectories""")
    p.add_argument('mode', choices=('2d', '3d', 'hist2d', 'movie2d', 'movie2d_notebook'), nargs='?',
                   default='movie2d', help='Type of display')
    p.add_argument('-p', '--particlefile', type=str, default='MyParticle.nc',
                   help='Name of particle file')
    p.add_argument('-f', '--tracerfile', type=str, default=None,
                   help='Name of tracer file to display underneath particle trajectories')
    p.add_argument('-flon', '--tracerfilelon', type=str, default='x',
                   help='Name of longitude dimension in tracer file')
    p.add_argument('-flat', '--tracerfilelat', type=str, default='y',
                   help='Name of latitude dimension in tracer file')
    p.add_argument('-ffld', '--tracerfilefield', type=str, default='P',
                   help='Name of field in tracer file')
    p.add_argument('-r', '--recordedvar', type=str, default=None,
                   help='Name of a variable recorded along trajectory')
    p.add_argument('-bins', type=int, default=20,
                   help='Number of bins for mode=hist2d')
    args = p.parse_args()

    plotTrajectoriesFile(args.particlefile, mode=args.mode, tracerfile=args.tracerfile,
                         tracerfield=args.tracerfilefield, tracerlon=args.tracerfilelon,
                         tracerlat=args.tracerfilelat, recordedvar=args.recordedvar,
                         bins=args.bins, show_plt=True)
<|endoftext|>


<commit_before>import gettext
import os
from django.shortcuts import render

def interface_messages(langcode):
    '''
    Provides a dictionary to feed into the context, with an ISO 639-1 or -2 language code as input.
    '''

    LANGUAGE = langcode

    translation_directory = os.path.join(os.getcwd(), 'locale')
    translation = gettext.translation('interface', localedir=translation_directory, languages=[langcode])
    translation.install()

    output = {
                'brand': _('Wikipedia Requests'),
                'login_via_wikipedia': _('Login via Wikipedia'),
                'search': _('Search'),
                'add': _('Add'),
                'help': _('Help'),
                'about': _('About')
             }

    return output

def select_language(request):
    available = [
                    {'code': 'en', 'label': 'English'}
                ]

    context = {
                  'interface': interface_messages('en'),
                  'language': 'en',
                  'available': available
              }

    return render(request, 'requestoid/select_language.html', context = context)<commit_msg>Alternative to installing gettext<commit_after>import gettext
import os
from django.shortcuts import render

def interface_messages(langcode):
    '''
    Provides a dictionary to feed into the context, with an ISO 639-1 or -2 language code as input.
    '''

    LANGUAGE = langcode

    translation_directory = os.path.join(os.getcwd(), 'locale')
    translation = gettext.translation('interface', localedir=translation_directory, languages=[langcode])
    _ = translation.gettext

    output = {
                'brand': _('Wikipedia Requests'),
                'login_via_wikipedia': _('Login via Wikipedia'),
                'search': _('Search'),
                'add': _('Add'),
                'help': _('Help'),
                'about': _('About')
             }

    return output

def select_language(request):
    available = [
                    {'code': 'en', 'label': 'English'}
                ]

    context = {
                  'interface': interface_messages('en'),
                  'language': 'en',
                  'available': available
              }

    return render(request, 'requestoid/select_language.html', context = context)<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
    pygments.lexers.verification
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Lexer for Intermediate Verification Languages (IVLs).

    :copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

from pygments.lexer import RegexLexer, include, words
from pygments.token import Comment, Operator, Keyword, Name, Number, \
    Punctuation, Whitespace

__all__ = ['BoogieLexer', 'SilverLexer']

class BoogieLexer(RegexLexer):
    """
    For `Boogie <https://boogie.codeplex.com/>`_ source code.

    .. versionadded:: 2.1
    """
    name = 'Boogie'
    aliases = ['boogie']
    filenames = ['*.bpl']

    tokens = {
        'root': [
            # Whitespace and Comments
            (r'\n', Whitespace),
            (r'\s+', Whitespace),
            (r'//[/!](.*?)\n', Comment.Doc),
            (r'//(.*?)\n', Comment.Single),
            (r'/\*', Comment.Multiline, 'comment'),

            (words((
                'axiom', 'break', 'call', 'ensures', 'else', 'exists', 'function',
                'forall', 'if', 'invariant', 'modifies', 'procedure',  'requires',
                'then', 'var', 'while'),
             suffix=r'\b'), Keyword),
            (words(('const',), suffix=r'\b'), Keyword.Reserved),

            (words(('bool', 'int', 'ref'), suffix=r'\b'), Keyword.Type),
            include('numbers'),
            (r"(>=|<=|:=|!=|==>|&&|\|\||[+/\-=>*<\[\]])", Operator),
            (r"([{}():;,.])", Punctuation),
            # Identifier
            (r'[a-zA-Z_]\w*', Name),
        ],
        'comment': [
            (r'[^*/]+', Comment.Multiline),
            (r'/\*', Comment.Multiline, '#push'),
            (r'\*/', Comment.Multiline, '#pop'),
            (r'[*/]', Comment.Multiline),
        ],
        'numbers': [
            (r'[0-9]+', Number.Integer),
        ],
    }

class SilverLexer(RegexLexer):
    """
    For `Silver <https://bitbucket.org/viperproject/silver>`_ source code.

    .. versionadded:: 2.2
    """
    name = 'Silver'
    aliases = ['silver']
    filenames = ['*.sil', '*.vpr']

    tokens = {
        'root': [
            # Whitespace and Comments
            (r'\n', Whitespace),
            (r'\s+', Whitespace),
            (r'//[/!](.*?)\n', Comment.Doc),
            (r'//(.*?)\n', Comment.Single),
            (r'/\*', Comment.Multiline, 'comment'),

            (words((
                'result', 'true', 'false', 'null', 'method', 'function',
                'predicate', 'program', 'domain', 'axiom', 'var', 'returns',
                'field', 'define', 'requires', 'ensures', 'invariant',
                'fold', 'unfold', 'inhale', 'exhale', 'new', 'assert',
                'assume', 'goto', 'while', 'if', 'elseif', 'else', 'fresh',
                'constraining', 'Seq', 'Set', 'Multiset', 'union', 'intersection',
                'setminus', 'subset', 'unfolding', 'in', 'old', 'forall', 'exists',
                'acc', 'wildcard', 'write', 'none', 'epsilon', 'perm', 'unique',
                'apply', 'package', 'folding', 'label', 'forperm'),
             suffix=r'\b'), Keyword),
            (words(('Int', 'Perm', 'Bool', 'Ref'), suffix=r'\b'), Keyword.Type),
            include('numbers'),

            (r'[!%&*+=|?:<>/\-\[\]]', Operator),
            (r'([{}():;,.])', Punctuation),
            # Identifier
            (r'[\w$]\w*', Name),
        ],
        'comment': [
            (r'[^*/]+', Comment.Multiline),
            (r'/\*', Comment.Multiline, '#push'),
            (r'\*/', Comment.Multiline, '#pop'),
            (r'[*/]', Comment.Multiline),
        ],
        'numbers': [
            (r'[0-9]+', Number.Integer),
        ],
    }
<commit_msg>Fixed split lines errors; moved contract keywords to a separate category; added a rule for triggers.<commit_after># -*- coding: utf-8 -*-
"""
    pygments.lexers.verification
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    Lexer for Intermediate Verification Languages (IVLs).

    :copyright: Copyright 2006-2019 by the Pygments team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

from pygments.lexer import RegexLexer, include, words
from pygments.token import Comment, Operator, Keyword, Name, Number, \
    Punctuation, Text, Generic

__all__ = ['BoogieLexer', 'SilverLexer']

class BoogieLexer(RegexLexer):
    """
    For `Boogie <https://boogie.codeplex.com/>`_ source code.

    .. versionadded:: 2.1
    """
    name = 'Boogie'
    aliases = ['boogie']
    filenames = ['*.bpl']

    tokens = {
        'root': [
            # Whitespace and Comments
            (r'\n', Text),
            (r'\s+', Text),
            (r'\\\n', Text),  # line continuation
            (r'//[/!](.*?)\n', Comment.Doc),
            (r'//(.*?)\n', Comment.Single),
            (r'/\*', Comment.Multiline, 'comment'),

            (words((
                'axiom', 'break', 'call', 'ensures', 'else', 'exists', 'function',
                'forall', 'if', 'invariant', 'modifies', 'procedure',  'requires',
                'then', 'var', 'while'),
             suffix=r'\b'), Keyword),
            (words(('const',), suffix=r'\b'), Keyword.Reserved),

            (words(('bool', 'int', 'ref'), suffix=r'\b'), Keyword.Type),
            include('numbers'),
            (r"(>=|<=|:=|!=|==>|&&|\|\||[+/\-=>*<\[\]])", Operator),
            (r'\{.*?\}', Generic.Emph), #triggers
            (r"([{}():;,.])", Punctuation),
            # Identifier
            (r'[a-zA-Z_]\w*', Name),
        ],
        'comment': [
            (r'[^*/]+', Comment.Multiline),
            (r'/\*', Comment.Multiline, '#push'),
            (r'\*/', Comment.Multiline, '#pop'),
            (r'[*/]', Comment.Multiline),
        ],
        'numbers': [
            (r'[0-9]+', Number.Integer),
        ],
    }

class SilverLexer(RegexLexer):
    """
    For `Silver <https://bitbucket.org/viperproject/silver>`_ source code.

    .. versionadded:: 2.2
    """
    name = 'Silver'
    aliases = ['silver']
    filenames = ['*.sil', '*.vpr']

    tokens = {
        'root': [
            # Whitespace and Comments
            (r'\n', Text),
            (r'\s+', Text),
            (r'\\\n', Text),  # line continuation
            (r'//[/!](.*?)\n', Comment.Doc),
            (r'//(.*?)\n', Comment.Single),
            (r'/\*', Comment.Multiline, 'comment'),

            (words((
                'result', 'true', 'false', 'null', 'method', 'function',
                'predicate', 'program', 'domain', 'axiom', 'var', 'returns',
                'field', 'define', 'fold', 'unfold', 'inhale', 'exhale', 'new', 'assert',
                'assume', 'goto', 'while', 'if', 'elseif', 'else', 'fresh',
                'constraining', 'Seq', 'Set', 'Multiset', 'union', 'intersection',
                'setminus', 'subset', 'unfolding', 'in', 'old', 'forall', 'exists',
                'acc', 'wildcard', 'write', 'none', 'epsilon', 'perm', 'unique',
                'apply', 'package', 'folding', 'label', 'forperm'),
             suffix=r'\b'), Keyword),
            (words(('requires', 'ensures', 'invariant'), suffix=r'\b'), Name.Decorator),
            (words(('Int', 'Perm', 'Bool', 'Ref', 'Rational'), suffix=r'\b'), Keyword.Type),
            include('numbers'),
            (r'[!%&*+=|?:<>/\-\[\]]', Operator),
            (r'\{.*?\}', Generic.Emph), #triggers
            (r'([{}():;,.])', Punctuation),
            # Identifier
            (r'[\w$]\w*', Name),
        ],
        'comment': [
            (r'[^*/]+', Comment.Multiline),
            (r'/\*', Comment.Multiline, '#push'),
            (r'\*/', Comment.Multiline, '#pop'),
            (r'[*/]', Comment.Multiline),
        ],
        'numbers': [
            (r'[0-9]+', Number.Integer),
        ],
    }
<|endoftext|>


<commit_before># coding=utf-8
# Copyright 2018 Sascha Schirra
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" A ND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

__all__ = ['elf','pe','mach_o']
VERSION = '0.10.0'
<commit_msg>version changed<commit_after># coding=utf-8
# Copyright 2018 Sascha Schirra
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors
# may be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" A ND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

__all__ = ['elf','pe','mach_o']
VERSION = '0.10.1'
<|endoftext|>


<commit_before>"""CLI application for discovering and controlling Apple TVs."""

import sys
import inspect
import logging
import binascii
import asyncio

import argparse
from argparse import ArgumentTypeError
from zeroconf import Zeroconf

import pyatv
import pyatv.pairing
from pyatv import (const, dmap, exceptions, tag_definitions)
from pyatv.interface import retrieve_commands

class PushListener:
    """Internal listener for push updates."""

    @staticmethod
    def playstatus_update(_, playstatus):
        """Print what is currently playing when it changes."""
        print(str(playstatus))
        print(20*'-')

    @staticmethod
    def playstatus_error(updater, exception):
        """Inform about an error and restart push updates."""
        print("An error occurred (restarting): {0}".format(exception))
        updater.start(initial_delay=1)

def _in_range(lower, upper):
    def _checker(value):
        if int(value) >= lower and int(value) < upper:
            return int(value)
        raise ArgumentTypeError('Must be greater >= {} and < {}'.format(
            lower, upper))
    return _checker

@asyncio.coroutine
def cli_handler(loop):
    """Application starts here."""
    parser = argparse.ArgumentParser()

    parser.add_argument('command')
    parser.add_argument('--name', help='apple tv name',
                        dest='name', default='Apple TV')
    parser.add_argument('--address', help='device ip address or hostname',
                        dest='address', default=None)
    parser.add_argument('-t', '--scan-timeout', help='timeout when scanning',
                        dest='scan_timeout', type=_in_range(1, 10),
                        metavar='TIMEOUT', default=3)
    parser.add_argument('--version', action='version',
                        help='version of atvremote and pyatv',
                        version='%(prog)s {0}'.format(const.__version__))

    pairing = parser.add_argument_group('pairing')
    pairing.add_argument('--remote-name', help='remote pairing name',
                         dest='remote_name', default='pyatv')
    pairing.add_argument('-p', '--pin', help='pairing pin code',
                         dest='pin_code', type=_in_range(0, 9999),
                         metavar='PIN', default=1234)
    pairing.add_argument('--pairing-guid',
                         help='pairing guid (16 chars hex)',
                         dest='pairing_guid', default=None)

    ident = parser.add_mutually_exclusive_group()
    ident.add_argument('-a', '--autodiscover',
                       help='automatically find a device',
                       action='store_true', dest='autodiscover', default=False)
    ident.add_argument('--login_id', help='home sharing id or pairing guid',
                       dest='login_id', default=None)

    debug = parser.add_argument_group('debugging')
    debug.add_argument('-v', '--verbose', help='increase output verbosity',
                       action='store_true', dest='verbose')
    debug.add_argument('--developer', help='show developer commands',
                       action='store_true', dest='developer',
                       default=False)
    debug.add_argument('--debug', help='print debug information',
                       action='store_true', dest='debug')

    args = parser.parse_args()
    loglevel = logging.WARNING
    if args.verbose:
        loglevel = logging.INFO
    if args.debug:
        loglevel = logging.DEBUG

    logging.basicConfig(level=loglevel,
                        format='%(levelname)s: %(message)s')
    logging.getLogger('requests').setLevel(logging.WARNING)

    # Sanity checks that not can be done natively by argparse
    if (args.login_id and not args.address) or \
            (not args.login_id and args.address):
        parser.error('both --login_id and --address must be given')

    if args.command == 'scan':
        return (yield from _handle_scan(args, loop))
    elif args.command == 'pair':
        return (yield from _handle_pairing(args, loop))
    elif args.autodiscover:
        return (yield from _handle_autodiscover(args, loop))
    elif args.login_id:
        return (yield from _handle_command(args, loop))
    else:
        logging.error('To autodiscover an Apple TV, add -a')

    return 1

@asyncio.coroutine
def _handle_scan(args, loop):
    atvs = yield from pyatv.scan_for_apple_tvs(
        loop, timeout=args.scan_timeout, only_home_sharing=False)
    _print_found_apple_tvs(atvs)

    return 0

def _print_found_apple_tvs(atvs, outstream=sys.stdout):
    print('Found Apple TVs:', file=outstream)
    for apple_tv in atvs:
        if apple_tv.login_id is None:
            msg = ' - {0} at {1} (home sharing disabled)'.format(
                apple_tv.name, apple_tv.address)
        else:
            msg = ' - {0} at {1} (login id: {2})'.format(
                apple_tv.name, apple_tv.address, apple_tv.login_id)
        print(msg, file=outstream)

    print("\nNote: You must use 'pair' with devices "
          "that have home sharing disabled", file=outstream)

@asyncio.coroutine
def _handle_pairing(args, loop):
    handler = pyatv.pair_with_apple_tv(
        loop, args.pin_code, args.remote_name, pairing_guid=args.pairing_guid)
    print('Use pin {} to pair with "{}" (press ENTER to stop)'.format(
        args.pin_code, args.remote_name))
    print('Using pairing guid: 0x' + handler.pairing_guid)
    print('Note: If remote does not show up, try rebooting your Apple TV')

    yield from handler.start(Zeroconf())
    yield from loop.run_in_executor(None, sys.stdin.readline)
    yield from handler.stop()

    # Give some feedback to the user
    if handler.has_paired:
        print('Pairing seems to have succeeded, yey!')
        print('You may now use this login id: 0x{}'.format(
            handler.pairing_guid))
    else:
        print('No response from Apple TV!')

    return 0

@asyncio.coroutine
def _handle_autodiscover(args, loop):
    atvs = yield from pyatv.scan_for_apple_tvs(
        loop, timeout=args.scan_timeout, abort_on_found=True)
    if len(atvs) == 0:
        logging.error('Could not find any Apple TV on current network')
        return 1
    elif len(atvs) > 1:
        logging.error('Found more than one Apple TV; '
                      'specify one using --address and --login_id')
        _print_found_apple_tvs(atvs, outstream=sys.stderr)
        return 1

    # Simple hack to re-use existing command handling and respect options
    apple_tv = atvs[0]
    args.address = apple_tv.address
    args.login_id = apple_tv.login_id
    args.name = apple_tv.name
    logging.info('Auto-discovered %s at %s', args.name, args.address)
    yield from _handle_command(args, loop)

    return 0

def _print_commands(title, obj, newline=True):
    commands = ' - ' + '\n - '.join(
        map(lambda x: x[0] + ' - ' + x[1], sorted(obj.items())))
    print('{} commands:\n{}{}'.format(
        title, commands, '\n' if newline else ''))

def _extract_command_with_args(cmd):
    """Parse input command with arguments.

    Parses the input command in such a way that the user may
    provide additional argument to the command. The format used is this:
      command=arg1,arg2,arg3,...
    all the additional arguments are passed as arguments to the target
    method.
    """
    equal_sign = cmd.find('=')
    if equal_sign == -1:
        return cmd, []
    else:
        command = cmd[0:equal_sign]
        args = cmd[equal_sign+1:].split(',')
        return command, args

@asyncio.coroutine
def _handle_command(args, loop):
    details = pyatv.AppleTVDevice(args.name, args.address, args.login_id)
    atv = pyatv.connect_to_apple_tv(details, loop)
    atv.push_updater.listener = PushListener()

    try:
        playing_resp = yield from atv.metadata.playing()
        ctrl = retrieve_commands(atv.remote_control, developer=args.developer)
        metadata = retrieve_commands(atv.metadata, developer=args.developer)
        playing = retrieve_commands(playing_resp, developer=args.developer)
        other = {'push_updates': 'Listen for push updates'}

        # Parse input command and argument from user
        cmd, cmd_args = _extract_command_with_args(args.command)

        if cmd == 'commands':
            _print_commands('Remote control', ctrl)
            _print_commands('Metadata', metadata)
            _print_commands('Playing', playing)
            _print_commands('Other', other, newline=False)

        elif cmd == 'artwork':
            artwork = yield from atv.metadata.artwork()
            if artwork is not None:
                with open('artwork.png', 'wb') as file:
                    file.write(artwork)
            else:
                print('No artwork is currently available.')

        elif cmd == 'push_updates':
            print('Press ENTER to stop')

            atv.push_updater.start()
            yield from loop.run_in_executor(None, sys.stdin.readline)
            atv.push_updater.stop()

        elif cmd in ctrl:
            yield from _exec_command(atv.remote_control, cmd, *cmd_args)

        elif cmd in metadata:
            yield from _exec_command(atv.metadata, cmd, *cmd_args)

        elif cmd in playing:
            yield from _exec_command(playing_resp, cmd, *cmd_args)

        else:
            logging.error('Unknown command: %s', args.command)

    finally:
        yield from atv.logout()

    return 0

@asyncio.coroutine
def _exec_command(obj, command, *args):
    try:
        # If the command to execute is a @property, the value returned by that
        # property will be stored in tmp. Otherwise it's a coroutine and we
        # have to yield for the result and wait until it is available.
        tmp = getattr(obj, command)
        if inspect.ismethod(tmp):
            value = yield from tmp(*args)
        else:
            value = tmp
        _pretty_print(value)
    except NotImplementedError:
        logging.fatal("Command '%s' is not supported by device", command)
    except exceptions.AuthenticationError as ex:
        logging.fatal('Authentication error: %s', str(ex))

def _pretty_print(data):
    if data is None:
        return

    if isinstance(data, bytes):
        print(binascii.hexlify(data))
    elif isinstance(data, list):
        print(dmap.pprint(data, tag_definitions.lookup_tag))
    else:
        print(data)

def main():
    """Start the asyncio event loop and runs the application."""
    # Helper method so that the coroutine exits cleanly if an exception
    # happens (which would leave resources dangling)
    def _run_application(loop):
        try:
            return cli_handler(loop)

        except KeyboardInterrupt:
            pass  # User pressed Ctrl+C, just ignore it

        except SystemExit:
            pass  # sys.exit() was used - do nothing

        except:  # pylint: disable=bare-except
            import traceback

            traceback.print_exc(file=sys.stderr)
            sys.stderr.writelines(
                '\n>>> An error occurred, full stack trace above\n')

        return 1

    try:
        loop = asyncio.get_event_loop()
        return loop.run_until_complete(_run_application(loop))
    except KeyboardInterrupt:
        pass

    return 1

if __name__ == '__main__':
    sys.exit(main())
<commit_msg>Support multiple commands to atvremote<commit_after>"""CLI application for discovering and controlling Apple TVs."""

import sys
import inspect
import logging
import binascii
import asyncio

import argparse
from argparse import ArgumentTypeError
from zeroconf import Zeroconf

import pyatv
import pyatv.pairing
from pyatv import (const, dmap, exceptions, tag_definitions)
from pyatv.interface import retrieve_commands

class PushListener:
    """Internal listener for push updates."""

    @staticmethod
    def playstatus_update(_, playstatus):
        """Print what is currently playing when it changes."""
        print(str(playstatus))
        print(20*'-')

    @staticmethod
    def playstatus_error(updater, exception):
        """Inform about an error and restart push updates."""
        print("An error occurred (restarting): {0}".format(exception))
        updater.start(initial_delay=1)

def _in_range(lower, upper):
    def _checker(value):
        if int(value) >= lower and int(value) < upper:
            return int(value)
        raise ArgumentTypeError('Must be greater >= {} and < {}'.format(
            lower, upper))
    return _checker

@asyncio.coroutine
def cli_handler(loop):
    """Application starts here."""
    parser = argparse.ArgumentParser()

    parser.add_argument('command', nargs='+')
    parser.add_argument('--name', help='apple tv name',
                        dest='name', default='Apple TV')
    parser.add_argument('--address', help='device ip address or hostname',
                        dest='address', default=None)
    parser.add_argument('-t', '--scan-timeout', help='timeout when scanning',
                        dest='scan_timeout', type=_in_range(1, 10),
                        metavar='TIMEOUT', default=3)
    parser.add_argument('--version', action='version',
                        help='version of atvremote and pyatv',
                        version='%(prog)s {0}'.format(const.__version__))

    pairing = parser.add_argument_group('pairing')
    pairing.add_argument('--remote-name', help='remote pairing name',
                         dest='remote_name', default='pyatv')
    pairing.add_argument('-p', '--pin', help='pairing pin code',
                         dest='pin_code', type=_in_range(0, 9999),
                         metavar='PIN', default=1234)
    pairing.add_argument('--pairing-guid',
                         help='pairing guid (16 chars hex)',
                         dest='pairing_guid', default=None)

    ident = parser.add_mutually_exclusive_group()
    ident.add_argument('-a', '--autodiscover',
                       help='automatically find a device',
                       action='store_true', dest='autodiscover', default=False)
    ident.add_argument('--login_id', help='home sharing id or pairing guid',
                       dest='login_id', default=None)

    debug = parser.add_argument_group('debugging')
    debug.add_argument('-v', '--verbose', help='increase output verbosity',
                       action='store_true', dest='verbose')
    debug.add_argument('--developer', help='show developer commands',
                       action='store_true', dest='developer',
                       default=False)
    debug.add_argument('--debug', help='print debug information',
                       action='store_true', dest='debug')

    args = parser.parse_args()
    loglevel = logging.WARNING
    if args.verbose:
        loglevel = logging.INFO
    if args.debug:
        loglevel = logging.DEBUG

    logging.basicConfig(level=loglevel,
                        format='%(levelname)s: %(message)s')
    logging.getLogger('requests').setLevel(logging.WARNING)

    # Sanity checks that not can be done natively by argparse
    if (args.login_id and not args.address) or \
            (not args.login_id and args.address):
        parser.error('both --login_id and --address must be given')

    if args.command[0] == 'scan':
        return (yield from _handle_scan(args, loop))
    elif args.command[0] == 'pair':
        return (yield from _handle_pairing(args, loop))
    elif args.autodiscover:
        return (yield from _handle_autodiscover(args, loop))
    elif args.login_id:
        return (yield from _handle_commands(args, loop))
    else:
        logging.error('To autodiscover an Apple TV, add -a')

    return 1

@asyncio.coroutine
def _handle_scan(args, loop):
    atvs = yield from pyatv.scan_for_apple_tvs(
        loop, timeout=args.scan_timeout, only_home_sharing=False)
    _print_found_apple_tvs(atvs)

    return 0

def _print_found_apple_tvs(atvs, outstream=sys.stdout):
    print('Found Apple TVs:', file=outstream)
    for apple_tv in atvs:
        if apple_tv.login_id is None:
            msg = ' - {0} at {1} (home sharing disabled)'.format(
                apple_tv.name, apple_tv.address)
        else:
            msg = ' - {0} at {1} (login id: {2})'.format(
                apple_tv.name, apple_tv.address, apple_tv.login_id)
        print(msg, file=outstream)

    print("\nNote: You must use 'pair' with devices "
          "that have home sharing disabled", file=outstream)

@asyncio.coroutine
def _handle_pairing(args, loop):
    handler = pyatv.pair_with_apple_tv(
        loop, args.pin_code, args.remote_name, pairing_guid=args.pairing_guid)
    print('Use pin {} to pair with "{}" (press ENTER to stop)'.format(
        args.pin_code, args.remote_name))
    print('Using pairing guid: 0x' + handler.pairing_guid)
    print('Note: If remote does not show up, try rebooting your Apple TV')

    yield from handler.start(Zeroconf())
    yield from loop.run_in_executor(None, sys.stdin.readline)
    yield from handler.stop()

    # Give some feedback to the user
    if handler.has_paired:
        print('Pairing seems to have succeeded, yey!')
        print('You may now use this login id: 0x{}'.format(
            handler.pairing_guid))
    else:
        print('No response from Apple TV!')

    return 0

@asyncio.coroutine
def _handle_autodiscover(args, loop):
    atvs = yield from pyatv.scan_for_apple_tvs(
        loop, timeout=args.scan_timeout, abort_on_found=True)
    if len(atvs) == 0:
        logging.error('Could not find any Apple TV on current network')
        return 1
    elif len(atvs) > 1:
        logging.error('Found more than one Apple TV; '
                      'specify one using --address and --login_id')
        _print_found_apple_tvs(atvs, outstream=sys.stderr)
        return 1

    # Simple hack to re-use existing command handling and respect options
    apple_tv = atvs[0]
    args.address = apple_tv.address
    args.login_id = apple_tv.login_id
    args.name = apple_tv.name
    logging.info('Auto-discovered %s at %s', args.name, args.address)

    return (yield from _handle_commands(args, loop))

def _print_commands(title, obj, newline=True):
    commands = ' - ' + '\n - '.join(
        map(lambda x: x[0] + ' - ' + x[1], sorted(obj.items())))
    print('{} commands:\n{}{}'.format(
        title, commands, '\n' if newline else ''))

def _extract_command_with_args(cmd):
    """Parse input command with arguments.

    Parses the input command in such a way that the user may
    provide additional argument to the command. The format used is this:
      command=arg1,arg2,arg3,...
    all the additional arguments are passed as arguments to the target
    method.
    """
    equal_sign = cmd.find('=')
    if equal_sign == -1:
        return cmd, []
    else:
        command = cmd[0:equal_sign]
        args = cmd[equal_sign+1:].split(',')
        return command, args

@asyncio.coroutine
def _handle_commands(args, loop):
    details = pyatv.AppleTVDevice(args.name, args.address, args.login_id)
    atv = pyatv.connect_to_apple_tv(details, loop)
    atv.push_updater.listener = PushListener()

    try:
        for cmd in args.command:
            ret = yield from _handle_command(args, cmd, atv, loop)
            if ret != 0:
                return ret
    finally:
        yield from atv.logout()

    return 0

@asyncio.coroutine
def _handle_command(args, cmd, atv, loop):
    playing_resp = yield from atv.metadata.playing()
    ctrl = retrieve_commands(atv.remote_control, developer=args.developer)
    metadata = retrieve_commands(atv.metadata, developer=args.developer)
    playing = retrieve_commands(playing_resp, developer=args.developer)
    other = {'push_updates': 'Listen for push updates'}

    # Parse input command and argument from user
    cmd, cmd_args = _extract_command_with_args(cmd)
    if cmd == 'commands':
        _print_commands('Remote control', ctrl)
        _print_commands('Metadata', metadata)
        _print_commands('Playing', playing)
        _print_commands('Other', other, newline=False)

    elif cmd == 'artwork':
        artwork = yield from atv.metadata.artwork()
        if artwork is not None:
            with open('artwork.png', 'wb') as file:
                file.write(artwork)
        else:
            print('No artwork is currently available.')
            return 1

    elif cmd == 'push_updates':
        print('Press ENTER to stop')

        atv.push_updater.start()
        yield from loop.run_in_executor(None, sys.stdin.readline)
        atv.push_updater.stop()

    elif cmd in ctrl:
        return (yield from _exec_command(atv.remote_control, cmd, *cmd_args))

    elif cmd in metadata:
        return (yield from _exec_command(atv.metadata, cmd, *cmd_args))

    elif cmd in playing:
        return (yield from _exec_command(playing_resp, cmd, *cmd_args))

    else:
        logging.error('Unknown command: %s', args.command[0])
        return 1

    return 0

@asyncio.coroutine
def _exec_command(obj, command, *args):
    try:
        # If the command to execute is a @property, the value returned by that
        # property will be stored in tmp. Otherwise it's a coroutine and we
        # have to yield for the result and wait until it is available.
        tmp = getattr(obj, command)
        if inspect.ismethod(tmp):
            value = yield from tmp(*args)
        else:
            value = tmp
        _pretty_print(value)
        return 0
    except NotImplementedError:
        logging.fatal("Command '%s' is not supported by device", command)
    except exceptions.AuthenticationError as ex:
        logging.fatal('Authentication error: %s', str(ex))
    return 1

def _pretty_print(data):
    if data is None:
        return
    if isinstance(data, bytes):
        print(binascii.hexlify(data))
    elif isinstance(data, list):
        print(dmap.pprint(data, tag_definitions.lookup_tag))
    else:
        print(data)

def main():
    """Start the asyncio event loop and runs the application."""
    # Helper method so that the coroutine exits cleanly if an exception
    # happens (which would leave resources dangling)
    @asyncio.coroutine
    def _run_application(loop):
        try:
            return (yield from cli_handler(loop))

        except KeyboardInterrupt:
            pass  # User pressed Ctrl+C, just ignore it

        except SystemExit:
            pass  # sys.exit() was used - do nothing

        except:  # pylint: disable=bare-except
            import traceback

            traceback.print_exc(file=sys.stderr)
            sys.stderr.writelines(
                '\n>>> An error occurred, full stack trace above\n')

        return 1

    try:
        loop = asyncio.get_event_loop()
        return loop.run_until_complete(_run_application(loop))
    except KeyboardInterrupt:
        pass

    return 1

if __name__ == '__main__':
    sys.exit(main())
<|endoftext|>


<commit_before>from argparse import ArgumentParser
from os import environ

import numpy as np
import xarray as xr

from parcels import Field
from parcels.plotting import cartopy_colorbar
from parcels.plotting import create_parcelsfig_axis
from parcels.plotting import plotfield
try:
    import matplotlib.animation as animation
    from matplotlib import rc
except:
    anim = None

def plotTrajectoriesFile(filename, mode='2d', tracerfile=None, tracerfield='P',
                         tracerlon='x', tracerlat='y', recordedvar=None, movie_forward=True,
                         bins=20, show_plt=True, central_longitude=0):
    """Quick and simple plotting of Parcels trajectories

    :param filename: Name of Parcels-generated NetCDF file with particle positions
    :param mode: Type of plot to show. Supported are '2d', '3d', 'hist2d',
                'movie2d' and 'movie2d_notebook'. The latter two give animations,
                with 'movie2d_notebook' specifically designed for jupyter notebooks
    :param tracerfile: Name of NetCDF file to show as background
    :param tracerfield: Name of variable to show as background
    :param tracerlon: Name of longitude dimension of variable to show as background
    :param tracerlat: Name of latitude dimension of variable to show as background
    :param recordedvar: Name of variable used to color particles in scatter-plot.
                Only works in 'movie2d' or 'movie2d_notebook' mode.
    :param movie_forward: Boolean whether to show movie in forward or backward mode (default True)
    :param bins: Number of bins to use in `hist2d` mode. See also https://matplotlib.org/api/_as_gen/matplotlib.pyplot.hist2d.html
    :param show_plt: Boolean whether plot should directly be show (for py.test)
    """

    environ["HDF5_USE_FILE_LOCKING"] = "FALSE"
    try:
        pfile = xr.open_dataset(str(filename), decode_cf=True)
    except:
        pfile = xr.open_dataset(str(filename), decode_cf=False)
    lon = np.ma.filled(pfile.variables['lon'], np.nan)
    lat = np.ma.filled(pfile.variables['lat'], np.nan)
    time = np.ma.filled(pfile.variables['time'], np.nan)
    z = np.ma.filled(pfile.variables['z'], np.nan)
    mesh = pfile.attrs['parcels_mesh'] if 'parcels_mesh' in pfile.attrs else 'spherical'

    if(recordedvar is not None):
        record = np.ma.filled(pfile.variables[recordedvar], np.nan)
    pfile.close()

    if tracerfile is not None and mode != 'hist2d':
        tracerfld = Field.from_netcdf(tracerfile, tracerfield, {'lon': tracerlon, 'lat': tracerlat})
        plt, fig, ax, cartopy = plotfield(tracerfld)
        if plt is None:
            return  # creating axes was not possible
        titlestr = ' and ' + tracerfield
    else:
        spherical = False if mode == '3d' or mesh == 'flat' else True
        plt, fig, ax, cartopy = create_parcelsfig_axis(spherical=spherical, central_longitude=central_longitude)
        if plt is None:
            return  # creating axes was not possible
        titlestr = ''

    if cartopy:
        for p in range(lon.shape[1]):
            lon[:, p] = [ln if ln < 180 else ln - 360 for ln in lon[:, p]]

    if mode == '3d':
        from mpl_toolkits.mplot3d import Axes3D  # noqa
        plt.clf()  # clear the figure
        ax = fig.gca(projection='3d')
        for p in range(len(lon)):
            ax.plot(lon[p, :], lat[p, :], z[p, :], '.-')
        ax.set_xlabel('Longitude')
        ax.set_ylabel('Latitude')
        ax.set_zlabel('Depth')
        ax.set_title('Particle trajectories')
    elif mode == '2d':
        if cartopy:
            ax.plot(np.transpose(lon), np.transpose(lat), '.-', transform=cartopy.crs.Geodetic())
        else:
            ax.plot(np.transpose(lon), np.transpose(lat), '.-')
        ax.set_title('Particle trajectories' + titlestr)
    elif mode == 'hist2d':
        _, _, _, cs = plt.hist2d(lon[~np.isnan(lon)], lat[~np.isnan(lat)], bins=bins)
        cartopy_colorbar(cs, plt, fig, ax)
        ax.set_title('Particle histogram')
    elif mode in ('movie2d', 'movie2d_notebook'):
        ax.set_xlim(np.nanmin((lon+central_longitude+180)%360 - 180), np.nanmax((lon+central_longitude+180)%360 - 180))
        ax.set_ylim(np.nanmin(lat), np.nanmax(lat))
        plottimes = np.unique(time)
        if not movie_forward:
            plottimes = np.flip(plottimes, 0)
        if isinstance(plottimes[0], (np.datetime64, np.timedelta64)):
            plottimes = plottimes[~np.isnat(plottimes)]
        else:
            try:
                plottimes = plottimes[~np.isnan(plottimes)]
            except:
                pass
        b = time == plottimes[0]

        def timestr(plottimes, index):
            if isinstance(plottimes[index], np.timedelta64):
                if plottimes[-1] > np.timedelta64(1, 'h'):
                    return str(plottimes[index].astype('timedelta64[h]'))
                elif plottimes[-1] > np.timedelta64(1, 's'):
                    return str(plottimes[index].astype('timedelta64[s]'))
            else:
                return str(plottimes[index])

        if cartopy:
            scat = ax.scatter(lon[b], lat[b], s=20, color='k', transform=cartopy.crs.Geodetic())
        else:
            scat = ax.scatter(lon[b], lat[b], s=20, color='k')
        ttl = ax.set_title('Particles' + titlestr + ' at time ' + timestr(plottimes, 0))
        frames = np.arange(0, len(plottimes))

        def animate(t):
            b = time == plottimes[t]
            scat.set_offsets(np.vstack((lon[b], lat[b])).transpose())
            ttl.set_text('Particle' + titlestr + ' at time ' + timestr(plottimes, t))
            if recordedvar is not None:
                scat.set_array(record[b])
            return scat,

        rc('animation', html='html5')
        anim = animation.FuncAnimation(fig, animate, frames=frames, interval=100, blit=False)
    else:
        raise RuntimeError('mode %s not known' % mode)

    if mode == 'movie2d_notebook':
        plt.close()
        return anim
    else:
        if show_plt:
            plt.show()
        return plt

if __name__ == "__main__":
    p = ArgumentParser(description="""Quick and simple plotting of Parcels trajectories""")
    p.add_argument('mode', choices=('2d', '3d', 'hist2d', 'movie2d', 'movie2d_notebook'), nargs='?',
                   default='movie2d', help='Type of display')
    p.add_argument('-p', '--particlefile', type=str, default='MyParticle.nc',
                   help='Name of particle file')
    p.add_argument('-f', '--tracerfile', type=str, default=None,
                   help='Name of tracer file to display underneath particle trajectories')
    p.add_argument('-flon', '--tracerfilelon', type=str, default='x',
                   help='Name of longitude dimension in tracer file')
    p.add_argument('-flat', '--tracerfilelat', type=str, default='y',
                   help='Name of latitude dimension in tracer file')
    p.add_argument('-ffld', '--tracerfilefield', type=str, default='P',
                   help='Name of field in tracer file')
    p.add_argument('-r', '--recordedvar', type=str, default=None,
                   help='Name of a variable recorded along trajectory')
    p.add_argument('-bins', type=int, default=20,
                   help='Number of bins for mode=hist2d')
    args = p.parse_args()

    plotTrajectoriesFile(args.particlefile, mode=args.mode, tracerfile=args.tracerfile,
                         tracerfield=args.tracerfilefield, tracerlon=args.tracerfilelon,
                         tracerlat=args.tracerfilelat, recordedvar=args.recordedvar,
                         bins=args.bins, show_plt=True)
<commit_msg>docstring<commit_after>from argparse import ArgumentParser
from os import environ

import numpy as np
import xarray as xr

from parcels import Field
from parcels.plotting import cartopy_colorbar
from parcels.plotting import create_parcelsfig_axis
from parcels.plotting import plotfield
try:
    import matplotlib.animation as animation
    from matplotlib import rc
except:
    anim = None

def plotTrajectoriesFile(filename, mode='2d', tracerfile=None, tracerfield='P',
                         tracerlon='x', tracerlat='y', recordedvar=None, movie_forward=True,
                         bins=20, show_plt=True, central_longitude=0):
    """Quick and simple plotting of Parcels trajectories

    :param filename: Name of Parcels-generated NetCDF file with particle positions
    :param mode: Type of plot to show. Supported are '2d', '3d', 'hist2d',
                'movie2d' and 'movie2d_notebook'. The latter two give animations,
                with 'movie2d_notebook' specifically designed for jupyter notebooks
    :param tracerfile: Name of NetCDF file to show as background
    :param tracerfield: Name of variable to show as background
    :param tracerlon: Name of longitude dimension of variable to show as background
    :param tracerlat: Name of latitude dimension of variable to show as background
    :param recordedvar: Name of variable used to color particles in scatter-plot.
                Only works in 'movie2d' or 'movie2d_notebook' mode.
    :param movie_forward: Boolean whether to show movie in forward or backward mode (default True)
    :param bins: Number of bins to use in `hist2d` mode. See also https://matplotlib.org/api/_as_gen/matplotlib.pyplot.hist2d.html
    :param show_plt: Boolean whether plot should directly be show (for py.test)
    :param central_longitude: Degrees East at which to center the plot
    """

    environ["HDF5_USE_FILE_LOCKING"] = "FALSE"
    try:
        pfile = xr.open_dataset(str(filename), decode_cf=True)
    except:
        pfile = xr.open_dataset(str(filename), decode_cf=False)
    lon = np.ma.filled(pfile.variables['lon'], np.nan)
    lat = np.ma.filled(pfile.variables['lat'], np.nan)
    time = np.ma.filled(pfile.variables['time'], np.nan)
    z = np.ma.filled(pfile.variables['z'], np.nan)
    mesh = pfile.attrs['parcels_mesh'] if 'parcels_mesh' in pfile.attrs else 'spherical'

    if(recordedvar is not None):
        record = np.ma.filled(pfile.variables[recordedvar], np.nan)
    pfile.close()

    if tracerfile is not None and mode != 'hist2d':
        tracerfld = Field.from_netcdf(tracerfile, tracerfield, {'lon': tracerlon, 'lat': tracerlat})
        plt, fig, ax, cartopy = plotfield(tracerfld)
        if plt is None:
            return  # creating axes was not possible
        titlestr = ' and ' + tracerfield
    else:
        spherical = False if mode == '3d' or mesh == 'flat' else True
        plt, fig, ax, cartopy = create_parcelsfig_axis(spherical=spherical, central_longitude=central_longitude)
        if plt is None:
            return  # creating axes was not possible
        titlestr = ''

    if cartopy:
        for p in range(lon.shape[1]):
            lon[:, p] = [ln if ln < 180 else ln - 360 for ln in lon[:, p]]

    if mode == '3d':
        from mpl_toolkits.mplot3d import Axes3D  # noqa
        plt.clf()  # clear the figure
        ax = fig.gca(projection='3d')
        for p in range(len(lon)):
            ax.plot(lon[p, :], lat[p, :], z[p, :], '.-')
        ax.set_xlabel('Longitude')
        ax.set_ylabel('Latitude')
        ax.set_zlabel('Depth')
        ax.set_title('Particle trajectories')
    elif mode == '2d':
        if cartopy:
            ax.plot(np.transpose(lon), np.transpose(lat), '.-', transform=cartopy.crs.Geodetic())
        else:
            ax.plot(np.transpose(lon), np.transpose(lat), '.-')
        ax.set_title('Particle trajectories' + titlestr)
    elif mode == 'hist2d':
        _, _, _, cs = plt.hist2d(lon[~np.isnan(lon)], lat[~np.isnan(lat)], bins=bins)
        cartopy_colorbar(cs, plt, fig, ax)
        ax.set_title('Particle histogram')
    elif mode in ('movie2d', 'movie2d_notebook'):
        ax.set_xlim(np.nanmin((lon+central_longitude+180)%360 - 180), np.nanmax((lon+central_longitude+180)%360 - 180))
        ax.set_ylim(np.nanmin(lat), np.nanmax(lat))
        plottimes = np.unique(time)
        if not movie_forward:
            plottimes = np.flip(plottimes, 0)
        if isinstance(plottimes[0], (np.datetime64, np.timedelta64)):
            plottimes = plottimes[~np.isnat(plottimes)]
        else:
            try:
                plottimes = plottimes[~np.isnan(plottimes)]
            except:
                pass
        b = time == plottimes[0]

        def timestr(plottimes, index):
            if isinstance(plottimes[index], np.timedelta64):
                if plottimes[-1] > np.timedelta64(1, 'h'):
                    return str(plottimes[index].astype('timedelta64[h]'))
                elif plottimes[-1] > np.timedelta64(1, 's'):
                    return str(plottimes[index].astype('timedelta64[s]'))
            else:
                return str(plottimes[index])

        if cartopy:
            scat = ax.scatter(lon[b], lat[b], s=20, color='k', transform=cartopy.crs.Geodetic())
        else:
            scat = ax.scatter(lon[b], lat[b], s=20, color='k')
        ttl = ax.set_title('Particles' + titlestr + ' at time ' + timestr(plottimes, 0))
        frames = np.arange(0, len(plottimes))

        def animate(t):
            b = time == plottimes[t]
            scat.set_offsets(np.vstack((lon[b], lat[b])).transpose())
            ttl.set_text('Particle' + titlestr + ' at time ' + timestr(plottimes, t))
            if recordedvar is not None:
                scat.set_array(record[b])
            return scat,

        rc('animation', html='html5')
        anim = animation.FuncAnimation(fig, animate, frames=frames, interval=100, blit=False)
    else:
        raise RuntimeError('mode %s not known' % mode)

    if mode == 'movie2d_notebook':
        plt.close()
        return anim
    else:
        if show_plt:
            plt.show()
        return plt

if __name__ == "__main__":
    p = ArgumentParser(description="""Quick and simple plotting of Parcels trajectories""")
    p.add_argument('mode', choices=('2d', '3d', 'hist2d', 'movie2d', 'movie2d_notebook'), nargs='?',
                   default='movie2d', help='Type of display')
    p.add_argument('-p', '--particlefile', type=str, default='MyParticle.nc',
                   help='Name of particle file')
    p.add_argument('-f', '--tracerfile', type=str, default=None,
                   help='Name of tracer file to display underneath particle trajectories')
    p.add_argument('-flon', '--tracerfilelon', type=str, default='x',
                   help='Name of longitude dimension in tracer file')
    p.add_argument('-flat', '--tracerfilelat', type=str, default='y',
                   help='Name of latitude dimension in tracer file')
    p.add_argument('-ffld', '--tracerfilefield', type=str, default='P',
                   help='Name of field in tracer file')
    p.add_argument('-r', '--recordedvar', type=str, default=None,
                   help='Name of a variable recorded along trajectory')
    p.add_argument('-bins', type=int, default=20,
                   help='Number of bins for mode=hist2d')
    args = p.parse_args()

    plotTrajectoriesFile(args.particlefile, mode=args.mode, tracerfile=args.tracerfile,
                         tracerfield=args.tracerfilefield, tracerlon=args.tracerfilelon,
                         tracerlat=args.tracerfilelat, recordedvar=args.recordedvar,
                         bins=args.bins, show_plt=True)
<|endoftext|>


<commit_before>"""
gallerize.main
~~~~~~~~~~~~~~

:Copyright: 2007-2021 Jochen Kupperschmidt
:License: MIT, see LICENSE for details.
"""

from __future__ import annotations
from collections import defaultdict
from dataclasses import dataclass
from itertools import islice
import os
from pathlib import Path
import shutil
import subprocess
import sys
from typing import Any, Iterable, Iterator, Optional, Sequence

from jinja2 import Environment, PackageLoader

IMAGE_CAPTION_EXTENSION: str = '.txt'
TEMPLATE_EXTENSION: str = '.html'
OUTPUT_HTML_EXTENSION: str = '.html'

PATH: str = os.path.dirname(os.path.abspath(__file__))
PATH_STATIC: str = os.path.join(PATH, 'static')
TEMPLATE_ENVIRONMENT: Environment = Environment(
    autoescape=True,
    loader=PackageLoader(__package__, 'templates'),
    trim_blocks=False,
)

@dataclass(frozen=True)
class Dimension:
    width: int
    height: int

def debug(message: str, *args: Any) -> None:
    print(message.format(*args))

def resize_image(
    src_filename: str, dst_filename: str, max_dimension: Dimension
) -> None:
    """Create a resized (and antialiased) version of an image."""
    dimension_str = f'{max_dimension.width:d}x{max_dimension.height:d}'
    cmd = ['convert', '-resize', dimension_str, src_filename, dst_filename]
    subprocess.check_call(cmd)

def render_html_to_file(
    template_name: str, context: dict[str, Any], path: str, page_name: str
) -> None:
    """Render the template and write the result to the given file."""
    context['url_for_page'] = lambda page: page + OUTPUT_HTML_EXTENSION
    html = render_template(template_name + TEMPLATE_EXTENSION, **context)

    filename = os.path.join(path, page_name + OUTPUT_HTML_EXTENSION)
    write_file(filename, html)

def render_template(template_filename: str, **context: dict[str, Any]) -> str:
    return TEMPLATE_ENVIRONMENT.get_template(template_filename).render(context)

def write_file(filename: str, content: str) -> None:
    debug('Writing "{}" ...', filename)
    Path(filename).write_text(content, encoding='utf-8')

def create_gallery(
    title: str,
    destination_path: str,
    resize: bool,
    max_image_size: Dimension,
    max_thumbnail_size: Dimension,
    full_image_filenames: list[str],
) -> Gallery:
    images = [create_image(image) for image in sorted(full_image_filenames)]
    link_images(images)

    return Gallery(
        title=title,
        destination_path=destination_path,
        resize=resize,
        max_image_size=max_image_size,
        max_thumbnail_size=max_thumbnail_size,
        images=images,
    )

@dataclass(frozen=True)
class Gallery:
    title: str
    destination_path: str
    resize: bool
    max_image_size: Dimension
    max_thumbnail_size: Dimension
    images: list[Image]

def link_images(images: list[Image]) -> None:
    """Assign the predecessor and successor for every image."""
    for previous_image, image, next_image in window(images):
        image.previous_image = previous_image
        image.next_image = next_image

def generate_gallery(gallery: Gallery) -> None:
    # Create destination path if it doesn't exist.
    if not os.path.exists(gallery.destination_path):
        debug(
            'Destination path "{}" does not exist, creating it.',
            gallery.destination_path,
        )
        os.mkdir(gallery.destination_path)

    generate_images(gallery)
    render_html_pages(gallery)
    copy_additional_static_files(gallery.destination_path)
    debug('Done.')

def generate_images(gallery: Gallery) -> None:
    for image in gallery.images:
        generate_image(image, gallery)
        generate_thumbnail(image, gallery)
        image.caption = load_caption(image)

def render_html_pages(gallery: Gallery) -> None:
    for image in gallery.images:
        render_html_page(gallery, image)
    render_html_index_page(gallery)

def render_html_index_page(gallery: Gallery) -> None:
    """Create an HTML document of thumbnails that link to single image
    documents.
    """
    context = {'gallery': gallery}
    render_html_to_file('index', context, gallery.destination_path, 'index')

def copy_additional_static_files(destination_path: str) -> None:
    if not os.path.exists(PATH_STATIC):
        debug(
            'Path "{}", does not exist; not copying any static files.',
            PATH_STATIC,
        )
        return

    filenames = list(sorted(os.listdir(PATH_STATIC)))
    if not filenames:
        debug('No static files to copy.')
    for filename in filenames:
        debug('Copying static file "{}" ...', filename)
        shutil.copy(
            os.path.join(PATH_STATIC, filename),
            os.path.join(destination_path, filename),
        )

def window(
    images: Iterable[Image],
) -> Iterator[tuple[Optional[Image], Image, Optional[Image]]]:
    """Return a sliding window over the images.

    Returns a triple. Its first and third element can be `None`, but the
    second element never is.
    """
    n = 3
    padded_list: list[Optional[Image]] = list(images)
    padded_list.insert(0, None)
    padded_list.append(None)

    it = iter(padded_list)
    result = tuple(islice(it, n))

    if len(result) == n:
        yield result  # type: ignore

    for elem in it:
        result = result[1:] + (elem,)
        yield result  # type: ignore

@dataclass
class Image:
    full_filename: str
    path: str
    filename: str
    thumbnail_filename: str
    page_name: str
    caption: Optional[str] = None
    previous_image: Optional[Image] = None
    next_image: Optional[Image] = None

def create_image(full_filename: str) -> Image:
    path, filename = os.path.split(full_filename)
    basename, extension = os.path.splitext(filename)
    thumbnail_filename = f'{basename}_t{extension}'

    return Image(
        full_filename=full_filename,
        path=path,
        filename=filename,
        thumbnail_filename=thumbnail_filename,
        page_name=basename,
    )

def generate_image(image: Image, gallery: Gallery) -> None:
    """Create a (optionally resized) copy of an image."""
    destination_filename = os.path.join(
        gallery.destination_path, image.filename
    )
    if gallery.resize:
        # Resize image.
        debug('Resizing image "{}" ...', image.full_filename)
        resize_image(
            image.full_filename,
            destination_filename,
            gallery.max_image_size,
        )
    else:
        # Copy image.
        debug('Copying image "{}" ...', image.full_filename)
        shutil.copy(image.full_filename, destination_filename)

def generate_thumbnail(image: Image, gallery: Gallery) -> None:
    """Create a preview of an image."""
    debug('Creating thumbnail "{}" ...', image.thumbnail_filename)
    destination_filename = os.path.join(
        gallery.destination_path, image.thumbnail_filename
    )
    resize_image(
        image.full_filename,
        destination_filename,
        gallery.max_thumbnail_size,
    )

def load_caption(image: Image) -> Optional[str]:
    """Load image caption from file."""
    caption_filename = os.path.join(
        image.path, image.filename + IMAGE_CAPTION_EXTENSION
    )
    return _read_first_line(caption_filename)

def _read_first_line(filename: str) -> Optional[str]:
    """Read the first line from the specified file."""
    try:
        text = Path(filename).read_text(encoding='utf-8')
        first_line = text.splitlines()[0]
        return first_line.strip()
    except IOError:
        # File does not exist (OK) or cannot be read (not really OK).
        return None

def render_html_page(gallery: Gallery, image: Image) -> None:
    """Create an HTML document for a single image."""
    context = {'gallery': gallery, 'image': image}
    render_html_to_file(
        'view', context, gallery.destination_path, image.page_name
    )

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

def handle_duplicate_filenames(paths: Iterable[str]) -> None:
    duplicates = find_duplicate_filenames(paths)
    if not duplicates:
        return

    print('Found duplicate filenames:')
    for filename, paths in duplicates:
        print(f'  + "{filename}" appears in the following paths:')
        for path in paths:
            print(f'    - {path}')

    sys.exit('Clashing target filenames, aborting.')

def find_duplicate_filenames(
    paths: Iterable[str],
) -> list[tuple[str, list[str]]]:
    d = defaultdict(list)
    for path in paths:
        key = os.path.basename(path).lower()
        d[key].append(path)

    return [item for item in d.items() if len(item[1]) > 1]
<commit_msg>Freeze `Image` dataclass<commit_after>"""
gallerize.main
~~~~~~~~~~~~~~

:Copyright: 2007-2021 Jochen Kupperschmidt
:License: MIT, see LICENSE for details.
"""

from __future__ import annotations
from collections import defaultdict
import dataclasses
from dataclasses import dataclass
from itertools import islice
import os
from pathlib import Path
import shutil
import subprocess
import sys
from typing import Any, Iterable, Iterator, Optional, Sequence

from jinja2 import Environment, PackageLoader

IMAGE_CAPTION_EXTENSION: str = '.txt'
TEMPLATE_EXTENSION: str = '.html'
OUTPUT_HTML_EXTENSION: str = '.html'

PATH: str = os.path.dirname(os.path.abspath(__file__))
PATH_STATIC: str = os.path.join(PATH, 'static')
TEMPLATE_ENVIRONMENT: Environment = Environment(
    autoescape=True,
    loader=PackageLoader(__package__, 'templates'),
    trim_blocks=False,
)

@dataclass(frozen=True)
class Dimension:
    width: int
    height: int

def debug(message: str, *args: Any) -> None:
    print(message.format(*args))

def resize_image(
    src_filename: str, dst_filename: str, max_dimension: Dimension
) -> None:
    """Create a resized (and antialiased) version of an image."""
    dimension_str = f'{max_dimension.width:d}x{max_dimension.height:d}'
    cmd = ['convert', '-resize', dimension_str, src_filename, dst_filename]
    subprocess.check_call(cmd)

def render_html_to_file(
    template_name: str, context: dict[str, Any], path: str, page_name: str
) -> None:
    """Render the template and write the result to the given file."""
    context['url_for_page'] = lambda page: page + OUTPUT_HTML_EXTENSION
    html = render_template(template_name + TEMPLATE_EXTENSION, **context)

    filename = os.path.join(path, page_name + OUTPUT_HTML_EXTENSION)
    write_file(filename, html)

def render_template(template_filename: str, **context: dict[str, Any]) -> str:
    return TEMPLATE_ENVIRONMENT.get_template(template_filename).render(context)

def write_file(filename: str, content: str) -> None:
    debug('Writing "{}" ...', filename)
    Path(filename).write_text(content, encoding='utf-8')

def create_gallery(
    title: str,
    destination_path: str,
    resize: bool,
    max_image_size: Dimension,
    max_thumbnail_size: Dimension,
    full_image_filenames: list[str],
) -> Gallery:
    images = [create_image(image) for image in sorted(full_image_filenames)]
    images = list(link_images(images))

    return Gallery(
        title=title,
        destination_path=destination_path,
        resize=resize,
        max_image_size=max_image_size,
        max_thumbnail_size=max_thumbnail_size,
        images=images,
    )

@dataclass(frozen=True)
class Gallery:
    title: str
    destination_path: str
    resize: bool
    max_image_size: Dimension
    max_thumbnail_size: Dimension
    images: list[Image]

def link_images(images: list[Image]) -> Iterator[Image]:
    """Assign the predecessor and successor for every image."""
    for previous_image, image, next_image in window(images):
        yield dataclasses.replace(
            image,
            previous_image=previous_image,
            next_image=next_image,
        )

def generate_gallery(gallery: Gallery) -> None:
    # Create destination path if it doesn't exist.
    if not os.path.exists(gallery.destination_path):
        debug(
            'Destination path "{}" does not exist, creating it.',
            gallery.destination_path,
        )
        os.mkdir(gallery.destination_path)

    gallery = add_image_captions(gallery)
    generate_images(gallery)
    render_html_pages(gallery)
    copy_additional_static_files(gallery.destination_path)
    debug('Done.')

def add_image_captions(gallery: Gallery) -> Gallery:
    captioned_images = [
        dataclasses.replace(image, caption=load_caption(image))
        for image in gallery.images
    ]

    return dataclasses.replace(gallery, images=captioned_images)

def generate_images(gallery: Gallery) -> None:
    for image in gallery.images:
        generate_image(image, gallery)
        generate_thumbnail(image, gallery)

def render_html_pages(gallery: Gallery) -> None:
    for image in gallery.images:
        render_html_page(gallery, image)
    render_html_index_page(gallery)

def render_html_index_page(gallery: Gallery) -> None:
    """Create an HTML document of thumbnails that link to single image
    documents.
    """
    context = {'gallery': gallery}
    render_html_to_file('index', context, gallery.destination_path, 'index')

def copy_additional_static_files(destination_path: str) -> None:
    if not os.path.exists(PATH_STATIC):
        debug(
            'Path "{}", does not exist; not copying any static files.',
            PATH_STATIC,
        )
        return

    filenames = list(sorted(os.listdir(PATH_STATIC)))
    if not filenames:
        debug('No static files to copy.')
    for filename in filenames:
        debug('Copying static file "{}" ...', filename)
        shutil.copy(
            os.path.join(PATH_STATIC, filename),
            os.path.join(destination_path, filename),
        )

def window(
    images: Iterable[Image],
) -> Iterator[tuple[Optional[Image], Image, Optional[Image]]]:
    """Return a sliding window over the images.

    Returns a triple. Its first and third element can be `None`, but the
    second element never is.
    """
    n = 3
    padded_list: list[Optional[Image]] = list(images)
    padded_list.insert(0, None)
    padded_list.append(None)

    it = iter(padded_list)
    result = tuple(islice(it, n))

    if len(result) == n:
        yield result  # type: ignore

    for elem in it:
        result = result[1:] + (elem,)
        yield result  # type: ignore

@dataclass(frozen=True)
class Image:
    full_filename: str
    path: str
    filename: str
    thumbnail_filename: str
    page_name: str
    caption: Optional[str] = None
    previous_image: Optional[Image] = None
    next_image: Optional[Image] = None

def create_image(full_filename: str) -> Image:
    path, filename = os.path.split(full_filename)
    basename, extension = os.path.splitext(filename)
    thumbnail_filename = f'{basename}_t{extension}'

    return Image(
        full_filename=full_filename,
        path=path,
        filename=filename,
        thumbnail_filename=thumbnail_filename,
        page_name=basename,
    )

def generate_image(image: Image, gallery: Gallery) -> None:
    """Create a (optionally resized) copy of an image."""
    destination_filename = os.path.join(
        gallery.destination_path, image.filename
    )
    if gallery.resize:
        # Resize image.
        debug('Resizing image "{}" ...', image.full_filename)
        resize_image(
            image.full_filename,
            destination_filename,
            gallery.max_image_size,
        )
    else:
        # Copy image.
        debug('Copying image "{}" ...', image.full_filename)
        shutil.copy(image.full_filename, destination_filename)

def generate_thumbnail(image: Image, gallery: Gallery) -> None:
    """Create a preview of an image."""
    debug('Creating thumbnail "{}" ...', image.thumbnail_filename)
    destination_filename = os.path.join(
        gallery.destination_path, image.thumbnail_filename
    )
    resize_image(
        image.full_filename,
        destination_filename,
        gallery.max_thumbnail_size,
    )

def load_caption(image: Image) -> Optional[str]:
    """Load image caption from file."""
    caption_filename = os.path.join(
        image.path, image.filename + IMAGE_CAPTION_EXTENSION
    )
    return _read_first_line(caption_filename)

def _read_first_line(filename: str) -> Optional[str]:
    """Read the first line from the specified file."""
    try:
        text = Path(filename).read_text(encoding='utf-8')
        first_line = text.splitlines()[0]
        return first_line.strip()
    except IOError:
        # File does not exist (OK) or cannot be read (not really OK).
        return None

def render_html_page(gallery: Gallery, image: Image) -> None:
    """Create an HTML document for a single image."""
    context = {'gallery': gallery, 'image': image}
    render_html_to_file(
        'view', context, gallery.destination_path, image.page_name
    )

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

def handle_duplicate_filenames(paths: Iterable[str]) -> None:
    duplicates = find_duplicate_filenames(paths)
    if not duplicates:
        return

    print('Found duplicate filenames:')
    for filename, paths in duplicates:
        print(f'  + "{filename}" appears in the following paths:')
        for path in paths:
            print(f'    - {path}')

    sys.exit('Clashing target filenames, aborting.')

def find_duplicate_filenames(
    paths: Iterable[str],
) -> list[tuple[str, list[str]]]:
    d = defaultdict(list)
    for path in paths:
        key = os.path.basename(path).lower()
        d[key].append(path)

    return [item for item in d.items() if len(item[1]) > 1]
<|endoftext|>


<commit_before>from django.db import models
from thumbnailer import thumbnailer2
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.conf import settings
from io import BytesIO
import os.path
from django.core.files.uploadedfile import InMemoryUploadedFile

from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType

class fakefile():
    url = ""
#    def url():
#        return("null")

# Create your models here.

class fileobject(models.Model):
    def uploadpath(instance, filename):
        return ("uploads/"+str(instance.content_type.name)+"/"+str(instance.object_id)+instance.subfolder+filename)

## These attributes point to the object/project/userProfile that this file belongs to.
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    parent = generic.GenericForeignKey('content_type', 'object_id')

    subfolder = models.CharField(max_length=256, default="/")
    filename = models.FileField(upload_to=uploadpath,null=True)
    filetype = models.CharField(max_length=16, blank=True, null=True, default="norender")

### I don't know why __unicode__() broke, but unicode() fixed it... so now we have both.
    def unicode(self):
        return str(self.filename)
    def __unicode__(self):
        return str(self.filename)
    def get_thumb(self, sizex, sizey):
        thumbmodel=thumbobject.objects.get_or_create(fileobject = self, filex=sizex, filey=sizey )[0]
        renderer = str(thumbmodel.filetype)
        if renderer != "ajax" or "norender" or "":
            return [thumbmodel,self,renderer]
        elif (renderer == "ajax"):
            return ["",self,self.renderer]
        else:
            return

    def save(self):
        super(fileobject, self).save()
        self.filetype = thumbnailer2.thumbnailify(self, (1,1))[1]
        super(fileobject, self).save()

    def delete(self, *args, **kwargs):
        from project.tasks import ThumbnailEnforcer
        self.filename.delete()
        super(fileobject, self).delete(*args, **kwargs)
        #parent does not need to implement an enf_consistancy method. It is optional.
        try:
            self.parent.enf_consistancy()
        except:
            pass

       #default_storage.delete(self.thumbname)

class thumbobject(models.Model):

    def uploadpath(instance, filename):
        return ("thumbs/"+str(instance.fileobject.content_type.name)+"/"+str(instance.fileobject.object_id)+"/"+str(instance.pk)+os.path.split(filename)[1])
    #A pointer to the file this is a thumbnail of.
    fileobject = models.ForeignKey(fileobject)
    #This is the actual thumbnail, stored using django storage, whatever that may be.
    filename = models.FileField(upload_to=uploadpath, blank=True, null=True)
    #What the file type is
    filetype = models.CharField(max_length=16, blank=True, null=True)
    #the size of the file.
    filex = models.PositiveSmallIntegerField()
    filey = models.PositiveSmallIntegerField()

    def __unicode__(self):
        return str(self.filename)

    class Meta:
        unique_together = ('filex', 'filey', "fileobject")

#        index_together = [['filex', 'filey', "fileobject"]]
    def save(self, generate=True, *args, **kwargs):
        if generate == True:
            self.filetype = "ajax"

        super(thumbobject, self,).save()
        if generate == True:
            from filemanager.tasks import thumbTask
            thumbTask.delay(self, self.fileobject)

    def delete(self, *args, **kwargs):
        self.filename.delete()
        super(thumbobject, self).delete(*args, **kwargs)

class zippedobject(models.Model):

    def __unicode__(self):
        return str(self.project.title)

    project = models.ForeignKey('project.Project', unique=True)
    filename = models.FileField(upload_to="projects/", blank=True, null=True)
    def save(self, generate=True, *args, **kwargs):
        super(zippedobject, self).save()
        if generate == True:
            from filemanager.tasks import zippedTask
            zippedTask.delay(self, self.project)

    def delete(self, *args, **kwargs):
        import warnings
        try:
            self.filename.delete()
        except:
            pass
        super(zippedobject, self).delete(*args, **kwargs)

import thumbnailer.shadowbox
import markdown

class htmlobject(models.Model):

    def __unicode__(self):
        return str(self.fileobject.filename)
    #A pointer to the file this is a thumbnail of.
    fileobject = models.ForeignKey(fileobject, unique=True)
    body_rendered = models.TextField('Entry body as HTML', blank=True, null=True)

    def save(self, *args, **kwargs):
        self.body_rendered = markdown.markdown(self.fileobject.filename.read(), safe_mode=True)
        super(htmlobject, self).save(*args, **kwargs)

<commit_msg>thumbobject post_delete to delete fileobjects.<commit_after>from django.db import models
from django.dispatch import receiver
from thumbnailer import thumbnailer2
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from django.conf import settings
from io import BytesIO
import os.path
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType

class fakefile():
    url = ""
#    def url():
#        return("null")

# Create your models here.

class fileobject(models.Model):
    def uploadpath(instance, filename):
        return ("uploads/"+str(instance.content_type.name)+"/"+str(instance.object_id)+instance.subfolder+filename)

## These attributes point to the object/project/userProfile that this file belongs to.
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    parent = generic.GenericForeignKey('content_type', 'object_id')

    subfolder = models.CharField(max_length=256, default="/")
    filename = models.FileField(upload_to=uploadpath,null=True)
    filetype = models.CharField(max_length=16, blank=True, null=True, default="norender")

### I don't know why __unicode__() broke, but unicode() fixed it... so now we have both.
    def unicode(self):
        return str(self.filename)
    def __unicode__(self):
        return str(self.filename)
    def get_thumb(self, sizex, sizey):
        thumbmodel=thumbobject.objects.get_or_create(fileobject = self, filex=sizex, filey=sizey )[0]
        renderer = str(thumbmodel.filetype)
        if renderer != "ajax" or "norender" or "":
            return [thumbmodel,self,renderer]
        elif (renderer == "ajax"):
            return ["",self,self.renderer]
        else:
            return

    def save(self):
        super(fileobject, self).save()
        self.filetype = thumbnailer2.thumbnailify(self, (1,1))[1]
        super(fileobject, self).save()

    def delete(self, *args, **kwargs):
        from project.tasks import ThumbnailEnforcer
        self.filename.delete()
        super(fileobject, self).delete(*args, **kwargs)
        #parent does not need to implement an enf_consistancy method. It is optional.
        try:
            self.parent.enf_consistancy()
        except:
            pass

       #default_storage.delete(self.thumbname)

class thumbobject(models.Model):

    def uploadpath(instance, filename):
        return ("thumbs/"+str(instance.fileobject.content_type.name)+"/"+str(instance.fileobject.object_id)+"/"+str(instance.pk)+os.path.split(filename)[1])
    #A pointer to the file this is a thumbnail of.
    fileobject = models.ForeignKey(fileobject)
    #This is the actual thumbnail, stored using django storage, whatever that may be.
    filename = models.FileField(upload_to=uploadpath, blank=True, null=True)
    #What the file type is
    filetype = models.CharField(max_length=16, blank=True, null=True)
    #the size of the file.
    filex = models.PositiveSmallIntegerField()
    filey = models.PositiveSmallIntegerField()

    def __unicode__(self):
        return str(self.filename)

    class Meta:
        unique_together = ('filex', 'filey', "fileobject")

#        index_together = [['filex', 'filey', "fileobject"]]
    def save(self, generate=True, *args, **kwargs):
        if generate == True:
            self.filetype = "ajax"

        super(thumbobject, self,).save()
        if generate == True:
            from filemanager.tasks import thumbTask
            thumbTask.delay(self, self.fileobject)

    def delete(self, *args, **kwargs):
        super(thumbobject, self).delete(*args, **kwargs)

@receiver(models.signals.post_delete, sender=thumbobject)
def delete_thumbdata(**kwargs):
    instance.filename.delete()

class zippedobject(models.Model):

    def __unicode__(self):
        return str(self.project.title)

    project = models.ForeignKey('project.Project', unique=True)
    filename = models.FileField(upload_to="projects/", blank=True, null=True)
    def save(self, generate=True, *args, **kwargs):
        super(zippedobject, self).save()
        if generate == True:
            from filemanager.tasks import zippedTask
            zippedTask.delay(self, self.project)

    def delete(self, *args, **kwargs):
        import warnings
        try:
            self.filename.delete()
        except:
            pass
        super(zippedobject, self).delete(*args, **kwargs)

import thumbnailer.shadowbox
import markdown

class htmlobject(models.Model):

    def __unicode__(self):
        return str(self.fileobject.filename)
    #A pointer to the file this is a thumbnail of.
    fileobject = models.ForeignKey(fileobject, unique=True)
    body_rendered = models.TextField('Entry body as HTML', blank=True, null=True)

    def save(self, *args, **kwargs):
        self.body_rendered = markdown.markdown(self.fileobject.filename.read(), safe_mode=True)
        super(htmlobject, self).save(*args, **kwargs)

<|endoftext|>


<commit_before>
import subprocess

class VarMaps(object):
    pass

def gen_qepcad_varmaps( var_list ):

    forwmap = {}

    for v in var_list:

        v = str(v)
        if v in forwmap:
            raise 'Error: Two variables with same string representation'

        vm = v.replace('_','')

        if vm not in forwmap.values():
            forwmap[v] = vm
        else:
            raise 'Error: Two variables with same map'

    backmap = dict( zip( forwmap.values(), forwmap.keys()) )

    varmaps = VarMaps();

    varmaps.forward = forwmap
    varmaps.backward = backmap

    return varmaps

def sym_to_qepcad( symexpr, vardictforw={} ):
    symexpr =  str(symexpr).replace('**','^').replace('*',' ')

    for var in vardictforw:
        symexpr = symexpr.replace(var,vardictforw[var])

    elements = symexpr.split()

    for i,e in enumerate(elements):
      if '/' in e:
        l,r = e.split('/')
        if not l.isnumeric():
          elements[i] = l + ' 1/' + r

    symexpr = ' '.join(elements)

    return symexpr

def qepcad_to_sym( qepcad_rel, vardictback={} ):

    elements = qepcad_rel.split()

    out_elements = []

    last_isalnum = False

    for i,e in enumerate(elements):

        e0 = e.split('^')[0]
        e = e.replace('^','**')

        isalnum = e0.isalnum()

        if isalnum and last_isalnum:
            out_elements.append('*')

        last_isalnum = isalnum

        if isalnum and e0 in vardictback:
            e = e.replace( e0, str(vardictback[e0]) )

        out_elements.append( e )

    return ' '.join(out_elements)

def gen_qepcad_input( freevars, qvars, prenex, vardictforw={} ):

  vars = '(' + ','.join( [ sym_to_qepcad(var) for var in freevars ] + [ sym_to_qepcad(var) for var in qvars ] ) + ')'

  for var in vardictforw:
    vars = vars.replace(var,vardictforw[var])

  freevarnum = str(len(freevars))

  prenex += '.' if prenex[-1] != '.' else ''

  qepcad_input = '[]\n'+vars+'\n'+freevarnum+'\n'+prenex+'\nfinish\n'

  return qepcad_input

def run_qepcad( qepcad_cmd, qepcad_input ):

    proc = subprocess.Popen(qepcad_cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE )

    proc.stdin.write(qepcad_input.encode())

    out = proc.communicate()

    outstr = out[0].decode("utf-8")

    #print(outstr)

    outlines = outstr.splitlines()

    return outlines[ outlines.index('An equivalent quantifier-free formula:') + 2 ]<commit_msg>Bugfix in deprecated code.<commit_after>
import subprocess

class VarMaps(object):
    pass

def gen_qepcad_varmaps( var_list ):

    forwmap = {}

    for v in var_list:

        v = str(v)
        if v in forwmap:
            raise 'Error: Two variables with same string representation'

        vm = v.replace('_','')

        if vm not in forwmap.values():
            forwmap[v] = vm
        else:
            raise 'Error: Two variables with same map'

    backmap = dict( zip( forwmap.values(), forwmap.keys()) )

    varmaps = VarMaps();

    varmaps.forward = forwmap
    varmaps.backward = backmap

    return varmaps

def sym_to_qepcad( symexpr, vardictforw=None ):
    if vardictforw is None:
        vardictforw = {}

    symexpr =  str(symexpr).replace('**','^').replace('*',' ')

    for var in vardictforw:
        symexpr = symexpr.replace(var,vardictforw[var])

    elements = symexpr.split()

    for i,e in enumerate(elements):
      if '/' in e:
        l,r = e.split('/')
        if not l.isnumeric():
          elements[i] = l + ' 1/' + r

    symexpr = ' '.join(elements)

    return symexpr

def qepcad_to_sym( qepcad_rel, vardictback=None ):
    if vardictforw is None:
        vardictforw = {}

    elements = qepcad_rel.split()

    out_elements = []

    last_isalnum = False

    for i,e in enumerate(elements):

        e0 = e.split('^')[0]
        e = e.replace('^','**')

        isalnum = e0.isalnum()

        if isalnum and last_isalnum:
            out_elements.append('*')

        last_isalnum = isalnum

        if isalnum and e0 in vardictback:
            e = e.replace( e0, str(vardictback[e0]) )

        out_elements.append( e )

    return ' '.join(out_elements)

def gen_qepcad_input( freevars, qvars, prenex, vardictforw=None ):
  if vardictforw is None:
      vardictforw = {}

  vars = '(' + ','.join( [ sym_to_qepcad(var) for var in freevars ] + [ sym_to_qepcad(var) for var in qvars ] ) + ')'

  for var in vardictforw:
    vars = vars.replace(var,vardictforw[var])

  freevarnum = str(len(freevars))

  prenex += '.' if prenex[-1] != '.' else ''

  qepcad_input = '[]\n'+vars+'\n'+freevarnum+'\n'+prenex+'\nfinish\n'

  return qepcad_input

def run_qepcad( qepcad_cmd, qepcad_input ):

    proc = subprocess.Popen(qepcad_cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE )

    proc.stdin.write(qepcad_input.encode())

    out = proc.communicate()

    outstr = out[0].decode("utf-8")

    #print(outstr)

    outlines = outstr.splitlines()

    return outlines[ outlines.index('An equivalent quantifier-free formula:') + 2 ]
<|endoftext|>