<commit_before>import argparse
import datetime
import os

import shutil
from typing import List

from sklearn import metrics
from time import time

import numpy
import numpy as np
from keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau
from keras.preprocessing.image import ImageDataGenerator

from TrainingHistoryPlotter import TrainingHistoryPlotter
from datasets.DatasetSplitter import DatasetSplitter
from datasets.HomusDatasetDownloader import HomusDatasetDownloader
from datasets.HomusImageGenerator import HomusImageGenerator
from models.ConfigurationFactory import ConfigurationFactory

def train_model(dataset_directory: str,
                model_name: str,
                show_plot_after_training: bool,
                delete_and_recreate_dataset_directory: bool,
                stroke_thicknesses: List[int],
                width: int,
                height: int,
                staff_line_vertical_offsets: List[int],
                training_minibatch_size: int,
                optimizer: str):
    raw_dataset_directory = os.path.join(dataset_directory, "raw")
    image_dataset_directory = os.path.join(dataset_directory, "images")

    if delete_and_recreate_dataset_directory:
        print("Deleting dataset directory {0}".format(dataset_directory))
        if os.path.exists(dataset_directory):
            shutil.rmtree(dataset_directory)

        dataset_downloader = HomusDatasetDownloader(raw_dataset_directory)
        dataset_downloader.download_and_extract_dataset()
        HomusImageGenerator.create_images(raw_dataset_directory, image_dataset_directory,
                                          stroke_thicknesses, width, height, staff_line_vertical_offsets)

        dataset_splitter = DatasetSplitter(image_dataset_directory, image_dataset_directory)
        dataset_splitter.delete_split_directories()
        dataset_splitter.split_images_into_training_validation_and_test_set()

    print("Training on dataset...")
    start_time = time()

    training_configuration = ConfigurationFactory.get_configuration_by_name(model_name, optimizer, width, height, training_minibatch_size)

    train_generator = ImageDataGenerator(rotation_range=training_configuration.rotation_range,
                                         zoom_range=training_configuration.zoom_range
                                         )
    training_data_generator = train_generator.flow_from_directory(
            os.path.join(image_dataset_directory, "training"),
            target_size=(training_configuration.input_image_rows,
                         training_configuration.input_image_columns),
            batch_size=training_configuration.training_minibatch_size
    )
    training_steps_per_epoch = np.math.ceil(training_data_generator.samples / training_data_generator.batch_size)

    validation_generator = ImageDataGenerator()
    validation_data_generator = validation_generator.flow_from_directory(
            os.path.join(image_dataset_directory, "validation"),
            target_size=(
                training_configuration.input_image_rows,
                training_configuration.input_image_columns),
            batch_size=training_configuration.training_minibatch_size)
    validation_steps_per_epoch = np.math.ceil(validation_data_generator.samples / validation_data_generator.batch_size)

    test_generator = ImageDataGenerator()
    test_data_generator = test_generator.flow_from_directory(os.path.join(image_dataset_directory, "test"),
                                                             target_size=(training_configuration.input_image_rows,
                                                                          training_configuration.input_image_columns),
                                                             batch_size=training_configuration.training_minibatch_size,
                                                             shuffle=False)
    test_steps_per_epoch = np.math.ceil(test_data_generator.samples / test_data_generator.batch_size)

    model = training_configuration.classifier()
    model.summary()

    print("Model {0} loaded.".format(training_configuration.name()))
    print(training_configuration.summary())

    best_model_path = "{1}_{0}.h5".format(training_configuration.name(), datetime.date.today())

    model_checkpoint = ModelCheckpoint(best_model_path, monitor="val_acc", save_best_only=True, verbose=1)
    early_stop = EarlyStopping(monitor='val_acc',
                               patience=training_configuration.number_of_epochs_before_early_stopping,
                               verbose=1)
    learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc',
                                                patience=training_configuration.number_of_epochs_before_reducing_learning_rate,
                                                verbose=1,
                                                factor=training_configuration.learning_rate_reduction_factor,
                                                min_lr=training_configuration.minimum_learning_rate)
    history = model.fit_generator(
            generator=training_data_generator,
            steps_per_epoch=training_steps_per_epoch,
            epochs=training_configuration.number_of_epochs,
            callbacks=[model_checkpoint, early_stop, learning_rate_reduction],
            validation_data=validation_data_generator,
            validation_steps=validation_steps_per_epoch
    )

    print("Loading best model from check-point and testing...")
    # For some models, loading the model directly does not work, but loading the weights does
    # (see https://github.com/fchollet/keras/issues/4044#issuecomment-254921595)
    # best_model = keras.models.load_model(best_model_path)
    best_model = training_configuration.classifier()
    best_model.load_weights(best_model_path)

    test_data_generator.reset()
    class_labels = list(test_data_generator.class_indices.keys())
    true_classes = test_data_generator.classes
    predictions = best_model.predict_generator(test_data_generator, steps=test_steps_per_epoch)
    predicted_classes = numpy.argmax(predictions, axis=1)

    report = metrics.classification_report(true_classes, predicted_classes, target_names=class_labels)
    # accuracy = metrics.accuracy_score(true_classes, predicted_classes) # is the same as from evaluate_generator

    test_data_generator.reset()
    evaluation = best_model.evaluate_generator(test_data_generator, steps=test_steps_per_epoch)

    print(report)
    print("Total Loss: {0:.5f}".format(evaluation[0]))
    print("Total Accuracy: {0:0.5f}%".format(evaluation[1] * 100))
    print("Total Error: {0:0.5f}%".format((1 - evaluation[1]) * 100))

    end_time = time()
    print("Execution time: %.1fs" % (end_time - start_time))

    TrainingHistoryPlotter.plot_history(history,
                                        "{1}_{0}_{2:.1f}p.png".format(training_configuration.name(),
                                                                      datetime.date.today(),
                                                                      evaluation[1] * 100),
                                        show_plot=show_plot_after_training)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.register("type", "bool", lambda v: v.lower() == "true")
    parser.add_argument(
            "--dataset_directory",
            type=str,
            default="data",
            help="The directory, that is used for storing the images during training")
    parser.add_argument(
            "--model_name",
            type=str,
            default="vgg",
            help="The model used for training the network. Currently allowed values are \'simple\' or \'vgg\'")
    parser.add_argument(
            "--show_plot_after_training",
            nargs="?",
            const=False,
            type="bool",
            default=True,
            help="Whether to show a plot with the accuracies after training or not.")
    parser.add_argument(
            "--delete_and_recreate_dataset_directory",
            nargs="?",
            const=True,
            type="bool",
            default=True,
            help="Whether to delete and recreate the dataset-directory (by downloading the appropriate "
                 "files from the internet) or simply use whatever data currently is inside of that directory.")
    parser.add_argument("-s", "--stroke_thicknesses", dest="stroke_thicknesses", default="3",
                        help="Stroke thicknesses for drawing the generated bitmaps. May define comma-separated list "
                             "of multiple stroke thicknesses, e.g. '1,2,3'")
    parser.add_argument("-offsets", "--staff_line_vertical_offsets", dest="offsets", default="",
                        help="Optional vertical offsets in pixel for drawing the symbols with superimposed "
                             "staff-lines starting at this pixel-offset from the top. Multiple offsets possible, "
                             "e.g. '81,88,95'")
    parser.add_argument("--width", dest="width", default="128", help="Width of the generated images in pixel")
    parser.add_argument("--height", dest="height", default="224", help="Height of the generated images in pixel")
    parser.add_argument("--minibatch_size", dest="training_minibatch_size", default="64", help="Size of the minibatches for training")
    parser.add_argument("--optimizer", dest="optimizer", default="Adadelta", help="The optimizer used for the training")

    flags, unparsed = parser.parse_known_args()

    offsets = None
    if flags.offsets != "":
        offsets = [int(o) for o in flags.offsets.split(',')]

    train_model(flags.dataset_directory,
                flags.model_name,
                flags.show_plot_after_training,
                flags.delete_and_recreate_dataset_directory,
                [int(s) for s in flags.stroke_thicknesses.split(',')],
                int(flags.width),
                int(flags.height),
                offsets,
                flags.training_minibatch_size,
                flags.optimizer)
<commit_msg>Fixed type-error when calling train_model<commit_after>import argparse
import datetime
import os

import shutil
from typing import List

from sklearn import metrics
from time import time

import numpy
import numpy as np
from keras.callbacks import EarlyStopping, ModelCheckpoint, ReduceLROnPlateau
from keras.preprocessing.image import ImageDataGenerator

from TrainingHistoryPlotter import TrainingHistoryPlotter
from datasets.DatasetSplitter import DatasetSplitter
from datasets.HomusDatasetDownloader import HomusDatasetDownloader
from datasets.HomusImageGenerator import HomusImageGenerator
from models.ConfigurationFactory import ConfigurationFactory

