<commit_before>import os
import json
import asyncio
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,
                                               NavigationToolbar2TkAgg)
import matplotlib.pyplot as plt
import tkinter as tk
import tkinter.filedialog
from tkinter import ttk
import click

from . import pre, pipe, draw, io, __version__

@asyncio.coroutine
def _async(coroutine, *args):
    loop = asyncio.get_event_loop()
    return (yield from loop.run_in_executor(None, coroutine, *args))

STANDARD_MARGIN = (3, 3, 12, 12)

class Launch(tk.Tk):
    def __init__(self, params_dict=None):
        super().__init__()
        self.title('Skeleton analysis tool')
        self.crop_radius = tk.IntVar(value=0, name='Crop radius')
        self.smooth_method = tk.StringVar(value='Gaussian',
                                          name='Smoothing method')
        self.smooth_method._choices = pre.SMOOTH_METHODS
        self.smooth_radius = tk.DoubleVar(value=0.1, name='Smoothing radius')
        self.threshold_radius = tk.DoubleVar(value=50e-9,
                                             name='Threshold radius')
        self.brightness_offset = tk.DoubleVar(value=0.075,
                                              name='Brightness offset')
        self.image_format = tk.StringVar(value='auto',
                                         name='Image format')
        self.scale_metadata_path = tk.StringVar(value='Scan/PixelHeight',
                                                name='Scale metadata path')
        self.preview_skeleton_plots = tk.BooleanVar(value=True, name='Live '
                                                    'preview skeleton plot?')
        self.save_skeleton_plots = tk.BooleanVar(value=True,
                                                 name='Save skeleton plot?')
        self.skeleton_plot_prefix = tk.StringVar(value='skeleton-plot-',
                                         name='Prefix for skeleton plots')
        self.output_filename = tk.StringVar(value='skeleton.xlsx',
                                            name='Output filename')
        self.parameters = [
            self.crop_radius,
            self.smooth_method,
            self.smooth_radius,
            self.threshold_radius,
            self.brightness_offset,
            self.image_format,
            self.scale_metadata_path,
            self.preview_skeleton_plots,
            self.save_skeleton_plots,
            self.skeleton_plot_prefix,
            self.output_filename,
        ]

        self.input_files = []
        self.output_folder = None

        if params_dict is None:
            params_dict = {}
        self.params_dict = params_dict.copy()
        self.parameter_config(params_dict)

        # allow resizing
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)

        self.create_main_frame()

    def parameter_config(self, params_dict):
        """Set parameter values from a config dictionary."""
        if isinstance(params_dict, str):
            if params_dict.startswith('{'):  # JSON string
                params_dict = json.loads(params_dict)
            else:  # config file
                with open(params_dict) as params_fin:
                    params_dict = json.load(params_fin)
            self.params_dict.update(params_dict)
        name2param = {p._name.lower(): p for p in self.parameters}
        for param, value in self.params_dict.items():
            if param.lower() in name2param:
                name2param[param].set(value)
                params_dict.pop(param)
        for param, value in params_dict.copy().items():
            if param.lower() == 'input files':
                self.input_files = value
                params_dict.pop(param)
            elif param.lower() == 'output folder':
                self.output_folder = Path(os.path.expanduser(value))
                params_dict.pop(param)
            elif param.lower() == 'version':
                print(f'Parameter file version: {params_dict.pop(param)}')
        for param in params_dict:
            print(f'Parameter not recognised: {param}')

    def save_parameters(self, filename=None):
        out = {p._name.lower(): p.get() for p in self.parameters}
        out['input files'] = self.input_files
        out['output folder'] = str(self.output_folder)
        out['version'] = __version__
        if filename is None:
            return json.dumps(out)
        attempt = 0
        base, ext = os.path.splitext(filename)
        while os.path.exists(filename):
            filename = f'{base} ({attempt}){ext}'
            attempt += 1
        with open(filename, mode='wt') as fout:
            json.dump(out, fout, indent=2)

    def create_main_frame(self):
        main = ttk.Frame(master=self, padding=STANDARD_MARGIN)
        main.grid(row=0, column=0, sticky='nsew')
        self.create_parameters_frame(main)
        self.create_buttons_frame(main)
        main.pack()

    def create_parameters_frame(self, parent):
        parameters = ttk.Frame(master=parent, padding=STANDARD_MARGIN)
        parameters.grid(sticky='nsew')

        heading = ttk.Label(parameters, text='Analysis parameters')
        heading.grid(column=0, row=0, sticky='n')

        for i, param in enumerate(self.parameters, start=1):
            param_label = ttk.Label(parameters, text=param._name)
            param_label.grid(row=i, column=0, sticky='nsew')
            if type(param) == tk.BooleanVar:
                param_entry = ttk.Checkbutton(parameters, variable=param)
            elif hasattr(param, '_choices'):
                param_entry = ttk.OptionMenu(parameters, param, param.get(),
                                             *param._choices.keys())
            else:
                param_entry = ttk.Entry(parameters, textvariable=param)
            param_entry.grid(row=i, column=1, sticky='nsew')

    def create_buttons_frame(self, parent):
        buttons = ttk.Frame(master=parent, padding=STANDARD_MARGIN)
        buttons.grid(sticky='nsew')
        actions = [
            ('Choose config', self.choose_config_file),
            ('Choose files', self.choose_input_files),
            ('Choose output folder', self.choose_output_folder),
            ('Run', lambda: asyncio.ensure_future(self.run()))
        ]
        for col, (action_name, action) in enumerate(actions):
            button = ttk.Button(buttons, text=action_name,
                                command=action)
            button.grid(row=0, column=col)

    def choose_config_file(self):
        config_file = tk.filedialog.askopenfilename()
        self.parameter_config(config_file)

    def choose_input_files(self):
        self.input_files = tk.filedialog.askopenfilenames()
        if len(self.input_files) > 0 and self.output_folder is None:
            self.output_folder = Path(os.path.dirname(self.input_files[0]))

    def choose_output_folder(self):
        self.output_folder = Path(
                tk.filedialog.askdirectory(initialdir=self.output_folder))

    def make_figure_window(self):
        self.figure_window = tk.Toplevel(self)
        self.figure_window.wm_title('Preview')
        screen_dpi = self.figure_window.winfo_fpixels('1i')
        screen_width = self.figure_window.winfo_screenwidth()  # in pixels
        figure_width = screen_width / 2 / screen_dpi
        figure_height = 0.75 * figure_width
        self.figure = Figure(figsize=(figure_width, figure_height),
                             dpi=screen_dpi)
        ax0 = self.figure.add_subplot(221)
        axes = [self.figure.add_subplot(220 + i, sharex=ax0, sharey=ax0)
                for i in range(2, 5)]
        self.axes = np.array([ax0] + axes)
        canvas = FigureCanvasTkAgg(self.figure, master=self.figure_window)
        canvas.show()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        toolbar = NavigationToolbar2TkAgg(canvas, self.figure_window)
        toolbar.update()
        canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)

    async def run(self):
        print('Input files:')
        for file in self.input_files:
            print('  ', file)
        print('Parameters:')
        for param in self.parameters:
            p = param.get()
            print('  ', param, type(p), p)
        print('Output:', self.output_folder)
        save_skeleton = ('' if not self.save_skeleton_plots.get() else
                         self.skeleton_plot_prefix.get())
        images_iterator = pipe.process_images(
                self.input_files, self.image_format.get(),
                self.threshold_radius.get(),
                self.smooth_radius.get(),
                self.brightness_offset.get(),
                self.scale_metadata_path.get(),
                crop_radius=self.crop_radius.get(),
                smooth_method=self.smooth_method.get())
        if self.preview_skeleton_plots.get():
            self.make_figure_window()
        elif self.save_skeleton_plots.get():
            self.figure = plt.figure()
            ax0 = self.figure.add_subplot(221)
            axes = [self.figure.add_subplot(220 + i, sharex=ax0, sharey=ax0)
                    for i in range(2, 5)]
            self.axes = np.array([ax0] + axes)
        self.save_parameters(self.output_folder / 'skan-config.json')
        for i, result in enumerate(images_iterator):
            if i < len(self.input_files):
                filename, image, thresholded, skeleton, framedata = result
                if save_skeleton:
                    for ax in self.axes:
                        ax.clear()
                    w, h = draw.pixel_perfect_figsize(image)
                    self.figure.set_size_inches(4*w, 4*h)
                    draw.pipeline_plot(image, thresholded, skeleton, framedata,
                                       figure=self.figure, axes=self.axes)
                    output_basename = (save_skeleton +
                                       os.path.basename(
                                           os.path.splitext(filename)[0]) +
                                       '.png')
                    output_filename = str(self.output_folder / output_basename)
                    self.figure.savefig(output_filename)
                if self.preview_skeleton_plots.get():
                    self.figure.canvas.draw_idle()
            else:
                result_full, result_image = result
                io.write_excel(str(self.output_folder /
                                   self.output_filename.get()),
                               branches=result_full,
                               images=result_image,
                               parameters=json.loads(self.save_parameters()))

