import time
import math
import numpy as np
import yaml
from motion_plot.differ_motion_plot import UTurnMPC
from casadi_MPC.Modell_Differential.casadi_differ_reference_line import CasADi_MPC_differ
from gears.cubic_spline_planner import Spline2D
from controller.lqr_speed_steer_control import LQR_Controller
from controller.casadi_mpc_controller import CasADi_MPC_differ_Control
import matplotlib.pyplot as plt

class MPC_LQR_Controller:

    def __init__(self):
        self.LF = 2.  # distance from rear to vehicle front end
        self.LB = 2.  # distance from rear to vehicle back end
        self.W = 2.

        self.dt = 0.02
        self.local_horizon = 2
        self.window = int(self.local_horizon / self.dt)
        self.state_num = 5
        self.GOAL_DIS = 5.5  # goal distance
        self.STOP_SPEED = 0.5 / 3.6  # stop speed
        self.MAX_TIME = 500.0  # max simulation time

        self.max_v = 2.0
        self.max_omega = np.deg2rad(120.0)  # maximum steering angle[rad]
        self.max_acc = 2.0
        self.max_omega_rate = np.deg2rad(720.0)  # maximum steering angle[rad]
        self.ds = 0.5 * self.dt * self.max_v

        # iterative paramter
        self.MAX_ITER = 3  # Max iteration
        self.DU_TH = 0.1  # iteration finish param
        self.ref_v = [0.]
        self.ref_omega = [0.]
        self.ref_a = [0.]
        self.ref_omega_rate = [0.]
        self.fb_a = [[0., 0., 0., 0., 0.]]
        self.fb_o_rate = [[0., 0., 0., 0., 0.]]
        self.fb_a = [0.]
        self.fb_o_rate = [0.]

        self.TARGET_SPEED = 10.0 / 3.6  # [m/s] target speed
        self.N_IND_SEARCH = 10  # Search index number
        self.lqr = LQR_Controller()
        self.show_animation = True

    class State:
        """
        vehicle state class
        """

        def __init__(self, x=0.0, y=0.0, yaw=0.0, v=0.0, omega=0.):
            self.x = x
            self.y = y
            self.yaw = yaw
            self.v = v
            self.omega = omega
            self.predelta = None

    def expand_path(self, refpath, ds):
        x = refpath[0, :]
        y = refpath[1, :]
        sp = Spline2D(x, y)
        s = np.arange(0, sp.s[-1], ds)

        rx, ry, ryaw, rk = [], [], [], []

        for i_s in s:
            ix, iy = sp.calc_position(i_s)
            rx.append(ix)
            ry.append(iy)
            yaw_ = sp.calc_yaw(i_s)
            ryaw.append(self.normalize_angle(yaw_))
            rk.append(sp.calc_curvature(i_s))

        return np.array([rx, ry, ryaw, rk])

    def normalize_angle(self, yaw):
        return (yaw + np.pi) % (2 * np.pi) - np.pi

    def coordinate_transform(self, yaw, t, path, mode):
        yaw = self.normalize_angle(yaw)
        yawlist = np.array([self.normalize_angle(i) for i in path[2, :]])

        rot = np.array([[np.cos(yaw), -np.sin(yaw)],
                        [np.sin(yaw), np.cos(yaw)]])
        trans = np.repeat(t[:, None], len(path.T), axis=1)

        if mode == "body to world":
            newpath = rot @ path[:2, :] + trans
            newyaw = yawlist + yaw
        else:
            newpath = rot.T @ (path[:2, :] - trans)
            newyaw = yawlist - yaw

        newyaw = [self.normalize_angle(i) for i in newyaw]

        return np.vstack((newpath, newyaw))

    def calc_nearest_index(self, state, global_path, pind):
        cx = global_path[0, :]
        cy = global_path[1, :]
        cyaw = global_path[2, :]

        dx = [state.x - icx for icx in cx[pind:(pind + self.N_IND_SEARCH)]]
        dy = [state.y - icy for icy in cy[pind:(pind + self.N_IND_SEARCH)]]

        d = [idx ** 2 + idy ** 2 for (idx, idy) in zip(dx, dy)]

        mind = min(d)

        ind = d.index(mind) + pind

        mind = np.sqrt(mind)

        dxl = cx[ind] - state.x
        dyl = cy[ind] - state.y

        angle = self.normalize_angle(cyaw[ind] - math.atan2(dyl, dxl))
        if angle < 0:
            mind *= -1

        return ind, mind

    def update_state(self, state, u_v, u_o):
        # if u_o >= self.max_omega:
        #     u_o = self.max_omega
        # if u_o <= - self.max_omega:
        #     u_o = - self.max_omega
        #

        if u_o >= self.max_omega_rate:
            u_o = self.max_omega_rate
        if u_o <= - self.max_omega_rate:
            u_o = - self.max_omega_rate

        if u_v >= self.max_acc:
            u_v = self.max_acc
        if u_o <= - self.max_acc:
            u_v = - self.max_acc

        noise = np.random.random(5) * 0.1

        state.x = state.x + state.v * np.cos(state.yaw) * self.dt + noise[1] * 0
        state.y = state.y + state.v * np.sin(state.yaw) * self.dt + noise[1] * 0
        state.yaw = self.normalize_angle(state.yaw + state.omega * self.dt) + noise[2] * 0.
        state.v = state.v + u_v * self.dt + noise[0] * 0
        state.omega = self.normalize_angle(state.omega + u_o * self.dt) + noise[0] * 0.
        if state.v >= self.max_v:
            state.v = self.max_v
        if state.v <= 0.:
            state.v = 0.

        return state

    def local_planner(self, x0, original_path, transformation=False):
        if transformation:
            x0_ext = np.repeat(x0[:, None], 3, axis=1)
            x0_ = self.coordinate_transform(x0[2], x0[:2], x0_ext, mode="world to body")
            x0_b = np.copy(x0)
            x0_b[:3] = x0_[:3, 0]

            ref_path = self.coordinate_transform(original_path[2, 0], original_path[:2, 0], original_path,
                                                 mode="world to body")
            x0_in = x0_b
            path_in = ref_path
        else:
            x0_in = x0
            path_in = original_path

        # states: (x ,y ,theta ,v , steer, a, steer_rate, jerk)
        cmpc = CasADi_MPC_differ()
        cmpc.dt0 = self.dt
        # ref_traj = self.expand_path(original_path, self.ds)
        cmpc.horizon = self.window
        cmpc.v_end = cmpc.v_max
        cmpc.omega_end = cmpc.omega_max
        # print("mpc start:{}, {}".format(x0[0], x0[1]))

        cmpc.init_model_reference_line(x0_in, path_in)
        op_traj, op_control = cmpc.get_result_reference_line()

        # print("ds:", dt * cmpc.v_max, " horizon after expasion:", len(ref_traj.T))
        # print("MPC total time:{:.3f}s".format(time.time() - start_time))
        if transformation:
            print("[b] smooth start:{}, {}".format(op_traj[0, 0], op_traj[1, 0]))
            op_path = self.coordinate_transform(original_path[2, 0], original_path[:2, 0], op_traj,
                                                mode="body to world")
            print("[w] smooth start:{}, {}".format(op_path[0, 0], op_path[1, 0]))
            return np.vstack([op_path, op_control])
        else:
            return np.vstack([op_traj, op_control])

    def linear_error_mpc_control(self, index, xref, x0):
        mpc = CasADi_MPC_differ_Control()
        ex0 = x0 - xref[:5, index]
        mpc.dt0 = self.dt
        mpc.horizon = 5
        mpc.init_model_controller_conic(index, ex0, xref)
        # mpc.init_model_controller_qpsol(index, ex0, xref)
        op_traj = mpc.get_mpc_result()

        oa = op_traj[0, :]
        od = op_traj[1, :]

        return oa, od

    def check_goal(self, state, goal, tind, nind):
        # check goal
        dx = state.x - goal[0]
        dy = state.y - goal[1]
        d = np.hypot(dx, dy)

        isgoal = (d <= self.GOAL_DIS)

        if abs(tind - nind) >= 10:
            isgoal = False

        isstop = (abs(state.v) <= 0.05)

        if isgoal and isstop:
            return True

        return False

    def calc_ref_trajectory(self, state, global_path, pind):
        window = self.window + 1
        xref = np.zeros((4, window))
        ncourse = len(global_path.T)

        ind, _ = self.calc_nearest_index(state, global_path, pind)

        if pind >= ind:
            ind = pind

        xref[0, 0] = global_path[0, ind]
        xref[1, 0] = global_path[1, ind]
        xref[2, 0] = global_path[2, ind]
        xref[3, 0] = global_path[3, ind]
        travel = 0.0

        for i in range(window):
            # travel += abs(state.v) * self.dt
            # travel += window
            # dind = int(round(travel / self.ds))
            dind = int(window)
            if (ind + dind) < ncourse:
                xref[0, i] = global_path[0, i + ind]
                xref[1, i] = global_path[1, i + ind]
                xref[2, i] = global_path[2, i + ind]
                xref[3, i] = global_path[3, i + ind]

            else:
                xref[0, i] = global_path[0, ncourse - 1]
                xref[1, i] = global_path[1, ncourse - 1]
                xref[2, i] = global_path[2, ncourse - 1]
                xref[3, i] = global_path[3, ncourse - 1]

        # xref = self.expand_path(xref, self.ds)
        return xref, ind

    def do_simulation(self, global_path, initial_state):
        """
        Simulation

        cx: course x position list
        cy: course y position list
        cy: course yaw position list
        ck: course curvature list
        sp: speed profile
        dl: course tick [m]

        """

        goal = global_path[:2, -1]

        state = initial_state

        time = 0.0
        x = [state.x]
        y = [state.y]
        yaw = [state.yaw]
        v = [state.v]
        omega = [state.omega]
        t = [0.0]
        e, e_th = 0.0, 0.0
        elist = [0.]
        ethlist = [0.]
        planlist = []
        target_ind, _ = self.calc_nearest_index(state, global_path, 0)
        # cyaw = smooth_yaw(cyaw)
        alpha = 0.3
        tick = int(alpha * self.window)
        vm, om = None, None

        while self.MAX_TIME >= time:
            xref, target_ind = self.calc_ref_trajectory(state, global_path, target_ind)

            if (time > 15.) and (np.max(xref[2, :]) == np.max(xref[2, :])):
                break

            x0 = np.array([state.x, state.y, state.yaw, state.v, state.omega])

            if tick % int(alpha * self.window) == 0:
                # current state
                # print("tick:{}, start:{:.3f}, {:.3f}, {:.3f}, status: {:.3f},{:.3f}".format(tick, state.x, state.y,
                #                                                                             state.yaw, state.v,
                #                                                                             state.yaw))
                # print("ref start:{}, {}".format(xref[0, 0], xref[1, 0]))
                # print("tick:{}, ref goal:{}, {}".format(tick, xref[0, -1], xref[1, -1]))
                smooth_traj = self.local_planner(x0, xref)
                planlist.append(smooth_traj)

            pp = 0
            if tick % 1 == 0:
                # for k in range(len(smooth_path)):
                # print("tick:{}, fin plan status: {:.3f},{:.3f}".format(tick, smooth_control[0, -1],
                #                                                        smooth_control[1, -1]))
                #
                # print("tick:{}, control: {:.3f},{:.3f}".format(tick, state.v, state.yaw))
                # state, e, e_th = self.controller(state, smooth_control, smooth_path, e, e_th)
                kk = tick % int(alpha * self.window)
                # am, dm = self.iterative_linear_mpc_control(smooth_traj, x0, vm, om)
                am, dm = self.linear_error_mpc_control(kk, smooth_traj, x0)
                self.fb_a.append(am[pp])
                self.fb_o_rate.append(dm[pp])
                state = self.update_state(state, 1 * am[pp] + smooth_traj[5, kk], 1 * dm[pp] + smooth_traj[6, kk])
                print("qp a:{:.3f} dmax:{:.3f}".format(am[pp], np.max(dm)))
                self.ref_v.append(smooth_traj[3, kk])
                self.ref_omega.append(smooth_traj[4, kk])
                self.ref_a.append(smooth_traj[5, kk])
                self.ref_omega_rate.append(smooth_traj[6, kk])
                time = time + self.dt

                x.append(state.x)
                y.append(state.y)
                yaw.append(state.yaw)
                v.append(state.v)
                omega.append(state.omega)
                elist.append(e)
                ethlist.append(e_th)
                t.append(time)

                # if self.check_goal(state, goal, target_ind, len(global_path.T)):
                #     break

                if self.show_animation:  # pragma: no cover
                    plt.cla()
                    # for stopping simulation with the esc key.
                    plt.gcf().canvas.mpl_connect('key_release_event',
                                                 lambda event: [exit(0) if event.key == 'escape' else None])
                    if smooth_traj is not None:
                        plt.plot(smooth_traj[0, :], smooth_traj[1, :], "xc", label="MPC smooth")

                    plt.plot(global_path[0, :], global_path[1, :], color="orange", label="course")
                    plt.plot(x, y, "xr", label="realtime traj")
                    plt.plot(xref[0, :], xref[1, :], color="purple", label="xref")
                    plt.plot(global_path[0, target_ind], global_path[1, target_ind], "xg", label="target")
                    self.plot_car(state)
                    plt.axis("equal")
                    plt.grid(True)
                    plt.title("Time[s]:{:.2f}, speed[m/s]:{:.2f}".format(time, state.v))
                    # f = plt.figure()
                    # self.plot_info_realtime(x, y, yaw, v, omega, elist, ethlist)
                    if tick % 10 == 0:
                        plt.pause(0.001)

            tick += 1

            if self.check_goal(state, goal, target_ind, len(global_path.T)):
                print("Goal")
                break

        real_traj = np.array([x, y, yaw, v, omega])
        errors = np.array([elist, ethlist])
        return t, real_traj, errors, planlist

    def plot_arrow(self, x, y, yaw, length=1.5, width=0.5):  # pragma: no cover
        plt.arrow(x, y, length * np.cos(yaw), length * np.sin(yaw),
                  head_length=width, head_width=width)
        plt.plot(x, y)

    def plot_car(self, state):

        outline = np.array([
            [self.LF, self.LF, -self.LB, -self.LB, self.LF],
            [self.W / 2, -self.W / 2, - self.W / 2, self.W / 2, self.W / 2]])

        Rot1 = np.array([[np.cos(state.yaw), -np.sin(state.yaw)],
                         [np.sin(state.yaw), np.cos(state.yaw)]])
        outline = Rot1 @ outline
        outline[0, :] += state.x
        outline[1, :] += state.y
        plt.plot(np.array(outline[0, :]).flatten(),
                 np.array(outline[1, :]).flatten(), "-", color='cadetblue')

        self.plot_arrow(state.x, state.y, state.yaw)

    def plot_info_realtime(self, x, y, yaw, v, omega, elist, ethlist):
        plt.cla()

        ax = plt.subplot(211)
        ax.plot(np.rad2deg(yaw), "-r", label="lqr yaw")
        ax.plot(np.rad2deg(omega), "-c", label="lqr omega")
        ax.plot(np.rad2deg(self.ref_omega), "-b", label="ref omega")
        ax.plot(np.rad2deg(ethlist), color="tab:purple", label="heading error")
        ax.grid(True)
        ax.legend()
        ax.set_xlabel("line length[m]")
        ax.set_ylabel("lqr yaw angle[deg]")

        # f3 = plt.figure()
        # ax = plt.subplot(211)
        # # ax.plot(op_input[0, :], "-g", label="mpc v")
        # ax.legend()
        # ax.grid(True)
        # ax.set_ylabel("speed [m/s]")
        ax = plt.subplot(212)
        ax.plot(v, "-r", label="lqr v")
        ax.plot(self.ref_v, "-b", label="ref v")
        ax.plot(self.ref_a, "-b", label="ref a")
        ax.plot(elist, color="tab:purple", label="lateral error")
        ax.grid(True)
        ax.legend()
        ax.set_xlabel("line length[m]")
        ax.set_ylabel("speed [m/s]")

    def plot_info(self, global_path, flow, error, planlist, plot_controller_u=True):

        diff_s = np.diff(global_path[:2, :], axis=1)
        sum_s = np.sum(np.hypot(diff_s[0], diff_s[1]))
        # plt.cla()
        s_realtime = np.linspace(0, sum_s, len(flow.T))
        ds = s_realtime[1]
        s_reference = np.linspace(0, sum_s, len(global_path.T))
        # sline = np.arange(0, ds * len(self.fb_a[3]), ds)

        f2 = plt.figure()
        ax = plt.subplot(221)
        # ax.plot(s_reference, np.rad2deg(global_path[0, :]), "-r", label="ref x")
        # ax.plot(s_reference, np.rad2deg(global_path[0, :]), "-r", label="ref x")
        ax.plot(s_realtime, flow[3, :], "-r", label="real v")
        ax.plot(s_realtime, self.ref_v, "-b", label="used ref v")
        ax.plot(s_realtime, self.ref_a, "-.", color="tab:blue", label="used ref a")
        # for i, line in enumerate(self.fb_a):
        #     ax.plot(i * ds + sline, line, "-.", color='crimson', label="fb a")
        if plot_controller_u:
            ax.plot(s_realtime, self.fb_a, "-.", color='crimson', label="fb a")

        ax.plot(s_realtime, error[0, :], color="tab:purple", label="lateral error")
        ax.grid(True)
        ax.legend()
        ax.set_xlabel("line length[m]")
        ax.set_ylabel("y [m]")

        ax = plt.subplot(223)
        ax.plot(s_reference, np.rad2deg(global_path[2, :]), "-g", label="ref yaw")
        ax.plot(s_realtime, np.rad2deg(flow[2, :]), "-r", label="real yaw")
        ax.plot(s_realtime, np.rad2deg(self.ref_omega), "-b", label="used ref omega")
        ax.plot(s_realtime, np.rad2deg(self.ref_omega_rate), "-.", color="tab:blue", label="used ref omega_rate")
        # for i, line in enumerate(self.fb_o_rate):
        #     ax.plot(i * ds + sline, np.rad2deg(line), "-.", color='crimson', label="fb o rate")
        if plot_controller_u:
            ax.plot(s_realtime, np.rad2deg(self.fb_o_rate), "-.", color='crimson', label="fb o rate")

        ax.plot(s_realtime, np.rad2deg(flow[4, :]), "-c", label="lqr omega")
        ax.plot(s_realtime, np.rad2deg(error[1, :]), color="tab:purple", label="heading error")
        ax.grid(True)
        ax.legend()
        ax.set_xlabel("line length[m]")
        ax.set_ylabel("angle [deg]")

        ax = plt.subplot(222)
        ax.plot(global_path[0, :], "tab:blue", label="ref x")
        ax.plot(flow[0, :], "-r", label="real x")
        ax.plot(error[0, :], color="tab:purple", label="lateral error")
        ax.grid(True)
        ax.legend()
        ax.set_xlabel("line length[m]")
        ax.set_ylabel("x [m]")

        ax = plt.subplot(224)
        ax.plot(global_path[1, :], "tab:blue", label="ref y")
        ax.plot(flow[1, :], "-r", label="real y")
        ax.grid(True)
        ax.legend()
        ax.set_xlabel("line length[m]")
        ax.set_ylabel("y [m]")

        f2 = plt.figure()
        ax = plt.subplot(211)
        ax.plot(s_realtime, flow[3, :], "-r", label="real v")
        ax.plot(s_realtime, self.ref_v, "-b", label="used ref v")
        strecke = sum_s / len(planlist)
        for i, plan in enumerate(planlist):
            sline = np.linspace(i * strecke, (i + 1) * strecke, len(plan.T))
            ax.plot(sline, plan[3, :], color="tab:green", label="plan v")

        ax.grid(True)
        ax.legend()
        ax.set_xlabel("line length[m]")
        ax.set_ylabel("y [m]")

        ax = plt.subplot(212)
        for i, plan in enumerate(planlist):
            sline = np.linspace(i * strecke, (i + 1) * strecke, len(plan.T))
            ax.plot(sline, np.abs(plan[4, :] / (plan[3, :] + 1e-5)), color="tab:green", label="cal curvature")
        ax.plot(s_reference, global_path[3, :], color="green", label="ref curvature")
        ax.grid(True)
        # ax.legend()
        ax.set_xlabel("line length[m]")
        ax.set_ylabel("curvature")

        f = plt.figure()
        ax = plt.subplot(111)
        ax.plot(global_path[0, :], global_path[1, :], "-g", label="waypoints")
        # ax.plot(cx, cy, "-r", label="target course")
        ax.plot(flow[0, :], flow[1, :], "xb", label="tracking")
        ax.grid(True)
        ax.axis("equal")
        ax.set_xlabel("x[m]")
        ax.set_ylabel("y[m]")
        ax.legend()