def train_model(dataset_directory: str,
                model_name: str,
                show_plot_after_training: bool,
                delete_and_recreate_dataset_directory: bool,
                stroke_thicknesses: List[int],
                width: int,
                height: int,
                staff_line_vertical_offsets: List[int],
                training_minibatch_size: int,
                optimizer: str):
    raw_dataset_directory = os.path.join(dataset_directory, "raw")
    image_dataset_directory = os.path.join(dataset_directory, "images")

    if delete_and_recreate_dataset_directory:
        print("Deleting dataset directory {0}".format(dataset_directory))
        if os.path.exists(dataset_directory):
            shutil.rmtree(dataset_directory)

        dataset_downloader = HomusDatasetDownloader(raw_dataset_directory)
        dataset_downloader.download_and_extract_dataset()
        HomusImageGenerator.create_images(raw_dataset_directory, image_dataset_directory,
                                          stroke_thicknesses, width, height, staff_line_vertical_offsets)

        dataset_splitter = DatasetSplitter(image_dataset_directory, image_dataset_directory)
        dataset_splitter.delete_split_directories()
        dataset_splitter.split_images_into_training_validation_and_test_set()

    print("Training on dataset...")
    start_time = time()

    training_configuration = ConfigurationFactory.get_configuration_by_name(model_name, optimizer, width, height, training_minibatch_size)

    train_generator = ImageDataGenerator(rotation_range=training_configuration.rotation_range,
                                         zoom_range=training_configuration.zoom_range
                                         )
    training_data_generator = train_generator.flow_from_directory(
            os.path.join(image_dataset_directory, "training"),
            target_size=(training_configuration.input_image_rows,
                         training_configuration.input_image_columns),
            batch_size=training_configuration.training_minibatch_size
    )
    training_steps_per_epoch = np.math.ceil(training_data_generator.samples / training_data_generator.batch_size)

    validation_generator = ImageDataGenerator()
    validation_data_generator = validation_generator.flow_from_directory(
            os.path.join(image_dataset_directory, "validation"),
            target_size=(
                training_configuration.input_image_rows,
                training_configuration.input_image_columns),
            batch_size=training_configuration.training_minibatch_size)
    validation_steps_per_epoch = np.math.ceil(validation_data_generator.samples / validation_data_generator.batch_size)

    test_generator = ImageDataGenerator()
    test_data_generator = test_generator.flow_from_directory(os.path.join(image_dataset_directory, "test"),
                                                             target_size=(training_configuration.input_image_rows,
                                                                          training_configuration.input_image_columns),
                                                             batch_size=training_configuration.training_minibatch_size,
                                                             shuffle=False)
    test_steps_per_epoch = np.math.ceil(test_data_generator.samples / test_data_generator.batch_size)

    model = training_configuration.classifier()
    model.summary()

    print("Model {0} loaded.".format(training_configuration.name()))
    print(training_configuration.summary())

    best_model_path = "{1}_{0}.h5".format(training_configuration.name(), datetime.date.today())

    model_checkpoint = ModelCheckpoint(best_model_path, monitor="val_acc", save_best_only=True, verbose=1)
    early_stop = EarlyStopping(monitor='val_acc',
                               patience=training_configuration.number_of_epochs_before_early_stopping,
                               verbose=1)
    learning_rate_reduction = ReduceLROnPlateau(monitor='val_acc',
                                                patience=training_configuration.number_of_epochs_before_reducing_learning_rate,
                                                verbose=1,
                                                factor=training_configuration.learning_rate_reduction_factor,
                                                min_lr=training_configuration.minimum_learning_rate)
    history = model.fit_generator(
            generator=training_data_generator,
            steps_per_epoch=training_steps_per_epoch,
            epochs=training_configuration.number_of_epochs,
            callbacks=[model_checkpoint, early_stop, learning_rate_reduction],
            validation_data=validation_data_generator,
            validation_steps=validation_steps_per_epoch
    )

    print("Loading best model from check-point and testing...")
    # For some models, loading the model directly does not work, but loading the weights does
    # (see https://github.com/fchollet/keras/issues/4044#issuecomment-254921595)
    # best_model = keras.models.load_model(best_model_path)
    best_model = training_configuration.classifier()
    best_model.load_weights(best_model_path)

    test_data_generator.reset()
    class_labels = list(test_data_generator.class_indices.keys())
    true_classes = test_data_generator.classes
    predictions = best_model.predict_generator(test_data_generator, steps=test_steps_per_epoch)
    predicted_classes = numpy.argmax(predictions, axis=1)

    report = metrics.classification_report(true_classes, predicted_classes, target_names=class_labels)
    # accuracy = metrics.accuracy_score(true_classes, predicted_classes) # is the same as from evaluate_generator

    test_data_generator.reset()
    evaluation = best_model.evaluate_generator(test_data_generator, steps=test_steps_per_epoch)

    print(report)
    print("Total Loss: {0:.5f}".format(evaluation[0]))
    print("Total Accuracy: {0:0.5f}%".format(evaluation[1] * 100))
    print("Total Error: {0:0.5f}%".format((1 - evaluation[1]) * 100))

    end_time = time()
    print("Execution time: %.1fs" % (end_time - start_time))

    TrainingHistoryPlotter.plot_history(history,
                                        "{1}_{0}_{2:.1f}p.png".format(training_configuration.name(),
                                                                      datetime.date.today(),
                                                                      evaluation[1] * 100),
                                        show_plot=show_plot_after_training)

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.register("type", "bool", lambda v: v.lower() == "true")
    parser.add_argument(
            "--dataset_directory",
            type=str,
            default="data",
            help="The directory, that is used for storing the images during training")
    parser.add_argument(
            "--model_name",
            type=str,
            default="vgg",
            help="The model used for training the network. Currently allowed values are \'simple\' or \'vgg\'")
    parser.add_argument(
            "--show_plot_after_training",
            nargs="?",
            const=False,
            type="bool",
            default=True,
            help="Whether to show a plot with the accuracies after training or not.")
    parser.add_argument(
            "--delete_and_recreate_dataset_directory",
            nargs="?",
            const=True,
            type="bool",
            default=True,
            help="Whether to delete and recreate the dataset-directory (by downloading the appropriate "
                 "files from the internet) or simply use whatever data currently is inside of that directory.")
    parser.add_argument("-s", "--stroke_thicknesses", dest="stroke_thicknesses", default="3",
                        help="Stroke thicknesses for drawing the generated bitmaps. May define comma-separated list "
                             "of multiple stroke thicknesses, e.g. '1,2,3'")
    parser.add_argument("-offsets", "--staff_line_vertical_offsets", dest="offsets", default="",
                        help="Optional vertical offsets in pixel for drawing the symbols with superimposed "
                             "staff-lines starting at this pixel-offset from the top. Multiple offsets possible, "
                             "e.g. '81,88,95'")
    parser.add_argument("--width", dest="width", default="128", help="Width of the generated images in pixel")
    parser.add_argument("--height", dest="height", default="224", help="Height of the generated images in pixel")
    parser.add_argument("--minibatch_size", dest="training_minibatch_size", default="64", help="Size of the minibatches for training")
    parser.add_argument("--optimizer", dest="optimizer", default="Adadelta", help="The optimizer used for the training")

    flags, unparsed = parser.parse_known_args()

    offsets = None
    if flags.offsets != "":
        offsets = [int(o) for o in flags.offsets.split(',')]

    train_model(flags.dataset_directory,
                flags.model_name,
                flags.show_plot_after_training,
                flags.delete_and_recreate_dataset_directory,
                [int(s) for s in flags.stroke_thicknesses.split(',')],
                int(flags.width),
                int(flags.height),
                offsets,
                int(flags.training_minibatch_size),
                flags.optimizer)
<|endoftext|>


<commit_before>#!/usr/bin/python

'''
Release script for IMAPClient
'''

import os
import sys
import imapclient

DIST_DIR = '../dist'

def fatal(msg):
    sys.stderr.write(msg+'\n')
    sys.exit(1)

os.system('find -name "*.pyc" | xargs rm -f')
os.system('rm -rf IMAPClient.egg-info')

# Check for stale commits
if os.popen('svn status').readlines():
    fatal("Stale commits, please fix")

# Tag in svn
exitcode = os.system('svn cp '
    '-m "tagging IMAPClient %(version)s" '
    'file:///home/msmits/svnroot/trunk/IMAPClient '
    'file:///home/msmits/svnroot/tags/IMAPClient/%(version)s' % {
        'version': imapclient.__version__ })
if exitcode:
    sys.exit(2)

# Create distribution archives
os.system('python setup.py sdist -d %s --formats=gztar,zip' % DIST_DIR)
os.system('rm -rf IMAPClient.egg-info')
<commit_msg>update SVN url<commit_after>#!/usr/bin/python

'''
Release script for IMAPClient
'''

import os
import sys
import imapclient

DIST_DIR = '../dist'
SVN_URL = 'svn://jiali.freshfoo.com'

def fatal(msg):
    sys.stderr.write(msg+'\n')
    sys.exit(1)

os.system('find -name "*.pyc" | xargs rm -f')
os.system('rm -rf IMAPClient.egg-info')

# Check for stale commits
if os.popen('svn status').readlines():
    fatal("Stale commits, please fix")

# Tag in svn
exitcode = os.system('svn cp '
    '-m "tagging IMAPClient %(version)s" '
    '%(svn_url)s/trunk/IMAPClient '
    '%(svn_url)s/tags/IMAPClient/%(version)s' % dict(
        version=imapclient.__version__,
        svn_url=SVN_URL,
    )
)
if exitcode:
    sys.exit(2)