def tk_update(loop, app):
    try:
        app.update()
    except tkinter.TclError:
        loop.stop()
        return
    loop.call_later(.01, tk_update, loop, app)

@click.command()
@click.option('-c', '--config', default='',
              help='JSON configuration file.')
def launch(config):
    params = json.load(open(config)) if config else None
    app = Launch(params)
    loop = asyncio.get_event_loop()
    tk_update(loop, app)
    loop.run_forever()
<commit_msg>Add shape index filter step<commit_after>import os
import json
import asyncio
from pathlib import Path
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,
                                               NavigationToolbar2TkAgg)
import matplotlib.pyplot as plt
import tkinter as tk
import tkinter.filedialog
from tkinter import ttk
import click

from . import pre, pipe, draw, io, __version__

@asyncio.coroutine
def _async(coroutine, *args):
    loop = asyncio.get_event_loop()
    return (yield from loop.run_in_executor(None, coroutine, *args))

STANDARD_MARGIN = (3, 3, 12, 12)

class Launch(tk.Tk):
    def __init__(self, params_dict=None):
        super().__init__()
        self.title('Skeleton analysis tool')
        self.crop_radius = tk.IntVar(value=0, name='Crop radius')
        self.smooth_method = tk.StringVar(value='Gaussian',
                                          name='Smoothing method')
        self.smooth_method._choices = pre.SMOOTH_METHODS
        self.smooth_radius = tk.DoubleVar(value=0.1, name='Smoothing radius')
        self.threshold_radius = tk.DoubleVar(value=50e-9,
                                             name='Threshold radius')
        self.brightness_offset = tk.DoubleVar(value=0.075,
                                              name='Brightness offset')
        self.image_format = tk.StringVar(value='auto',
                                         name='Image format')
        self.scale_metadata_path = tk.StringVar(value='Scan/PixelHeight',
                                                name='Scale metadata path')
        self.preview_skeleton_plots = tk.BooleanVar(value=True, name='Live '
                                                    'preview skeleton plot?')
        self.save_skeleton_plots = tk.BooleanVar(value=True,
                                                 name='Save skeleton plot?')
        self.skeleton_plot_prefix = tk.StringVar(value='skeleton-plot-',
                                         name='Prefix for skeleton plots')
        self.output_filename = tk.StringVar(value='skeleton.xlsx',
                                            name='Output filename')
        self.parameters = [
            self.crop_radius,
            self.smooth_method,
            self.smooth_radius,
            self.threshold_radius,
            self.brightness_offset,
            self.image_format,
            self.scale_metadata_path,
            self.preview_skeleton_plots,
            self.save_skeleton_plots,
            self.skeleton_plot_prefix,
            self.output_filename,
        ]

        self.input_files = []
        self.output_folder = None

        if params_dict is None:
            params_dict = {}
        self.params_dict = params_dict.copy()
        self.parameter_config(params_dict)

        # allow resizing
        self.rowconfigure(0, weight=1)
        self.columnconfigure(0, weight=1)

        self.create_main_frame()

    def parameter_config(self, params_dict):
        """Set parameter values from a config dictionary."""
        if isinstance(params_dict, str):
            if params_dict.startswith('{'):  # JSON string
                params_dict = json.loads(params_dict)
            else:  # config file
                with open(params_dict) as params_fin:
                    params_dict = json.load(params_fin)
            self.params_dict.update(params_dict)
        name2param = {p._name.lower(): p for p in self.parameters}
        for param, value in self.params_dict.items():
            if param.lower() in name2param:
                name2param[param].set(value)
                params_dict.pop(param)
        for param, value in params_dict.copy().items():
            if param.lower() == 'input files':
                self.input_files = value
                params_dict.pop(param)
            elif param.lower() == 'output folder':
                self.output_folder = Path(os.path.expanduser(value))
                params_dict.pop(param)
            elif param.lower() == 'version':
                print(f'Parameter file version: {params_dict.pop(param)}')
        for param in params_dict:
            print(f'Parameter not recognised: {param}')

    def save_parameters(self, filename=None):
        out = {p._name.lower(): p.get() for p in self.parameters}
        out['input files'] = self.input_files
        out['output folder'] = str(self.output_folder)
        out['version'] = __version__
        if filename is None:
            return json.dumps(out)
        attempt = 0
        base, ext = os.path.splitext(filename)
        while os.path.exists(filename):
            filename = f'{base} ({attempt}){ext}'
            attempt += 1
        with open(filename, mode='wt') as fout:
            json.dump(out, fout, indent=2)

    def create_main_frame(self):
        main = ttk.Frame(master=self, padding=STANDARD_MARGIN)
        main.grid(row=0, column=0, sticky='nsew')
        self.create_parameters_frame(main)
        self.create_buttons_frame(main)
        main.pack()

    def create_parameters_frame(self, parent):
        parameters = ttk.Frame(master=parent, padding=STANDARD_MARGIN)
        parameters.grid(sticky='nsew')

        heading = ttk.Label(parameters, text='Analysis parameters')
        heading.grid(column=0, row=0, sticky='n')

        for i, param in enumerate(self.parameters, start=1):
            param_label = ttk.Label(parameters, text=param._name)
            param_label.grid(row=i, column=0, sticky='nsew')
            if type(param) == tk.BooleanVar:
                param_entry = ttk.Checkbutton(parameters, variable=param)
            elif hasattr(param, '_choices'):
                param_entry = ttk.OptionMenu(parameters, param, param.get(),
                                             *param._choices.keys())
            else:
                param_entry = ttk.Entry(parameters, textvariable=param)
            param_entry.grid(row=i, column=1, sticky='nsew')

    def create_buttons_frame(self, parent):
        buttons = ttk.Frame(master=parent, padding=STANDARD_MARGIN)
        buttons.grid(sticky='nsew')
        actions = [
            ('Choose config', self.choose_config_file),
            ('Choose files', self.choose_input_files),
            ('Choose output folder', self.choose_output_folder),
            ('Run', lambda: asyncio.ensure_future(self.run()))
        ]
        for col, (action_name, action) in enumerate(actions):
            button = ttk.Button(buttons, text=action_name,
                                command=action)
            button.grid(row=0, column=col)

    def choose_config_file(self):
        config_file = tk.filedialog.askopenfilename()
        self.parameter_config(config_file)

    def choose_input_files(self):
        self.input_files = tk.filedialog.askopenfilenames()
        if len(self.input_files) > 0 and self.output_folder is None:
            self.output_folder = Path(os.path.dirname(self.input_files[0]))

    def choose_output_folder(self):
        self.output_folder = Path(
                tk.filedialog.askdirectory(initialdir=self.output_folder))

    def make_figure_window(self):
        self.figure_window = tk.Toplevel(self)
        self.figure_window.wm_title('Preview')
        screen_dpi = self.figure_window.winfo_fpixels('1i')
        screen_width = self.figure_window.winfo_screenwidth()  # in pixels
        figure_width = screen_width / 2 / screen_dpi
        figure_height = 0.75 * figure_width
        self.figure = Figure(figsize=(figure_width, figure_height),
                             dpi=screen_dpi)
        ax0 = self.figure.add_subplot(221)
        axes = [self.figure.add_subplot(220 + i, sharex=ax0, sharey=ax0)
                for i in range(2, 5)]
        self.axes = np.array([ax0] + axes)
        canvas = FigureCanvasTkAgg(self.figure, master=self.figure_window)
        canvas.show()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        toolbar = NavigationToolbar2TkAgg(canvas, self.figure_window)
        toolbar.update()
        canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)

    async def run(self):
        print('Input files:')
        for file in self.input_files:
            print('  ', file)
        print('Parameters:')
        for param in self.parameters:
            p = param.get()
            print('  ', param, type(p), p)
        print('Output:', self.output_folder)
        save_skeleton = ('' if not self.save_skeleton_plots.get() else
                         self.skeleton_plot_prefix.get())
        images_iterator = pipe.process_images(
                self.input_files, self.image_format.get(),
                self.threshold_radius.get(),
                self.smooth_radius.get(),
                self.brightness_offset.get(),
                self.scale_metadata_path.get(),
                crop_radius=self.crop_radius.get(),
                smooth_method=self.smooth_method.get())
        if self.preview_skeleton_plots.get():
            self.make_figure_window()
        elif self.save_skeleton_plots.get():
            self.figure = plt.figure()
            ax0 = self.figure.add_subplot(221)
            axes = [self.figure.add_subplot(220 + i, sharex=ax0, sharey=ax0)
                    for i in range(2, 5)]
            self.axes = np.array([ax0] + axes)
        self.save_parameters(self.output_folder / 'skan-config.json')
        for i, result in enumerate(images_iterator):
            if i < len(self.input_files):
                filename, image, thresholded, skeleton, framedata = result
                if save_skeleton:
                    for ax in self.axes:
                        ax.clear()
                    w, h = draw.pixel_perfect_figsize(image)
                    self.figure.set_size_inches(4*w, 4*h)
                    draw.pipeline_plot(image, thresholded, skeleton, framedata,
                                       figure=self.figure, axes=self.axes)
                    output_basename = (save_skeleton +
                                       os.path.basename(
                                           os.path.splitext(filename)[0]) +
                                       '.png')
                    output_filename = str(self.output_folder / output_basename)
                    self.figure.savefig(output_filename)
                if self.preview_skeleton_plots.get():
                    self.figure.canvas.draw_idle()
            else:
                result_full, result_image = result
                result_filtered = result_full[result_full['mean shape index']>0.125 &
                                              result_full['mean shape index']<0.625]
                io.write_excel(str(self.output_folder /
                                   self.output_filename.get()),
                               branches=result_full,
                               images=result_image,
                               filtered=result_filtered,
                               parameters=json.loads(self.save_parameters()))