def get_raw_course(mpc, dl):
    ax = [0.0, 5.0, 10.0, 10]
    ay = [0.0, -5.0, 2.0, 10]
    control_points = np.array([ax, ay])

    return mpc.expand_path(control_points, ds=dl)

def main():
    address = "../../config_differ_smoother.yaml"

    load_file = False
    dl = 0.5
    address = "../../config_differ_smoother.yaml"
    with open(address, 'r', encoding='utf-8') as f:
        param = yaml.load(f)
    ut = UTurnMPC()
    ut.set_parameters(param)
    mpclqr = MPC_LQR_Controller()

    if not load_file:
        raw_course = get_raw_course(mpclqr, dl)
        # global_path = mpclqr.expand_path(raw_course, 0.05)
        global_path = mpclqr.expand_path(raw_course, mpclqr.ds)
        ran = np.random.rand(5)
        x0_ = global_path[0, 0] - 2.5 * 1
        y0_ = global_path[1, 0] - .5 * 1
        yaw0_ = global_path[2, 0] + ran[2] * 1
        v0_ = 0.
        omega0_ = 0.

        state0 = mpclqr.State(x=x0_, y=y0_, yaw=yaw0_, v=v0_, omega=omega0_)

        t, real_traj, error_traj, planlist = mpclqr.do_simulation(global_path, state0)

        mpclqr.plot_info(global_path, real_traj, error_traj, planlist)

        # np.savez("../../data/smoothed_traj_differ", dt=dt, traj=op_path, control=op_input,
        #          refpath=raw_course)

    plt.show()