# Create distribution archives
os.system('python setup.py sdist -d %s --formats=gztar,zip' % DIST_DIR)
os.system('rm -rf IMAPClient.egg-info')
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""Pylab (matplotlib) support utilities."""
from __future__ import print_function

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

from io import BytesIO

from IPython.core.display import _pngxy
from IPython.utils.decorators import flag_calls
from IPython.utils import py3compat

# If user specifies a GUI, that dictates the backend, otherwise we read the
# user's mpl default from the mpl rc structure
backends = {'tk': 'TkAgg',
            'gtk': 'GTKAgg',
            'gtk3': 'GTK3Agg',
            'wx': 'WXAgg',
            'qt': 'Qt4Agg', # qt3 not supported
            'qt4': 'Qt4Agg',
            'qt5': 'Qt5Agg',
            'osx': 'MacOSX',
            'nbagg': 'nbAgg',
            'notebook': 'nbAgg',
            'inline' : 'module://ipykernel.pylab.backend_inline'}

# We also need a reverse backends2guis mapping that will properly choose which
# GUI support to activate based on the desired matplotlib backend.  For the
# most part it's just a reverse of the above dict, but we also need to add a
# few others that map to the same GUI manually:
backend2gui = dict(zip(backends.values(), backends.keys()))
# Our tests expect backend2gui to just return 'qt'
backend2gui['Qt4Agg'] = 'qt'
# In the reverse mapping, there are a few extra valid matplotlib backends that
# map to the same GUI support
backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
backend2gui['GTK3Cairo'] = 'gtk3'
backend2gui['WX'] = 'wx'
backend2gui['CocoaAgg'] = 'osx'

#-----------------------------------------------------------------------------
# Matplotlib utilities
#-----------------------------------------------------------------------------

def getfigs(*fig_nums):
    """Get a list of matplotlib figures by figure numbers.

    If no arguments are given, all available figures are returned.  If the
    argument list contains references to invalid figures, a warning is printed
    but the function continues pasting further figures.

    Parameters
    ----------
    figs : tuple
        A tuple of ints giving the figure numbers of the figures to return.
    """
    from matplotlib._pylab_helpers import Gcf
    if not fig_nums:
        fig_managers = Gcf.get_all_fig_managers()
        return [fm.canvas.figure for fm in fig_managers]
    else:
        figs = []
        for num in fig_nums:
            f = Gcf.figs.get(num)
            if f is None:
                print('Warning: figure %s not available.' % num)
            else:
                figs.append(f.canvas.figure)
        return figs

def figsize(sizex, sizey):
    """Set the default figure size to be [sizex, sizey].

    This is just an easy to remember, convenience wrapper that sets::

      matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
    """
    import matplotlib
    matplotlib.rcParams['figure.figsize'] = [sizex, sizey]

def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs):
    """Print a figure to an image, and return the resulting file data

    Returned data will be bytes unless ``fmt='svg'``,
    in which case it will be unicode.

    Any keyword args are passed to fig.canvas.print_figure,
    such as ``quality`` or ``bbox_inches``.
    """
    from matplotlib import rcParams
    # When there's an empty figure, we shouldn't return anything, otherwise we
    # get big blank areas in the qt console.
    if not fig.axes and not fig.lines:
        return

    dpi = rcParams['savefig.dpi']
    if fmt == 'retina':
        dpi = dpi * 2
        fmt = 'png'

    # build keyword args
    kw = dict(
        format=fmt,
        facecolor=fig.get_facecolor(),
        edgecolor=fig.get_edgecolor(),
        dpi=dpi,
        bbox_inches=bbox_inches,
    )
    # **kwargs get higher priority
    kw.update(kwargs)

    bytes_io = BytesIO()
    fig.canvas.print_figure(bytes_io, **kw)
    data = bytes_io.getvalue()
    if fmt == 'svg':
        data = data.decode('utf-8')
    return data

def retina_figure(fig, **kwargs):
    """format a figure as a pixel-doubled (retina) PNG"""
    pngdata = print_figure(fig, fmt='retina', **kwargs)
    # Make sure that retina_figure acts just like print_figure and returns
    # None when the figure is empty.
    if pngdata is None:
        return
    w, h = _pngxy(pngdata)
    metadata = dict(width=w//2, height=h//2)
    return pngdata, metadata

# We need a little factory function here to create the closure where
# safe_execfile can live.
def mpl_runner(safe_execfile):
    """Factory to return a matplotlib-enabled runner for %run.

    Parameters
    ----------
    safe_execfile : function
      This must be a function with the same interface as the
      :meth:`safe_execfile` method of IPython.

    Returns
    -------
    A function suitable for use as the ``runner`` argument of the %run magic
    function.
    """

    def mpl_execfile(fname,*where,**kw):
        """matplotlib-aware wrapper around safe_execfile.

        Its interface is identical to that of the :func:`execfile` builtin.

        This is ultimately a call to execfile(), but wrapped in safeties to
        properly handle interactive rendering."""

        import matplotlib
        import matplotlib.pylab as pylab

        #print '*** Matplotlib runner ***' # dbg
        # turn off rendering until end of script
        is_interactive = matplotlib.rcParams['interactive']
        matplotlib.interactive(False)
        safe_execfile(fname,*where,**kw)
        matplotlib.interactive(is_interactive)
        # make rendering call now, if the user tried to do it
        if pylab.draw_if_interactive.called:
            pylab.draw()
            pylab.draw_if_interactive.called = False

    return mpl_execfile

def select_figure_formats(shell, formats, **kwargs):
    """Select figure formats for the inline backend.

    Parameters
    ==========
    shell : InteractiveShell
        The main IPython instance.
    formats : str or set
        One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
    **kwargs : any
        Extra keyword arguments to be passed to fig.canvas.print_figure.
    """
    from matplotlib.figure import Figure
    from ipykernel.pylab import backend_inline

    svg_formatter = shell.display_formatter.formatters['image/svg+xml']
    png_formatter = shell.display_formatter.formatters['image/png']
    jpg_formatter = shell.display_formatter.formatters['image/jpeg']
    pdf_formatter = shell.display_formatter.formatters['application/pdf']

    if isinstance(formats, py3compat.string_types):
        formats = {formats}
    # cast in case of list / tuple
    formats = set(formats)

    [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]

    supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
    bad = formats.difference(supported)
    if bad:
        bs = "%s" % ','.join([repr(f) for f in bad])
        gs = "%s" % ','.join([repr(f) for f in supported])
        raise ValueError("supported formats are: %s not %s" % (gs, bs))

    if 'png' in formats:
        png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
    if 'retina' in formats or 'png2x' in formats:
        png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))
    if 'jpg' in formats or 'jpeg' in formats:
        jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', **kwargs))
    if 'svg' in formats:
        svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg', **kwargs))
    if 'pdf' in formats:
        pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf', **kwargs))

#-----------------------------------------------------------------------------
# Code for initializing matplotlib and importing pylab
#-----------------------------------------------------------------------------

def find_gui_and_backend(gui=None, gui_select=None):
    """Given a gui string return the gui and mpl backend.

    Parameters
    ----------
    gui : str
        Can be one of ('tk','gtk','wx','qt','qt4','inline').
    gui_select : str
        Can be one of ('tk','gtk','wx','qt','qt4','inline').
        This is any gui already selected by the shell.

    Returns
    -------
    A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
    'WXAgg','Qt4Agg','module://ipykernel.pylab.backend_inline').
    """

    import matplotlib

    if gui and gui != 'auto':
        # select backend based on requested gui
        backend = backends[gui]
    else:
        # We need to read the backend from the original data structure, *not*
        # from mpl.rcParams, since a prior invocation of %matplotlib may have
        # overwritten that.
        # WARNING: this assumes matplotlib 1.1 or newer!!
        backend = matplotlib.rcParamsOrig['backend']
        # In this case, we need to find what the appropriate gui selection call
        # should be for IPython, so we can activate inputhook accordingly
        gui = backend2gui.get(backend, None)

        # If we have already had a gui active, we need it and inline are the
        # ones allowed.
        if gui_select and gui != gui_select:
            gui = gui_select
            backend = backends[gui]

    return gui, backend

def activate_matplotlib(backend):
    """Activate the given backend and set interactive to True."""

    import matplotlib
    matplotlib.interactive(True)

    # Matplotlib had a bug where even switch_backend could not force
    # the rcParam to update. This needs to be set *before* the module
    # magic of switch_backend().
    matplotlib.rcParams['backend'] = backend

    import matplotlib.pyplot
    matplotlib.pyplot.switch_backend(backend)

    # This must be imported last in the matplotlib series, after
    # backend/interactivity choices have been made
    import matplotlib.pylab as pylab

    pylab.show._needmain = False
    # We need to detect at runtime whether show() is called by the user.
    # For this, we wrap it into a decorator which adds a 'called' flag.
    pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)

def import_pylab(user_ns, import_all=True):
    """Populate the namespace with pylab-related values.

    Imports matplotlib, pylab, numpy, and everything from pylab and numpy.

    Also imports a few names from IPython (figsize, display, getfigs)

    """

    # Import numpy as np/pyplot as plt are conventions we're trying to
    # somewhat standardize on.  Making them available to users by default
    # will greatly help this.
    s = ("import numpy\n"
          "import matplotlib\n"
          "from matplotlib import pylab, mlab, pyplot\n"
          "np = numpy\n"
          "plt = pyplot\n"
          )
    exec(s, user_ns)

    if import_all:
        s = ("from matplotlib.pylab import *\n"
             "from numpy import *\n")
        exec(s, user_ns)

    # IPython symbols to add
    user_ns['figsize'] = figsize
    from IPython.core.display import display
    # Add display and getfigs to the user's namespace
    user_ns['display'] = display
    user_ns['getfigs'] = getfigs

def configure_inline_support(shell, backend):
    """Configure an IPython shell object for matplotlib use.

    Parameters
    ----------
    shell : InteractiveShell instance

    backend : matplotlib backend
    """
    # If using our svg payload backend, register the post-execution
    # function that will pick up the results for display.  This can only be
    # done with access to the real shell object.

    # Note: if we can't load the inline backend, then there's no point
    # continuing (such as in terminal-only shells in environments without
    # zeromq available).
    try:
        from ipykernel.pylab.backend_inline import InlineBackend
    except ImportError:
        return
    from matplotlib import pyplot

    cfg = InlineBackend.instance(parent=shell)
    cfg.shell = shell
    if cfg not in shell.configurables:
        shell.configurables.append(cfg)

    if backend == backends['inline']:
        from ipykernel.pylab.backend_inline import flush_figures
        shell.events.register('post_execute', flush_figures)

        # Save rcParams that will be overwrittern
        shell._saved_rcParams = dict()
        for k in cfg.rc:
            shell._saved_rcParams[k] = pyplot.rcParams[k]
        # load inline_rc
        pyplot.rcParams.update(cfg.rc)
        new_backend_name = "inline"
    else:
        from ipykernel.pylab.backend_inline import flush_figures
        try:
            shell.events.unregister('post_execute', flush_figures)
        except ValueError:
            pass
        if hasattr(shell, '_saved_rcParams'):
            pyplot.rcParams.update(shell._saved_rcParams)
            del shell._saved_rcParams
        new_backend_name = "other"

    # only enable the formats once -> don't change the enabled formats (which the user may
    # has changed) when getting another "%matplotlib inline" call.
    # See https://github.com/ipython/ipykernel/issues/29
    cur_backend = getattr(configure_inline_support, "current_backend", "unset")
    if new_backend_name != cur_backend:
        # Setup the default figure format
        select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs)
        configure_inline_support.current_backend = new_backend_name
<commit_msg>reshow nbagg figures on display<commit_after># -*- coding: utf-8 -*-
"""Pylab (matplotlib) support utilities."""
from __future__ import print_function

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

from io import BytesIO

from IPython.core.display import _pngxy
from IPython.utils.decorators import flag_calls
from IPython.utils import py3compat

# If user specifies a GUI, that dictates the backend, otherwise we read the
# user's mpl default from the mpl rc structure
backends = {'tk': 'TkAgg',
            'gtk': 'GTKAgg',
            'gtk3': 'GTK3Agg',
            'wx': 'WXAgg',
            'qt': 'Qt4Agg', # qt3 not supported
            'qt4': 'Qt4Agg',
            'qt5': 'Qt5Agg',
            'osx': 'MacOSX',
            'nbagg': 'nbAgg',
            'notebook': 'nbAgg',
            'inline' : 'module://ipykernel.pylab.backend_inline'}

# We also need a reverse backends2guis mapping that will properly choose which
# GUI support to activate based on the desired matplotlib backend.  For the
# most part it's just a reverse of the above dict, but we also need to add a
# few others that map to the same GUI manually:
backend2gui = dict(zip(backends.values(), backends.keys()))
# Our tests expect backend2gui to just return 'qt'
backend2gui['Qt4Agg'] = 'qt'
# In the reverse mapping, there are a few extra valid matplotlib backends that
# map to the same GUI support
backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
backend2gui['GTK3Cairo'] = 'gtk3'
backend2gui['WX'] = 'wx'
backend2gui['CocoaAgg'] = 'osx'

#-----------------------------------------------------------------------------
# Matplotlib utilities
#-----------------------------------------------------------------------------

def getfigs(*fig_nums):
    """Get a list of matplotlib figures by figure numbers.

    If no arguments are given, all available figures are returned.  If the
    argument list contains references to invalid figures, a warning is printed
    but the function continues pasting further figures.

    Parameters
    ----------
    figs : tuple
        A tuple of ints giving the figure numbers of the figures to return.
    """
    from matplotlib._pylab_helpers import Gcf
    if not fig_nums:
        fig_managers = Gcf.get_all_fig_managers()
        return [fm.canvas.figure for fm in fig_managers]
    else:
        figs = []
        for num in fig_nums:
            f = Gcf.figs.get(num)
            if f is None:
                print('Warning: figure %s not available.' % num)
            else:
                figs.append(f.canvas.figure)
        return figs

def figsize(sizex, sizey):
    """Set the default figure size to be [sizex, sizey].

    This is just an easy to remember, convenience wrapper that sets::

      matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
    """
    import matplotlib
    matplotlib.rcParams['figure.figsize'] = [sizex, sizey]

def print_figure(fig, fmt='png', bbox_inches='tight', **kwargs):
    """Print a figure to an image, and return the resulting file data

    Returned data will be bytes unless ``fmt='svg'``,
    in which case it will be unicode.

    Any keyword args are passed to fig.canvas.print_figure,
    such as ``quality`` or ``bbox_inches``.
    """
    from matplotlib import rcParams
    # When there's an empty figure, we shouldn't return anything, otherwise we
    # get big blank areas in the qt console.
    if not fig.axes and not fig.lines:
        return

    dpi = rcParams['savefig.dpi']
    if fmt == 'retina':
        dpi = dpi * 2
        fmt = 'png'

    # build keyword args
    kw = dict(
        format=fmt,
        facecolor=fig.get_facecolor(),
        edgecolor=fig.get_edgecolor(),
        dpi=dpi,
        bbox_inches=bbox_inches,
    )
    # **kwargs get higher priority
    kw.update(kwargs)

    bytes_io = BytesIO()
    fig.canvas.print_figure(bytes_io, **kw)
    data = bytes_io.getvalue()
    if fmt == 'svg':
        data = data.decode('utf-8')
    return data

def retina_figure(fig, **kwargs):
    """format a figure as a pixel-doubled (retina) PNG"""
    pngdata = print_figure(fig, fmt='retina', **kwargs)
    # Make sure that retina_figure acts just like print_figure and returns
    # None when the figure is empty.
    if pngdata is None:
        return
    w, h = _pngxy(pngdata)
    metadata = dict(width=w//2, height=h//2)
    return pngdata, metadata

# We need a little factory function here to create the closure where
# safe_execfile can live.
def mpl_runner(safe_execfile):
    """Factory to return a matplotlib-enabled runner for %run.

    Parameters
    ----------
    safe_execfile : function
      This must be a function with the same interface as the
      :meth:`safe_execfile` method of IPython.

    Returns
    -------
    A function suitable for use as the ``runner`` argument of the %run magic
    function.
    """

    def mpl_execfile(fname,*where,**kw):
        """matplotlib-aware wrapper around safe_execfile.

        Its interface is identical to that of the :func:`execfile` builtin.

        This is ultimately a call to execfile(), but wrapped in safeties to
        properly handle interactive rendering."""

        import matplotlib
        import matplotlib.pylab as pylab

        #print '*** Matplotlib runner ***' # dbg
        # turn off rendering until end of script
        is_interactive = matplotlib.rcParams['interactive']
        matplotlib.interactive(False)
        safe_execfile(fname,*where,**kw)
        matplotlib.interactive(is_interactive)
        # make rendering call now, if the user tried to do it
        if pylab.draw_if_interactive.called:
            pylab.draw()
            pylab.draw_if_interactive.called = False

    return mpl_execfile

def _reshow_nbagg_figure(fig):
    """reshow an nbagg figure"""
    try:
        reshow = fig.canvas.manager.reshow
    except AttributeError:
        raise NotImplementedError()
    else:
        reshow()

def select_figure_formats(shell, formats, **kwargs):
    """Select figure formats for the inline backend.

    Parameters
    ==========
    shell : InteractiveShell
        The main IPython instance.
    formats : str or set
        One or a set of figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
    **kwargs : any
        Extra keyword arguments to be passed to fig.canvas.print_figure.
    """
    import matplotlib
    from matplotlib.figure import Figure
    from ipykernel.pylab import backend_inline

    svg_formatter = shell.display_formatter.formatters['image/svg+xml']
    png_formatter = shell.display_formatter.formatters['image/png']
    jpg_formatter = shell.display_formatter.formatters['image/jpeg']
    pdf_formatter = shell.display_formatter.formatters['application/pdf']

    if isinstance(formats, py3compat.string_types):
        formats = {formats}
    # cast in case of list / tuple
    formats = set(formats)

    [ f.pop(Figure, None) for f in shell.display_formatter.formatters.values() ]

    if matplotlib.backends.backend.lower() == 'nbagg':
        formatter = shell.display_formatter.ipython_display_formatter
        formatter.for_type(Figure, _reshow_nbagg_figure)

    supported = {'png', 'png2x', 'retina', 'jpg', 'jpeg', 'svg', 'pdf'}
    bad = formats.difference(supported)
    if bad:
        bs = "%s" % ','.join([repr(f) for f in bad])
        gs = "%s" % ','.join([repr(f) for f in supported])
        raise ValueError("supported formats are: %s not %s" % (gs, bs))

    if 'png' in formats:
        png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
    if 'retina' in formats or 'png2x' in formats:
        png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))
    if 'jpg' in formats or 'jpeg' in formats:
        jpg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'jpg', **kwargs))
    if 'svg' in formats:
        svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg', **kwargs))
    if 'pdf' in formats:
        pdf_formatter.for_type(Figure, lambda fig: print_figure(fig, 'pdf', **kwargs))

#-----------------------------------------------------------------------------
# Code for initializing matplotlib and importing pylab
#-----------------------------------------------------------------------------

def find_gui_and_backend(gui=None, gui_select=None):
    """Given a gui string return the gui and mpl backend.

    Parameters
    ----------
    gui : str
        Can be one of ('tk','gtk','wx','qt','qt4','inline').
    gui_select : str
        Can be one of ('tk','gtk','wx','qt','qt4','inline').
        This is any gui already selected by the shell.

    Returns
    -------
    A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
    'WXAgg','Qt4Agg','module://ipykernel.pylab.backend_inline').
    """

    import matplotlib

    if gui and gui != 'auto':
        # select backend based on requested gui
        backend = backends[gui]
    else:
        # We need to read the backend from the original data structure, *not*
        # from mpl.rcParams, since a prior invocation of %matplotlib may have
        # overwritten that.
        # WARNING: this assumes matplotlib 1.1 or newer!!
        backend = matplotlib.rcParamsOrig['backend']
        # In this case, we need to find what the appropriate gui selection call
        # should be for IPython, so we can activate inputhook accordingly
        gui = backend2gui.get(backend, None)

        # If we have already had a gui active, we need it and inline are the
        # ones allowed.
        if gui_select and gui != gui_select:
            gui = gui_select
            backend = backends[gui]

    return gui, backend

def activate_matplotlib(backend):
    """Activate the given backend and set interactive to True."""

    import matplotlib
    matplotlib.interactive(True)

    # Matplotlib had a bug where even switch_backend could not force
    # the rcParam to update. This needs to be set *before* the module
    # magic of switch_backend().
    matplotlib.rcParams['backend'] = backend

    import matplotlib.pyplot
    matplotlib.pyplot.switch_backend(backend)

    # This must be imported last in the matplotlib series, after
    # backend/interactivity choices have been made
    import matplotlib.pylab as pylab

    pylab.show._needmain = False
    # We need to detect at runtime whether show() is called by the user.
    # For this, we wrap it into a decorator which adds a 'called' flag.
    pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)

def import_pylab(user_ns, import_all=True):
    """Populate the namespace with pylab-related values.

    Imports matplotlib, pylab, numpy, and everything from pylab and numpy.

    Also imports a few names from IPython (figsize, display, getfigs)

    """

    # Import numpy as np/pyplot as plt are conventions we're trying to
    # somewhat standardize on.  Making them available to users by default
    # will greatly help this.
    s = ("import numpy\n"
          "import matplotlib\n"
          "from matplotlib import pylab, mlab, pyplot\n"
          "np = numpy\n"
          "plt = pyplot\n"
          )
    exec(s, user_ns)

    if import_all:
        s = ("from matplotlib.pylab import *\n"
             "from numpy import *\n")
        exec(s, user_ns)

    # IPython symbols to add
    user_ns['figsize'] = figsize
    from IPython.core.display import display
    # Add display and getfigs to the user's namespace
    user_ns['display'] = display
    user_ns['getfigs'] = getfigs

def configure_inline_support(shell, backend):
    """Configure an IPython shell object for matplotlib use.

    Parameters
    ----------
    shell : InteractiveShell instance

    backend : matplotlib backend
    """
    # If using our svg payload backend, register the post-execution
    # function that will pick up the results for display.  This can only be
    # done with access to the real shell object.

    # Note: if we can't load the inline backend, then there's no point
    # continuing (such as in terminal-only shells in environments without
    # zeromq available).
    try:
        from ipykernel.pylab.backend_inline import InlineBackend
    except ImportError:
        return
    from matplotlib import pyplot

    cfg = InlineBackend.instance(parent=shell)
    cfg.shell = shell
    if cfg not in shell.configurables:
        shell.configurables.append(cfg)

    if backend == backends['inline']:
        from ipykernel.pylab.backend_inline import flush_figures
        shell.events.register('post_execute', flush_figures)

        # Save rcParams that will be overwrittern
        shell._saved_rcParams = dict()
        for k in cfg.rc:
            shell._saved_rcParams[k] = pyplot.rcParams[k]
        # load inline_rc
        pyplot.rcParams.update(cfg.rc)
        new_backend_name = "inline"
    else:
        from ipykernel.pylab.backend_inline import flush_figures
        try:
            shell.events.unregister('post_execute', flush_figures)
        except ValueError:
            pass
        if hasattr(shell, '_saved_rcParams'):
            pyplot.rcParams.update(shell._saved_rcParams)
            del shell._saved_rcParams
        new_backend_name = "other"

    # only enable the formats once -> don't change the enabled formats (which the user may
    # has changed) when getting another "%matplotlib inline" call.
    # See https://github.com/ipython/ipykernel/issues/29
    cur_backend = getattr(configure_inline_support, "current_backend", "unset")
    if new_backend_name != cur_backend:
        # Setup the default figure format
        select_figure_formats(shell, cfg.figure_formats, **cfg.print_figure_kwargs)
        configure_inline_support.current_backend = new_backend_name
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.contrib import admin

from import_export.admin import ImportExportModelAdmin
from import_export.resources import ModelResource
from import_export.fields import Field
import import_export.widgets as widgets

from .models import Company
from .models import SupplierPart
from .models import SupplierPriceBreak
from .models import ManufacturerPart, ManufacturerPartParameter

from part.models import Part

class CompanyResource(ModelResource):
    """ Class for managing Company data import/export """

    class Meta:
        model = Company
        exclude = ('is_deleted', )
        skip_unchanged = True
        report_skipped = False
        clean_model_instances = True

class CompanyAdmin(ImportExportModelAdmin):

    resource_class = CompanyResource

    list_display = ('name', 'website', 'contact')

    search_fields = [
        'name',
        'description',
    ]

class SupplierPartResource(ModelResource):
    """
    Class for managing SupplierPart data import/export
    """

    part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))

    part_name = Field(attribute='part__full_name', readonly=True)

    supplier = Field(attribute='supplier', widget=widgets.ForeignKeyWidget(Company))

    supplier_name = Field(attribute='supplier__name', readonly=True)

    class Meta:
        model = SupplierPart
        skip_unchanged = True
        report_skipped = True
        clean_model_instances = True

class SupplierPartAdmin(ImportExportModelAdmin):

    resource_class = SupplierPartResource

    list_display = ('part', 'supplier', 'SKU')

    search_fields = [
        'company__name',
        'part__name',
        'MPN',
        'SKU',
    ]

    autocomplete_fields = ('part', 'supplier', 'manufacturer_part',)

class ManufacturerPartResource(ModelResource):
    """
    Class for managing ManufacturerPart data import/export
    """

    part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))

    part_name = Field(attribute='part__full_name', readonly=True)

    manufacturer = Field(attribute='manufacturer', widget=widgets.ForeignKeyWidget(Company))

    manufacturer_name = Field(attribute='manufacturer__name', readonly=True)

    class Meta:
        model = ManufacturerPart
        skip_unchanged = True
        report_skipped = True
        clean_model_instances = True

class ManufacturerPartAdmin(ImportExportModelAdmin):
    """
    Admin class for ManufacturerPart model
    """

    resource_class = ManufacturerPartResource

    list_display = ('part', 'manufacturer', 'MPN')

    search_fields = [
        'manufacturer__name',
        'part__name',
        'MPN',
    ]

    autocomplete_fields = ('part', 'manufacturer',)

class ManufacturerPartParameterResource(ModelResource):
    """
    Class for managing ManufacturerPartParameter data import/export
    """

    class Meta:
        model = ManufacturerPartParameter
        skip_unchanged = True
        report_skipped = True
        clean_model_instance = True

class ManufacturerPartParameterAdmin(ImportExportModelAdmin):
    """
    Admin class for ManufacturerPartParameter model
    """

    resource_class = ManufacturerPartParameterResource

    list_display = ('manufacturer_part', 'name', 'value')

    search_fields = [
        'manufacturer_part__manufacturer__name',
        'name',
        'value'
    ]

    autocomplete_fields = ('manufacturer_part',)

class SupplierPriceBreakResource(ModelResource):
    """ Class for managing SupplierPriceBreak data import/export """

    part = Field(attribute='part', widget=widgets.ForeignKeyWidget(SupplierPart))

    supplier_id = Field(attribute='part__supplier__pk', readonly=True)

    supplier_name = Field(attribute='part__supplier__name', readonly=True)

    part_name = Field(attribute='part__part__full_name', readonly=True)

    SKU = Field(attribute='part__SKU', readonly=True)

    MPN = Field(attribute='part__MPN', readonly=True)

    class Meta:
        model = SupplierPriceBreak
        skip_unchanged = True
        report_skipped = False
        clean_model_instances = True

class SupplierPriceBreakAdmin(ImportExportModelAdmin):

    resource_class = SupplierPriceBreakResource

    list_display = ('part', 'quantity', 'price')

    autocomplete_fields = ('part',)

admin.site.register(Company, CompanyAdmin)
admin.site.register(SupplierPart, SupplierPartAdmin)
admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin)

admin.site.register(ManufacturerPart, ManufacturerPartAdmin)
admin.site.register(ManufacturerPartParameter, ManufacturerPartParameterAdmin)
<commit_msg>do not show in admin<commit_after># -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.contrib import admin

from import_export.admin import ImportExportModelAdmin
from import_export.resources import ModelResource
from import_export.fields import Field
import import_export.widgets as widgets

from .models import Company
from .models import SupplierPart
from .models import SupplierPriceBreak
from .models import ManufacturerPart, ManufacturerPartParameter

from part.models import Part

class CompanyResource(ModelResource):
    """ Class for managing Company data import/export """

    class Meta:
        model = Company
        exclude = ('is_deleted', )
        skip_unchanged = True
        report_skipped = False
        clean_model_instances = True

class CompanyAdmin(ImportExportModelAdmin):

    resource_class = CompanyResource

    list_display = ('name', 'website', 'contact')

    search_fields = [
        'name',
        'description',
    ]

    exclude = ('is_deleted',)

class SupplierPartResource(ModelResource):
    """
    Class for managing SupplierPart data import/export
    """

    part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))

    part_name = Field(attribute='part__full_name', readonly=True)

    supplier = Field(attribute='supplier', widget=widgets.ForeignKeyWidget(Company))

    supplier_name = Field(attribute='supplier__name', readonly=True)

    class Meta:
        model = SupplierPart
        skip_unchanged = True
        report_skipped = True
        clean_model_instances = True

class SupplierPartAdmin(ImportExportModelAdmin):

    resource_class = SupplierPartResource

    list_display = ('part', 'supplier', 'SKU')

    search_fields = [
        'company__name',
        'part__name',
        'MPN',
        'SKU',
    ]

    autocomplete_fields = ('part', 'supplier', 'manufacturer_part',)

class ManufacturerPartResource(ModelResource):
    """
    Class for managing ManufacturerPart data import/export
    """

    part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))

    part_name = Field(attribute='part__full_name', readonly=True)

    manufacturer = Field(attribute='manufacturer', widget=widgets.ForeignKeyWidget(Company))

    manufacturer_name = Field(attribute='manufacturer__name', readonly=True)

    class Meta:
        model = ManufacturerPart
        skip_unchanged = True
        report_skipped = True
        clean_model_instances = True

class ManufacturerPartAdmin(ImportExportModelAdmin):
    """
    Admin class for ManufacturerPart model
    """

    resource_class = ManufacturerPartResource

    list_display = ('part', 'manufacturer', 'MPN')

    search_fields = [
        'manufacturer__name',
        'part__name',
        'MPN',
    ]

    autocomplete_fields = ('part', 'manufacturer',)

class ManufacturerPartParameterResource(ModelResource):
    """
    Class for managing ManufacturerPartParameter data import/export
    """

    class Meta:
        model = ManufacturerPartParameter
        skip_unchanged = True
        report_skipped = True
        clean_model_instance = True

class ManufacturerPartParameterAdmin(ImportExportModelAdmin):
    """
    Admin class for ManufacturerPartParameter model
    """

    resource_class = ManufacturerPartParameterResource

    list_display = ('manufacturer_part', 'name', 'value')

    search_fields = [
        'manufacturer_part__manufacturer__name',
        'name',
        'value'
    ]

    autocomplete_fields = ('manufacturer_part',)

class SupplierPriceBreakResource(ModelResource):
    """ Class for managing SupplierPriceBreak data import/export """

    part = Field(attribute='part', widget=widgets.ForeignKeyWidget(SupplierPart))

    supplier_id = Field(attribute='part__supplier__pk', readonly=True)

    supplier_name = Field(attribute='part__supplier__name', readonly=True)

    part_name = Field(attribute='part__part__full_name', readonly=True)

    SKU = Field(attribute='part__SKU', readonly=True)

    MPN = Field(attribute='part__MPN', readonly=True)

    class Meta:
        model = SupplierPriceBreak
        skip_unchanged = True
        report_skipped = False
        clean_model_instances = True

class SupplierPriceBreakAdmin(ImportExportModelAdmin):

    resource_class = SupplierPriceBreakResource

    list_display = ('part', 'quantity', 'price')

    autocomplete_fields = ('part',)

admin.site.register(Company, CompanyAdmin)
admin.site.register(SupplierPart, SupplierPartAdmin)
admin.site.register(SupplierPriceBreak, SupplierPriceBreakAdmin)

admin.site.register(ManufacturerPart, ManufacturerPartAdmin)
admin.site.register(ManufacturerPartParameter, ManufacturerPartParameterAdmin)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
"""
Base Class for InvenTree plugins
"""
import logging
import os
import inspect
from datetime import datetime
import pathlib
import warnings

from django.conf import settings
from django.db.utils import OperationalError, ProgrammingError
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from django.urls.base import reverse

from plugin.helpers import get_git_log, GitStatus

logger = logging.getLogger("inventree")

class MetaBase:
    """Base class for a plugins metadata"""

    # Override the plugin name for each concrete plugin instance
    PLUGIN_NAME = ''

    PLUGIN_SLUG = None

    PLUGIN_TITLE = None

    def plugin_name(self):
        """
        Name of plugin
        """
        return self.PLUGIN_NAME

    @property
    def name(self):
        """
        Name of plugin
        """
        return self.plugin_name()

    def plugin_slug(self):
        """
        Slug of plugin
        If not set plugin name slugified
        """

        slug = getattr(self, 'PLUGIN_SLUG', None)

        if slug is None:
            slug = self.plugin_name()

        return slugify(slug.lower())

    @property
    def slug(self):
        """
        Slug of plugin
        """
        return self.plugin_slug()

    def plugin_title(self):
        """
        Title of plugin
        """

        if self.PLUGIN_TITLE:
            return self.PLUGIN_TITLE
        else:
            return self.plugin_name()

    @property
    def human_name(self):
        """
        Human readable name of plugin
        """
        return self.plugin_title()

    def plugin_config(self):
        """
        Return the PluginConfig object associated with this plugin
        """

        try:
            import plugin.models

            cfg, _ = plugin.models.PluginConfig.objects.get_or_create(
                key=self.plugin_slug(),
                name=self.plugin_name(),
            )
        except (OperationalError, ProgrammingError):
            cfg = None

        return cfg

    def is_active(self):
        """
        Return True if this plugin is currently active
        """

        cfg = self.plugin_config()

        if cfg:
            return cfg.active
        else:
            return False  # pragma: no cover

class MixinBase:
    """
    Base set of mixin functions and mechanisms
    """

    def __init__(self, *args, **kwargs) -> None:
        self._mixinreg = {}
        self._mixins = {}
        super().__init__(*args, **kwargs)

    def mixin(self, key):
        """
        Check if mixin is registered
        """
        return key in self._mixins

    def mixin_enabled(self, key):
        """
        Check if mixin is registered, enabled and ready
        """
        if self.mixin(key):
            fnc_name = self._mixins.get(key)

            # Allow for simple case where the mixin is "always" ready
            if fnc_name is True:
                return True

            return getattr(self, fnc_name, True)
        return False

    def add_mixin(self, key: str, fnc_enabled=True, cls=None):
        """
        Add a mixin to the plugins registry
        """

        self._mixins[key] = fnc_enabled
        self.setup_mixin(key, cls=cls)

    def setup_mixin(self, key, cls=None):
        """
        Define mixin details for the current mixin -> provides meta details for all active mixins
        """

        # get human name
        human_name = getattr(cls.MixinMeta, 'MIXIN_NAME', key) if cls and hasattr(cls, 'MixinMeta') else key

        # register
        self._mixinreg[key] = {
            'key': key,
            'human_name': human_name,
        }

    @property
    def registered_mixins(self, with_base: bool = False):
        """
        Get all registered mixins for the plugin
        """

        mixins = getattr(self, '_mixinreg', None)
        if mixins:
            # filter out base
            if not with_base and 'base' in mixins:
                del mixins['base']
            # only return dict
            mixins = [a for a in mixins.values()]
        return mixins

class InvenTreePlugin(MixinBase, MetaBase):
    """
    The InvenTreePlugin class is used to integrate with 3rd party software

    DO NOT USE THIS DIRECTLY, USE plugin.InvenTreePlugin
    """

    AUTHOR = None
    DESCRIPTION = None
    PUBLISH_DATE = None
    VERSION = None
    WEBSITE = None
    LICENSE = None

    def __init__(self):
        super().__init__()
        self.add_mixin('base')
        self.def_path = inspect.getfile(self.__class__)
        self.path = os.path.dirname(self.def_path)

        self.define_package()

    @property
    def _is_package(self):
        """
        Is the plugin delivered as a package
        """
        return getattr(self, 'is_package', False)

    @property
    def is_sample(self):
        """
        Is this plugin part of the samples?
        """
        path = str(self.package_path)
        return path.startswith('plugin/samples/')

    # region properties
    @property
    def description(self):
        """
        Description of plugin
        """
        description = getattr(self, 'DESCRIPTION', None)
        if not description:
            description = self.plugin_name()
        return description

    @property
    def author(self):
        """
        Author of plugin - either from plugin settings or git
        """
        author = getattr(self, 'AUTHOR', None)
        if not author:
            author = self.package.get('author')
        if not author:
            author = _('No author found')  # pragma: no cover
        return author

    @property
    def pub_date(self):
        """
        Publishing date of plugin - either from plugin settings or git
        """
        pub_date = getattr(self, 'PUBLISH_DATE', None)
        if not pub_date:
            pub_date = self.package.get('date')
        else:
            pub_date = datetime.fromisoformat(str(pub_date))
        if not pub_date:
            pub_date = _('No date found')  # pragma: no cover
        return pub_date

    @property
    def version(self):
        """
        Version of plugin
        """
        version = getattr(self, 'VERSION', None)
        return version

    @property
    def website(self):
        """
        Website of plugin - if set else None
        """
        website = getattr(self, 'WEBSITE', None)
        return website

    @property
    def license(self):
        """
        License of plugin
        """
        lic = getattr(self, 'LICENSE', None)
        return lic
    # endregion

    @property
    def package_path(self):
        """
        Path to the plugin
        """
        if self._is_package:
            return self.__module__  # pragma: no cover
        return pathlib.Path(self.def_path).relative_to(settings.BASE_DIR)

    @property
    def settings_url(self):
        """
        URL to the settings panel for this plugin
        """
        return f'{reverse("settings")}#select-plugin-{self.slug}'

    # region package info
    def _get_package_commit(self):
        """
        Get last git commit for the plugin
        """
        return get_git_log(self.def_path)

    def _get_package_metadata(self):
        """
        Get package metadata for plugin
        """
        return {}  # pragma: no cover  # TODO add usage for package metadata

    def define_package(self):
        """
        Add package info of the plugin into plugins context
        """
        package = self._get_package_metadata() if self._is_package else self._get_package_commit()

        # process date
        if package.get('date'):
            package['date'] = datetime.fromisoformat(package.get('date'))

        # process sign state
        sign_state = getattr(GitStatus, str(package.get('verified')), GitStatus.N)
        if sign_state.status == 0:
            self.sign_color = 'success'  # pragma: no cover
        elif sign_state.status == 1:
            self.sign_color = 'warning'
        else:
            self.sign_color = 'danger'  # pragma: no cover

        # set variables
        self.package = package
        self.sign_state = sign_state
    # endregion

class IntegrationPluginBase(InvenTreePlugin):
    def __init__(self, *args, **kwargs):
        """Send warning about using this reference"""
        # TODO remove in 0.8.0
        warnings.warn("This import is deprecated - use InvenTreePlugin", DeprecationWarning)
        super().__init__(*args, **kwargs)
<commit_msg>restructure<commit_after># -*- coding: utf-8 -*-
"""
Base Class for InvenTree plugins
"""
import logging
import os
import inspect
from datetime import datetime
import pathlib
import warnings

from django.conf import settings
from django.db.utils import OperationalError, ProgrammingError
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from django.urls.base import reverse

from plugin.helpers import get_git_log, GitStatus

logger = logging.getLogger("inventree")

class MetaBase:
    """Base class for a plugins metadata"""

    # Override the plugin name for each concrete plugin instance
    PLUGIN_NAME = ''

    PLUGIN_SLUG = None

    PLUGIN_TITLE = None

    def plugin_name(self):
        """
        Name of plugin
        """
        return self.PLUGIN_NAME

    @property
    def name(self):
        """
        Name of plugin
        """
        return self.plugin_name()

    def plugin_slug(self):
        """
        Slug of plugin
        If not set plugin name slugified
        """

        slug = getattr(self, 'PLUGIN_SLUG', None)

        if slug is None:
            slug = self.plugin_name()

        return slugify(slug.lower())

    @property
    def slug(self):
        """
        Slug of plugin
        """
        return self.plugin_slug()

    def plugin_title(self):
        """
        Title of plugin
        """

        if self.PLUGIN_TITLE:
            return self.PLUGIN_TITLE
        else:
            return self.plugin_name()

    @property
    def human_name(self):
        """
        Human readable name of plugin
        """
        return self.plugin_title()

    def plugin_config(self):
        """
        Return the PluginConfig object associated with this plugin
        """

        try:
            import plugin.models

            cfg, _ = plugin.models.PluginConfig.objects.get_or_create(
                key=self.plugin_slug(),
                name=self.plugin_name(),
            )
        except (OperationalError, ProgrammingError):
            cfg = None

        return cfg

    def is_active(self):
        """
        Return True if this plugin is currently active
        """

        cfg = self.plugin_config()

        if cfg:
            return cfg.active
        else:
            return False  # pragma: no cover

class MixinBase:
    """
    Base set of mixin functions and mechanisms
    """

    def __init__(self, *args, **kwargs) -> None:
        self._mixinreg = {}
        self._mixins = {}
        super().__init__(*args, **kwargs)

    def mixin(self, key):
        """
        Check if mixin is registered
        """
        return key in self._mixins

    def mixin_enabled(self, key):
        """
        Check if mixin is registered, enabled and ready
        """
        if self.mixin(key):
            fnc_name = self._mixins.get(key)

            # Allow for simple case where the mixin is "always" ready
            if fnc_name is True:
                return True

            return getattr(self, fnc_name, True)
        return False

    def add_mixin(self, key: str, fnc_enabled=True, cls=None):
        """
        Add a mixin to the plugins registry
        """

        self._mixins[key] = fnc_enabled
        self.setup_mixin(key, cls=cls)

    def setup_mixin(self, key, cls=None):
        """
        Define mixin details for the current mixin -> provides meta details for all active mixins
        """

        # get human name
        human_name = getattr(cls.MixinMeta, 'MIXIN_NAME', key) if cls and hasattr(cls, 'MixinMeta') else key

        # register
        self._mixinreg[key] = {
            'key': key,
            'human_name': human_name,
        }

    @property
    def registered_mixins(self, with_base: bool = False):
        """
        Get all registered mixins for the plugin
        """

        mixins = getattr(self, '_mixinreg', None)
        if mixins:
            # filter out base
            if not with_base and 'base' in mixins:
                del mixins['base']
            # only return dict
            mixins = [a for a in mixins.values()]
        return mixins

class InvenTreePlugin(MixinBase, MetaBase):
    """
    The InvenTreePlugin class is used to integrate with 3rd party software

    DO NOT USE THIS DIRECTLY, USE plugin.InvenTreePlugin
    """

    AUTHOR = None
    DESCRIPTION = None
    PUBLISH_DATE = None
    VERSION = None
    WEBSITE = None
    LICENSE = None

    def __init__(self):
        super().__init__()
        self.add_mixin('base')
        self.def_path = inspect.getfile(self.__class__)
        self.path = os.path.dirname(self.def_path)

        self.define_package()

    # region properties
    @property
    def description(self):
        """
        Description of plugin
        """
        description = getattr(self, 'DESCRIPTION', None)
        if not description:
            description = self.plugin_name()
        return description

    @property
    def author(self):
        """
        Author of plugin - either from plugin settings or git
        """
        author = getattr(self, 'AUTHOR', None)
        if not author:
            author = self.package.get('author')
        if not author:
            author = _('No author found')  # pragma: no cover
        return author

    @property
    def pub_date(self):
        """
        Publishing date of plugin - either from plugin settings or git
        """
        pub_date = getattr(self, 'PUBLISH_DATE', None)
        if not pub_date:
            pub_date = self.package.get('date')
        else:
            pub_date = datetime.fromisoformat(str(pub_date))
        if not pub_date:
            pub_date = _('No date found')  # pragma: no cover
        return pub_date

    @property
    def version(self):
        """
        Version of plugin
        """
        version = getattr(self, 'VERSION', None)
        return version

    @property
    def website(self):
        """
        Website of plugin - if set else None
        """
        website = getattr(self, 'WEBSITE', None)
        return website

    @property
    def license(self):
        """
        License of plugin
        """
        lic = getattr(self, 'LICENSE', None)
        return lic
    # endregion

    @property
    def _is_package(self):
        """
        Is the plugin delivered as a package
        """
        return getattr(self, 'is_package', False)

    @property
    def is_sample(self):
        """
        Is this plugin part of the samples?
        """
        path = str(self.package_path)
        return path.startswith('plugin/samples/')

    @property
    def package_path(self):
        """
        Path to the plugin
        """
        if self._is_package:
            return self.__module__  # pragma: no cover
        return pathlib.Path(self.def_path).relative_to(settings.BASE_DIR)

    @property
    def settings_url(self):
        """
        URL to the settings panel for this plugin
        """
        return f'{reverse("settings")}#select-plugin-{self.slug}'

    # region package info
    def _get_package_commit(self):
        """
        Get last git commit for the plugin
        """
        return get_git_log(self.def_path)

    def _get_package_metadata(self):
        """
        Get package metadata for plugin
        """
        return {}  # pragma: no cover  # TODO add usage for package metadata

    def define_package(self):
        """
        Add package info of the plugin into plugins context
        """
        package = self._get_package_metadata() if self._is_package else self._get_package_commit()

        # process date
        if package.get('date'):
            package['date'] = datetime.fromisoformat(package.get('date'))

        # process sign state
        sign_state = getattr(GitStatus, str(package.get('verified')), GitStatus.N)
        if sign_state.status == 0:
            self.sign_color = 'success'  # pragma: no cover
        elif sign_state.status == 1:
            self.sign_color = 'warning'
        else:
            self.sign_color = 'danger'  # pragma: no cover

        # set variables
        self.package = package
        self.sign_state = sign_state
    # endregion

class IntegrationPluginBase(InvenTreePlugin):
    def __init__(self, *args, **kwargs):
        """Send warning about using this reference"""
        # TODO remove in 0.8.0
        warnings.warn("This import is deprecated - use InvenTreePlugin", DeprecationWarning)
        super().__init__(*args, **kwargs)
<|endoftext|>


<commit_before>#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""main module."""
import os
import time

