import numpy as np
from skimage.transform import resize

class Image:

    def __init__(self, image: np.array):
        self.image = image

        self.streamlit = None  # Representation of what the computer see
        self.prediction = None  # Image array sanitized, to make a prediction

    def get_streamlit_displayable(self):
        """
        Return a representation of what the computer see. This image is still
        displayable in Streamlit.
        :return: A numpy array of the image.
        :rtype: np.array
        """

        if self.streamlit is None:

            # Grayscale the image
            gray_scaled = np.dot(self.image[..., :3], [0.2989, 0.5870, 0.1140])

            # Scale from [0, 255] to [0, 1]
            scaled = gray_scaled / 255

            # Resize to 64x64
            self.streamlit = resize(scaled, (64, 64))

        # Return a 28x28 (w, h) [0, 1] image
        return self.streamlit

    def get_prediction_ready(self):
        """
        Create a prediction-ready sanitized version of the image. This is what
        the model needs to make a prediction.
        :return: a torch Tensor of the image.
        :rtype: np.array
        """

        self.get_streamlit_displayable()

        if self.prediction is None:

            # Reshape to (1, 1, 28, 28)
            width, height, *_ = self.streamlit.shape
            reshaped = self.streamlit.reshape(1, width, height, 1)

            # Transform to float32
            self.prediction = reshaped.astype('float32')

        return self.prediction

    def is_empty(self):
        """Return true if self.image is an empty black image."""

        self.get_streamlit_displayable()
        if np.max(self.streamlit) == np.min(self.streamlit) == 0:
            return True

        return False


import matplotlib.pyplot as plt
file = open("1.txt","r")
str = file.read()
file.close()
list1 = str.split()
freq = [list1.count(d) for d in list1]
#print(dict(zip(list1,freq)))

#dic = dict(zip(list1,freq))
total = 0

for i in list1:
    total += 1

print(total)

per = []
for i in freq:
    per.append((i/total)*100)

#print(dict(zip(list1,per)))
file = open("3.txt","w+")
file.write(str(dict(zip(list1,per))))
li = []
count = 0
for i in freq:
    li.append(count)
    count+=1
print(li)
plt.barh(list1,per)
plt.ylabel("percentage")
plt.xlabel("word")
plt.show()
file.close()


import numpy as np
import pandas as pd
import h5py
import os
import skimage.io as sio
import tqdm
import argparse
import pickle as pkl

def save_dict_to_hdf5(dic, filename):
    """
    ....
    """
    if os.path.exists(filename):
        return
    with h5py.File(filename, 'w') as h5file:
        recursively_save_dict_contents_to_group(h5file, '/', dic)

def recursively_save_dict_contents_to_group(h5file, path, dic):
    """
    ....
    """
    for key, item in dic.items():
        if isinstance(item, (np.ndarray, np.int64, np.float64, str, bytes)):
            h5file[path + key] = item
        elif isinstance(item, dict):
            recursively_save_dict_contents_to_group(h5file, path + key + '/', item)
        else:
            raise ValueError('Cannot save %s type'%type(item))

def load_dict_from_hdf5(filename):
    """
    ....
    """
    with h5py.File(filename, 'r') as h5file:
        return recursively_load_dict_contents_from_group(h5file, '/')

def recursively_load_dict_contents_from_group(h5file, path):
    """
    ....
    """
    ans = {}
    for key, item in h5file[path].items():
        if isinstance(item, h5py._hl.dataset.Dataset):
            ans[key] = item.value
        elif isinstance(item, h5py._hl.group.Group):
            ans[key] = recursively_load_dict_contents_from_group(h5file, path + key + '/')
    return ans

def load_data(path):
    # Load the pointcloud.npz and points.npz file
    pc_file = np.load(os.path.join(path, "pointcloud.npz"))
    points_file = np.load(os.path.join(path, "points.npz"))

    # create image placeholder and camera data placeholder
    img_data = []
    cam_data = None

    # Load images
    for imx in os.listdir(os.path.join(path, "img_choy2016")):
        current = os.path.join(path, "img_choy2016", imx)
        if 'npz' in imx:
            cam_data = np.load(current)
        else:
            img_current = sio.imread(current)
            if img_current.ndim == 2:
                img_current = np.stack([img_current, img_current, img_current], axis=-1)
            img_data.append(img_current)
    img_data = np.asarray(img_data)

    all_data = {
        'images': img_data,
        'camera': dict(cam_data),
        'points': dict(points_file),
        'pointcloud': dict(pc_file)
    }

    return all_data

def main(args):
    data_root = args.dataroot
    dataset_dir = args.output

    # Create the output folder
    os.makedirs(os.path.join(dataset_dir, "hdf_data"), exist_ok=True)
    save_path = os.path.join(dataset_dir, "hdf_data")

    file_lists = {
        'train.lst': [],
        'test.lst': [],
        'val.lst': []
    }

    # iterate over each class in the dataset
    for cid in os.listdir(data_root):
        # Get the path to each object and list of objects
        objs_path = os.path.join(data_root, cid)
        if "metadata" in cid.lower():
            continue
        obj_list = os.listdir(objs_path)

        # iterate over each object in the dataset class
        for obx in tqdm.tqdm(obj_list):
            current_path = os.path.join(objs_path, obx)
            new_filename = "{}_{}.h5".format(cid, obx)

            try:
                if os.path.exists(os.path.join(save_path, new_filename)):
                    continue

                # If possible, load the object and it's propertiess
                data_current = load_data(current_path)

                # Save the output into the HDF5 file at the output location
                save_dict_to_hdf5(data_current, os.path.join(save_path, new_filename))
            except:
                # Print the file name for error logs
                if obx.lower() in ["train.lst", "test.lst", "val.lst"]:
                    # read each file
                    f = open(current_path, 'r')
                    flist = ["{}_{}.h5".format(cid, yx) for yx in f.read().split()]
                    f.close()

                    # Append to file lists
                    file_lists[obx] += flist
                else:
                    print("Error at {}-{}".format(cid, obx))

    # Now save the file lists as well
    for kx in file_lists.keys():
        # Get each file list and save
        print("Processing list for {}".format(kx))
        flist = "\n".join(file_lists[kx])
        f = open(os.path.join(save_path, kx), 'w')
        f.write(flist)
        f.close()
    print("Saved data with train-test-val splits...")

if __name__ == "__main__":
    # Create the argument parser and parse the script parameters
    parser = argparse.ArgumentParser(description='Process dataset to create HDF5 data file for each object')
    parser.add_argument('--dataroot', action='store', type=str, help="dataset path for the preprocessed shapenet files")
    parser.add_argument('--output', action='store', type=str, help="output data folder to save the dataset")

    args = parser.parse_args()

    # Run the main function
    main(args)


from .. import Availability, Class, Constant, Define, Method, Parameter, Type

gx_class = Class('DOCEGEO',
                 doc="Technical Services GX functions Header File for Docegeo")

gx_defines = [
]

gx_methods = {
    'Docegeo': [

        Method('IPSectionPlot_DOCEGEO', module='geocsdocegeo', version='6.3.0',
               availability=Availability.EXTENSION,
               doc="Docegeo IP section plot",
               return_type=Type.VOID,
               return_doc="Nothing",
               parameters = [
                   Parameter('param0', type="IP",
                             doc="IP handle"),
                   Parameter('param1', type="DB",
                             doc="GDB handle"),
                   Parameter('param2', type=Type.STRING,
                             doc="Current line"),
                   Parameter('param3', type=Type.STRING,
                             doc="Map tag name"),
                   Parameter('param4', type=Type.STRING,
                             doc="Map name"),
                   Parameter('param5', type=Type.STRING,
                             doc="Control file")
               ]),

        Method('WPSectionPlot_DOCEGEO', module='geocsdocegeo', version='6.3.0',
               availability=Availability.EXTENSION,
               doc="Docegeo Target section plot",
               return_type=Type.VOID,
               return_doc="Nothing",
               parameters = [
                   Parameter('param0', type="DH",
                             doc="DH handle"),
                   Parameter('param1', type=Type.STRING,
                             doc="Map name"),
                   Parameter('param2', type=Type.STRING,
                             doc="Section INI (*.ins) file")
               ]),

        Method('QCReject_DOCEGEO', module='geocsdocegeo', version='6.3.0',
               availability=Availability.EXTENSION,
               doc="QC reject function",
               return_type=Type.VOID,
               return_doc="Nothing",
               parameters = [
                   Parameter('param0', type="DB",
                             doc="DB handle"),
                   Parameter('param1', type="DB_SYMB",
                             doc="Line"),
                   Parameter('param2', type=Type.INT32_T,
                             doc="New QC channel?"),
                   Parameter('param3', type="DB_SYMB",
                             doc="QC channel symbol"),
                   Parameter('param4', type=Type.DOUBLE,
                             doc="Min I"),
                   Parameter('param5', type=Type.DOUBLE,
                             doc="Max Noise"),
                   Parameter('param6', type=Type.DOUBLE,
                             doc="Max Error"),
                   Parameter('param7', type=Type.DOUBLE,
                             doc="Max RS"),
                   Parameter('param8', type=Type.DOUBLE,
                             doc="Max Vp"),
                   Parameter('param9', type="DB_SYMB",
                             doc="I Channel"),
                   Parameter('param10', type="DB_SYMB",
                             doc="IP Channel"),
                   Parameter('param11', type="DB_SYMB",
                             doc="Err Channel"),
                   Parameter('param12', type="DB_SYMB",
                             doc="Rs Channel"),
                   Parameter('param13', type="DB_SYMB",
                             doc="Vp Channel"),
                   Parameter('param14', type="DB_SYMB",
                             doc="IPlog Channel")
               ]),

        Method('PlotThompson_DOCEGEO', module='geocsdocegeo', version='6.3.0',
               availability=Availability.EXTENSION,
               doc="Thompson duplicate results plot",
               return_type=Type.VOID,
               return_doc="Nothing",
               parameters = [
                   Parameter('param0', type="MVIEW",
                             doc="MVIEW handle"),
                   Parameter('param1', type="VV",
                             doc="Data"),
                   Parameter('param2', type=Type.DOUBLE,
                             doc="Detect limit"),
                   Parameter('param3', type=Type.STRING,
                             doc="Plot title"),
                   Parameter('param4', type=Type.STRING,
                             doc="Unit"),
                   Parameter('param5', type=Type.DOUBLE,
                             doc="X location"),
                   Parameter('param6', type=Type.DOUBLE,
                             doc="Y location"),
                   Parameter('param7', type=Type.DOUBLE,
                             doc="X size"),
                   Parameter('param8', type=Type.DOUBLE,
                             doc="Y size")
               ]),

        Method('PlobPlot_DOCEGEO', module='geocsdocegeo', version='6.3.0',
               availability=Availability.EXTENSION,
               doc="Docegeo ProbPlot function",
               return_type=Type.VOID,
               return_doc="Nothing",
               parameters = [
                   Parameter('param0', type="DB",
                             doc="DB handle"),
                   Parameter('param1', type=Type.STRING,
                             doc="Control file")
               ])
    ]
}