if __name__ == '__main__':
    main()


#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
.. $Id$
"""

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

from pyramid import httpexceptions as hexc

from pyramid.view import view_config
from pyramid.view import view_defaults

from requests.structures import CaseInsensitiveDict

from zope import component

from zope import interface

from zope.cachedescriptors.property import Lazy

from zope.component.hooks import site as current_site

from zope.event import notify

from zope.intid.interfaces import IIntIds

from zope.security.interfaces import IPrincipal

from zope.securitypolicy.interfaces import Allow
from zope.securitypolicy.interfaces import IPrincipalRoleManager

from nti.app.base.abstract_views import AbstractAuthenticatedView

from nti.app.externalization.error import raise_json_error

from nti.app.externalization.view_mixins import ModeledContentUploadRequestUtilsMixin
from nti.app.externalization.view_mixins import BatchingUtilsMixin

from nti.app.products.courseware.views import CourseAdminPathAdapter

from nti.app.products.courseware_admin import MessageFactory as _
from nti.app.products.courseware_admin import VIEW_EXPLICTLY_ADMINISTERED_COURSES

from nti.app.products.courseware_admin.interfaces import ICourseAdminsContainer
from nti.app.products.courseware_admin.interfaces import ICourseAdminSummary
from nti.app.products.courseware_admin.interfaces import CourseAdminSummary

from nti.app.users.views.view_mixins import UsersCSVExportMixin
from nti.app.users.views.view_mixins import AbstractUserViewMixin

from nti.common.string import is_true

from nti.contenttypes.courses.common import get_course_packages

from nti.contenttypes.courses.interfaces import RID_TA
from nti.contenttypes.courses.interfaces import RID_INSTRUCTOR
from nti.contenttypes.courses.interfaces import RID_CONTENT_EDITOR

from nti.contenttypes.courses.interfaces import CourseRolesSynchronized

from nti.contenttypes.courses.interfaces import ICourseCatalog
from nti.contenttypes.courses.interfaces import ICourseInstance
from nti.contenttypes.courses.interfaces import ICourseCatalogEntry
from nti.contenttypes.courses.interfaces import IGlobalCourseCatalog

from nti.contenttypes.courses.sharing import update_package_permissions

from nti.contenttypes.courses.utils import get_course_instructors
from nti.contenttypes.courses.utils import get_instructed_and_edited_courses

from nti.coremetadata.interfaces import IDeactivatedUser

from nti.dataserver import authorization as nauth

from nti.dataserver.authorization import is_admin_or_site_admin

from nti.dataserver.interfaces import IUser

from nti.dataserver.users.users import User

from nti.externalization.interfaces import LocatedExternalDict
from nti.externalization.interfaces import StandardExternalFields

from nti.links.interfaces import ILinkExternalHrefOnly

from nti.links.links import Link

from nti.links.externalization import render_link

from nti.site.hostpolicy import get_host_site

from nti.site.site import get_component_hierarchy_names

from nti.traversal.traversal import find_interface

ITEMS = StandardExternalFields.ITEMS
ITEM_COUNT = StandardExternalFields.ITEM_COUNT
TOTAL = StandardExternalFields.TOTAL

logger = __import__('logging').getLogger(__name__)

@view_config(route_name='objects.generic.traversal',
             renderer='rest',
             request_method='GET',
             name='AuditUsageInfo',
             permission=nauth.ACT_NTI_ADMIN,
             context=ICourseCatalog)
class CatalogUsageSummary(AbstractAuthenticatedView):

    def __call__(self):
        items = {}

        provenance_link_cache = {}

        for catalog_entry in self.context.iterCatalogEntries():

            # We want to indicate what catalog each course comes from
            # for this admin view. We look for the ICourseCatalog and render a link to it.
            # We can't render links to the global catalog so if a course
            # is global we omit the provenance
            provenance = None
            owner_catalog = find_interface(catalog_entry, ICourseCatalog)

            if not IGlobalCourseCatalog.providedBy(owner_catalog):
                provenance = provenance_link_cache.get(owner_catalog, None)
                if provenance is None:
                    owner_catalog_link = Link(owner_catalog)
                    interface.alsoProvides(owner_catalog_link, ILinkExternalHrefOnly)
                    provenance = render_link(owner_catalog_link)
                    provenance_link_cache[owner_catalog] = provenance

            course_summary = {}
            course_summary['provenance'] = provenance

            roles = {}

            course = ICourseInstance(catalog_entry)

            prm = IPrincipalRoleManager(course)
            for role in (RID_TA, RID_INSTRUCTOR, RID_CONTENT_EDITOR,):
                pids = (pid for (pid, setting) in prm.getPrincipalsForRole(role)
                        if setting == Allow)
                pids = [pid for pid in pids if User.get_user(pid) is not None
                        and not IDeactivatedUser.providedBy(User.get_user(pid))]
                roles[role] = pids

            course_summary['roles'] = roles

            items[catalog_entry.ntiid] = course_summary

        result = LocatedExternalDict()
        result.__parent__ = self.context
        result.__name__ = self.request.view_name
        result[ITEMS] = items
        result[ITEM_COUNT] = result[TOTAL] = len(items)
        return result

@view_config(context=ICourseInstance)
@view_config(context=ICourseCatalogEntry)
@view_defaults(route_name='objects.generic.traversal',
               renderer='rest',
               request_method='POST',
               name='UpdateCourseContentPermissions',
               permission=nauth.ACT_NTI_ADMIN)
class CourseContentPermissionUpdateView(AbstractAuthenticatedView):
    """
    Update the package permissions for a course.
    """

    def __call__(self):
        packages = get_course_packages(self.context)
        update_package_permissions(self.context, added=packages)
        return hexc.HTTPNoContent()

@view_config(context=CourseAdminPathAdapter)
@view_defaults(route_name='objects.generic.traversal',
               renderer='rest',
               request_method='GET',
               name='GetCourse',
               permission=nauth.ACT_READ)
class GetCourseView(AbstractAuthenticatedView):

    def readInput(self):
        result = CaseInsensitiveDict(self.request.params)
        return result

    def checkAccess(self):
        if not is_admin_or_site_admin(self.remoteUser):
            raise hexc.HTTPForbidden()

    def __call__(self):
        self.checkAccess()
        values = self.readInput()
        admin = values.get('admin') or values.get('adminLevel')
        if not admin:
            raise_json_error(self.request,
                             hexc.HTTPUnprocessableEntity,
                             {
                                 'message': _(u"Must provide an admin level."),
                             },
                             None)

        name = values.get('name') or values.get('key')
        if not name:
            raise_json_error(self.request,
                             hexc.HTTPUnprocessableEntity,
                             {
                                 'message': _(u"Must provide a course key/name."),
                             },
                             None)

        site = values.get('site')
        names = get_component_hierarchy_names()
        if site:
            site = site.lower()
            if site not in names:
                raise_json_error(self.request,
                                 hexc.HTTPUnprocessableEntity,
                                 {
                                     'message': _(u"Invalid site."),
                                 },
                                 None)
            else:
                sites = (get_host_site(site),)
        else:
            sites = [get_host_site(x) for x in names]

        section = values.get('section')
        for site in sites:
            with current_site(site):
                catalog = component.queryUtility(ICourseCatalog)
                if catalog is None:
                    continue
                try:
                    course = catalog[admin][name]
                    if section:
                        course = course.SubInstances[section]
                    return course
                except (AttributeError, KeyError):
                    pass
        raise hexc.HTTPNotFound()

@view_config(context=CourseAdminPathAdapter)
@view_defaults(route_name='objects.generic.traversal',
               renderer='rest',
               request_method='GET',
               name='GetCatalogEntry',
               permission=nauth.ACT_READ)
class GetCatalogEntryView(GetCourseView):

    def __call__(self):
        result = super(GetCatalogEntryView, self).__call__()
        return ICourseCatalogEntry(result)

@view_config(name='SyncInstructors')
@view_defaults(route_name='objects.generic.traversal',
               renderer='rest',
               context=ICourseInstance,
               request_method='POST',
               permission=nauth.ACT_NTI_ADMIN)
class SyncCourseInstructorsView(AbstractAuthenticatedView):

    def validate(self, name):
        user = User.get_user(name)
        return IUser.providedBy(user)

    def __call__(self):
        course = ICourseInstance(self.context)
        # get and validate role instructors
        role_inst = {
            x.lower() for x in get_course_instructors(course) if self.validate(x)
        }
        # get course instructors
        # pylint: disable=not-an-iterable
        course_insts = {
            IPrincipal(x).id for x in course.instructors or ()
        }
        course_insts = {x.lower() for x in course_insts if self.validate(x)}

        # all instructors
        instructors = course_insts.union(role_inst)
        if not instructors:
            raise_json_error(self.request,
                             hexc.HTTPUnprocessableEntity,
                             {
                                 'message': _(u"Course does not have valid instructors."),
                             },
                             None)

        # reset all TA/Instructor roles while saving valid mappings
        # pylint: disable=too-many-function-args
        roles = {}
        manager = IPrincipalRoleManager(course)
        for role, principal, setting in list(manager.getPrincipalsAndRoles() or ()):
            if role in (RID_TA, RID_INSTRUCTOR):
                pid = getattr(principal, 'id', principal).lower()
                if pid in instructors and setting is Allow:
                    roles[pid] = role
                manager.unsetRoleForPrincipal(role, principal)

        # set course instructors
        course.instructors = tuple(
            IPrincipal(User.get_user(x)) for x in sorted(instructors)
        )

        # set course roles
        for principal in course.instructors:
            pid = principal.id.lower()
            role = roles.get(pid, RID_INSTRUCTOR)
            manager.assignRoleToPrincipal(role, pid)

        # notify
        notify(CourseRolesSynchronized(course))
        return hexc.HTTPNoContent()

@view_config(name='SyncInstructors')
@view_defaults(route_name='objects.generic.traversal',
               renderer='rest',
               context=ICourseCatalogEntry,
               request_method='POST',
               permission=nauth.ACT_NTI_ADMIN)
class SyncCatalogEntryInstructorsView(SyncCourseInstructorsView):
    pass

@view_config(context=ICourseAdminsContainer)
@view_defaults(route_name='objects.generic.traversal',
             renderer='rest',
             request_method='GET',
             permission=nauth.ACT_CONTENT_EDIT)
class CourseAdminsGetView(AbstractUserViewMixin):
    """
    Return all course admins (instructors and editors of any course in the site)
    Filter by only instructors or only editors if requested
    """

    @Lazy
    def filterInstructors(self):
        # pylint: disable=no-member
        return is_true(self.params.get('filterInstructors', 'False'))

    @Lazy
    def filterEditors(self):
        # pylint: disable=no-member
        return is_true(self.params.get('filterEditors', 'False'))

    def get_externalizer(self, unused_entity):
        return 'admin-summary'

    def get_entity_intids(self, site=None):
        course_admin_intids = self.context.course_admin_intids(filterInstructors=self.filterInstructors, filterEditors=self.filterEditors)
        for doc_id in course_admin_intids:
            yield doc_id

    def _batch_selector(self, user):
        return CourseAdminSummary(user, self.context)

    def _post_numeric_sorting(self, ext_res, sort_on, reverse):
        """
        Sorts the `Items` in the result dict in-place, using the sort_on
        and reverse params.
        """
        ext_res[ITEMS] = sorted(ext_res[ITEMS],
                                key=lambda x: getattr(x.user, sort_on, 0),
                                reverse=reverse)

    def __call__(self):
        return self._do_call()

@view_config(context=ICourseAdminsContainer)
@view_defaults(route_name='objects.generic.traversal',
               request_method='GET',
               accept='text/csv',
               permission=nauth.ACT_CONTENT_EDIT)
class CourseAdminsCSVView(CourseAdminsGetView,
                       UsersCSVExportMixin):

    def _get_filename(self):
        return u'course_admins.csv'

    def __call__(self):
        self.check_access()
        return self._create_csv_response()

@view_config(context=ICourseAdminsContainer)
@view_defaults(route_name='objects.generic.traversal',
               request_method='POST',
               renderer='rest',
               permission=nauth.ACT_CONTENT_EDIT,
               request_param='format=text/csv')
class CourseAdminsCSVPOSTView(CourseAdminsCSVView,
                              ModeledContentUploadRequestUtilsMixin):

    def readInput(self):
        if self.request.POST:
            result = {'usernames': self.request.params.getall('usernames') or []}
        elif self.request.body:
            result = super(CourseAdminsCSVPOSTView, self).readInput()
        else:
            result = self.request.params
        return CaseInsensitiveDict(result)

    @Lazy
    def _params(self):
        return self.readInput()

    def _get_result_iter(self):
        usernames = self._params.get('usernames', ())
        if not usernames:
            return super(CourseAdminsCSVPOSTView, self)._get_result_iter()
        intids = component.getUtility(IIntIds)
        result = []
        for username in usernames:
            user = User.get_user(username)
            if user is None:
                continue
            user_intid = intids.queryId(user)
            if user_intid is None:
                continue
            # Validate the user is in the original result set
            if user_intid in self.filtered_intids:
                result.append(user)
        return result

@view_config(route_name='objects.generic.traversal',
             renderer='rest',
             context=ICourseAdminSummary,
             name=VIEW_EXPLICTLY_ADMINISTERED_COURSES,
             permission=nauth.ACT_CONTENT_EDIT,
             request_method='GET')
class CoursesExplicitlyAdministeredView(AbstractAuthenticatedView,
                                        BatchingUtilsMixin):
    """
    A view that returns the courses a user administers (instructs/edits).
    """

    _DEFAULT_BATCH_START = 0
    _DEFAULT_BATCH_SIZE = None

    def __call__(self):
        result = LocatedExternalDict()
        courses = get_instructed_and_edited_courses(self.context.user)

        if len(courses) is 0:
            raise_json_error(self.request,
                             hexc.HTTPNotFound,
                             {
                                 'message': _(u"User has no administered courses."),
                                 'code': 'NoUserExplicitlyAdministeredCourses'
                             },
                             None)

        courses = sorted(courses, key=lambda x:x.title)
        result[TOTAL] = len(courses)
        self._batch_items_iterable(result, courses)
        return result


import argparse,glob,os,sys
import cv2
from easydict import EasyDict as edict
from natsort import natsorted
from random import shuffle

def load_reg(path):
    fs = cv2.FileStorage(path,cv2.FILE_STORAGE_READ)
    rect = fs.getNode("rectangles")
    return rect.mat().reshape(-1,4)

def createlstinfo(path_in):
    lst_images = natsorted(glob.glob(path_in+"/*.png"))
    print("Number of images",len(lst_images))
    lst_info = []

    for fname in lst_images:
        config = edict()
        path_name = fname.split('.')[0]
        config.fname = path_name
        reg_path = path_name +".reg"
        if os.path.isfile(reg_path):
            BBs = load_reg(reg_path)
            config.xmins = [BB[0] for BB in BBs]
            config.ymins = [BB[1] for BB in BBs]
            config.xmaxs = [BB[0] + BB[2] for BB in BBs]
            config.ymaxs = [BB[1] + BB[3] for BB in BBs]
        else:
            config.xmins = None
            config.ymins = None
            config.xmaxs = None
            config.ymaxs = None

        lst_info.append(config)
    return lst_info

def writeLabel(path_out,lst_info):

    path_label = os.path.join(path_out, "labels")
    if not os.path.exists(path_label):
        os.makedirs(path_label)
    fimage = "fimages.txt"
    flabel = "flabel.txt"

    if not os.path.exists(path_label):
        print("The path: ",path_label," don't exist")
        os.makedirs(path_label)
        print("Creating")
    for info in lst_info:
        path_label_txt = os.path.join(path_label,info.fname.split('/')[-1]+".txt")
        if info.xmins:
            print("Image: "+info.fname+": "+str(len(info.xmins))+" labels\r")
            with open(path_label_txt,'w') as file:
                for idx in range(len(info.xmins)):
                    file.write("defecto 0.00 0 0 %d %d %d %d -1 -1 -1 -1 -1 -1 -1\n"\
                               %(info.xmins[idx],info.ymins[idx],info.xmaxs[idx],info.ymaxs[idx]))

            with open(os.path.join(path_out,fimage), 'a') as file:
                file.write("%s"%(info.fname)+".png\n")
            with open(os.path.join(path_out, flabel), 'a') as file:
                file.write("%s\n"%(path_label_txt))

    print("Creating ",os.path.join(path_out,fimage))
    print("Creating ",os.path.join(path_out,flabel))

if __name__=="__main__":
    ap = argparse.ArgumentParser()
    ap.add_argument("--path_in", required=True,
                    help="a las carpetas con las imagenes png y .reg")
    ap.add_argument("--path_out", required=True,
                    help="path a la carpeta donde se crearan las labels y los archivos txts con las rutas")
    FLAGS = ap.parse_args()

    lst_info = createlstinfo(FLAGS.path_in)
    writeLabel(FLAGS.path_out, lst_info)


import torch

from models import Transformer
from utils.helpers import count_params, create_pad_mask, create_subsequent_mask

vocab_size = 100
n_heads = 4
max_length = 10
n_blocks = 3
d_model = 64
d_ff = 4 * d_model
d_k = d_v = d_model // n_heads
p_drop = 0.1

batch_size = 2

x_lengths = torch.randint(1, max_length, (batch_size,))
x_mask = create_pad_mask(x_lengths)
x = torch.randint(1, vocab_size, (batch_size, x_lengths.max()))

y_lengths = torch.randint(1, max_length, (batch_size,))
y_mask = create_subsequent_mask(y_lengths, pad_mask=create_pad_mask(y_lengths))
y = torch.randint(1, vocab_size, (batch_size, y_lengths.max()))

model = Transformer(
    vocab_size, n_heads, max_length, n_blocks, d_model, d_ff, d_k, d_v, p_drop
)

print(model)
count_params(model)

print(x_lengths, x.shape, x_mask.shape, y_lengths, y.shape, y_mask.shape)
print(x_mask == 0, y_mask == 0, sep="\n")
# logits = model(x, x_mask, y, y_mask)
# print(logits)
# print(logits.size())


import pygame
from pyc_man.actors import PacMan
from pyc_man.behaviors import DyingPacMan
from .running import RunningState
from pyc_man.game_state.ready import ReadyState

class LoseState(ReadyState):
    __music_on_startup__ = 'death'

    def __init__(self, message, message_color, next_state):
        super().__init__(next_state, subtitle=message, subtitle_color=message_color)

        # Persistent values
        # just to silence warnings
        self.level = None
        self.sprites = None
        self.actors = None

        self.pacman = None

    def handle_input_event(self, event):
        super().handle_input_event(event)
        if not self.pacman.animation.current().is_finished():
            self.done = False

    def setup(self, **persist_values):
        pygame.time.wait(500)
        super().setup(**persist_values)
        if self.title in self.ui:
            self.ui.remove(self.title)
        if self.subtitle in self.ui:
            self.ui.remove(self.subtitle)

        for actor in self.actors:
            if isinstance(actor, PacMan):
                self.pacman = actor
                actor.change_behavior(PacMan.Behavior.DEAD)
                actor.flip_state()
            else:
                self.level.remove(actor)

    def update(self, timedelta):
        self.pacman.animate(timedelta)
        if self.pacman.animation.current().is_finished():
            if self.title:
                self.ui.add(self.title)
            if self.subtitle:
                self.ui.add(self.subtitle)

        self.dirty = True


import pyfiglet
from colorama import init
from termcolor import colored

init()

user_input = input("Please enter your text: ")
color = input("What color do you want?: ")
available_colors = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']

if color not in available_colors:
    color = 'magenta'

result = colored(pyfiglet.figlet_format(user_input), color)
print(result)


#!/usr/bin/env python
#
# Update a redis server cache when an evenement is trigger
# in MySQL replication log
#

from pymysqlreplication import BinLogStreamReader
from pymysqlreplication.row_event import *

mysql_settings = {'host': '127.0.0.1', 'port': 3306, 'user': 'root', 'passwd': ''}

import json
import cherrypy
class Streamer(object):
    def __init__(self):
        self.stream = BinLogStreamReader(connection_settings = mysql_settings,
                                         only_events = [DeleteRowsEvent, WriteRowsEvent, UpdateRowsEvent], blocking = True, resume_stream = True)

    def index(self):
        cherrypy.response.headers['Content-Type'] = 'text/plain'
        def content():
            for binlogevent in self.stream:
                for row in binlogevent.rows:
                    if isinstance(binlogevent, DeleteRowsEvent):
                        yield json.dumps({
                          "action": "delete",
                          "id": row["values"]["id"]}) + "\n"
                    elif isinstance(binlogevent, UpdateRowsEvent):
                        yield json.dumps({
                          "action": "update",
                          "id": row["after_values"]["id"],
                          "doc": row["after_values"]}) + "\n"
                    elif isinstance(binlogevent, WriteRowsEvent):
                        yield json.dumps({
                          "action": "insert",
                          "id": row["values"]["id"],
                          "doc": row["values"]}) + "\n"
        return content()

    index.exposed = True
    index._cp_config = {"response.stream": True}

cherrypy.quickstart(Streamer())


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import datetime

class Migration(migrations.Migration):

    dependencies = [
        ('bupadiu', '0006_auto_20181212_1412'),
    ]

    operations = [
        migrations.CreateModel(
            name='Card',
            fields=[
                ('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
                ('user_id', models.CharField(max_length=32, blank=True, null=True)),
                ('card_number', models.CharField(max_length=32, blank=True, null=True)),
                ('image', models.CharField(max_length=32, blank=True, null=True)),
                ('updated_time', models.DateTimeField(default=datetime.datetime.now)),
                ('created_time', models.DateTimeField(default=datetime.datetime.now)),
            ],
        ),
    ]


from sqlalchemy import inspect
from flask_sqlalchemy import SQLAlchemy
from api.database.database_config import engine, base

db = SQLAlchemy()

def create_tables_if_not_exist():
    inspector = inspect(engine)
    existing_tables = inspector.get_table_names()
    table_names_to_create = [table_name for table_name in base.metadata.tables.keys() if table_name not in existing_tables]

    if table_names_to_create:
        base.metadata.create_all(bind=engine, )

def init_db(app):
    db.init_app(app)
    with app.app_context():
        db.create_all()


#!/usr/bin/env python

import yaml
import json
import subprocess

# CentOS 6 EPEL provides an alternate Jinja2 package
try:
    from jinja2 import BaseLoader
    from jinja2 import Environment
except ImportError:
    import sys
    sys.path.insert(0, '/usr/lib/python2.6/site-packages/Jinja2-2.6-py2.6.egg')
    from jinja2 import BaseLoader
    from jinja2 import Environment

from linchpin.exceptions import LinchpinError
from linchpin.exceptions import ValidationError

def dict_representer(dumper, data):
    _mapping_tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG
    return dumper.represent_mapping(_mapping_tag, data.iteritems())

def dict_constructor(loader, node):
    return dict(loader.construct_pairs(node))

class DataParser(object):

    def __init__(self):

        self._mapping_tag = yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG

    def process(self, file_w_path, data=None, data_w_path=None):
        """ Processes the PinFile and any data (if a template)
        using Jinja2. Returns json of PinFile, topology, layout,
        and hooks.

        :param file_w_path:
            Full path to the provided file to process

        :param data:
            A JSON representation of data mapped to a Jinja2 template in
            file_w_path

        :param data_w_path:
            If data is passed as a file, this is used
        """

        if not data:
            data = '{}'

        with open(file_w_path, 'r') as stream:
            file_data = stream.read()

            if data_w_path:
                with open(data_w_path, 'r') as strm:
                    data = strm.read()

            try:
                file_data = self.render(file_data, data)
                return self.parse_json_yaml(file_data)
            except TypeError:
                error_txt = "Error attempting to parse PinFile data file."
                error_txt += "\nTemplate-data files require a prepended '@'"
                error_txt += " (eg. '@/path/to/template-data.yml')"
                error_txt += "\nPerhaps the path to the PinFile or"
                error_txt += " template-data is missing or the incorrect path?."
                raise ValidationError(error_txt)

        return self.load_pinfile(file_w_path)

    def render(self, template, context):
        """
        Performs the rendering of template and context data using
        Jinja2.

        :param template:
            Full path to the Jinja2 template

        :param context:
            A dictionary of variables to be rendered againt the template
        """

        c = self.parse_json_yaml(context)
        t = Environment(loader=BaseLoader).from_string(template)
        return t.render(c)

    def parse_json_yaml(self, data):

        """ parses yaml file into json object """

        d = None

        # Setup support for ordered dicts so we do not lose ordering
        # when importing from YAML
        yaml.add_representer(dict, dict_representer)
        yaml.add_constructor(self._mapping_tag, dict_constructor)

        try:
            data = yaml.load(data)
        except Exception as e:
            raise LinchpinError('YAML parsing error: {}'.format(e))

        if isinstance(data, dict):
            return data

        return d

    def run_script(self, script):

        sp = None
        try:
            sp = subprocess.Popen(script,
                                  stdout=subprocess.PIPE,
                                  stderr=subprocess.PIPE)
        except OSError as e:
            raise ValidationError("problem running {0} ({1})".format(script, e))

        (stdout, stderr) = sp.communicate()

        if sp.returncode != 0:
            raise ValidationError("Script {0} had execution error"
                                  " ({1})".format(script, e))

        return stdout

    def load_pinfile(self, pinfile):
        # try to convert the data into json
        pf = None
        try:
            with open(pinfile, 'r') as stream:
                pf_data = stream.read()
                pf = self.parse_json_yaml(pf_data)
        except ValidationError as e:
            pass

        if not pf:
            # assume not json, so dynamic script
            # once executed, verify it is json
            try:
                pf = json.loads(self.run_script(pinfile))
            except Exception as e:
                raise LinchpinError(e)

        return pf

    def write_json(self, provision_data, pf_outfile):

        with open(pf_outfile, 'w') as outfile:
            json.dump(provision_data, outfile, indent=4)


"""Flask app for Cupcakes"""
from flask import Flask, request, render_template, redirect, json, jsonify
from models import db, connect_db, Cupcake
from sqlalchemy import text
# from flask_debugtoolbar import DebugToolbarExtension

app = Flask(__name__)
app.app_context().push()
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql:///cupcakes'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_ECHO'] = True
# debug = DebugToolbarExtension(app)

connect_db(app)

def serialize_cupcake(cupcake):
    """Serialize a dessert SQLAlchemy obj to dictionary."""

    return {
        "id": cupcake.id,
        "flavor": cupcake.flavor,
        "size": cupcake.size,
        "rating": cupcake.rating,
        "image": cupcake.image,
    }

@app.route('/api/cupcakes', methods=["GET", "POST"])
def all_cupcakes():

    if request.method == 'GET':
        cupcakes = Cupcake.query.all()
        serialized = [serialize_cupcake(c) for c in cupcakes]

        return jsonify(cupcakes=serialized)
    else:
        flavor = request.json["flavor"]
        size = request.json["size"]
        rating = request.json["rating"]
    # image = request.json["image"]

        new_cupcake = Cupcake(flavor=flavor, size=size, rating=rating)

        db.session.add(new_cupcake)
        db.session.commit()

        serialized = serialize_cupcake(new_cupcake)

        return (jsonify(cupcake=serialized), 201)

@app.route('/api/cupcakes/<int:id>', methods=["GET", "DELETE", "PATCH"])
def get_cupcake(id):
    if request.method == 'GET':
        cupcake = Cupcake.query.get_or_404(id)
        serialized = serialize_cupcake(cupcake)

        return jsonify(cupcake=serialized)

    elif request.method == 'DELETE':
        cupcake = Cupcake.query.get_or_404(id)

        db.session.delete(cupcake)
        db.session.commit()

        return jsonify({'Message': "Deleted"})

    # request.method == 'PATCH':
    else:
        cupcake = Cupcake.query.get_or_404(id)
        cupcake.flavor = request.json.get("flavor", cupcake.flavor)
        cupcake.size = request.json.get("size", cupcake.size)
        cupcake.rating = request.json.get("rating", cupcake.rating)

        db.session.commit()

        serialized = serialize_cupcake(cupcake)

        return jsonify(cupcake=serialized)


import re
import sys

def find_close_parenthese(s):
    assert s[0] == "("
    count = 1
    for i in range(1, len(s)):
        if s[i] == "(":
            count += 1
        elif s[i] == ")":
            count -= 1
        if count == 0:
            return i
    return -1

def find_close_bracket(s):
    assert s[0] == "{"
    count = 1
    for i in range(1, len(s)):
        if s[i] == "{":
            count += 1
        elif s[i] == "}":
            count -= 1
        if count == 0:
            return i
    return -1

def lua_find_transform(text, transform_name):
    # Search for lines starting with the transform name, ignoring, for
    # example, lines starting with lua comments, that is, with "--".
    prog = re.compile("(^|\n\s*)" + re.escape(transform_name) + "\s*=\s*transform\(")
    match = re.search(prog, text)
    if not match:
        return (None, None, None)
    open_bracket = match.end() - 1
    close_bracket = find_close_parenthese(text[open_bracket:])
    if close_bracket < 0:
        return (None, None, None)
    close_bracket = open_bracket + close_bracket
    transform_text = text[open_bracket : close_bracket + 1]
    return (transform_text, open_bracket, close_bracket)

def lua_read_transform(filename, transform_name):
    try:
        with open(filename, "r") as f:
            contents = f.read()
    except IOError:
        return None
    (transform_text, open_bracket, close_bracket) = lua_find_transform(
        contents, transform_name
    )
    prog = re.compile("\(vec3\((.*)\),\s*quat4\((.*)\)\s*\)")
    m = re.match(prog, transform_text)
    if m == None:
        print(
            "Could not extract the transform from string: %s " % transform_text,
            file=sys.stderr,
        )
        return None
    trans = list(map(float, [x.strip() for x in m.group(1).split(",")]))
    quat = list(map(float, [x.strip() for x in m.group(2).split(",")]))
    t = quat[0]
    return (trans, quat)

def lua_replace_transform(filename, transform_name, transform):
    try:
        with open(filename, "r") as f:
            contents = f.read()
    except IOError:
        print("Failed to open lua file.", file=sys.stderr)
        return True
    (transform_text, open_bracket, close_bracket) = lua_find_transform(
        contents, transform_name
    )
    if transform_text == None:
        print(
            "Failed to read field %s from %s."
            % (
                transform_name,
                filename,
            ),
            file=sys.stderr,
        )
        return True

    new_transform_text = "(vec3(%.8g, %.8g, %.8g), quat4(%.8g, %.8g, %.8g, %.8g))" % (
        transform[0][0],
        transform[0][1],
        transform[0][2],
        transform[1][0],
        transform[1][1],
        transform[1][2],
        transform[1][3],
    )
    output_text = (
        contents[:open_bracket] + new_transform_text + contents[close_bracket + 1 :]
    )

    print(("Updating " + transform_name + " in file: " + filename))
    try:
        with open(filename, "w") as f:
            f.write(output_text)
    except IOError:
        print("Failed to open lua file " + filename, file=sys.stderr)
        return True
    return False


import os
import time

import extract_utility

class ExtractItem():
    """check status of item in extract queue
    """
    def __init__(self, config, boundary, dataset, data,
                 extract_type, temporal_type, version):

        self.config = config

        self.client = self.config.client

        self.c_extracts = self.client.asdf.extracts
        self.c_msr = self.client.asdf.msr

        self.base = os.path.join(
            config.branch_dir, "outputs/extracts", version.replace('.', '_'))

        self.boundary = boundary
        self.dataset = dataset
        self.data = data
        self.extract_type = extract_type
        self.temporal_type = temporal_type
        self.version = version

        exo = extract_utility.ExtractObject()
        self.extract_options = exo._extract_options

        if self.extract_type not in self.extract_options:
            raise Exception('invalid extract type')

        self.extract_path = None

    def __exists_in_db(self):
        """check if extract exists in extract queue db collection
        """
        check_data = {
            "boundary": self.boundary,
            "data": self.data,
            "extract_type": self.extract_type,
            "version": self.version
        }

        search = self.c_extracts.find_one(check_data)

        exists = search is not None
        status = search['status'] if exists else None

        # print 'extract db info'
        # print exists
        # print status

        return exists, status

    def __exists_in_file(self):
        """check if extract file exists
        """
        temporal = self.temporal_type
        if temporal in ["None", None, "na", '']:
            temporal = "na"

        # full file name
        output_name = '{0}.{1}.{2}.csv'.format(self.dataset,
                                               temporal,
                                               self.extract_type)

        # absolute output path
        extract_path = os.path.join(
            self.base, self.boundary, "cache", self.dataset, output_name)

        # need as attribute so it can be added to merge list
        # outside class instance
        self.extract_path = extract_path

        extract_exists = os.path.isfile(extract_path) and os.stat(extract_path).st_size > 0

        # print 'extract file info'
        # print extract_path
        # print extract_exists

        return extract_exists

    def exists(self):
        """check if extract exists and status

        - check for extract db entry and file
        - check if extract is completed, waiting to be run, or
           encountered an error
        """
        db_exists, db_status = self.__exists_in_db()

        file_exists = self.__exists_in_file()

        valid_exists = False
        valid_completed = False

        if db_exists:

            if db_status in [0,2,3]:
                valid_exists = True

            elif db_status == 1:

                if file_exists:
                    valid_exists = True
                    valid_completed = True
                else:
                    valid_completed = "Error"
                    self.c_extracts.update(
                        {
                            'boundary': self.boundary,
                            'data': self.data,
                            'extract_type': self.extract_type,
                            'version': self.version
                        }, {
                            '$set': {"status": -1}
                        })

            else:
                valid_exists = True
                valid_completed = "Error"

        return valid_exists, valid_completed

    def add_to_queue(self, classification):
        """add extract item to asdf->extracts mongodb collection
        """
        ctime = int(time.time())

        query = {
            'boundary': self.boundary,
            'data': self.data,
            'extract_type': self.extract_type,
            'version': self.version
        }

        details = {
            'classification': classification,
            'generator': 'det',
            'priority': 0,
            'update_time': ctime,
            'attempts': 0
        }

        full_insert = query.copy()
        full_insert.update(details)

        # check if exists
        search = self.c_extracts.find_one(query)

        if search is None:
            full_insert['status'] = 0
            full_insert['submit_time'] = ctime

            # insert full
            insert = self.c_extracts.insert(full_insert)

        # update priority if needed
        elif search is not None and search['generator'] != 'det':
                update = self.c_extracts.update(query,
                                                {'$set': details})

        return True, ctime