import cfscrape
import click
import mechanicalsoup
import requests
import structlog

from iqdb_tagger import models
from iqdb_tagger.__init__ import db_version
from iqdb_tagger.utils import default_db_path, thumb_folder, user_data_dir
from iqdb_tagger.custom_parser import get_tags

db = '~/images/! tagged'
DEFAULT_SIZE = 150, 150
DEFAULT_PLACE = 'iqdb'
minsim = 75
services = ['1', '2', '3', '4', '5', '6', '10', '11']
forcegray = False
log = structlog.getLogger()
iqdb_url_dict = {
    'iqdb': ('http://iqdb.org', models.ImageMatch.SP_IQDB),
    'danbooru': (
        'http://danbooru.iqdb.org',
        models.ImageMatch.SP_DANBOORU
    ),
}

def get_page_result(image, url):
    """Get page result.

    Args:
        image: Image path to be uploaded.
    Returns:
        HTML page from the result.
    """
    br = mechanicalsoup.StatefulBrowser(soup_config={'features': 'lxml'})
    br.raise_on_404 = True
    br.open(url)
    html_form = br.select_form('form')
    html_form.input({'file': image})
    br.submit_selected()
    # if ok, will output: <Response [200]>
    return br.get_current_page()

def get_posted_image(
        img_path, resize=False, size=None, output_thumb_folder=None):
    """Get posted image."""
    if output_thumb_folder is None:
        output_thumb_folder = thumb_folder

    img, _ = models.ImageModel.get_or_create_from_path(img_path)
    def_thumb_rel, _ = models.ThumbnailRelationship.get_or_create_from_image(
        image=img, thumb_folder=output_thumb_folder, size=DEFAULT_SIZE)
    resized_thumb_rel = None

    if resize and size:
        resized_thumb_rel, _ = \
            models.ThumbnailRelationship.get_or_create_from_image(
                image=img, thumb_folder=output_thumb_folder, size=size
            )
    elif resize:
        # use thumbnail if no size is given
        resized_thumb_rel = def_thumb_rel
    else:
        log.debug('Unknown config.', resize=resize, size=size)

    return resized_thumb_rel.thumbnail \
        if resized_thumb_rel is not None else img