from django.urls import path, re_path

from . import views

urlpatterns = [
    path('list', views.list, name='list'),
    path('table', views.table, name='table'),
    path('task/<int:id>', views.task, name='task'),
    path('form', views.form, name='form'),
    path('form_process', views.form_process, name='form_process'),
]


from __future__ import unicode_literals

from taxi.projects import Activity, Project, ProjectsDb

from . import CommandTestCase

class AddCommandTestCase(CommandTestCase):
    def test_add_single_choice(self):
        project = Project(1, 'test project', Project.STATUS_ACTIVE)
        project.activities = [Activity(2, 'test activity', 0)]
        p = ProjectsDb(self.taxi_dir)
        p.update([project])

        self.run_command('add', ['test project'], input='test_alias')

        with open(self.config_file, 'r') as f:
            self.assertIn('test_alias = 1/2\n', f.readlines())

    def test_add_multiple_choices(self):
        p1 = Project(1, 'test project', Project.STATUS_ACTIVE)
        p1.activities = [Activity(2, 'test activity', 0)]
        p2 = Project(2, 'test project 2', Project.STATUS_ACTIVE)
        p2.activities = [Activity(3, 'test activity 2', 0)]
        p = ProjectsDb(self.taxi_dir)
        p.update([p1, p2])

        self.run_command('add', ['test project'], input='1\ntest_alias')

        with open(self.config_file, 'r') as f:
            self.assertIn('test_alias = 2/3\n', f.readlines())

    def test_add_inactive_project(self):
        project = Project(1, 'test project', Project.STATUS_FINISHED)
        project.activities = [Activity(2, 'test activity', 0)]
        p = ProjectsDb(self.taxi_dir)
        p.update([project])

        output = self.run_command('add', ['test project'], input='test_alias')

        self.assertIn("No active project matches your search string", output)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Sep 13 17:34:48 2019