def tk_update(loop, app):
    try:
        app.update()
    except tkinter.TclError:
        loop.stop()
        return
    loop.call_later(.01, tk_update, loop, app)

@click.command()
@click.option('-c', '--config', default='',
              help='JSON configuration file.')
def launch(config):
    params = json.load(open(config)) if config else None
    app = Launch(params)
    loop = asyncio.get_event_loop()
    tk_update(loop, app)
    loop.run_forever()
<|endoftext|>


<commit_before>import os
import os.path
import shutil

from base_funcs import *
from base import InhibitorObject

class InhibitorSnapshot(InhibitorObject):
    """
    Manage a single snapshot such that catalyst will be happy with it.

    TODO:
        - support git branches
    """

    def __init__(self, name, **keywords):
        self.name = name

        settings_conf = {
            'snapshot': {
                'required_keys':    ['type', 'src'],
                'valid_keys':       ['rev'],
                'init_args':        {'name':name}
            }
        }

        super(InhibitorSnapshot, self).__init__(
            settings_conf=settings_conf,
            **keywords)

        self.repodir        = os.path.join(self.base['repo_cache'] + name ) + '/'
        self.snapdir        = self.base['snapshots']
        self.snapfile       = None
        self.type           = self.snapshot['type']

        if 'force' in keywords:
            self.force = True
        else:
            self.force = False

        if not 'rev' in self.snapshot:
            self.rev = None
        else:
            self.rev            = self.snapshot['rev']

        if not self.type in ['svn', 'git']:
            raise InhibitorError('Unknown snapshot src type:  \'%s\'' % self.src)

        for p in [self.repodir, self.snapdir]:
            if not os.path.exists(p):
                try:
                    os.makedirs(p)
                except OSError, e:
                    raise InhibitorError('Cannot create path %s : %s' % (p, e))

    def run(self):
        if self.type == 'git':
            self._git_create_snapshot()
        elif self.type == 'svn':
            self._svn_create_snapshot()

        self.create_cachedir()

    def create_cachedir(self):
        base_dir = os.path.join(self.base['snapshot_cache'], '%s-%s' % (self.name, self.rev) )
        if os.path.exists(base_dir):
            shutil.rmtree(base_dir)

        os.makedirs(base_dir)
        os.symlink('overlay', os.path.join(base_dir, 'portage'))

        cmd('tar -xjpf %s -C %s/' % (self.snapfile, base_dir))
        if 'catalyst_support' in self.base and self.base['catalyst_support']:
            write_hashfile(base_dir, self.snapfile, {'md5':None}, dest_filename='catalyst-hash')

    def _git_create_snapshot(self):
        myenv={'GIT_DIR':os.path.join(self.repodir, '.git')}

        if not os.path.isdir( myenv['GIT_DIR'] ):
            if os.path.exists(self.repodir):
                warn('%s is not a git repository.  Removing.' % self.repodir)
                try:
                    shutil.rmtree(self.repodir)
                except OSError, e:
                    InhibitorError('Cannot clean directory: %s' % e)

            cmd('git clone %s %s' % (self.src, self.repodir))
        else:
            cmd('git pull', env=myenv)

        if self.rev == None:
            self.rev = file_getline( os.path.join(myenv['GIT_DIR'], 'refs', 'heads', 'master') )
            self.rev = self.rev[:7]

        self.snapfile = os.path.join(self.snapdir, '%s-%s.tar.bz2'
            % (self.name, self.rev))

        if not self.force and os.path.exists( self.snapfile ):
            info('Skipping archive step, %s already exists' % os.path.basename(self.snapfile))
            return

        if os.path.exists(self.snapfile):
            try:
                os.unlink(self.snapfile)
            except OSError, e:
                raise InhibitorError("Failed to remove %s: %s" % (self.snapfile, e))

        cmd('git archive --format=tar --prefix=overlay/ %s | bzip2 --fast -f > %s'
            % (self.rev, self.snapfile), env=myenv)

        md5_hash = get_checksum(self.snapfile)
        write_hashfile(self.snapdir, self.snapfile, {'md5':md5_hash})

    def _svn_create_snapshot(self):
        svndir = os.path.join(self.repodir, '.svn')
        if not os.path.isdir(svndir):
            if os.path.exists(self.repodir):
                warn('%s is not a svn repository.  Removing.' % self.repodir)
                try:
                    shutil.rmtree(self.repodir)
                except OSError, e:
                    InhibitorError('Cannot clean directory: %s' %e)
            cmd('svn checkout %s %s' % (self.src, self.repodir))

        if self.rev == None:
            cmd('svn up %s' % (self.repodir))
            self.rev = cmd_out("svn info %s | awk '/Revision/{print $2}'" % self.repodir)
            self.rev = 'r' + self.rev
        else:
            if self.rev[0] != 'r':
                self.rev = 'r' + self.rev
            cmd('svn up -%s %s' % (self.rev, self.repodir))

        self.snapfile = os.path.join(self.snapdir, '%s-%s.tar.bz2'
            % (self.name, self.rev))

        if not self.force and os.path.exists( self.snapfile ):
            info('Skipping archive step, %s already exists' % os.path.basename(self.snapfile))
            return

        cmd("cd %s;tar -cjf %s --exclude='.svn' --transform='s,^,overlay/,' ./"
            % (self.repodir, self.snapfile) )