def init_program(db_path=None):
    """Init program."""
    # create user data dir
    if not os.path.isdir(user_data_dir):
        os.makedirs(user_data_dir, exist_ok=True)
        log.debug('User data dir created.')
    # create thumbnail folder
    if not os.path.isdir(thumb_folder):
        os.makedirs(thumb_folder, exist_ok=True)
        log.debug('Thumbnail folder created.')

    # database
    if db_path is None:
        db_path = default_db_path
    models.init_db(db_path, db_version)

def get_tags(browser, url, scraper):
    """Get tags."""
    # compatibility
    br = browser

    br.open(url)
    page = br.get_current_page()
    tags = get_tags(page, url, scraper)
    if tags:
        for tag in tags:
            tag_parts = tag.split(':', 1)
            if ':' in tag:
                tag_name = tag_parts[1]
                namespace = tag_parts[0]
            else:
                tag_name = tag_parts[0]
                namespace = None
            tag_model, _ = models.Tag.get_or_create(
                name=tag_name, namespace=namespace)
            MatchTagRelationship.get_or_create(match=match_result, tag=tag_model)
        return tags
    else:
        log.debug('No tags found.')

@click.command()
@click.option(
    '--place', type=click.Choice(['iqdb', 'danbooru']),
    default=DEFAULT_PLACE, help='Specify iqdb place, default:{}'.format(DEFAULT_PLACE)
)
@click.option('--resize', is_flag=True, help='Use resized image.')
@click.option('--size', is_flag=True, help='Specify resized image.')
@click.option('--db-path', help='Specify Database path.')
@click.option(
    '--match-filter', type=click.Choice(['default', 'best-match']),
    default='default', help='Filter the result.')