@author: rick
"""

import tensorflow as tf

from yolo.src.utils import param_file_access
from yolo.src.datasets.std import inputs_builder
import time
import os
import numpy as np
import cv2
from PIL import Image
import matplotlib.pyplot as plt
from yolo.src.utils import model_utils,data_augment,imgaug_utils
import tensorflow.contrib.slim as slim

prefetch_queue = slim.prefetch_queue

flags = tf.app.flags
FLAGS = flags.FLAGS

flags.DEFINE_string('list_path', '/home/psdz/TK/tensorFlowTrain/samples_to_train/MscocoDet/Set/train.txt',
                    'List path.')
flags.DEFINE_string('tfrecord_path',
                    '/home/psdz/TK/tensorFlowTrain/samples_to_train/MscocoDet/TFRecord/train.record',
                    'Tfrecord path.')
flags.DEFINE_string('label_map_path',
                    '/home/psdz/TK/tensorFlowTrain/samples_to_train/MscocoDet/LabelMap/label_map.json',
                    'Label map path.')
flags.DEFINE_string('label_name_key', 'display_name',
                    'Label name key in label map, e.g. display_name')
flags.DEFINE_boolean('include_classify', False,
                     'Is include classify.')
flags.DEFINE_boolean('include_detect', True,
                     'Is include detect.')
flags.DEFINE_boolean('include_detect_label', False,
                    'Is include detect label.')
flags.DEFINE_boolean('include_segment', False,
                     'Is include segment.')
flags.DEFINE_integer('resize_height', 512,
                     'Image resized height.')
flags.DEFINE_integer('resize_width', 512,
                     'Image resized width.')
flags.DEFINE_string('image_save_dir', None,
                    'Image save directory')

def _build_name_to_label_map(label_map_path, label_name_key):
    name_list = param_file_access.get_label_map_value_list_by_key(label_map_path, key=label_name_key)
    id_list = [i for i in range(0, len(name_list))]
    name_list_tensor = tf.constant(name_list)
    id_list_tensor = tf.constant(id_list, dtype=tf.int64)
    return model_utils.build_tensor_map(
            key_list_tensor=name_list_tensor,
            value_list_tensor=id_list_tensor,
            default_value=-1)

def _preprocess_fn(sample, fixed_height, fixed_width,fast_mode=False,build_label_from_name=(not FLAGS.include_detect_label)):
    image = sample[inputs_builder.IMAGE]
    num_objs = sample[inputs_builder.NUM_OBJS]
    bboxes = sample[inputs_builder.OBJ_BOX]
    labels = sample[inputs_builder.OBJ_LABEL]
    labels = tf.cast(labels, dtype=tf.int32)

    t0 = time.time()
    # imgaug_utils.PIECEWISEAFFINE['probability'] = .0
    # aug = imgaug_utils.Augment(image, num_objs, bboxes, fixed_height, fixed_width, max_num_objects=100)
    # image, bboxes = aug(p=.5)  # p表示有p的概率会应用数据增强，p取0~1之间。

    image = tf.image.convert_image_dtype(image, dtype=tf.float32)  # 将unit8的图片类型转成范围在[0,1]的float32类型
    num_distort_cases = 1 if fast_mode else 4
    # image = data_augment.apply_with_random_selector(
    #     image,
    #     lambda x, ordering: data_augment.distort_color(x, ordering, fast_mode),
    #     num_cases=num_distort_cases)

    image, bboxes = tf.cond(tf.random_uniform([]) > .5,
                            lambda: data_augment.random_crop(image, bboxes),
                            lambda: data_augment.do_nothing(image, bboxes))
    image, bboxes = tf.cond(tf.random_uniform([]) > .5,
                            lambda: data_augment.do_nothing(image, bboxes),
                            lambda: data_augment.random_pad_image(image, bboxes))
    new_height = tf.shape(image)[0]
    new_width = tf.shape(image)[1]
    sample[inputs_builder.HEIGHT]=new_height
    sample[inputs_builder.WIDTH]=new_width
    image = tf.image.resize_images(
        image, [fixed_height, fixed_width],
        method=tf.image.ResizeMethod.BILINEAR,
        align_corners=True)
    image_with_boxes = tf.image.draw_bounding_boxes(tf.expand_dims(image, 0),
                                                  tf.expand_dims(bboxes, 0))[0]

    print('aug time:', time.time() - t0)

    sample[inputs_builder.IMAGE] = image
    sample[inputs_builder.OBJ_BOX] = bboxes
    # sample[inputs_builder.OBJ_LABEL] = labels
    if inputs_builder.NUM_OBJS in sample and build_label_from_name:
        tensor_map = _build_name_to_label_map(FLAGS.label_map_path, FLAGS.label_name_key)
        sample[inputs_builder.OBJ_LABEL] = model_utils.mapping_tensor(
                sample[inputs_builder.OBJ_LABEL_NAME], tensor_map)

    if inputs_builder.MASK in sample:
        mask = sample[inputs_builder.MASK]
        if mask is not None:
            mask = tf.image.resize_images(
                mask, [fixed_height, fixed_width],
                method=tf.image.ResizeMethod.NEAREST_NEIGHBOR,
                align_corners=True)
        sample[inputs_builder.MASK] = mask
    return sample

def _save_image(image, image_format, save_dir, cnt, mask=None, mask_format=None):
    if save_dir is not None:
        print('save image')
        image_save_path = os.path.join(
            save_dir, 'img_{0}.{1}'.format(cnt, image_format))
        cv2.imwrite(image_save_path, image)
        if mask is not None and mask_format is not None:
            mask_save_path = os.path.join(
                save_dir, 'img_{0}_mask.{1}'.format(cnt, mask_format))
            cv2.imwrite(mask_save_path, mask)

        img_np_tmp = np.array(Image.open(image_save_path))
        print(np.shape(img_np_tmp))
        print(img_np_tmp.dtype)
        cnt = cnt + 1
        return cnt

if __name__ == '__main__':
    print('test================================')
    batch_size = 1
    bbox_color_bgr = (0, 255, 0)
    bbox_thickness = 1
    bbox_text_font = cv2.FONT_HERSHEY_SIMPLEX
    bbox_text_scale = 0.4
    bbox_text_thickness = 1
    mask_alpha = 0.6
    max_num_objects = 50
    num_samples = len(param_file_access.get_txt_params(FLAGS.list_path))
    num_classes = len(param_file_access.get_label_map_class_id_list(FLAGS.label_map_path))
    tfrecord_path = FLAGS.tfrecord_path
    label_map_path = FLAGS.label_map_path
    fixed_height = FLAGS.resize_height
    fixed_width = FLAGS.resize_width
    image_save_dir = FLAGS.image_save_dir

    builder = inputs_builder.StdTFRecordInputs()
    with tf.Graph().as_default() as graph:
        preprocess_args = [fixed_height, fixed_width]
        input_queue = builder.get(
            tfrecord_path=tfrecord_path,
            num_samples=num_samples,
            batch_size=batch_size,
            include_classify=FLAGS.include_classify,
            include_detect=FLAGS.include_detect,
            include_segment=FLAGS.include_segment,
            preprocess_fn=_preprocess_fn,
            preprocess_args=preprocess_args,
            max_num_labels=num_classes,
            max_num_objects=max_num_objects,
            is_shuffle=False)
        samples = input_queue.dequeue()

        with tf.Session() as sess:
            tf.tables_initializer().run()
            coord = tf.train.Coordinator()
            threads = tf.train.start_queue_runners(coord=coord)

            cnt = 0
            over = False
            while not over:
                print('======================')
                samples2 = sess.run(samples)
                name_list = samples2[inputs_builder.NAME]
                image_list = samples2[inputs_builder.IMAGE]
                image_format_list = samples2[inputs_builder.IMAGE_FORMAT]
                for i in range(len(name_list)):
                    print('%d-------------' % i)
                    image = image_list[i]
                    image_format = str(image_format_list[i], 'utf-8')
                    for key, value in samples2.items():
                        v = value[i]
                        t = type(v)
                        if t is bytes:
                            print('{0}: {1}'.format(key, str(v, 'utf-8')))
                        else:
                            if t is np.ndarray:
                                data = v.dtype if np.size(v) > 1000000 else v
                            else:
                                data = v
                            print('{0}: shape={1}, data={2}, dtype={3}'.format(
                                key, np.shape(v), data, v.dtype))

                    image = np.uint8(image * 255)
                    if inputs_builder.MASK in samples2:
                        mask = np.squeeze(samples2[inputs_builder.MASK][i])
                        mask_format = str(samples2[inputs_builder.MASK_FORMAT][i], 'utf-8')
                        mask_image = Image.fromarray(mask)
                        mask_palette = param_file_access.get_label_map_palette(label_map_path)
                        mask_image.putpalette(mask_palette)
                        plt.figure(0)
                        plt.subplot(1, 3, 1)
                        plt.imshow(mask_image)
                        plt.subplot(1, 3, 2)
                        plt.imshow(image)
                        plt.subplot(1, 3, 3)
                        plt.imshow(image)
                        plt.imshow(mask_image, alpha=mask_alpha)
                        plt.show()

                        cnt = _save_image(
                            image, image_format, image_save_dir, cnt,
                            mask, mask_format)
                    else:
                        str_labels = ''
                        image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
                        if inputs_builder.NUM_OBJS in samples2:
                            num_boxes = samples2[inputs_builder.NUM_OBJS][i]
                            box_label_list = samples2[inputs_builder.OBJ_LABEL][i]
                            box_label_name_list = samples2[inputs_builder.OBJ_LABEL_NAME][i]
                            box_list = samples2[inputs_builder.OBJ_BOX][i]
                            for j in range(num_boxes):
                                box_label = box_label_list[j]
                                box_label_name = str(box_label_name_list[j], 'utf-8')
                                box = box_list[j]
                                box[1] = int(box[1] * fixed_width)  # xmin
                                box[0] = int(box[0] * fixed_height)  # ymin
                                box[3] = int(box[3] * fixed_width)  # xmax
                                box[2] = int(box[2] * fixed_height)  # ymax
                                text = '%s: %d' % (box_label_name, box_label)
                                cv2.rectangle(image,
                                              (box[1], box[0]),
                                              (box[3], box[2]),
                                              bbox_color_bgr,
                                              bbox_thickness)
                                cv2.putText(image,
                                            text,
                                            (box[1], box[0]),
                                            bbox_text_font,
                                            bbox_text_scale,
                                            bbox_color_bgr,
                                            bbox_text_thickness)
                        if inputs_builder.NUM_LABELS in samples2:
                            num_labels = samples2[inputs_builder.NUM_LABELS][i]
                            label_list = samples2[inputs_builder.LABEL][i]
                            str_labels = ''
                            splite_str = ','
                            for j in range(num_labels):
                                id_name_dict = param_file_access.get_label_map_class_id_name_dict(
                                    label_map_path,FLAGS.label_name_key)
                                print(id_name_dict)
                                label_id = label_list[j]
                                str_labels = (
                                        str_labels +
                                        str(label_id) + ':' +
                                        id_name_dict[label_id] + splite_str)
                            str_labels = str_labels.strip(splite_str)
                            print(str_labels)

                        title = str_labels if str_labels is not '' else ''
                        cv2.namedWindow(title, cv2.WINDOW_AUTOSIZE)
                        cv2.imshow(title, image)
                        cnt = _save_image(image, image_format, image_save_dir, cnt)
                        if cv2.waitKey(0) & 0xFF == 27:  # enter Esc to quit
                            cv2.destroyAllWindows()
                            over = True
                            break
                        else:  # enter other key to next
                            cv2.destroyAllWindows()
                            continue

            cv2.destroyAllWindows()

            coord.request_stop()
            coord.join(threads)


import time
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation, Flatten,Reshape
from keras.layers.convolutional import Convolution2D, MaxPooling2D
from tensorflow.examples.tutorials.mnist import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

x_train, y_train = mnist.train.images,mnist.train.labels
x_test, y_test = mnist.test.images, mnist.train.labels
x_train = x_train.reshape(-1, 28, 28,1).astype('float32')
x_test = x_test.reshape(-1,28, 28,1).astype('float32')

def net_model():
    model=Sequential()
    model.add(Convolution2D(32,(5,5),padding='same',
                            input_shape=(28, 28, 1)))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(2,2),padding='same'))
    model.add(Convolution2D(64,(5,5),padding='same'))
    model.add(Activation('relu'))
    model.add(MaxPooling2D(pool_size=(2, 2), strides=(2,2),padding='same'))
    model.add(Reshape((7*7*64,)))
    model.add(Dense(1024))
    model.add(Activation('relu'))
    model.add(Dropout(0.5))
    model.add(Dense(10))
    model.add(Activation('softmax'))
    model.compile(loss='categorical_crossentropy',optimizer='sgd',metrics=["accuracy"])
    return model
model=net_model()
train_start=time.time()
model.fit(x_train,y_train,batch_size=50,epochs=10,verbose=1)
train_end=time.time()
train_time=train_end-train_start
print('The time cost in train:%f'%train_time)


# -*- coding: utf-8 -*-

from odoo import models, fields,api,_
from odoo.addons import decimal_precision as dp
from odoo.tools import float_compare
from odoo.exceptions import UserError
import logging
_logger = logging.getLogger(__name__)
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT

class OrdenesDeReparacion(models.Model):
    _name = 'ordenes.de.reparacion'
    _inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin']
    _order = 'id desc'

    @api.depends('refacciones_ids.price_total','refacciones_ids.product_uom_qty','refacciones_ids.product_uom_qty','refacciones_ids.price_unit')
    def _amount_all_refacciones(self):
        """
        Compute the total amounts of the Refacciones.
        """
        for order in self:
            amount_untaxed = amount_tax = 0.0
            for line in order.refacciones_ids:
                amount_untaxed += line.price_subtotal
                amount_tax += line.price_tax
            order.update({
                'amount_untaxed_refacciones': amount_untaxed,
                'amount_tax_refacciones': amount_tax,
                'amount_total_refacciones': amount_untaxed + amount_tax,
            })

    @api.depends('mano_de_obra_ids.price_total','refacciones_ids.product_uom_qty','refacciones_ids.product_uom_qty','refacciones_ids.price_unit')
    def _amount_all_mano(self):
        """
        Compute the total amounts of the Refacciones.
        """
        for order in self:
            amount_untaxed = amount_tax = 0.0
            for line in order.mano_de_obra_ids:
                amount_untaxed += line.price_subtotal
                amount_tax += line.price_tax
            order.update({
                'amount_untaxed_mano': amount_untaxed,
                'amount_tax_mano': amount_tax,
                'amount_total_mano': amount_untaxed + amount_tax,
            })

    @api.depends('otros_talleres_ids.price_total','refacciones_ids.product_uom_qty','refacciones_ids.product_uom_qty','refacciones_ids.price_unit')
    def _amount_all_otros(self):
        """
        Compute the total amounts of the Refacciones.
        """
        for order in self:
            amount_untaxed = amount_tax = 0.0
            for line in order.otros_talleres_ids:
                amount_untaxed += line.price_subtotal
                amount_tax += line.price_tax
            order.update({
                'amount_untaxed_otros': amount_untaxed,
                'amount_tax_otros': amount_tax,
                'amount_total_otros': amount_untaxed + amount_tax,
            })

    @api.multi
    @api.depends('amount_total_otros','amount_total_mano', 'amount_total_refacciones')
    def _compute_total_orden_de_reparacion(self):
        for order in self:
            order.total_orden_de_reparacion = order.amount_total_otros + order.amount_total_mano + order.amount_total_refacciones

    @api.model
    def _get_default_team(self):
        return self.env['crm.team']._get_default_team_id()

    name = fields.Char(
        string="Orden de reparación",
        copy=False,
        required=True,
        default=lambda self: _('New'),)
    partner_id = fields.Many2one("res.partner",'Cliente',domain=[('customer','=',True)],required=True)
    cotizar_id = fields.Many2one("res.partner",'Cotizar a',domain=[('customer','=',True)])
    vehiculo_id = fields.Many2one("taller.vehiculos",'Vehiculo', required=True)
    mecanico_id = fields.Many2one("res.partner",'Mécanico',domain=[('mecanico','=',True)])
    proveedor_id = fields.Many2one("res.partner",'Proveedor',domain=[('supplier','=',True)])
    fecha = fields.Date("Fecha")

    currency_id = fields.Many2one("res.currency", string="Currency", readonly=True, default=lambda self: self.env.user.company_id.currency_id)
    company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env['res.company']._company_default_get('ordenes.de.reparacion'))
    refacciones_ids = fields.One2many('refacciones.ordenes.de.reparacion','reparacion_id', 'Refacciones', copy=True, auto_join=True)
    mano_de_obra_ids = fields.One2many('mano.deobra.ordenes.de.reparacion','reparacion_id', 'Mano de obra', copy=True, auto_join=True)
    otros_talleres_ids = fields.One2many('otros.talleres.ordenes.de.reparacion','reparacion_id', 'Otros talleres', copy=True, auto_join=True)

    amount_untaxed_refacciones = fields.Monetary(string='Refacciones Untaxed Amount', store=True, readonly=True, compute='_amount_all_refacciones',track_visibility='onchange', track_sequence=5)
    amount_tax_refacciones = fields.Monetary(string='Refacciones Taxes', store=True, readonly=True, compute='_amount_all_refacciones')
    amount_total_refacciones = fields.Monetary(string='Refacciones Total', store=True, readonly=True,track_visibility='always', compute='_amount_all_refacciones', track_sequence=6)

    amount_untaxed_mano = fields.Monetary(string='Mano de obra Untaxed Amount', store=True, readonly=True, compute='_amount_all_mano',track_visibility='onchange', track_sequence=5)
    amount_tax_mano = fields.Monetary(string='Mano de obra Taxes', store=True, readonly=True, compute='_amount_all_mano')
    amount_total_mano = fields.Monetary(string='Mano de obra Total', store=True, readonly=True,track_visibility='always', compute='_amount_all_mano', track_sequence=6)

    amount_untaxed_otros = fields.Monetary(string='Otros Untaxed Amount', store=True, readonly=True, compute='_amount_all_otros',track_visibility='onchange', track_sequence=5)
    amount_tax_otros = fields.Monetary(string='Otros Taxes', store=True, readonly=True, compute='_amount_all_otros')
    amount_total_otros = fields.Monetary(string='Otros Total', store=True, readonly=True,track_visibility='always', compute='_amount_all_otros', track_sequence=6)

    total_orden_de_reparacion = fields.Float("Total orden de reparación",compute="_compute_total_orden_de_reparacion", readonly=True, store=True)
    note = fields.Text('Notas')
    state = fields.Selection([('Presupuestada','Presupuestada'),('Confirmada','Confirmada'), ('Cerrada','Cerrada'), ('Cancelado','Cancelado')],string='States', default='Presupuestada')
    orden_de_servicio_de_garantia = fields.Selection([('Pagada por el provedor', 'Pagada por el provedor'), ('Pagara por la empresa', 'Pagada por la empresa')], string='Orden de servicio de garantia')

    READONLY_STATES = {
        'Confirmada': [('readonly', True)],
        'Cerrada': [('readonly', True)],
    }

    operating_unit_id = fields.Many2one(
        comodel_name='operating.unit', states=READONLY_STATES,
        string='Operating Unit', default=lambda self: (self.env['res.users'].operating_unit_default_get(self.env.uid)))

    warehouse_id = fields.Many2one('stock.warehouse', string='Warehouse')
    dest_location_id = fields.Many2one('stock.location', 'Ubicacion destino')

    user_id = fields.Many2one('res.users', string='Salesperson', index=True, track_visibility='onchange', track_sequence=2, default=lambda self: self.env.user)
    team_id = fields.Many2one('crm.team', 'Sales Team', change_default=True, default=_get_default_team)
    procurement_group_id = fields.Many2one('procurement.group', 'Procurement Group', copy=False)
    incoming_group_id = fields.Many2one('procurement.group', 'Procurement Group', copy=False)
    picking_ids = fields.One2many('stock.picking', 'ordenes_id', string='Pickings')
    delivery_count = fields.Integer(string='Delivery Orders', compute='_compute_picking_ids')

    in_picking_count = fields.Integer(compute='_compute_incoming_picking', string='Picking count', default=0, store=True)
    in_picking_ids = fields.Many2many('stock.picking', compute='_compute_incoming_picking', string='Receptions', copy=False, store=True)

    purchase_ids = fields.One2many('purchase.order', 'ordenes_id', string='Purchase Orders')
    purchase_count = fields.Integer(string='Purchase Orders', compute='_compute_purchase_orders', store=True)

    sale_order_count = fields.Integer(compute='_compute_sale_order_count', string='Sale Order Count')
    sale_order_ids = fields.One2many('sale.order', 'ordenes_id', 'Sales Order')

    @api.depends('sale_order_ids')
    def _compute_sale_order_count(self):
        for ordenes in self:
            ordenes.sale_order_count = len(ordenes.sale_order_ids)

    @api.depends('purchase_ids')
    def _compute_purchase_orders(self):
        for ordenes in self:
            ordenes.purchase_count = len(ordenes.purchase_ids)

    @api.depends('refacciones_ids.in_move_ids.returned_move_ids',
                 'refacciones_ids.in_move_ids.state',
                 'refacciones_ids.in_move_ids.picking_id')
    def _compute_incoming_picking(self):
        for ordenes in self:
            pickings = self.env['stock.picking']
            for line in ordenes.refacciones_ids:
                # We keep a limited scope on purpose. Ideally, we should also use move_orig_ids and
                # do some recursive search, but that could be prohibitive if not done correctly.
                moves = line.in_move_ids | line.in_move_ids.mapped('returned_move_ids')
                pickings |= moves.mapped('picking_id')
            ordenes.in_picking_ids = pickings
            ordenes.in_picking_count = len(pickings)

    @api.depends('picking_ids')
    def _compute_picking_ids(self):
        for ordenes in self:
            ordenes.delivery_count = len(ordenes.picking_ids)

    @api.onchange('user_id')
    def onchange_user_id(self):
        if self.user_id:
            self.team_id = self.env['crm.team']._get_default_team_id(self.user_id.id)

    @api.onchange('operating_unit_id')
    def _onchange_operating_unit_id(self):
        domain = []
        if self.operating_unit_id:
            self.dest_location_id = self.operating_unit_id.dest_location_id
            self.warehouse_id = self.operating_unit_id.warehouse_id
            warehouses = self.env['stock.warehouse'].search([('operating_unit_id', '=',self.operating_unit_id.id)])
            if warehouses:
                self.warehouse_id = warehouses[0].id
                domain = {'warehouse_id': [('id','in',warehouses.ids)]}
            else:
                domain = {'warehouse_id': [('id','in',[])]}
        res = {}
        if domain:
            res['domain'] = domain
        return res

    @api.model
    def get_company_address(self, record):
        address = []
        if record.company_id.street:
            address.append(record.company_id.street)
        if record.company_id.street2:
            address.append(record.company_id.street2)
        if record.company_id.zip:
            address.append(record.company_id.zip)
        if record.company_id.city:
            address.append(record.company_id.city)

#         if record.company_id.country_id:
#             address.append(record.company_id.country_id.name)
        ', '.join(address)
        return ', '.join(address)

    @api.multi
    def unlink(self):
        for ordenes in self:
            if ordenes.sale_order_ids or ordenes.purchase_ids or ordenes.in_picking_ids or ordenes.picking_ids:
                raise UserError("You are not allowed to delete this document.")
        return super(OrdenesDeReparacion, self).unlink()

    @api.model
    def create(self, vals):
        if vals.get('name', _('New')) == _('New'):
            vals['name'] = \
                self.env['ir.sequence'].next_by_code('ordenes.de.reparacion') or _('New')
        if not vals.get('fecha'):
            vals['fecha'] = fields.date.today()

        return super(OrdenesDeReparacion, self).create(vals)

    @api.multi
    def action_ordenes_de_reparacion_send(self):
        '''
        This function opens a window to compose an email, with the template message loaded by default
        '''
        self.ensure_one()
        ir_model_data = self.env['ir.model.data']
        try:
            template_id = ir_model_data.get_object_reference('taller', 'email_template_ordenes_de_reparacion')[1]
        except ValueError:
            template_id = False
        try:
            compose_form_id = ir_model_data.get_object_reference('mail', 'email_compose_message_wizard_form')[1]
        except ValueError:
            compose_form_id = False
        ctx = {
            'default_model': self._name,
            'default_res_id': self.ids[0],
            'default_use_template': bool(template_id),
            'default_template_id': template_id,
            'default_composition_mode': 'comment',
            'mark_so_as_sent': True,
            #'custom_layout': "sale.mail_template_data_notification_email_sale_order",
            #'proforma': self.env.context.get('proforma', False),
            'force_email': True
        }
        return {
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'mail.compose.message',
            'views': [(compose_form_id, 'form')],
            'view_id': compose_form_id,
            'target': 'new',
            'context': ctx,
        }

    @api.multi
    def action_view_in_purchase_orders(self):
        action = self.env.ref('purchase.purchase_form_action')
        result = action.read()[0]
        result['context'] = {}
        purchase_ids = self.mapped('purchase_ids')
        # choose the view_mode accordingly
        if not purchase_ids or len(purchase_ids) > 1:
            result['domain'] = "[('id','in',%s)]" % (purchase_ids.ids)
        elif len(purchase_ids) == 1:
            res = self.env.ref('purchase.purchase_order_form', False)
            result['views'] = [(res and res.id or False, 'form')]
            result['res_id'] = purchase_ids.id
        return result

    @api.multi
    def action_view_in_picking(self):
        """ This function returns an action that display existing incoming picking orders of given ordenes ids. When only one found, show the picking immediately.
        """
        action = self.env.ref('stock.action_picking_tree_all')
        result = action.read()[0]
        # override the context to get rid of the default filtering on operation type
        result['context'] = {}
        pick_ids = self.mapped('in_picking_ids')
        # choose the view_mode accordingly
        if not pick_ids or len(pick_ids) > 1:
            result['domain'] = "[('id','in',%s)]" % (pick_ids.ids)
        elif len(pick_ids) == 1:
            res = self.env.ref('stock.view_picking_form', False)
            result['views'] = [(res and res.id or False, 'form')]
            result['res_id'] = pick_ids.id
        return result

    @api.multi
    def action_view_delivery(self):
        '''
        This function returns an action that display existing delivery orders
        of given ordenes de reparacion ids. It can either be a in a list or in a form
        view, if there is only one delivery order to show.
        '''
        action = self.env.ref('stock.action_picking_tree_all').read()[0]

        pickings = self.mapped('picking_ids')
        if len(pickings) > 1:
            action['domain'] = [('id', 'in', pickings.ids)]
        elif pickings:
            action['views'] = [(self.env.ref('stock.view_picking_form').id, 'form')]
            action['res_id'] = pickings.id
        return action

    @api.multi
    def action_confirmar(self):
        purchase_obj = self.env['purchase.order']
        po_line_obj = self.env['purchase.order.line'].sudo()
        for ordenes in self:
            if self.warehouse_id:
                ordenes.refacciones_ids._action_launch_stock_rule()

                po_exist = {}
                for line in ordenes.otros_talleres_ids:
                    partner = line.proveedor_id
                    if not partner:
                        continue
                    po = po_exist.get(partner)
                    if not po:
                        vals = ordenes._prepare_purchase_order(partner)
                        po = purchase_obj.sudo().create(vals)
                        po_exist.update({partner: po})
                    poline_vals = self._prepare_purchase_order_line(line,po)
                    po_line_obj.create(poline_vals)

        self.write({'state':'Confirmada'})
        return True

    @api.multi
    def action_cancelar(self):
        for orden in self:
            if orden.picking_ids or orden.purchase_ids:
                raise UserError("No se puede cancelar la orden de reparación")
        self.write({'state':'Cancelado'})
        return True

    @api.multi
    def _prepare_purchase_order_line(self, line, po):
        #product_uom = line.product_uom
        product_qty = line.product_uom_qty
        product = line.product_id
        partner = po.partner_id
        #procurement_uom_po_qty = product_uom._compute_quantity(product_qty, product.uom_po_id)
        seller = product._select_seller(
            partner_id=partner,
            quantity=product_qty,
            date=po.date_order and po.date_order.date(),
            uom_id=product.uom_po_id)

        taxes = product.supplier_taxes_id
        fpos = po.fiscal_position_id
        taxes_id = fpos.map_tax(taxes, product, seller.name) if fpos else taxes
        if taxes_id:
            taxes_id = taxes_id.filtered(lambda x: x.company_id.id == po.company_id.id)

#         price_unit = self.env['account.tax']._fix_tax_included_price_company(seller.price, product.supplier_taxes_id, taxes_id, po.company_id) if seller else 0.0
#         if price_unit and seller and po.currency_id and seller.currency_id != po.currency_id:
#             price_unit = seller.currency_id._convert(
#                 price_unit, po.currency_id, po.company_id, po.date_order or fields.Date.today())

        product_lang = product.with_context({
            'lang': partner.lang,
            'partner_id': partner.id,
        })
        name = product_lang.display_name
        if product_lang.description_purchase:
            name += '\n' + product_lang.description_purchase

        date_planned = self.env['purchase.order.line']._get_date_planned(seller, po=po).strftime(DEFAULT_SERVER_DATETIME_FORMAT)

        return {
            'name': name,
            'product_qty': product_qty,
            'product_id': product.id,
            'product_uom': product.uom_po_id.id,
            'price_unit': line.costo_proveedor,
            'date_planned': date_planned,
            'taxes_id': [(6, 0, taxes_id.ids)],
            'order_id': po.id,
            #'move_dest_ids': [(4, x.id) for x in values.get('move_dest_ids', [])],
        }

    def _prepare_purchase_order(self,  partner):
        purchase_date = fields.Datetime.now()
        company = self.company_id
        fpos = self.env['account.fiscal.position'].with_context(force_company=company.id).get_fiscal_position(partner.id)
        return {
            'partner_id': partner.id,
            'picking_type_id': self.warehouse_id.in_type_id.id,
            'operating_unit_id': self.operating_unit_id.id,
            'requesting_operating_unit_id': self.operating_unit_id.id,
            'company_id': company.id,
            'currency_id': partner.with_context(force_company=company.id).property_purchase_currency_id.id or self.env.user.company_id.currency_id.id,
            'origin': self.name,
            'payment_term_id': partner.with_context(force_company=company.id).property_supplier_payment_term_id.id,
            'date_order': purchase_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
            'fiscal_position_id': fpos,
            'ordenes_id' : self.id,
        }

    @api.multi
    def action_reabrir(self):
        self.write({'state':'Confirmada'})
        return True

    @api.multi
    def action_carrar(self):
        pickings = self.picking_ids.filtered(lambda x:x.state!='done')
        if pickings:
            msg = ', '.join(pickings.mapped('name'))
            msg = 'orders ' if len(pickings)>1 else 'order ' + msg
            raise UserError("La orden %s está abierta, no es posible cambiar al estado de “Cerrado”. Cierre todos los documentos de almacén primero."%(msg))
        self.write({'state':'Cerrada'})
        return True

    @api.multi
    def action_crear_venta(self):
        order_obj = self.env['sale.order']
        order_line_obj = self.env['sale.order.line']

        order_fields = order_obj._fields.keys()
        order_line_fields = order_line_obj._fields.keys()

        order_vals = order_obj.default_get(order_fields)
        warehouse = self.warehouse_id
        if not warehouse:
            warehouse  = self.env['stock.warehouse'].search([('operating_unit_id','=', self.operating_unit_id.id)], limit=1)
        if warehouse:
                order_vals.update({'warehouse_id': warehouse.id})
        if self.orden_de_servicio_de_garantia == 'Pagada por el provedor':
           order_vals.update({'operating_unit_id': self.operating_unit_id.id,'partner_id':self.proveedor_id.id, 'ordenes_id': self.id, 'orden_de_reparacion': True})
        else:
           order_vals.update({'operating_unit_id': self.operating_unit_id.id,'partner_id':self.partner_id.id, 'ordenes_id': self.id, 'orden_de_reparacion': True})
        new_order = order_obj.new(order_vals)
        new_order.onchange_partner_id()
        order_vals = new_order._convert_to_write({name: new_order[name] for name in new_order._cache})
        if order_vals.get('team_id'):
            team = self.env['crm.team'].browse(order_vals.get('team_id'))
            if team.operating_unit_id and team.operating_unit_id.id!=self.operating_unit_id.id:
                order_vals.update({'team_id': False})
        if self.orden_de_servicio_de_garantia == 'Pagada por el provedor':
            order_vals.update({'diario': self.operating_unit_id.garantia_proveedor.id})
        if self.orden_de_servicio_de_garantia == 'Pagara por la empresa':
            order_vals.update({'diario': self.operating_unit_id.garantia_empresa.id})

        order_rec = order_obj.create(order_vals)
        order_rec.onchange_tipo()

        default_order_line_vals = order_line_obj.default_get(order_line_fields)
        for line in self.refacciones_ids:
            order_line_vals = dict(default_order_line_vals)
            order_line_vals.update({
                                    'order_id' : order_rec.id,
                                    'product_id': line.product_id.id,
                                    })
            new_order_line = order_line_obj.new(order_line_vals)
            new_order_line.product_id_change()
            order_line_vals = new_order_line._convert_to_write({name: new_order_line[name] for name in new_order_line._cache})
            order_line_vals.update({'name': line.name, 'price_unit': line.price_unit, 'product_uom_qty': line.product_uom_qty, 'product_uom': line.product_uom.id, 'tax_id': [(6,0,line.tax_id.ids)]})
            order_line_obj.create(order_line_vals)

        for line in self.mano_de_obra_ids:
            order_line_vals = dict(default_order_line_vals)
            order_line_vals.update({
                                    'order_id' : order_rec.id,
                                    'product_id': line.product_id.id,
                                    })
            new_order_line = order_line_obj.new(order_line_vals)
            new_order_line.product_id_change()
            order_line_vals = new_order_line._convert_to_write({name: new_order_line[name] for name in new_order_line._cache})
            order_line_vals.update({'name': line.name, 'price_unit': line.price_unit, 'product_uom_qty': line.product_uom_qty, 'product_uom': line.product_uom.id, 'tax_id': [(6,0,line.tax_id.ids)]})
            order_line_obj.create(order_line_vals)

        for line in self.otros_talleres_ids:
            order_line_vals = dict(default_order_line_vals)
            order_line_vals.update({
                                    'order_id' : order_rec.id,
                                    'product_id': line.product_id.id,
                                    })
            new_order_line = order_line_obj.new(order_line_vals)
            new_order_line.product_id_change()
            order_line_vals = new_order_line._convert_to_write({name: new_order_line[name] for name in new_order_line._cache})
            order_line_vals.update({'name': line.name, 'price_unit': line.price_unit, 'product_uom_qty': line.product_uom_qty, 'product_uom': line.product_uom.id, 'tax_id': [(6,0,line.tax_id.ids)]})
            order_line_obj.create(order_line_vals)

        return True

    @api.multi
    def action_modificar_refacciones(self):
        action = self.env.ref('taller.action_modificar_refacciones_wizard').read()[0]
        #wizard_obj = self.env['modificar.refacciones']
        wizard_lines = []
        for line in self.refacciones_ids:
            wizard_lines.append({'product_id': line.product_id.id,'name':line.name, 'product_uom_qty' : line.product_uom_qty,
                                 'product_uom_qty_new': line.product_uom_qty, 'refacciones_line_id': line.id,'price_unit': line.price_unit, 'tax_id': [(6,0,line.tax_id.ids)]})
#             wizard_rec = wizard_obj.create({'product_id': line.product_id.id, 'product_uom_qty' : line.product_uom_qty, 'product_uom_qty_new': line.product_uom_qty, 'refacciones_line_id': line.id})
#             wizard_ids.append(wizard_rec.id)

        action['context'] = {'default_line_ids': wizard_lines,'default_reparacion_id': self.id}
        return action

    @api.multi
    def action_modificar_mano_de_obra(self):
        action = self.env.ref('taller.action_mano_de_obra_wizard_wizard').read()[0]
        #wizard_obj = self.env['modificar.refacciones']
        wizard_lines = []
        for line in self.mano_de_obra_ids:
            wizard_lines.append({'product_id': line.product_id.id,'name':line.name,
                                 'mecanico_id': line.mecanico_id.id,'product_uom':line.product_uom.id,
                                 'product_uom_qty' : line.product_uom_qty, 'price_unit': line.price_unit,
                                 'tax_id': [(6,0,line.tax_id.ids)],
                                 'mano_de_obra_line_id': line.id,
                                 'comision' : line.comision})
#             wizard_rec = wizard_obj.create({'product_id': line.product_id.id, 'product_uom_qty' : line.product_uom_qty, 'product_uom_qty_new': line.product_uom_qty, 'refacciones_line_id': line.id})
#             wizard_ids.append(wizard_rec.id)

        action['context'] = {'default_line_ids': wizard_lines,'default_reparacion_id': self.id}
        return action

    @api.multi
    def _get_destination_location(self):
        self.ensure_one()
        return self.warehouse_id.in_type_id.default_location_dest_id.id

    @api.model
    def _prepare_incoming_picking(self):
        if not self.incoming_group_id:
            self.incoming_group_id = self.incoming_group_id.create({
                'name': self.name,
                'partner_id': self.partner_id.id
            })
        if not self.partner_id.property_stock_supplier.id:
            raise UserError(_("You must set a Vendor Location for this partner %s") % self.partner_id.name)
        return {
            'picking_type_id': self.warehouse_id.in_type_id.id,
            'partner_id': self.partner_id.id,
            'date': self.fecha,
            'origin': self.name,
            'location_dest_id': self._get_destination_location(),
            'location_id': self.partner_id.property_stock_supplier.id,
            'company_id': self.company_id.id,
        }

class RefaccionesOrdenesDeReparacion(models.Model):
    _name = 'refacciones.ordenes.de.reparacion'

    @api.depends('product_uom_qty', 'price_unit', 'tax_id')
    def _compute_amount(self):
        """
        Compute the amounts of line.
        """
        for line in self:
            price = line.price_unit
            taxes = line.tax_id.compute_all(price, line.reparacion_id.currency_id, line.product_uom_qty, product=line.product_id)
            line.update({
                'price_tax': sum(t.get('amount', 0.0) for t in taxes.get('taxes', [])),
                'price_total': taxes['total_included'],
                'price_subtotal': taxes['total_excluded'],
            })
    reparacion_id = fields.Many2one("ordenes.de.reparacion",'Orden de reparación')
    product_id = fields.Many2one("product.product",'Producto')
    name = fields.Text(string='Description', required=True)
    product_uom_qty = fields.Float(string='Cantidad', digits=dp.get_precision('Product Unit of Measure'), required=True, default=1.0)
    product_uom = fields.Many2one('uom.uom', string='Unit of Measure')
    price_unit = fields.Float('Precio unitario', required=True, digits=dp.get_precision('Product Price'), default=0.0)

    tax_id = fields.Many2many('account.tax', string='Impuestos', domain=['|', ('active', '=', False), ('active', '=', True)])

    price_subtotal = fields.Monetary(compute='_compute_amount', string='Subtotal', readonly=True, store=True)
    price_tax = fields.Float(compute='_compute_amount', string='Total Tax', readonly=True, store=True)
    price_total = fields.Monetary(compute='_compute_amount', string='Total', readonly=True, store=True)
    company_id = fields.Many2one(related='reparacion_id.company_id', string='Company', store=True, readonly=True)
    currency_id = fields.Many2one(related='reparacion_id.currency_id', depends=['reparacion_id'], store=True, string='Currency', readonly=True)
    move_ids = fields.One2many('stock.move', 'refacciones_line_id', string='Stock Moves')
    in_move_ids = fields.One2many('stock.move', 'in_refacciones_line_id', string='Incoming Stock Moves')
    route_id = fields.Many2one('stock.location.route', string='Route', domain=[('sale_selectable', '=', True)], ondelete='restrict')
    date_order = fields.Date(related='reparacion_id.fecha', string='Order Date', readonly=True)
    move_dest_ids = fields.One2many('stock.move', 'created_ordernes_de_line_id', 'Downstream Moves')

    def _get_real_price_currency(self, product, rule_id, qty, uom, pricelist_id):
        """Retrieve the price before applying the pricelist
            :param obj product: object of current product record
            :parem float qty: total quentity of product
            :param tuple price_and_rule: tuple(price, suitable_rule) coming from pricelist computation
            :param obj uom: unit of measure of current order line
            :param integer pricelist_id: pricelist id of sales order"""
        PricelistItem = self.env['product.pricelist.item']
        field_name = 'lst_price'
        currency_id = None
        product_currency = None
        if rule_id:
            pricelist_item = PricelistItem.browse(rule_id)
            if pricelist_item.pricelist_id.discount_policy == 'without_discount':
                while pricelist_item.base == 'pricelist' and pricelist_item.base_pricelist_id and pricelist_item.base_pricelist_id.discount_policy == 'without_discount':
                    price, rule_id = pricelist_item.base_pricelist_id.with_context(uom=uom.id).get_product_price_rule(product, qty, self.reparacion_id.partner_id)
                    pricelist_item = PricelistItem.browse(rule_id)

            if pricelist_item.base == 'standard_price':
                field_name = 'standard_price'
            if pricelist_item.base == 'pricelist' and pricelist_item.base_pricelist_id:
                field_name = 'price'
                product = product.with_context(pricelist=pricelist_item.base_pricelist_id.id)
                product_currency = pricelist_item.base_pricelist_id.currency_id
            currency_id = pricelist_item.pricelist_id.currency_id

        product_currency = product_currency or(product.company_id and product.company_id.currency_id) or self.env.user.company_id.currency_id
        if not currency_id:
            currency_id = product_currency
            cur_factor = 1.0
        else:
            if currency_id.id == product_currency.id:
                cur_factor = 1.0
            else:
                cur_factor = currency_id._get_conversion_rate(product_currency, currency_id, self.company_id or self.env.user.company_id, self.reparacion_id.fecha or fields.Date.today())

        product_uom = self.env.context.get('uom') or product.uom_id.id
        if uom and uom.id != product_uom:
            # the unit price is in a different uom
            uom_factor = uom._compute_price(1.0, product.uom_id)
        else:
            uom_factor = 1.0

        return product[field_name] * uom_factor * cur_factor, currency_id

    @api.multi
    def _get_display_price(self, product):
        pricelist = self.reparacion_id.partner_id.property_product_pricelist
        if pricelist.discount_policy == 'with_discount':
            return product.with_context(pricelist=pricelist.id).price
        product_context = dict(self.env.context, partner_id=self.reparacion_id.partner_id.id, date=self.reparacion_id.fecha, uom=self.product_uom.id)

        final_price, rule_id = pricelist.with_context(product_context).get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.reparacion_id.partner_id)
        base_price, currency = self.with_context(product_context)._get_real_price_currency(product, rule_id, self.product_uom_qty, self.product_uom, pricelist.id)
        if currency != pricelist.currency_id:
            base_price = currency._convert(
                base_price, pricelist.currency_id,
                self.reparacion_id.company_id, self.reparacion_id.fecha or fields.Date.today())
        # negative discounts (= surcharge) are included in the display price
        return max(base_price, final_price)

    @api.multi
    @api.onchange('product_id')
    def product_id_change(self):
        if not self.product_id:
            return {'domain': {'product_uom': []}}

        vals = {}
        domain = {'product_uom': [('category_id', '=', self.product_id.uom_id.category_id.id)]}
        if not self.product_uom or (self.product_id.uom_id.id != self.product_uom.id):
            vals['product_uom'] = self.product_id.uom_id
            vals['product_uom_qty'] = self.product_uom_qty or 1.0

        pricelist = self.reparacion_id.partner_id.property_product_pricelist
        product = self.product_id.with_context(
            lang=self.reparacion_id.partner_id.lang,
            partner=self.reparacion_id.partner_id,
            quantity=vals.get('product_uom_qty') or self.product_uom_qty,
            date=self.reparacion_id.fecha,
            pricelist = pricelist.id,
            uom=self.product_uom.id
        )

        result = {'domain': domain}

        name = product.get_product_multiline_description_sale()

        vals.update(name=name)

        fpos = self.reparacion_id.partner_id.property_account_position_id
        # If company_id is set, always filter taxes by the company
        taxes = self.product_id.taxes_id.filtered(lambda r: not self.company_id or r.company_id == self.company_id)
        self.tax_id = fpos.map_tax(taxes, self.product_id, self.reparacion_id.partner_id) if fpos else taxes

        if pricelist and self.reparacion_id.partner_id:
            vals['price_unit'] = self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id)
        self.update(vals)
        return result

    @api.multi
    def _prepare_procurement_values(self, group_id=False):
        """ Prepare specific key for moves or other components that will be created from a stock rule
        comming from a sale order line. This method could be override in order to add other custom key that could
        be used in move/po creation.
        """
        self.ensure_one()
        values = {
            'company_id': self.reparacion_id.company_id,
            'group_id': group_id,
            #'sale_line_id': self.id,
            #'date_planned': date_planned,
            'refacciones_line_id': self.id,
            'route_ids': self.route_id,
            'warehouse_id': self.reparacion_id.warehouse_id or False,
            'partner_id': self.reparacion_id.partner_id.id,
        }
        return values

    def _get_qty_procurement(self):
        self.ensure_one()
        qty = 0.0
        for move in self.move_ids.filtered(lambda r: r.state != 'cancel'):
            if move.picking_code in ['outgoing', 'internal']: #Nilesh Changes for internal picking
                qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
            elif move.picking_code == 'incoming':
                qty -= move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
        return qty

    @api.multi
    def _action_launch_stock_rule(self):
        """
        Launch procurement group run method with required/custom fields genrated by a
        sale order line. procurement group will launch '_run_pull', '_run_buy' or '_run_manufacture'
        depending on the sale order line product rule.
        """
        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
        errors = []
        proc_group_obj = self.env['procurement.group']
        for line in self:
            if not line.product_id.type in ('consu','product'):
                continue
            qty = line._get_qty_procurement()
            if float_compare(qty, line.product_uom_qty, precision_digits=precision) >= 0:
                continue

            group_id = line.reparacion_id.procurement_group_id
            if not group_id:
                group_id = proc_group_obj.create({
                    'name': line.reparacion_id.name, 'move_type': 'direct',
                    'ordenes_id': line.reparacion_id.id,
                    'partner_id': line.reparacion_id.partner_id.id,
                })
                line.reparacion_id.procurement_group_id = group_id
            else:
                # In case the procurement group is already created and the order was
                # cancelled, we need to update certain values of the group.
                updated_vals = {}
                if group_id.partner_id != line.reparacion_id.partner_id:
                    updated_vals.update({'partner_id': line.reparacion_id.partner_id.id})
                if updated_vals:
                    group_id.write(updated_vals)

            values = line._prepare_procurement_values(group_id=group_id)
            product_qty = line.product_uom_qty - qty

            procurement_uom = line.product_uom
            quant_uom = line.product_id.uom_id
            get_param = self.env['ir.config_parameter'].sudo().get_param
            if procurement_uom.id != quant_uom.id and get_param('stock.propagate_uom') != '1':
                product_qty = line.product_uom._compute_quantity(product_qty, quant_uom, rounding_method='HALF-UP')
                procurement_uom = quant_uom

            try:
                dest_location = line.reparacion_id.dest_location_id or line.reparacion_id.operating_unit_id.dest_location_id or line.reparacion_id.partner_id.property_stock_customer
                location = line.reparacion_id.operating_unit_id.location_id or dest_location
                values.update({'location_id': location.id}) #, 'location_dest_id': dest_location.id
                rule_exist = proc_group_obj.with_context(ordenes_de_reparacion=True)._get_rule(line.product_id, dest_location, values)
                if not rule_exist:
                    self.create_procurement_rule(line.reparacion_id.warehouse_id, dest_location, location)
                proc_group_obj.with_context(ordenes_de_reparacion=True).run(line.product_id, product_qty, procurement_uom, dest_location, line.name, line.reparacion_id.name, values)
            except UserError as error:
                errors.append(error.name)
        if errors:
            raise UserError('\n'.join(errors))
        return True

    @api.model
    def create_procurement_rule(self, warehouse, location, location_src):
        rule_obj = self.env['stock.rule'].sudo()
        field_list = rule_obj._fields.keys()
        vals = rule_obj.default_get(field_list)

        route_exist = self.env['stock.location.route'].sudo().search([('warehouse_selectable','=',True), ('warehouse_ids','in', [warehouse.id])] , limit=1)
        if not route_exist:
            route_exist = self.env['stock.location.route'].sudo().search([('active','=',False),('warehouse_selectable','=',True), ('warehouse_ids','in', [warehouse.id])] , limit=1)
            if not route_exist:
                route_exist = self.env['stock.location.route'].sudo().create({
                                                                'name': '%s: Internal Transfer in 1 step'%(warehouse.name),
                                                                'warehouse_selectable' : True,
                                                                'warehouse_ids' : [(6,0,[warehouse.id])],
                                                                })
            else:
                route_exist.write({'active': True})

        vals.update({
                'action':'pull',
                'name' : '%s: %s --> %s'%(warehouse.code, location.name, location_src.name),
                'picking_type_id' : warehouse.int_type_id.id,
                'location_src_id': location_src.id,
                'location_id': location.id,
                'procure_method' : 'make_to_stock',
                'route_id' : route_exist.id,
                'warehouse_id' : warehouse.id,
                })
        rule = rule_obj.create(vals)
        return rule

    @api.multi
    def _create_or_update_picking(self):
        for line in self:
            if line.product_id.type in ('product', 'consu'):
                pickings = line.reparacion_id.in_picking_ids.filtered(lambda x: x.state not in ('done', 'cancel') and x.location_dest_id.usage in ('internal', 'transit'))
                picking = pickings and pickings[0] or False
                if not picking:
                    res = line.reparacion_id._prepare_incoming_picking()
                    picking = self.env['stock.picking'].create(res)
                move_vals = line._prepare_incoming_stock_moves(picking)
                for move_val in move_vals:
                    self.env['stock.move']\
                        .create(move_val)\
                        ._action_confirm()\
                        ._action_assign()

    @api.multi
    def _get_stock_move_price_unit(self):
        self.ensure_one()
        line = self[0]
        order = line.reparacion_id
        price_unit = line.price_unit
        if line.tax_id:
            price_unit = line.tax_id.with_context(round=False).compute_all(
                price_unit, currency=line.reparacion_id.currency_id, quantity=1.0, product=line.product_id, partner=line.reparacion_id.partner_id
            )['total_excluded']
        if line.product_uom.id != line.product_id.uom_id.id:
            price_unit *= line.product_uom.factor / line.product_id.uom_id.factor
        if order.currency_id != order.company_id.currency_id:
            price_unit = order.currency_id._convert(
                price_unit, order.company_id.currency_id, self.company_id, self.date_order or fields.Date.today(), round=False)
        return price_unit

    @api.multi
    def _prepare_incoming_stock_moves(self, picking):
        """ Prepare the stock moves data for one ordenes de line. This function returns a list of
        dictionary ready to be used in stock.move's create()
        """
        self.ensure_one()
        res = []
        if self.product_id.type not in ['product', 'consu']:
            return res
        qty = 0.0
        price_unit = self._get_stock_move_price_unit()
        for move in self.in_move_ids.filtered(lambda x: x.state != 'cancel' and not x.location_dest_id.usage == "supplier"):
            qty += move.product_uom._compute_quantity(move.product_uom_qty, self.product_uom, rounding_method='HALF-UP')
        warehouse = self.reparacion_id.warehouse_id
        picking_type = warehouse.in_type_id
        template = {
            # truncate to 2000 to avoid triggering index limit error
            # TODO: remove index in master?
            'name': (self.name or '')[:2000],
            'product_id': self.product_id.id,
            'product_uom': self.product_uom.id,
            'date': self.reparacion_id.fecha,
            #'date_expected': self.date_planned,
            'location_id': self.reparacion_id.partner_id.property_stock_supplier.id,
            'location_dest_id': self.reparacion_id._get_destination_location(),
            'picking_id': picking.id,
            #'partner_id': self.reparacion_id.dest_address_id.id,
            'move_dest_ids': [(4, x) for x in self.move_dest_ids.ids],
            'state': 'draft',
            'in_refacciones_line_id': self.id,
            'company_id': self.reparacion_id.company_id.id,
            'price_unit': price_unit,
            'picking_type_id': picking_type.id,
            'group_id': self.reparacion_id.incoming_group_id.id,
            'origin': self.reparacion_id.name,
            'route_ids': [(6, 0, [x.id for x in warehouse.route_ids])] or [],
            'warehouse_id': warehouse.id,
        }

        diff_quantity = self._context.get('force_product_qty')
        if not diff_quantity:
            diff_quantity = self.product_uom_qty - qty
        if float_compare(diff_quantity, 0.0,  precision_rounding=self.product_uom.rounding) > 0:
            quant_uom = self.product_id.uom_id
            get_param = self.env['ir.config_parameter'].sudo().get_param
            if self.product_uom.id != quant_uom.id and get_param('stock.propagate_uom') != '1':
                product_qty = self.product_uom._compute_quantity(diff_quantity, quant_uom, rounding_method='HALF-UP')
                template['product_uom'] = quant_uom.id
                template['product_uom_qty'] = product_qty
            else:
                template['product_uom_qty'] = diff_quantity
            res.append(template)
        return res

class ManoDeObraOrdenesDeReparacion(models.Model):
    _name = 'mano.deobra.ordenes.de.reparacion'

    @api.depends('product_uom_qty', 'price_unit', 'tax_id')
    def _compute_amount(self):
        """
        Compute the amounts of line.
        """
        for line in self:
            price = line.price_unit
            taxes = line.tax_id.compute_all(price, line.reparacion_id.currency_id, line.product_uom_qty, product=line.product_id)
            line.update({
                'price_tax': sum(t.get('amount', 0.0) for t in taxes.get('taxes', [])),
                'price_total': taxes['total_included'],
                'price_subtotal': taxes['total_excluded'],
            })
    reparacion_id = fields.Many2one("ordenes.de.reparacion",'Orden de reparación')
    product_id = fields.Many2one("product.product",'Producto',domain=[('mano_de_obra','=',True)])
    mecanico_id = fields.Many2one("res.partner",'Mécanico',domain=[('mecanico','=',True)])
    name = fields.Text(string='Description', required=True)
    product_uom_qty = fields.Float(string='Cantidad', digits=dp.get_precision('Product Unit of Measure'), required=True, default=1.0)
    product_uom = fields.Many2one('uom.uom', string='Unit of Measure')
    comision = fields.Float(string="Comision")
    price_unit = fields.Float('Precio unitario', required=True, digits=dp.get_precision('Product Price'), default=0.0)

    tax_id = fields.Many2many('account.tax', string='Impuestos', domain=['|', ('active', '=', False), ('active', '=', True)])

    price_subtotal = fields.Monetary(compute='_compute_amount', string='Subtotal', readonly=True, store=True)
    price_tax = fields.Float(compute='_compute_amount', string='Total Tax', readonly=True, store=True)
    price_total = fields.Monetary(compute='_compute_amount', string='Total', readonly=True, store=True)
    company_id = fields.Many2one(related='reparacion_id.company_id', string='Company', store=True, readonly=True)
    currency_id = fields.Many2one(related='reparacion_id.currency_id', depends=['reparacion_id'], store=True, string='Currency', readonly=True)
    detalle = fields.Char(string='Detalles')

    def _get_real_price_currency(self, product, rule_id, qty, uom, pricelist_id):
        """Retrieve the price before applying the pricelist
            :param obj product: object of current product record
            :parem float qty: total quentity of product
            :param tuple price_and_rule: tuple(price, suitable_rule) coming from pricelist computation
            :param obj uom: unit of measure of current order line
            :param integer pricelist_id: pricelist id of sales order"""
        PricelistItem = self.env['product.pricelist.item']
        field_name = 'lst_price'
        currency_id = None
        product_currency = None
        if rule_id:
            pricelist_item = PricelistItem.browse(rule_id)
            if pricelist_item.pricelist_id.discount_policy == 'without_discount':
                while pricelist_item.base == 'pricelist' and pricelist_item.base_pricelist_id and pricelist_item.base_pricelist_id.discount_policy == 'without_discount':
                    price, rule_id = pricelist_item.base_pricelist_id.with_context(uom=uom.id).get_product_price_rule(product, qty, self.reparacion_id.partner_id)
                    pricelist_item = PricelistItem.browse(rule_id)

            if pricelist_item.base == 'standard_price':
                field_name = 'standard_price'
            if pricelist_item.base == 'pricelist' and pricelist_item.base_pricelist_id:
                field_name = 'price'
                product = product.with_context(pricelist=pricelist_item.base_pricelist_id.id)
                product_currency = pricelist_item.base_pricelist_id.currency_id
            currency_id = pricelist_item.pricelist_id.currency_id

        product_currency = product_currency or(product.company_id and product.company_id.currency_id) or self.env.user.company_id.currency_id
        if not currency_id:
            currency_id = product_currency
            cur_factor = 1.0
        else:
            if currency_id.id == product_currency.id:
                cur_factor = 1.0
            else:
                cur_factor = currency_id._get_conversion_rate(product_currency, currency_id, self.company_id or self.env.user.company_id, self.reparacion_id.fecha or fields.Date.today())

        product_uom = self.env.context.get('uom') or product.uom_id.id
        if uom and uom.id != product_uom:
            # the unit price is in a different uom
            uom_factor = uom._compute_price(1.0, product.uom_id)
        else:
            uom_factor = 1.0

        return product[field_name] * uom_factor * cur_factor, currency_id

    @api.multi
    def _get_display_price(self, product):
        pricelist = self.reparacion_id.partner_id.property_product_pricelist
        if pricelist.discount_policy == 'with_discount':
            return product.with_context(pricelist=pricelist.id).price
        product_context = dict(self.env.context, partner_id=self.reparacion_id.partner_id.id, date=self.reparacion_id.fecha, uom=self.product_uom.id)

        final_price, rule_id = pricelist.with_context(product_context).get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.reparacion_id.partner_id)
        base_price, currency = self.with_context(product_context)._get_real_price_currency(product, rule_id, self.product_uom_qty, self.product_uom, pricelist.id)
        if currency != pricelist.currency_id:
            base_price = currency._convert(
                base_price, pricelist.currency_id,
                self.reparacion_id.company_id, self.reparacion_id.fecha or fields.Date.today())
        # negative discounts (= surcharge) are included in the display price
        return max(base_price, final_price)

    @api.multi
    @api.onchange('product_id')
    def product_id_change(self):
        if not self.product_id:
            return {'domain': {'product_uom': []}}

        vals = {}
        domain = {'product_uom': [('category_id', '=', self.product_id.uom_id.category_id.id)]}
        if not self.product_uom or (self.product_id.uom_id.id != self.product_uom.id):
            vals['product_uom'] = self.product_id.uom_id
            vals['product_uom_qty'] = self.product_uom_qty or 1.0

        pricelist = self.reparacion_id.partner_id.property_product_pricelist
        product = self.product_id.with_context(
            lang=self.reparacion_id.partner_id.lang,
            partner=self.reparacion_id.partner_id,
            quantity=vals.get('product_uom_qty') or self.product_uom_qty,
            date=self.reparacion_id.fecha,
            pricelist = pricelist.id,
            uom=self.product_uom.id
        )

        result = {'domain': domain}

        name = product.get_product_multiline_description_sale()

        vals.update(name=name)

        fpos = self.reparacion_id.partner_id.property_account_position_id
        # If company_id is set, always filter taxes by the company
        taxes = self.product_id.taxes_id.filtered(lambda r: not self.company_id or r.company_id == self.company_id)
        self.tax_id = fpos.map_tax(taxes, self.product_id, self.reparacion_id.partner_id) if fpos else taxes

        if pricelist and self.reparacion_id.partner_id:
            vals['price_unit'] = self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id)
        self.update(vals)
        return result

class OtrosTalleresOrdenesDeReparacion(models.Model):
    _name = 'otros.talleres.ordenes.de.reparacion'

    @api.depends('product_uom_qty', 'price_unit', 'tax_id')
    def _compute_amount(self):
        """
        Compute the amounts of line.
        """
        for line in self:
            price = line.price_unit
            taxes = line.tax_id.compute_all(price, line.reparacion_id.currency_id, line.product_uom_qty, product=line.product_id)
            line.update({
                'price_tax': sum(t.get('amount', 0.0) for t in taxes.get('taxes', [])),
                'price_total': taxes['total_included'],
                'price_subtotal': taxes['total_excluded'],
            })
    reparacion_id = fields.Many2one("ordenes.de.reparacion",'Orden de reparación')
    product_id = fields.Many2one("product.product",'Producto')
    name = fields.Text(string='Description', required=True)
    proveedor_id = fields.Many2one("res.partner",'Proveedor',domain=[('supplier','=',True)])
    product_uom_qty = fields.Float(string='Cantidad', digits=dp.get_precision('Product Unit of Measure'), required=True, default=1.0)
    product_uom = fields.Many2one('uom.uom', string='Unit of Measure')
    price_unit = fields.Float('Precio unitario', required=True, digits=dp.get_precision('Product Price'), default=0.0)
    costo_proveedor = fields.Float('Costo provedor', digits=dp.get_precision('Product Price'), default=0.0)
    tax_id = fields.Many2many('account.tax', string='Impuestos', domain=['|', ('active', '=', False), ('active', '=', True)])

    price_subtotal = fields.Monetary(compute='_compute_amount', string='Subtotal', readonly=True, store=True)
    price_tax = fields.Float(compute='_compute_amount', string='Total Tax', readonly=True, store=True)
    price_total = fields.Monetary(compute='_compute_amount', string='Total', readonly=True, store=True)
    company_id = fields.Many2one(related='reparacion_id.company_id', string='Company', store=True, readonly=True)
    currency_id = fields.Many2one(related='reparacion_id.currency_id', depends=['reparacion_id'], store=True, string='Currency', readonly=True)
    pct_percent = fields.Float('PCT(%)')

    @api.onchange('pct_percent', 'costo_proveedor')
    def onchange_pct_costo(self):
        if self.pct_percent and self.costo_proveedor:
            self.price_unit = self.costo_proveedor * (1+self.pct_percent/100)

    def _get_real_price_currency(self, product, rule_id, qty, uom, pricelist_id):
        """Retrieve the price before applying the pricelist
            :param obj product: object of current product record
            :parem float qty: total quentity of product
            :param tuple price_and_rule: tuple(price, suitable_rule) coming from pricelist computation
            :param obj uom: unit of measure of current order line
            :param integer pricelist_id: pricelist id of sales order"""
        PricelistItem = self.env['product.pricelist.item']
        field_name = 'lst_price'
        currency_id = None
        product_currency = None
        if rule_id:
            pricelist_item = PricelistItem.browse(rule_id)
            if pricelist_item.pricelist_id.discount_policy == 'without_discount':
                while pricelist_item.base == 'pricelist' and pricelist_item.base_pricelist_id and pricelist_item.base_pricelist_id.discount_policy == 'without_discount':
                    price, rule_id = pricelist_item.base_pricelist_id.with_context(uom=uom.id).get_product_price_rule(product, qty, self.reparacion_id.partner_id)
                    pricelist_item = PricelistItem.browse(rule_id)

            if pricelist_item.base == 'standard_price':
                field_name = 'standard_price'
            if pricelist_item.base == 'pricelist' and pricelist_item.base_pricelist_id:
                field_name = 'price'
                product = product.with_context(pricelist=pricelist_item.base_pricelist_id.id)
                product_currency = pricelist_item.base_pricelist_id.currency_id
            currency_id = pricelist_item.pricelist_id.currency_id

        product_currency = product_currency or(product.company_id and product.company_id.currency_id) or self.env.user.company_id.currency_id
        if not currency_id:
            currency_id = product_currency
            cur_factor = 1.0
        else:
            if currency_id.id == product_currency.id:
                cur_factor = 1.0
            else:
                cur_factor = currency_id._get_conversion_rate(product_currency, currency_id, self.company_id or self.env.user.company_id, self.reparacion_id.fecha or fields.Date.today())

        product_uom = self.env.context.get('uom') or product.uom_id.id
        if uom and uom.id != product_uom:
            # the unit price is in a different uom
            uom_factor = uom._compute_price(1.0, product.uom_id)
        else:
            uom_factor = 1.0

        return product[field_name] * uom_factor * cur_factor, currency_id

    @api.multi
    def _get_display_price(self, product):
        pricelist = self.reparacion_id.partner_id.property_product_pricelist
        if pricelist.discount_policy == 'with_discount':
            return product.with_context(pricelist=pricelist.id).price
        product_context = dict(self.env.context, partner_id=self.reparacion_id.partner_id.id, date=self.reparacion_id.fecha, uom=self.product_uom.id)

        final_price, rule_id = pricelist.with_context(product_context).get_product_price_rule(self.product_id, self.product_uom_qty or 1.0, self.reparacion_id.partner_id)
        base_price, currency = self.with_context(product_context)._get_real_price_currency(product, rule_id, self.product_uom_qty, self.product_uom, pricelist.id)
        if currency != pricelist.currency_id:
            base_price = currency._convert(
                base_price, pricelist.currency_id,
                self.reparacion_id.company_id, self.reparacion_id.fecha or fields.Date.today())
        # negative discounts (= surcharge) are included in the display price
        return max(base_price, final_price)

    @api.multi
    @api.onchange('product_id')
    def product_id_change(self):
        if not self.product_id:
            return {'domain': {'product_uom': []}}

        vals = {}
        domain = {'product_uom': [('category_id', '=', self.product_id.uom_id.category_id.id)]}
        if not self.product_uom or (self.product_id.uom_id.id != self.product_uom.id):
            vals['product_uom'] = self.product_id.uom_id
            vals['product_uom_qty'] = self.product_uom_qty or 1.0

        pricelist = self.reparacion_id.partner_id.property_product_pricelist
        product = self.product_id.with_context(
            lang=self.reparacion_id.partner_id.lang,
            partner=self.reparacion_id.partner_id,
            quantity=vals.get('product_uom_qty') or self.product_uom_qty,
            date=self.reparacion_id.fecha,
            pricelist = pricelist.id,
            uom=self.product_uom.id
        )

        result = {'domain': domain}

        name = product.get_product_multiline_description_sale()

        vals.update(name=name)

        fpos = self.reparacion_id.partner_id.property_account_position_id
        # If company_id is set, always filter taxes by the company
        taxes = self.product_id.taxes_id.filtered(lambda r: not self.company_id or r.company_id == self.company_id)
        self.tax_id = fpos.map_tax(taxes, self.product_id, self.reparacion_id.partner_id) if fpos else taxes

        if pricelist and self.reparacion_id.partner_id:
            vals['price_unit'] = self.env['account.tax']._fix_tax_included_price_company(self._get_display_price(product), product.taxes_id, self.tax_id, self.company_id)
        self.update(vals)
        return result


import json
from os import PathLike
from pathlib import Path
from typing import Iterator
from deprecated import deprecated
from jsonschema import validate
from nplinker.schemas import STRAIN_MAPPINGS_SCHEMA
from .logconfig import LogConfig
from .strains import Strain
from .utils import list_dirs
from .utils import list_files

logger = LogConfig.getLogger(__name__)

class StrainCollection():

    def __init__(self):
        """A collection of Strain objects."""
        self._strains: list[Strain] = []
        # dict of strain name (id and alias) to primary strain object
        self._strain_dict_name: dict[str, Strain] = {}

    def __repr__(self) -> str:
        return str(self)

    def __str__(self) -> str:
        if len(self) > 20:
            return f'StrainCollection(n={len(self)})'

        return f'StrainCollection(n={len(self)}) [' + ','.join(
            s.id for s in self._strains) + ']'

    def __len__(self) -> int:
        return len(self._strains)

    def __eq__(self, other) -> bool:
        if isinstance(other, StrainCollection):
            return (self._strains == other._strains
                    and self._strain_dict_name == other._strain_dict_name)
        return NotImplemented

    def __contains__(self, item: str | Strain) -> bool:
        """Check if the strain collection contains the given strain.

        The given strain could be a Strain object, or a strain id or alias.
        """
        if isinstance(item, str):
            return item in self._strain_dict_name
        if isinstance(item, Strain):
            return item.id in self._strain_dict_name
        raise TypeError(f"Expected Strain or str, got {type(item)}")

    def __iter__(self) -> Iterator[Strain]:
        return iter(self._strains)

    def add(self, strain: Strain) -> None:
        """Add strain to the collection.

        If the strain already exists, merge the aliases.

        Args:
            strain(Strain): The strain to add.
        """
        # if the strain exists, merge the aliases
        if strain.id in self._strain_dict_name:
            existing: Strain = self.lookup(strain.id)
            for alias in strain.aliases:
                existing.add_alias(alias)
                self._strain_dict_name[alias] = existing
        else:
            self._strains.append(strain)
            self._strain_dict_name[strain.id] = strain
            for alias in strain.aliases:
                self._strain_dict_name[alias] = strain

    def remove(self, strain: Strain):
        """Remove a strain from the collection.

        Args:
            strain(Strain): The strain to remove.
        """
        if strain.id in self._strain_dict_name:
            self._strains.remove(strain)
            del self._strain_dict_name[strain.id]
            for alias in strain.aliases:
                del self._strain_dict_name[alias]

    def filter(self, strain_set: set[Strain]):
        """
        Remove all strains that are not in strain_set from the strain collection
        """
        # note that we need to copy the list of strains, as we are modifying it
        for strain in self._strains.copy():
            if strain not in strain_set:
                self.remove(strain)

    def lookup(self, name: str) -> Strain:
        """Lookup a strain by name (id or alias).

        Args:

            name(str): Strain name (id or alias) to lookup.

        Returns:
            Strain: Strain identified by the given name.

        Raises:
            KeyError: If the strain name is not found.
        """
        if name in self:
            return self._strain_dict_name[name]
        raise KeyError(f"Strain {name} not found in strain collection.")

    @staticmethod
    def read_json(file: str | PathLike) -> 'StrainCollection':
        """Read a strain mappings JSON file and return a StrainCollection object.

        Args:
            file(str | PathLike): Path to the strain mappings JSON file.

        Returns:
            StrainCollection: StrainCollection object.
        """
        with open(file, 'r') as f:
            json_data = json.load(f)

        # validate json data
        validate(instance=json_data, schema=STRAIN_MAPPINGS_SCHEMA)

        strain_collection = StrainCollection()
        for data in json_data['strain_mappings']:
            strain = Strain(data['strain_id'])
            for alias in data['strain_alias']:
                strain.add_alias(alias)
            strain_collection.add(strain)
        return strain_collection

    def to_json(self, file: str | PathLike | None = None) -> str | None:
        """Convert the StrainCollection object to a JSON string.

        Args:
            file(str | PathLike | None): Path to output JSON file. If None,
                return the JSON string instead.

        Returns:
            str | None: If `file` is None, return the JSON string. Otherwise,
                write the JSON string to the given file.
        """
        data_list = [{
            "strain_id": strain.id,
            "strain_alias": list(strain.aliases)
        } for strain in self]
        json_data = {"strain_mappings": data_list, "version": "1.0"}

        # validate json data
        validate(instance=json_data, schema=STRAIN_MAPPINGS_SCHEMA)

        if file is not None:
            with open(file, 'w') as f:
                json.dump(json_data, f)
            return None
        return json.dumps(json_data)