if __name__ == '__main__':
    import getopt
    name        = None
    src         = None
    type        = None
    rev         = None
    force       = False
    configfile  = None

    usage =  """snapshot.py [ARGUMENTS]
Creates a snapshot from a specified subversion or git repository.

REQUIRED ARGUMENTS:
    -n, --name <name>       Snapshot name
    -s, --src  <src>        Snapshot source
    -c, --config <path>     Config file path

OPTIONAL ARGUMENTS:
    -t, --type <type>       Snapshot type
    -r, --rev  <rev>        Revison to snapshot (default is HEAD)
    -f, --force             Force overwriting of files.
    -h, --help              This screen
"""

    try:
        sa, la = getopt.gnu_getopt(sys.argv[1:], "n:s:t:r:fhc:",
                    [   'name',
                        'source',
                        'type',
                        'rev',
                        'force',
                        'help',
                        'config' ])
    except getopt.GetoptError, e:
        raise InhibitorError("Error parsing commandline: %s" % e)

    args = {}
    for o, a in sa:
        if o in ('-n', '--name'):
            name = a
        elif o in ('-s', '--source'):
            args['src'] = a
        elif o in ('-t', '--type'):
            args['type'] = a
        elif o in ('-r', '--rev'):
            args['rev'] = a
        elif o in ('-f', '--force'):
            args['force'] = True
        elif o in ('-h', '--help'):
            print usage
            sys.exit(0)
        elif o in ('-c', '--config'):
            args['config_file'] = a
        else:
            import errno
            str=''
            if a:
               str = "="+a
            raise InhibitorError("Unknown option in command line '%s'%s" % (o, str))
    if name == None:
        raise InhibitorError("name (-n) must be defined.")

    InhibitorSnapshot(name, **args).run()

<commit_msg>snapshot:  Set repodir correctly.<commit_after>import os
import os.path
import shutil

from base_funcs import *
from base import InhibitorObject

class InhibitorSnapshot(InhibitorObject):
    """
    Manage a single snapshot such that catalyst will be happy with it.

    TODO:
        - support git branches
    """

    def __init__(self, name, **keywords):
        self.name = name

        settings_conf = {
            'snapshot': {
                'required_keys':    ['type', 'src'],
                'valid_keys':       ['rev'],
                'init_args':        {'name':name}
            }
        }

        super(InhibitorSnapshot, self).__init__(
            settings_conf=settings_conf,
            **keywords)

        self.repodir        = os.path.join(self.base['repo_cache'], name ) + '/'
        self.snapdir        = self.base['snapshots']
        self.snapfile       = None
        self.type           = self.snapshot['type']

        if 'force' in keywords:
            self.force = True
        else:
            self.force = False

        if not 'rev' in self.snapshot:
            self.rev = None
        else:
            self.rev            = self.snapshot['rev']

        if not self.type in ['svn', 'git']:
            raise InhibitorError('Unknown snapshot src type:  \'%s\'' % self.src)

        for p in [self.repodir, self.snapdir]:
            if not os.path.exists(p):
                try:
                    os.makedirs(p)
                except OSError, e:
                    raise InhibitorError('Cannot create path %s : %s' % (p, e))

    def run(self):
        if self.type == 'git':
            self._git_create_snapshot()
        elif self.type == 'svn':
            self._svn_create_snapshot()

        self.create_cachedir()

    def create_cachedir(self):
        base_dir = os.path.join(self.base['snapshot_cache'], '%s-%s' % (self.name, self.rev) )
        if os.path.exists(base_dir):
            shutil.rmtree(base_dir)

        os.makedirs(base_dir)
        os.symlink('overlay', os.path.join(base_dir, 'portage'))

        cmd('tar -xjpf %s -C %s/' % (self.snapfile, base_dir))
        if 'catalyst_support' in self.base and self.base['catalyst_support']:
            write_hashfile(base_dir, self.snapfile, {'md5':None}, dest_filename='catalyst-hash')

    def _git_create_snapshot(self):
        myenv={'GIT_DIR':os.path.join(self.repodir, '.git')}

        if not os.path.isdir( myenv['GIT_DIR'] ):
            if os.path.exists(self.repodir):
                warn('%s is not a git repository.  Removing.' % self.repodir)
                try:
                    shutil.rmtree(self.repodir)
                except OSError, e:
                    InhibitorError('Cannot clean directory: %s' % e)

            cmd('git clone %s %s' % (self.src, self.repodir))
        else:
            cmd('git pull', env=myenv)

        if self.rev == None:
            self.rev = file_getline( os.path.join(myenv['GIT_DIR'], 'refs', 'heads', 'master') )
            self.rev = self.rev[:7]

        self.snapfile = os.path.join(self.snapdir, '%s-%s.tar.bz2'
            % (self.name, self.rev))

        if not self.force and os.path.exists( self.snapfile ):
            info('Skipping archive step, %s already exists' % os.path.basename(self.snapfile))
            return

        if os.path.exists(self.snapfile):
            try:
                os.unlink(self.snapfile)
            except OSError, e:
                raise InhibitorError("Failed to remove %s: %s" % (self.snapfile, e))

        cmd('git archive --format=tar --prefix=overlay/ %s | bzip2 --fast -f > %s'
            % (self.rev, self.snapfile), env=myenv)

        md5_hash = get_checksum(self.snapfile)
        write_hashfile(self.snapdir, self.snapfile, {'md5':md5_hash})

    def _svn_create_snapshot(self):
        svndir = os.path.join(self.repodir, '.svn')
        if not os.path.isdir(svndir):
            if os.path.exists(self.repodir):
                warn('%s is not a svn repository.  Removing.' % self.repodir)
                try:
                    shutil.rmtree(self.repodir)
                except OSError, e:
                    InhibitorError('Cannot clean directory: %s' %e)
            cmd('svn checkout %s %s' % (self.src, self.repodir))

        if self.rev == None:
            cmd('svn up %s' % (self.repodir))
            self.rev = cmd_out("svn info %s | awk '/Revision/{print $2}'" % self.repodir)
            self.rev = 'r' + self.rev
        else:
            if self.rev[0] != 'r':
                self.rev = 'r' + self.rev
            cmd('svn up -%s %s' % (self.rev, self.repodir))

        self.snapfile = os.path.join(self.snapdir, '%s-%s.tar.bz2'
            % (self.name, self.rev))

        if not self.force and os.path.exists( self.snapfile ):
            info('Skipping archive step, %s already exists' % os.path.basename(self.snapfile))
            return

        cmd("cd %s;tar -cjf %s --exclude='.svn' --transform='s,^,overlay/,' ./"
            % (self.repodir, self.snapfile) )