@click.argument('image')
def main(
    image, resize=False, size=None,
    db_path=None, place=DEFAULT_PLACE, match_filter='default'
):
    """Get similar image from iqdb."""
    init_program(db_path)
    post_img = get_posted_image(img_path=image, resize=resize, size=size)
    url, im_place = iqdb_url_dict[place]
    page = get_page_result(image=post_img.path, url=url)
    # if ok, will output: <Response [200]>
    result = list(models.ImageMatch.get_or_create_from_page(
        page=page, image=post_img, place=im_place))
    result = [x[0] for x in result]
    if match_filter == 'best-match':
        result = [x for x in result if x.status == x.STATUS_BEST_MATCH]

    br = mechanicalsoup.StatefulBrowser(soup_config={'features': 'lxml'})
    br.raise_on_404 = True
    scraper = cfscrape.CloudflareScraper()
    MatchTagRelationship = models.MatchTagRelationship
    for item in result:
        match_result = item.match.match_result
        url = match_result.link

        print('{}|{}|{}'.format(
            item.similarity, item.status_verbose, url
        ))

        res = MatchTagRelationship.select().where(MatchTagRelationship.match == match_result)
        tags = [x.tag.full_name for x in res]
        if not tags:
            try:
                tags = get_tags(br, url, scraper)
            except requests.exceptions.ConnectionError as e:
                log.error(str(e), url=url)
        if tags:
            print('\n'.join(tags))
        else:
            log.debug('No tags found.')
        print('\n')
<commit_msg>chg: dev: remove unused time import<commit_after>#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""main module."""
import os

import cfscrape
import click
import mechanicalsoup
import requests
import structlog

from iqdb_tagger import models
from iqdb_tagger.__init__ import db_version
from iqdb_tagger.utils import default_db_path, thumb_folder, user_data_dir
from iqdb_tagger.custom_parser import get_tags

db = '~/images/! tagged'
DEFAULT_SIZE = 150, 150
DEFAULT_PLACE = 'iqdb'
minsim = 75
services = ['1', '2', '3', '4', '5', '6', '10', '11']
forcegray = False
log = structlog.getLogger()
iqdb_url_dict = {
    'iqdb': ('http://iqdb.org', models.ImageMatch.SP_IQDB),
    'danbooru': (
        'http://danbooru.iqdb.org',
        models.ImageMatch.SP_DANBOORU
    ),
}

def get_page_result(image, url):
    """Get page result.

    Args:
        image: Image path to be uploaded.
    Returns:
        HTML page from the result.
    """
    br = mechanicalsoup.StatefulBrowser(soup_config={'features': 'lxml'})
    br.raise_on_404 = True
    br.open(url)
    html_form = br.select_form('form')
    html_form.input({'file': image})
    br.submit_selected()
    # if ok, will output: <Response [200]>
    return br.get_current_page()