if __name__ == '__main__':
    import getopt
    name        = None
    src         = None
    type        = None
    rev         = None
    force       = False
    configfile  = None

    usage =  """snapshot.py [ARGUMENTS]
Creates a snapshot from a specified subversion or git repository.

REQUIRED ARGUMENTS:
    -n, --name <name>       Snapshot name
    -s, --src  <src>        Snapshot source
    -c, --config <path>     Config file path

OPTIONAL ARGUMENTS:
    -t, --type <type>       Snapshot type
    -r, --rev  <rev>        Revison to snapshot (default is HEAD)
    -f, --force             Force overwriting of files.
    -h, --help              This screen
"""

    try:
        sa, la = getopt.gnu_getopt(sys.argv[1:], "n:s:t:r:fhc:",
                    [   'name',
                        'source',
                        'type',
                        'rev',
                        'force',
                        'help',
                        'config' ])
    except getopt.GetoptError, e:
        raise InhibitorError("Error parsing commandline: %s" % e)

    args = {}
    for o, a in sa:
        if o in ('-n', '--name'):
            name = a
        elif o in ('-s', '--source'):
            args['src'] = a
        elif o in ('-t', '--type'):
            args['type'] = a
        elif o in ('-r', '--rev'):
            args['rev'] = a
        elif o in ('-f', '--force'):
            args['force'] = True
        elif o in ('-h', '--help'):
            print usage
            sys.exit(0)
        elif o in ('-c', '--config'):
            args['config_file'] = a
        else:
            import errno
            str=''
            if a:
               str = "="+a
            raise InhibitorError("Unknown option in command line '%s'%s" % (o, str))
    if name == None:
        raise InhibitorError("name (-n) must be defined.")

    InhibitorSnapshot(name, **args).run()

<|endoftext|>


<commit_before>from datetime import datetime
from devilry.apps.core.testhelper import TestHelper
from devilry.apps.core.models import Period

from .base import SubjectAdminSeleniumTestCase
from .base import RenameBasenodeTestMixin
from .base import DeleteBasenodeTestMixin
from .base import EditAdministratorsTestMixin

class PeriodTestCommonMixin(object):
    def browseToPeriod(self, id):
        self.browseTo('/period/{id}/'.format(id=id))

    def loginToPeriod(self, username, id):
        self.loginTo(username, '/period/{id}/'.format(id=id))