def get_posted_image(
        img_path, resize=False, size=None, output_thumb_folder=None):
    """Get posted image."""
    if output_thumb_folder is None:
        output_thumb_folder = thumb_folder

    img, _ = models.ImageModel.get_or_create_from_path(img_path)
    def_thumb_rel, _ = models.ThumbnailRelationship.get_or_create_from_image(
        image=img, thumb_folder=output_thumb_folder, size=DEFAULT_SIZE)
    resized_thumb_rel = None

    if resize and size:
        resized_thumb_rel, _ = \
            models.ThumbnailRelationship.get_or_create_from_image(
                image=img, thumb_folder=output_thumb_folder, size=size
            )
    elif resize:
        # use thumbnail if no size is given
        resized_thumb_rel = def_thumb_rel
    else:
        log.debug('Unknown config.', resize=resize, size=size)

    return resized_thumb_rel.thumbnail \
        if resized_thumb_rel is not None else img

def init_program(db_path=None):
    """Init program."""
    # create user data dir
    if not os.path.isdir(user_data_dir):
        os.makedirs(user_data_dir, exist_ok=True)
        log.debug('User data dir created.')
    # create thumbnail folder
    if not os.path.isdir(thumb_folder):
        os.makedirs(thumb_folder, exist_ok=True)
        log.debug('Thumbnail folder created.')

    # database
    if db_path is None:
        db_path = default_db_path
    models.init_db(db_path, db_version)

def get_tags(browser, url, scraper):
    """Get tags."""
    # compatibility
    br = browser

    br.open(url)
    page = br.get_current_page()
    tags = get_tags(page, url, scraper)
    if tags:
        for tag in tags:
            tag_parts = tag.split(':', 1)
            if ':' in tag:
                tag_name = tag_parts[1]
                namespace = tag_parts[0]
            else:
                tag_name = tag_parts[0]
                namespace = None
            tag_model, _ = models.Tag.get_or_create(
                name=tag_name, namespace=namespace)
            MatchTagRelationship.get_or_create(match=match_result, tag=tag_model)
        return tags
    else:
        log.debug('No tags found.')

@click.command()
@click.option(
    '--place', type=click.Choice(['iqdb', 'danbooru']),
    default=DEFAULT_PLACE, help='Specify iqdb place, default:{}'.format(DEFAULT_PLACE)
)
@click.option('--resize', is_flag=True, help='Use resized image.')
@click.option('--size', is_flag=True, help='Specify resized image.')
@click.option('--db-path', help='Specify Database path.')
@click.option(
    '--match-filter', type=click.Choice(['default', 'best-match']),
    default='default', help='Filter the result.')
@click.argument('image')
def main(
    image, resize=False, size=None,
    db_path=None, place=DEFAULT_PLACE, match_filter='default'
):
    """Get similar image from iqdb."""
    init_program(db_path)
    post_img = get_posted_image(img_path=image, resize=resize, size=size)
    url, im_place = iqdb_url_dict[place]
    page = get_page_result(image=post_img.path, url=url)
    # if ok, will output: <Response [200]>
    result = list(models.ImageMatch.get_or_create_from_page(
        page=page, image=post_img, place=im_place))
    result = [x[0] for x in result]
    if match_filter == 'best-match':
        result = [x for x in result if x.status == x.STATUS_BEST_MATCH]

    br = mechanicalsoup.StatefulBrowser(soup_config={'features': 'lxml'})
    br.raise_on_404 = True
    scraper = cfscrape.CloudflareScraper()
    MatchTagRelationship = models.MatchTagRelationship
    for item in result:
        match_result = item.match.match_result
        url = match_result.link

        print('{}|{}|{}'.format(
            item.similarity, item.status_verbose, url
        ))

        res = MatchTagRelationship.select().where(MatchTagRelationship.match == match_result)
        tags = [x.tag.full_name for x in res]
        if not tags:
            try:
                tags = get_tags(br, url, scraper)
            except requests.exceptions.ConnectionError as e:
                log.error(str(e), url=url)
        if tags:
            print('\n'.join(tags))
        else:
            log.debug('No tags found.')
        print('\n')
<|endoftext|>