class TestPeriodOverview(SubjectAdminSeleniumTestCase, PeriodTestCommonMixin,
                         RenameBasenodeTestMixin, DeleteBasenodeTestMixin):
    renamebutton_id = 'periodRenameButton'
    deletebutton_id = 'periodDeleteButton'

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni:admin(uniadmin)',
                            subjects=['duck9000'],
                            periods=['period1:admin(period1admin):ln(Period One)'])

    def test_doesnotexists(self):
        self.login('uniadmin')
        self.browseToPeriod(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('403: FORBIDDEN' in self.selenium.page_source)

    def test_doesnotexists_superadmin(self):
        self.testhelper.create_superuser('grandma')
        self.login('grandma')
        self.browseToPeriod(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('404: NOT FOUND' in self.selenium.page_source)

    def test_breadcrumb(self):
        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        breadcrumbtext = self.get_breadcrumbstring('duck9000')
        self.assertEquals(breadcrumbtext, ['All subjects', 'duck9000', 'period1'])

    def _get_assignment_url(self, assignment):
        return '#/assignment/{0}/'.format(assignment.id)

    def test_list_of_assignments(self):
        self.testhelper.add_to_path('uni;duck9000.period1.a1:ln(Assignment One)')
        self.testhelper.add_to_path('uni;duck9000.period1.a2:ln(Assignment Two)')
        self.testhelper.add_to_path('uni;duck9000.period2.first:ln(First Assignment)')

        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForText('Period One')
        self.waitForCssSelector('li.devilry_assignment')

        assignmentlist = self.selenium.find_element_by_css_selector('.devilry_listofassignments')
        self.assertEquals(len(assignmentlist.find_elements_by_css_selector('li.devilry_assignment')), 2)
        self.assertTrue('Assignment One' in self.selenium.page_source)
        self.assertTrue('Assignment Two' in self.selenium.page_source)
        self.assertFalse('First Assignment' in self.selenium.page_source)
        self.assertIn(self._get_assignment_url(self.testhelper.duck9000_period1_a1), self.selenium.page_source)
        self.assertIn(self._get_assignment_url(self.testhelper.duck9000_period1_a2), self.selenium.page_source)
        self.assertNotIn(self._get_assignment_url(self.testhelper.duck9000_period2_first), self.selenium.page_source)

    def test_dangerous_panel(self):
        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.waitForText('Delete duck9000.period1')
        self.waitForText('Rename duck9000.period1')
        self.assertIn('Once you delete a ', self.selenium.page_source)
        self.assertIn('should not done without a certain amount of consideration', self.selenium.page_source)

    def test_rename(self):
        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.rename_test_helper(self.testhelper.duck9000_period1)

    def test_rename_failure(self):
        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.rename_test_failure_helper()

    def test_delete(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['willbedeleted'])
        self.login('uniadmin')
        self.browseToPeriod(self.testhelper.sub_willbedeleted.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        periodurl = self.selenium.current_url
        self.perform_delete()
        self.waitFor(self.selenium, lambda s: s.current_url != periodurl) # Will time out and fail unless the page is changed after delete
        self.assertEquals(Period.objects.filter(id=self.testhelper.sub_willbedeleted.id).count(), 0)

    def test_delete_notparentadmin(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['willbedeleted:admin(willbedeletedadm)'])
        self.login('willbedeletedadm')
        self.browseToPeriod(self.testhelper.sub_willbedeleted.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.click_delete_button()
        self.waitForText('Only superusers can delete non-empty items') # Will time out and fail unless the dialog is shown

    def test_delete_not_empty(self):
        self.testhelper.add_to_path('uni;duck9000.period1.a1:ln(Assignment One)')
        self.login('uniadmin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.click_delete_button()
        self.waitForText('Only superusers can delete non-empty items') # Will time out and fail unless the dialog is shown

class TestPeriodEditAdministrators(SubjectAdminSeleniumTestCase, PeriodTestCommonMixin,
                                   EditAdministratorsTestMixin):

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni:admin(uniadmin)',
                            subjects=['sub'],
                            periods=['p1'])
        self.login('uniadmin')

    def getBasenode(self):
        return self.testhelper.sub_p1

    def browseToTestBasenode(self):
        self.browseToPeriod(self.getBasenode().id)

class TestPeriodEditDuration(SubjectAdminSeleniumTestCase, PeriodTestCommonMixin):
    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['p1:admin(p1admin)'])
        self.p1 = self.testhelper.sub_p1
        self.p1.start_time = datetime(2005, 1, 1)
        self.p1.end_time = datetime(2006, 1, 1)
        self.p1.save()
        self.loginToPeriod('p1admin', self.p1.id)

        self.readOnlyPanel = self.waitForAndFindElementByCssSelector('.devilry_subjectadmin_editperiod_duration_widget .containerwithedittitle')

    def _click_edit(self):
        button = self.waitForAndFindElementByCssSelector('.devilry_subjectadmin_editperiod_duration_widget .containerwithedittitle .edit_link')
        button.click()

        panel = self.waitForAndFindElementByCssSelector('.devilry_subjectadmin_editdurationpanel')
        self.start_time_datefield = panel.find_element_by_css_selector('.start_time_field .devilry_extjsextras_datefield input')
        self.start_time_timefield = panel.find_element_by_css_selector('.start_time_field .devilry_extjsextras_timefield input')
        self.end_time_datefield = panel.find_element_by_css_selector('.end_time_field .devilry_extjsextras_datefield input')
        self.end_time_timefield = panel.find_element_by_css_selector('.end_time_field .devilry_extjsextras_timefield input')
        self.savebutton = panel.find_element_by_css_selector('.okbutton button')
        self.cancelbutton = panel.find_element_by_css_selector('.cancelbutton button')
        self.editpanel = panel

    def _set_values(self, start_date, start_time, end_date, end_time):
        self.start_time_datefield.clear()
        self.start_time_timefield.clear()
        self.end_time_datefield.clear()
        self.end_time_timefield.clear()
        self.start_time_datefield.send_keys(start_date)
        self.start_time_timefield.send_keys(start_time)
        self.end_time_datefield.send_keys(end_date)
        self.end_time_timefield.send_keys(end_time)

    def _get_durationdisplay(self):
        return self.readOnlyPanel.find_element_by_css_selector('.durationdisplay').text.strip()

    def test_render(self):
        self.waitForDisplayed(self.readOnlyPanel)
        self.assertEquals(self._get_durationdisplay(),
                          '2005-01-01 00:00 - 2006-01-01 00:00')

    def test_edit(self):
        self._click_edit()
        self._set_values(start_date='2000-12-24', start_time='12:00',
                         end_date='2001-11-22', end_time='16:00')
        self.savebutton.click()
        self.waitForDisplayed(self.readOnlyPanel)
        self.assertEquals(self._get_durationdisplay(),
                          '2000-12-24 12:00 - 2001-11-22 16:00')
        p1 = self.testhelper.reload_from_db(self.p1)
        self.assertEquals(p1.start_time, datetime(2000, 12, 24, 12, 0))
        self.assertEquals(p1.end_time, datetime(2001, 11, 22, 16, 0))

    def test_errorhandling(self):
        self._click_edit()
        self._set_values(start_date='', start_time='12:00',
                         end_date='2001-11-22', end_time='16:00')
        self.savebutton.click()
        self.waitFor(self.editpanel, lambda p: 'Start time: This field is required' in p.text)
<commit_msg>devilry_subjectadmin.tests.period: Updated for smart breadcrumbs.<commit_after>from datetime import datetime
from devilry.apps.core.testhelper import TestHelper
from devilry.apps.core.models import Period

from .base import SubjectAdminSeleniumTestCase
from .base import RenameBasenodeTestMixin
from .base import DeleteBasenodeTestMixin
from .base import EditAdministratorsTestMixin

class PeriodTestCommonMixin(object):
    def browseToPeriod(self, id):
        self.browseTo('/period/{id}/'.format(id=id))

    def loginToPeriod(self, username, id):
        self.loginTo(username, '/period/{id}/'.format(id=id))

class TestPeriodOverview(SubjectAdminSeleniumTestCase, PeriodTestCommonMixin,
                         RenameBasenodeTestMixin, DeleteBasenodeTestMixin):
    renamebutton_id = 'periodRenameButton'
    deletebutton_id = 'periodDeleteButton'

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni:admin(uniadmin)',
                            subjects=['duck9000'],
                            periods=['period1:admin(period1admin):ln(Period One)'])

    def test_doesnotexists(self):
        self.login('uniadmin')
        self.browseToPeriod(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('403: FORBIDDEN' in self.selenium.page_source)

    def test_doesnotexists_superadmin(self):
        self.testhelper.create_superuser('grandma')
        self.login('grandma')
        self.browseToPeriod(100000)
        self.waitForCssSelector('.devilry_extjsextras_alertmessagelist')
        self.assertTrue('404: NOT FOUND' in self.selenium.page_source)

    def test_breadcrumb(self):
        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        breadcrumbtext = self.get_breadcrumbstring('duck9000')
        self.assertEquals(breadcrumbtext, ['All subjects', 'duck9000.period1'])

    def _get_assignment_url(self, assignment):
        return '#/assignment/{0}/'.format(assignment.id)

    def test_list_of_assignments(self):
        self.testhelper.add_to_path('uni;duck9000.period1.a1:ln(Assignment One)')
        self.testhelper.add_to_path('uni;duck9000.period1.a2:ln(Assignment Two)')
        self.testhelper.add_to_path('uni;duck9000.period2.first:ln(First Assignment)')

        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForText('Period One')
        self.waitForCssSelector('li.devilry_assignment')

        assignmentlist = self.selenium.find_element_by_css_selector('.devilry_listofassignments')
        self.assertEquals(len(assignmentlist.find_elements_by_css_selector('li.devilry_assignment')), 2)
        self.assertTrue('Assignment One' in self.selenium.page_source)
        self.assertTrue('Assignment Two' in self.selenium.page_source)
        self.assertFalse('First Assignment' in self.selenium.page_source)
        self.assertIn(self._get_assignment_url(self.testhelper.duck9000_period1_a1), self.selenium.page_source)
        self.assertIn(self._get_assignment_url(self.testhelper.duck9000_period1_a2), self.selenium.page_source)
        self.assertNotIn(self._get_assignment_url(self.testhelper.duck9000_period2_first), self.selenium.page_source)

    def test_dangerous_panel(self):
        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.waitForText('Delete duck9000.period1')
        self.waitForText('Rename duck9000.period1')
        self.assertIn('Once you delete a ', self.selenium.page_source)
        self.assertIn('should not done without a certain amount of consideration', self.selenium.page_source)

    def test_rename(self):
        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.rename_test_helper(self.testhelper.duck9000_period1)

    def test_rename_failure(self):
        self.login('period1admin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.rename_test_failure_helper()

    def test_delete(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['willbedeleted'])
        self.login('uniadmin')
        self.browseToPeriod(self.testhelper.sub_willbedeleted.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        periodurl = self.selenium.current_url
        self.perform_delete()
        self.waitFor(self.selenium, lambda s: s.current_url != periodurl) # Will time out and fail unless the page is changed after delete
        self.assertEquals(Period.objects.filter(id=self.testhelper.sub_willbedeleted.id).count(), 0)

    def test_delete_notparentadmin(self):
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['willbedeleted:admin(willbedeletedadm)'])
        self.login('willbedeletedadm')
        self.browseToPeriod(self.testhelper.sub_willbedeleted.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.click_delete_button()
        self.waitForText('Only superusers can delete non-empty items') # Will time out and fail unless the dialog is shown

    def test_delete_not_empty(self):
        self.testhelper.add_to_path('uni;duck9000.period1.a1:ln(Assignment One)')
        self.login('uniadmin')
        self.browseToPeriod(self.testhelper.duck9000_period1.id)
        self.waitForCssSelector('.devilry_subjectadmin_periodoverview')
        self.click_delete_button()
        self.waitForText('Only superusers can delete non-empty items') # Will time out and fail unless the dialog is shown

class TestPeriodEditAdministrators(SubjectAdminSeleniumTestCase, PeriodTestCommonMixin,
                                   EditAdministratorsTestMixin):

    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni:admin(uniadmin)',
                            subjects=['sub'],
                            periods=['p1'])
        self.login('uniadmin')

    def getBasenode(self):
        return self.testhelper.sub_p1

    def browseToTestBasenode(self):
        self.browseToPeriod(self.getBasenode().id)

class TestPeriodEditDuration(SubjectAdminSeleniumTestCase, PeriodTestCommonMixin):
    def setUp(self):
        self.testhelper = TestHelper()
        self.testhelper.add(nodes='uni',
                            subjects=['sub'],
                            periods=['p1:admin(p1admin)'])
        self.p1 = self.testhelper.sub_p1
        self.p1.start_time = datetime(2005, 1, 1)
        self.p1.end_time = datetime(2006, 1, 1)
        self.p1.save()
        self.loginToPeriod('p1admin', self.p1.id)

        self.readOnlyPanel = self.waitForAndFindElementByCssSelector('.devilry_subjectadmin_editperiod_duration_widget .containerwithedittitle')

    def _click_edit(self):
        button = self.waitForAndFindElementByCssSelector('.devilry_subjectadmin_editperiod_duration_widget .containerwithedittitle .edit_link')
        button.click()

        panel = self.waitForAndFindElementByCssSelector('.devilry_subjectadmin_editdurationpanel')
        self.start_time_datefield = panel.find_element_by_css_selector('.start_time_field .devilry_extjsextras_datefield input')
        self.start_time_timefield = panel.find_element_by_css_selector('.start_time_field .devilry_extjsextras_timefield input')
        self.end_time_datefield = panel.find_element_by_css_selector('.end_time_field .devilry_extjsextras_datefield input')
        self.end_time_timefield = panel.find_element_by_css_selector('.end_time_field .devilry_extjsextras_timefield input')
        self.savebutton = panel.find_element_by_css_selector('.okbutton button')
        self.cancelbutton = panel.find_element_by_css_selector('.cancelbutton button')
        self.editpanel = panel

    def _set_values(self, start_date, start_time, end_date, end_time):
        self.start_time_datefield.clear()
        self.start_time_timefield.clear()
        self.end_time_datefield.clear()
        self.end_time_timefield.clear()
        self.start_time_datefield.send_keys(start_date)
        self.start_time_timefield.send_keys(start_time)
        self.end_time_datefield.send_keys(end_date)
        self.end_time_timefield.send_keys(end_time)

    def _get_durationdisplay(self):
        return self.readOnlyPanel.find_element_by_css_selector('.durationdisplay').text.strip()

    def test_render(self):
        self.waitForDisplayed(self.readOnlyPanel)
        self.assertEquals(self._get_durationdisplay(),
                          '2005-01-01 00:00 - 2006-01-01 00:00')

    def test_edit(self):
        self._click_edit()
        self._set_values(start_date='2000-12-24', start_time='12:00',
                         end_date='2001-11-22', end_time='16:00')
        self.savebutton.click()
        self.waitForDisplayed(self.readOnlyPanel)
        self.assertEquals(self._get_durationdisplay(),
                          '2000-12-24 12:00 - 2001-11-22 16:00')
        p1 = self.testhelper.reload_from_db(self.p1)
        self.assertEquals(p1.start_time, datetime(2000, 12, 24, 12, 0))
        self.assertEquals(p1.end_time, datetime(2001, 11, 22, 16, 0))

    def test_errorhandling(self):
        self._click_edit()
        self._set_values(start_date='', start_time='12:00',
                         end_date='2001-11-22', end_time='16:00')
        self.savebutton.click()
        self.waitFor(self.editpanel, lambda p: 'Start time: This field is required' in p.text)
<|endoftext|>


<commit_before>### -*- coding: utf-8 -*- ####################################################
from optparse import make_option

from django.core.management.base import BaseCommand, CommandError
from django.db.models import get_app, get_model, FieldDoesNotExist

SIMILARS_LENGTH = 40

class Command(BaseCommand):
    '''Calculates similar albums for each album in database'''
    args = ''
    help = 'Returns missing or empty track files from the SoftLayer'
    option_list = BaseCommand.option_list + (
        make_option('--app',
                    dest='app',
                    help='Django application to get models from. Example: myApp'),
        make_option('--models',
                    dest='models',
                    help='Model names list. Example: User,Album,Track'),
        make_option('--fields',
                    dest='fields',
                    help='File fields list. Example: file,image,audio_track'),
    )

    def handle(self, app, models, fields, *args, **options):
        self.index = 1
        if not app:
            raise CommandError('You must specify --app option')
        if not models:
            raise CommandError('You must specify --models option')
        if not fields:
            raise CommandError('You must specify --fields option')

        def printTrack(obj, reason):
            print "%s. Instance: %s\n\tID: %s\n\tReason: %s" % \
                  (self.index, unicode(obj), obj.pk, reason)
            self.index += 1

        def get_missing_files(model, field_name):
            print '\t-------------Searching missing or incomplete %ss in %s:------------\n' % \
                  (field_name, model.__name__)
            for obj in model.objects.all():
                try:
                    if getattr(obj, field_name).file.size == 0:
                        printTrack(obj, 'Size is 0')
                except:
                    printTrack(obj, 'FILE NOT FOUND')
            print '----------------------------------Done--------------------------------------'

        model_names = models.split(',')
        field_names = fields.split(',')

        for model_name in model_names:
            model = get_model(app, model_name)
            for field_name in field_names:
                try:
                    model._meta.get_field(field_name)
                except FieldDoesNotExist:
                    continue
                get_missing_files(model, field_name)

<commit_msg>fixed print enother time<commit_after>### -*- coding: utf-8 -*- ####################################################
from optparse import make_option

from django.core.management.base import BaseCommand, CommandError
from django.db.models import get_app, get_model, FieldDoesNotExist

SIMILARS_LENGTH = 40

class Command(BaseCommand):
    '''Calculates similar albums for each album in database'''
    args = ''
    help = 'Returns missing or empty track files from the SoftLayer'
    option_list = BaseCommand.option_list + (
        make_option('--app',
                    dest='app',
                    help='Django application to get models from. Example: myApp'),
        make_option('--models',
                    dest='models',
                    help='Model names list. Example: User,Album,Track'),
        make_option('--fields',
                    dest='fields',
                    help='File fields list. Example: file,image,audio_track'),
    )

    def handle(self, app, models, fields, *args, **options):
        self.index = 1
        if not app:
            raise CommandError('You must specify --app option')
        if not models:
            raise CommandError('You must specify --models option')
        if not fields:
            raise CommandError('You must specify --fields option')

        def printTrack(obj, reason):
            print "%s. Instance: %s\n\tID: %s\n\tReason: %s" % \
                  (self.index, unicode(obj), obj.pk, reason)
            self.index += 1

        def get_missing_files(model, field_name):
            print '----------------Searching missing or incomplete %ss in %s:--------------\n' % \
                  (field_name, model.__name__)
            for obj in model.objects.all():
                try:
                    if getattr(obj, field_name).file.size == 0:
                        printTrack(obj, 'Size is 0')
                except:
                    printTrack(obj, 'FILE NOT FOUND')
            print '----------------------------------Done--------------------------------------'

        model_names = models.split(',')
        field_names = fields.split(',')

        for model_name in model_names:
            model = get_model(app, model_name)
            for field_name in field_names:
                try:
                    model._meta.get_field(field_name)
                except FieldDoesNotExist:
                    continue
                get_missing_files(model, field_name)

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
#
# six documentation build configuration file

import os
import sys

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.append(os.path.abspath('.'))

# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "1.0"

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ["sphinx.ext.intersphinx"]

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# The suffix of source filenames.
source_suffix = ".rst"

# The encoding of source files.
#source_encoding = "utf-8-sig"

# The master toctree document.
master_doc = "index"

# General information about the project.
project = u"six"
copyright = u"2010, Benjamin Peterson"

sys.path.append(os.path.abspath(os.path.join(".", "..")))
from six import __version__ as six_version
sys.path.pop()

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = six_version[:-2]
# The full version, including alpha/beta/rc tags.
release = six_version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ["_build"]

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = "default"

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'sixdoc'

# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ("index", "six.tex", u"six Documentation",
   u"Benjamin Peterson", "manual"),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output --------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ("index", "six", u"six Documentation",
     [u"Benjamin Peterson"], 1)
]

# -- Intersphinx ---------------------------------------------------------------

intersphinx_mapping = {"py2" : ("http://docs.python.org/", None),
                       "py3" : ("http://docs.python.org/py3k/", None)}
<commit_msg>update intersphinx links<commit_after># -*- coding: utf-8 -*-
#
# six documentation build configuration file

import os
import sys

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.append(os.path.abspath('.'))

# -- General configuration -----------------------------------------------------

# If your documentation needs a minimal Sphinx version, state it here.
needs_sphinx = "1.0"

# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ["sphinx.ext.intersphinx"]

# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]

# The suffix of source filenames.
source_suffix = ".rst"

# The encoding of source files.
#source_encoding = "utf-8-sig"

# The master toctree document.
master_doc = "index"

# General information about the project.
project = u"six"
copyright = u"2010, Benjamin Peterson"

sys.path.append(os.path.abspath(os.path.join(".", "..")))
from six import __version__ as six_version
sys.path.pop()

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = six_version[:-2]
# The full version, including alpha/beta/rc tags.
release = six_version

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None

# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ["_build"]

# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None

# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True

# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True

# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False

# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"

# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []

# -- Options for HTML output ---------------------------------------------------

# The theme to use for HTML and HTML Help pages.  See the documentation for
# a list of builtin themes.
html_theme = "default"

# Theme options are theme-specific and customize the look and feel of a theme
# further.  For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}

# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []

# The name for this set of Sphinx documents.  If None, it defaults to
# "<project> v<release> documentation".
#html_title = None

# A shorter title for the navigation bar.  Default is the same as html_title.
#html_short_title = None

# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None

# The name of an image file (within the static path) to use as favicon of the
# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None

# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]

# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'

# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True

# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}

# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}

# If false, no module index is generated.
#html_domain_indices = True

# If false, no index is generated.
#html_use_index = True

# If true, the index is split into individual pages for each letter.
#html_split_index = False

# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True

# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True

# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True

# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it.  The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''

# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = ''

# Output file base name for HTML help builder.
htmlhelp_basename = 'sixdoc'

# -- Options for LaTeX output --------------------------------------------------

# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'

# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'

# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
  ("index", "six.tex", u"six Documentation",
   u"Benjamin Peterson", "manual"),
]

# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None

# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False

# If true, show page references after internal links.
#latex_show_pagerefs = False

# If true, show URL addresses after external links.
#latex_show_urls = False

# Additional stuff for the LaTeX preamble.
#latex_preamble = ''

# Documents to append as an appendix to all manuals.
#latex_appendices = []

# If false, no module index is generated.
#latex_domain_indices = True

# -- Options for manual page output --------------------------------------------

# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
    ("index", "six", u"six Documentation",
     [u"Benjamin Peterson"], 1)
]

# -- Intersphinx ---------------------------------------------------------------

intersphinx_mapping = {"py2" : ("http://docs.python.org/2/", None),
                       "py3" : ("http://docs.python.org/3/", None)}
<|endoftext|>


<commit_before>from django.http import HttpResponseServerError

class AJAXSimpleExceptionResponse:
    """Thanks to newmaniese of http://www.djangosnippets.org/snippets/650/ .

    Full doc (copied from link above).
    When debugging AJAX with Firebug, if a response is 500, it is a
    pain to have to view the entire source of the pretty exception page.
    This is a simple middleware that just returns the exception without
    any markup. You can add this anywhere in your python path and then
    put it in you settings file. It will only unprettify your exceptions
    when there is a XMLHttpRequest header. Tested in FF2 with the YUI XHR.
    Comments welcome.

    EDIT: I recently changed the request checking to use the is_ajax() method.
    This gives you access to these simple exceptions for get requests as well
    (even though you could just point your browser there).
    """
    def process_exception(self, request, exception):
        #if settings.DEBUG:
        if request.META.get('HTTP_X_REQUESTED_WITH', None) == 'XMLHttpRequest':
            import sys, traceback
            (exc_type, exc_info, tb) = sys.exc_info()
            response = "%s\n" % exc_type.__name__
            response += "%s\n\n" % exc_info
            response += "TRACEBACK:\n"
            for tb in traceback.format_tb(tb):
                response += "%s\n" % tb
            return HttpResponseServerError(response)<commit_msg>using the general function request.is_ajax() (since django 1.0)<commit_after>from django.http import HttpResponseServerError

class AJAXSimpleExceptionResponse:
    """Thanks to newmaniese of http://www.djangosnippets.org/snippets/650/ .

    Full doc (copied from link above).
    When debugging AJAX with Firebug, if a response is 500, it is a
    pain to have to view the entire source of the pretty exception page.
    This is a simple middleware that just returns the exception without
    any markup. You can add this anywhere in your python path and then
    put it in you settings file. It will only unprettify your exceptions
    when there is a XMLHttpRequest header. Tested in FF2 with the YUI XHR.
    Comments welcome.

    EDIT: I recently changed the request checking to use the is_ajax() method.
    This gives you access to these simple exceptions for get requests as well
    (even though you could just point your browser there).
    """
    def process_exception(self, request, exception):
        #if settings.DEBUG:
        if request.is_ajax(): # new in django version 1.0
            import sys, traceback
            (exc_type, exc_info, tb) = sys.exc_info()
            response = "%s\n" % exc_type.__name__
            response += "%s\n\n" % exc_info
            response += "TRACEBACK:\n"
            for tb in traceback.format_tb(tb):
                response += "%s\n" % tb
            return HttpResponseServerError(response)<|endoftext|>