from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from .forms import CustForm, ProdForm, ServiceForm
from .models import Cust, Product, Service
from django.shortcuts import redirect
from django.views.generic.base import TemplateView # new

def home(request):
    products = Product.objects.filter(productdelete=False).order_by('pickupdate')
    services = Service.objects.filter(servicedelete=False).order_by('servicedate')
    return render(request,'mfs/home.html',({'services': services , 'products': products}))

def cust_list(request):
    custs = Cust.objects.filter(custdelete=False).order_by('lastname','firstname')
    return render(request, 'mfs/cust_list.html', {'custs': custs})

def deleted_customers(request):
    custs = Cust.objects.filter(custdelete=True).order_by('lastname','firstname')
    return render(request, 'mfs/cust_list.html', {'custs': custs})

def cust_detail(request, pk):
    cust = get_object_or_404(Cust, pk=pk)
    return render(request, 'mfs/cust_detail.html', {'cust': cust})

def cust_new(request):
    if request.method == "POST":
        form = CustForm(request.POST)
        if form.is_valid():
            cust = form.save(commit=False)
            cust.custaddedby = request.user
            cust.custadd_date = timezone.now()
            cust.save()
            return redirect('cust_detail', pk=cust.pk)
    else:
        form = CustForm()
    return render(request, 'mfs/cust_new.html', {'form': form})

def prod_new(request, custid):
    cust = get_object_or_404(Cust, pk=custid)
    if request.method == "POST":
        form = ProdForm(request.POST)
        if form.is_valid():
            product = form.save(commit=False)
            product.custid = cust
            product.prodaddedby = request.user
            product.prodadd_date = timezone.now()
            product.save()
            return redirect('prod_detail', pk=product.pk)
    else:
        form = ProdForm()
    return render(request, 'mfs/prod_new.html', {'form': form,'custid':custid})

def service_new(request, custid):
    cust = get_object_or_404(Cust, pk=custid)
    if request.method == "POST":
        form = ServiceForm(request.POST)
        if form.is_valid():
            service = form.save(commit=False)
            service.custid = cust
            service.servaddedby = request.user
            service.servadd_date = timezone.now()
            service.save()
            return redirect('service_detail', pk=service.pk)
    else:
        form = ServiceForm()
    return render(request, 'mfs/service_new.html', {'form': form, 'custid':custid})

def cust_edit(request, pk):
    cust = get_object_or_404(Cust, pk=pk)
    if request.method == "POST":
        form = CustForm(request.POST, instance=cust)
        if form.is_valid():
            cust = form.save(commit=False)
            cust.custupdatedby = request.user
            cust.custupdate_date = timezone.now()
            cust.save()
            return redirect('cust_detail', pk=cust.pk)
    else:
        form = CustForm(instance=cust)
    return render(request, 'mfs/cust_edit.html', {'form': form, 'cust':cust})

def prod_edit(request, pk):
    product = get_object_or_404(Product, pk=pk)
    if request.method == "POST":
        form = ProdForm(request.POST, instance=product)
        if form.is_valid():
            product = form.save(commit=False)
            product.produpdatedby = request.user
            product.produpdate_date = timezone.now()
            product.save()
            return redirect('prod_detail', pk=product.pk)
    else:
        form = ProdForm(instance=product)
    return render(request, 'mfs/prod_edit.html', {'form': form, 'product':product})

def service_edit(request, pk):
    service = get_object_or_404(Service, pk=pk)
    if request.method == "POST":
        form = ServiceForm(request.POST, instance=service)
        if form.is_valid():
            service = form.save(commit=False)
            service.servupdatedby = request.user
            service.servupdate_date = timezone.now()
            service.save()
            return redirect('service_detail', pk=service.pk)
    else:
        form = ServiceForm(instance=service)
    return render(request, 'mfs/service_edit.html', {'form': form, 'service':service})

def orders(request, custid):
    products = Product.objects.filter(custid_id=custid, productdelete = False).order_by('pickupdate')
    services = Service.objects.filter(custid_id=custid, servicedelete = False).order_by('servicedate')
    return render(request, 'mfs/orders.html', ({'services': services , 'products': products,'custid':custid}))

def deletedorders(request, custid):
    products = Product.objects.filter(custid=custid, productdelete = True).order_by('pickupdate')
    services = Service.objects.filter(custid=custid, servicedelete = True).order_by('servicedate')
    return render(request, 'mfs/deletedorders.html', ({'services': services , 'products': products, 'custid':custid}))

def prod_detail(request, pk):
    product = get_object_or_404(Product, pk=pk)
    return render(request, 'mfs/prod_detail.html', {'product': product})

def service_detail(request, pk):
    service = get_object_or_404(Service, pk=pk)
    return render(request, 'mfs/service_detail.html', {'service': service})


#loops real fast

my_nums = [1,2,3,4]
i = 0
sum = 0
while i < len(my_nums):
    sum = sum + my_nums[i]
    if sum >= 10:
        print(sum, " greater than 10")
        break
    i += 1
else:
    print("Sum never exceeded 10")


import os

import os
def listFiles(path):
    #from notes
    if (os.path.isdir(path) == False):
        # base case:  not a folder, but a file, so return singleton list with its path
        return [os.path.abspath('.') + "/" + path]
    else:
        # recursive case: it's a folder, return list of all paths
        files = [ ]
        for filename in os.listdir(path):
            files += listFiles(path + "/" + filename)
        return files

# file = listFiles("Files")
# file1 = open(file[0])
# contents = file1.read()
# print(file)
# file1.close()
#print(contents)
save2 = open('Save9', 'w')
save2.write("hi")
save2.close()
# file2 = open('/Users/rollingstudent/Desktop/TP3/Save2')
# contents2 = file2.read()
# print(contents2)


# @see http://stackoverflow.com/questions/457246/what-is-the-best-real-time-plotting-widget-for-wxpython

from Tkinter import *
import math, random, threading, time

class StripChart:

    def __init__(self, root):
        self.gf = self.makeGraph(root)
        self.cf = self.makeControls(root)
        self.gf.pack()
        self.cf.pack()
        self.Reset()

    def makeGraph(self, frame):
        self.sw = 1000
        self.h = 200
        self.top = 2
        gf = Canvas(frame, width=self.sw, height=self.h+10,
                    bg="#002", bd=0, highlightthickness=0)
        gf.p = PhotoImage(width=2*self.sw, height=self.h)
        self.item = gf.create_image(0, self.top, image=gf.p, anchor=NW)
        return(gf)

    def makeControls(self, frame):
        cf = Frame(frame, borderwidth=1, relief="raised")
        Button(cf, text="Run", command=self.Run).grid(column=2, row=2)
        Button(cf, text="Stop", command=self.Stop).grid(column=4, row=2)
        Button(cf, text="Reset", command=self.Reset).grid(column=6, row=2)
        self.fps = Label(cf, text="0 fps")
        self.fps.grid(column=2, row=4, columnspan=5)
        return(cf)

    def Run(self):
        self.go = 1
        for t in threading.enumerate():
            if t.name == "_gen_":
                print("already running")
                return
        threading.Thread(target=self.do_start, name="_gen_").start()

    def Stop(self):
        self.go = 0
        for t in threading.enumerate():
            if t.name == "_gen_":
                t.join()

    def Reset(self):
        self.Stop()
        self.clearstrip(self.gf.p, '#345')

    def do_start(self):
        t = 0
        y2 = 0
        tx = time.time()
        while self.go:
            y1 = 0.2*math.sin(0.02*math.pi*t)
            y2 = 0.9*y2 + 0.1*(random.random()-0.5)
            self.scrollstrip(self.gf.p,
               (0.25+y1,   0.25, 0.7+y2,   0.6,     0.7,   0.8),
               ( '#ff4', '#f40', '#4af', '#080', '#0f0', '#080'),
                 "" if t % 65 else "#088")

            t += 1
            if not t % 100:
                tx2 = time.time()
                self.fps.config(text='%d fps' % int(100/(tx2 - tx)))
                tx = tx2
            time.sleep(0.001)

    def clearstrip(self, p, color):  # Fill strip with background color
        self.bg = color              # save background color for scroll
        self.data = None             # clear previous data
        self.x = 0
        p.tk.call(p, 'put', color, '-to', 0, 0, p['width'], p['height'])

    def scrollstrip(self, p, data, colors, bar=""):   # Scroll the strip, add new data
        self.x = (self.x + 1) % self.sw               # x = double buffer position
        bg = bar if bar else self.bg
        p.tk.call(p, 'put', bg, '-to', self.x, 0,
                  self.x+1, self.h)
        p.tk.call(p, 'put', bg, '-to', self.x+self.sw, 0,
                  self.x+self.sw+1, self.h)
        self.gf.coords(self.item, -1-self.x, self.top)  # scroll to just-written column
        if not self.data:
            self.data = data
        for d in range(len(data)):
            y0 = int((self.h-1) * (1.0-self.data[d]))   # plot all the data points
            y1 = int((self.h-1) * (1.0-data[d]))
            ya, yb = sorted((y0, y1))
            for y in range(ya, yb+1):                   # connect the dots
                p.put(colors[d], (self.x,y))
                p.put(colors[d], (self.x+self.sw,y))
        self.data = data            # save for next call

def main():
    root = Tk()
    root.title("StripChart")
    app = StripChart(root)
    root.mainloop()

main()


import csv
import yaml
from workload_auto import logger

LOG = logger.get_logging(__name__)

def csv_file_read(filename):
    '''
    Function to read the CSV file.
    '''
    try:
        with open(filename, newline='') as csvfile:
            csvfile.seek(0)
            reader = csv.DictReader(csvfile)
            csv_list = []
            for row in reader:
                csv_map = {}
                for key, val in row.items():
                    csv_map.update({key.strip(): val.strip()})
                csv_list.append(csv_map)
            return csv_list, None
    except Exception as exc:
        LOG.error("CSV file read exception %s", exc)
        return [], exc

def yml_file_read(filename):
    '''
    Function to read the YML file.
    '''
    try:
        with open(filename, 'r') as file_desc:
            file_dict = yaml.full_load(file_desc)
            return file_dict, None
    except Exception as exc:
        LOG.error("YML file read exception %s", exc)
        return {}, exc


import numpy as np
from scipy.optimize import fsolve

#closest_point returns parameter and distance of closest point on bezier curve (given control points) for a certain data point outside the curve.

def bezier(a, b, c, d, t):
    return (
        np.power(1 - t, 3) * a + 3 * np.power(1 - t, 2) * t * b + 3 * (1 - t) * np.power(t, 2) * c + np.power(t, 3) * d,
        -3 * np.power(1 - t, 2) * a + 3 * np.power(1 - t, 2) * b - 6 * (1 - t) * t * b + 6 * (1 - t) * t * c - 3 * np.power(t, 2) * c + 3 * np.power(t, 2) * d
    )

def closest_point(control_points, point):
    ax, ay = point[0], point[1]
    a, a1 = control_points[0][0], control_points[0][1]
    b, b1 = control_points[1][0], control_points[1][1]
    c, c1 = control_points[2][0], control_points[2][1]
    d, d1 = control_points[3][0], control_points[3][1]

    x1 = lambda t: bezier(a, b, c, d, t)[0]
    Dx = lambda t: bezier(a, b, c, d, t)[1]
    y1 = lambda t: bezier(a1, b1, c1, d1, t)[0]
    Dy = lambda t: bezier(a1, b1, c1, d1, t)[1]

    eq1 = lambda t: (ax - x1(t))**2 + (ay - y1(t))**2
    eq2 = lambda t: (ax - x1(t))**2 + np.power(Dy(t), 2)
    eq3 = lambda t: (ay - y1(t))**2 + np.power(Dx(t), 2)
    eq4 = lambda t: (ax - x1(t)) * Dx(t) + (ay - y1(t)) * Dy(t)

    equations = [eq1, eq2, eq3, eq4]
    min_distance = float('inf')
    best_t = None

    for eq in equations:
        roots = fsolve(eq, (0.0, 1.0))
        for root in roots:
            if 0 <= root <= 1:
                distance = np.sqrt((ax - x1(root))**2 + (ay - y1(root))**2)
                if distance < min_distance:
                    min_distance = distance
                    best_t = root
    C_STATE = {"parameter": best_t, "dist": eq1(best_t)}

    return C_STATE

def discrepancy_test(control_points, points_):
    total_distance = 0.0

    for point in points_:
        closest = closest_point(control_points, point)
        total_distance += closest["dist"]

    return total_distance


import datetime
import logging
import traceback
from collections import defaultdict
from uuid import uuid4

from dateutil.relativedelta import relativedelta

from odoo import _, api, exceptions, fields, models
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from odoo.tools import safe_eval
from odoo.http import request

_logger = logging.getLogger(__name__)

DATE_RANGE_FUNCTION = {
    'minutes': lambda interval: relativedelta(minutes=interval),
    'hour': lambda interval: relativedelta(hours=interval),
    'day': lambda interval: relativedelta(days=interval),
    'month': lambda interval: relativedelta(months=interval),
    False: lambda interval: relativedelta(0),
}

DATE_RANGE_FACTOR = {
    'minutes': 1,
    'hour': 60,
    'day': 24 * 60,
    'month': 30 * 24 * 60,
    False: 0,
}

CREATE_TRIGGERS = [
    'on_create',

    'on_create_or_write',
    'on_priority_set',
    'on_stage_set',
    'on_state_set',
    'on_tag_set',
    'on_user_set',
]

WRITE_TRIGGERS = [
    'on_write',
    'on_archive',
    'on_unarchive',

    'on_create_or_write',
    'on_priority_set',
    'on_stage_set',
    'on_state_set',
    'on_tag_set',
    'on_user_set',
]

MAIL_TRIGGERS = ("on_message_received", "on_message_sent")

CREATE_WRITE_SET = set(CREATE_TRIGGERS + WRITE_TRIGGERS)

TIME_TRIGGERS = [
    'on_time',
    'on_time_created',
    'on_time_updated',
]

def get_webhook_request_payload():
    if not request:
        return None
    try:
        payload = request.get_json_data()
    except ValueError:
        payload = {**request.httprequest.args}
    return payload

class BaseAutomation(models.Model):
    _name = 'base.automation'
    _description = 'Automation Rule'

    name = fields.Char(string="Automation Rule Name", required=True, translate=True)
    description = fields.Html(string="Description")
    model_id = fields.Many2one(
        "ir.model", string="Model", required=True, ondelete="cascade", help="Model on which the automation rule runs."
    )
    model_name = fields.Char(related="model_id.model", string="Model Name", readonly=True, inverse="_inverse_model_name")
    model_is_mail_thread = fields.Boolean(related="model_id.is_mail_thread")
    action_server_ids = fields.One2many("ir.actions.server", "base_automation_id",
        context={'default_usage': 'base_automation'},
        string="Actions",
        compute="_compute_action_server_ids",
        store=True,
        readonly=False,
    )
    url = fields.Char(compute='_compute_url')
    webhook_uuid = fields.Char(string="Webhook UUID", readonly=True, copy=False, default=lambda self: str(uuid4()))
    record_getter = fields.Char(default="model.env[payload.get('_model')].browse(int(payload.get('_id')))",
                                help="This code will be run to find on which record the automation rule should be run.")
    log_webhook_calls = fields.Boolean(string="Log Calls", default=False)
    active = fields.Boolean(default=True, help="When unchecked, the rule is hidden and will not be executed.")

    @api.constrains("trigger", "model_id")
    def _check_trigger(self):
        for automation in self:
            if automation.trigger in MAIL_TRIGGERS and not automation.model_id.is_mail_thread:
                raise exceptions.ValidationError(_("Mail event can not be configured on model %s. Only models with discussion feature can be used.", automation.model_id.name))

    trigger = fields.Selection(
        [
            ('on_stage_set', "Stage is set to"),
            ('on_user_set', "User is set"),
            ('on_tag_set', "Tag is added"),
            ('on_state_set', "State is set to"),
            ('on_priority_set', "Priority is set to"),
            ('on_archive', "On archived"),
            ('on_unarchive', "On unarchived"),
            ('on_create_or_write', "On save"),
            ('on_create', "On creation"),  # deprecated, use 'on_create_or_write' instead
            ('on_write', "On update"),  # deprecated, use 'on_create_or_write' instead

            ('on_unlink', "On deletion"),
            ('on_change', "On UI change"),

            ('on_time', "Based on date field"),
            ('on_time_created', "After creation"),
            ('on_time_updated', "After last update"),

            ("on_message_received", "On incoming message"),
            ("on_message_sent", "On outgoing message"),

            ('on_webhook', "On webhook"),
        ], string='Trigger',
        compute='_compute_trigger_and_trigger_field_ids', readonly=False, store=True, required=True)
    trg_selection_field_id = fields.Many2one(
        'ir.model.fields.selection',
        string='Trigger Field',
        domain="[('field_id', 'in', trigger_field_ids)]",
        compute='_compute_trg_selection_field_id',
        readonly=False, store=True,
        help="Some triggers need a reference to a selection field. This field is used to store it.")
    trg_field_ref_model_name = fields.Char(
        string='Trigger Field Model',
        compute='_compute_trg_field_ref__model_and_display_names')
    trg_field_ref = fields.Many2oneReference(
        model_field='trg_field_ref_model_name',
        compute='_compute_trg_field_ref',
        string='Trigger Reference',
        readonly=False,
        store=True,
        help="Some triggers need a reference to another field. This field is used to store it.")
    trg_field_ref_display_name = fields.Char(
        string='Trigger Reference Display Name',
        compute='_compute_trg_field_ref__model_and_display_names')
    trg_date_id = fields.Many2one(
        'ir.model.fields', string='Trigger Date',
        compute='_compute_trg_date_id',
        readonly=False, store=True,
        domain="[('model_id', '=', model_id), ('ttype', 'in', ('date', 'datetime'))]",
        help="""When should the condition be triggered.
                If present, will be checked by the scheduler. If empty, will be checked at creation and update.""")
    trg_date_range = fields.Integer(
        string='Delay after trigger date',
        compute='_compute_trg_date_range_data',
        readonly=False, store=True,
        help="Delay after the trigger date. "
        "You can put a negative number if you need a delay before the "
        "trigger date, like sending a reminder 15 minutes before a meeting.")
    trg_date_range_type = fields.Selection(
        [('minutes', 'Minutes'), ('hour', 'Hours'), ('day', 'Days'), ('month', 'Months')],
        string='Delay type',
        compute='_compute_trg_date_range_data',
        readonly=False, store=True)
    trg_date_calendar_id = fields.Many2one(
        "resource.calendar", string='Use Calendar',
        compute='_compute_trg_date_calendar_id',
        readonly=False, store=True,
        help="When calculating a day-based timed condition, it is possible"
             "to use a calendar to compute the date based on working days.")
    filter_pre_domain = fields.Char(
        string='Before Update Domain',
        compute='_compute_filter_pre_domain',
        readonly=False, store=True,
        help="If present, this condition must be satisfied before the update of the record.")
    filter_domain = fields.Char(
        string='Apply on',
        help="If present, this condition must be satisfied before executing the automation rule.",
        compute='_compute_filter_domain',
        readonly=False, store=True
    )
    last_run = fields.Datetime(readonly=True, copy=False)
    on_change_field_ids = fields.Many2many(
        "ir.model.fields",
        relation="base_automation_onchange_fields_rel",
        compute='_compute_on_change_field_ids',
        readonly=False, store=True,
        string="On Change Fields Trigger",
        help="Fields that trigger the onchange.",
    )
    trigger_field_ids = fields.Many2many(
        'ir.model.fields', string='Trigger Fields',
        compute='_compute_trigger_and_trigger_field_ids', readonly=False, store=True,
        help="The automation rule will be triggered if and only if one of these fields is updated."
             "If empty, all fields are watched.")
    least_delay_msg = fields.Char(compute='_compute_least_delay_msg')

    # which fields have an impact on the registry and the cron
    CRITICAL_FIELDS = ['model_id', 'active', 'trigger', 'on_change_field_ids']
    RANGE_FIELDS = ['trg_date_range', 'trg_date_range_type']

    @api.constrains('model_id', 'action_server_ids')
    def _check_action_server_model(self):
        for rule in self:
            failing_actions = rule.action_server_ids.filtered(
                lambda action: action.model_id != rule.model_id
            )
            if failing_actions:
                raise exceptions.ValidationError(
                    _('Target model of actions %(action_names)s are different from rule model.',
                      action_names=', '.join(failing_actions.mapped('name'))
                     )
                )
    @api.depends("trigger", "webhook_uuid")
    def _compute_url(self):
        for automation in self:
            if automation.trigger != "on_webhook":
                automation.url = ""
            else:
                automation.url = "%s/web/hook/%s" % (automation.get_base_url(), automation.webhook_uuid)

    def _inverse_model_name(self):
        for rec in self:
            rec.model_id = self.env["ir.model"]._get(rec.model_name)

    @api.constrains('trigger', 'action_server_ids')
    def _check_trigger_state(self):
        for record in self:
            no_code_actions = record.action_server_ids.filtered(lambda a: a.state != 'code')
            if record.trigger == 'on_change' and no_code_actions:
                raise exceptions.ValidationError(
                    _('"On live update" automation rules can only be used with "Execute Python Code" action type.')
                )
            mail_actions = record.action_server_ids.filtered(
                lambda a: a.state in ['mail_post', 'followers', 'next_activity']
            )
            if record.trigger == 'on_unlink' and mail_actions:
                raise exceptions.ValidationError(
                    _('Email, follower or activity action types cannot be used when deleting records, '
                      'as there are no more records to apply these changes to!')
                )

    @api.depends('model_id')
    def _compute_action_server_ids(self):
        """ When changing / setting model, remove actions that are not targeting
        the same model anymore. """
        for rule in self.filtered('model_id'):
            actions_to_remove = rule.action_server_ids.filtered(
                lambda action: action.model_id != rule.model_id
            )
            if actions_to_remove:
                rule.action_server_ids = [(3, action.id) for action in actions_to_remove]

    @api.depends('trigger', 'trigger_field_ids')
    def _compute_trg_date_id(self):
        to_reset = self.filtered(lambda a: a.trigger not in TIME_TRIGGERS or len(a.trigger_field_ids) != 1)
        to_reset.trg_date_id = False
        for record in (self - to_reset):
            record.trg_date_id = record.trigger_field_ids

    @api.depends('trigger')
    def _compute_trg_date_range_data(self):
        to_reset = self.filtered(lambda a: a.trigger not in TIME_TRIGGERS)
        to_reset.trg_date_range = False
        to_reset.trg_date_range_type = False
        (self - to_reset).filtered(lambda a: not a.trg_date_range_type).trg_date_range_type = 'hour'

    @api.depends('trigger', 'trg_date_id', 'trg_date_range_type')
    def _compute_trg_date_calendar_id(self):
        to_reset = self.filtered(
            lambda a: a.trigger not in TIME_TRIGGERS or not a.trg_date_id or a.trg_date_range_type != 'day'
        )
        to_reset.trg_date_calendar_id = False

    @api.depends('trigger', 'trigger_field_ids')
    def _compute_trg_selection_field_id(self):
        to_reset = self.filtered(lambda a: a.trigger not in ['on_priority_set', 'on_state_set'] or len(a.trigger_field_ids) != 1)
        to_reset.trg_selection_field_id = False
        for automation in (self - to_reset):
            # always re-assign to an empty value to make sure we have no discrepencies
            automation.trg_selection_field_id = self.env['ir.model.fields.selection']

    @api.depends('trigger', 'trigger_field_ids')
    def _compute_trg_field_ref(self):
        to_reset = self.filtered(lambda a: a.trigger not in ['on_stage_set', 'on_tag_set'] or len(a.trigger_field_ids) != 1)
        to_reset.trg_field_ref = False
        for automation in (self - to_reset):
            relation = automation.trigger_field_ids.relation
            automation.trg_field_ref_model_name = relation
            # always re-assign to an empty value to make sure we have no discrepencies
            automation.trg_field_ref = self.env[relation]

    @api.depends('trg_field_ref', 'trigger_field_ids')
    def _compute_trg_field_ref__model_and_display_names(self):
        to_compute = self.filtered(lambda a: a.trigger in ['on_stage_set', 'on_tag_set'] and a.trg_field_ref is not False)
        # wondering why we check based on 'is not'? Because the ref could be an empty recordset
        # and we still need to introspec on the model in that case - not just ignore it
        to_reset = (self - to_compute)
        to_reset.trg_field_ref_model_name = False
        to_reset.trg_field_ref_display_name = False
        for automation in to_compute:
            relation = automation.trigger_field_ids.relation
            if not relation:
                automation.trg_field_ref_model_name = False
                automation.trg_field_ref_display_name = False
                continue
            resid = automation.trg_field_ref
            automation.trg_field_ref_model_name = relation
            automation.trg_field_ref_display_name = self.env[relation].browse(resid).display_name

    @api.depends('trigger', 'trigger_field_ids', 'trg_field_ref')
    def _compute_filter_pre_domain(self):
        to_reset = self.filtered(lambda a: a.trigger != 'on_tag_set' or len(a.trigger_field_ids) != 1)
        to_reset.filter_pre_domain = False
        for automation in (self - to_reset):
            field = automation.trigger_field_ids.name
            value = automation.trg_field_ref
            automation.filter_pre_domain = f"[('{field}', 'not in', [{value}])]" if value else False

    @api.depends('trigger', 'trigger_field_ids', 'trg_selection_field_id', 'trg_field_ref')
    def _compute_filter_domain(self):
        for record in self:
            trigger_fields_count = len(record.trigger_field_ids)
            if trigger_fields_count == 0:
                record.filter_domain = False

            elif trigger_fields_count == 1:
                field = record.trigger_field_ids.name
                trigger = record.trigger
                if trigger in ['on_state_set', 'on_priority_set']:
                    value = record.trg_selection_field_id.value
                    record.filter_domain = f"[('{field}', '=', '{value}')]" if value else False
                elif trigger == 'on_stage_set':
                    value = record.trg_field_ref
                    record.filter_domain = f"[('{field}', '=', {value})]" if value else False
                elif trigger == 'on_tag_set':
                    value = record.trg_field_ref
                    record.filter_domain = f"[('{field}', 'in', [{value}])]" if value else False
                elif trigger == 'on_user_set':
                    record.filter_domain = f"[('{field}', '!=', False)]"
                elif trigger in ['on_archive', 'on_unarchive']:
                    record.filter_domain = f"[('{field}', '=', {trigger == 'on_unarchive'})]"
                else:
                    record.filter_domain = False

    @api.depends('model_id', 'trigger')
    def _compute_on_change_field_ids(self):
        to_reset = self.filtered(lambda a: a.trigger != 'on_change')
        to_reset.on_change_field_ids = False
        for record in (self - to_reset).filtered('on_change_field_ids'):
            record.on_change_field_ids = record.on_change_field_ids.filtered(lambda field: field.model_id == record.model_id)

    @api.depends('model_id', 'trigger')
    def _compute_trigger_and_trigger_field_ids(self):
        for automation in self:
            domain = [('model_id', '=', automation.model_id.id)]
            if automation.trigger == 'on_stage_set':
                domain += [('ttype', '=', 'many2one'), ('name', 'in', ['stage_id', 'x_studio_stage_id'])]
            elif automation.trigger == 'on_tag_set':
                domain += [('ttype', '=', 'many2many'), ('name', 'in', ['tag_ids', 'x_studio_tag_ids'])]
            elif automation.trigger == 'on_priority_set':
                domain += [('ttype', '=', 'selection'), ('name', 'in', ['priority', 'x_studio_priority'])]
            elif automation.trigger == 'on_state_set':
                domain += [('ttype', '=', 'selection'), ('name', 'in', ['state', 'x_studio_state'])]
            elif automation.trigger == 'on_user_set':
                domain += [
                    ('relation', '=', 'res.users'),
                    ('ttype', 'in', ['many2one', 'many2many']),
                    ('name', 'in', ['user_id', 'user_ids', 'x_studio_user_id', 'x_studio_user_ids']),
                ]
            elif automation.trigger in ['on_archive', 'on_unarchive']:
                domain += [('ttype', '=', 'boolean'), ('name', 'in', ['active', 'x_active'])]
            elif automation.trigger == 'on_time_created':
                domain += [('ttype', '=', 'datetime'), ('name', '=', 'create_date')]
            elif automation.trigger == 'on_time_updated':
                domain += [('ttype', '=', 'datetime'), ('name', '=', 'write_date')]
            else:
                automation.trigger_field_ids = False
                continue
            if automation.model_id.is_mail_thread and automation.trigger in MAIL_TRIGGERS:
                continue

            automation.trigger_field_ids = self.env['ir.model.fields'].search(domain, limit=1)
            automation.trigger = False if not automation.trigger_field_ids else automation.trigger

    @api.onchange('trigger', 'action_server_ids')
    def _onchange_trigger_or_actions(self):
        no_code_actions = self.action_server_ids.filtered(lambda a: a.state != 'code')
        if self.trigger == 'on_change' and len(no_code_actions) > 0:
            trigger_field = self._fields['trigger']
            action_states = dict(self.action_server_ids._fields['state']._description_selection(self.env))
            return {'warning': {
                'title': _("Warning"),
                'message': _(
                    "The \"%(trigger_value)s\" %(trigger_label)s can only be "
                    "used with the \"%(state_value)s\" action type",
                    trigger_value=dict(trigger_field._description_selection(self.env))['on_change'],
                    trigger_label=trigger_field._description_string(self.env),
                    state_value=action_states['code'])
            }}

        MAIL_STATES = ('mail_post', 'followers', 'next_activity')
        mail_actions = self.action_server_ids.filtered(lambda a: a.state in MAIL_STATES)
        if self.trigger == 'on_unlink' and len(mail_actions) > 0:
            return {'warning': {
                'title': _("Warning"),
                'message': _(
                    "You cannot send an email, add followers or create an activity "
                    "for a deleted record.  It simply does not work."
                ),
            }}

    @api.model_create_multi
    def create(self, vals_list):
        base_automations = super(BaseAutomation, self).create(vals_list)
        self._update_cron()
        self._update_registry()
        return base_automations

    def write(self, vals):
        res = super(BaseAutomation, self).write(vals)
        if set(vals).intersection(self.CRITICAL_FIELDS):
            self._update_cron()
            self._update_registry()
        elif set(vals).intersection(self.RANGE_FIELDS):
            self._update_cron()
        return res

    def unlink(self):
        res = super(BaseAutomation, self).unlink()
        self._update_cron()
        self._update_registry()
        return res

    def action_rotate_webhook_uuid(self):
        for automation in self:
            automation.webhook_uuid = str(uuid4())

    def action_view_webhook_logs(self):
        self.ensure_one()
        return {
            'type': 'ir.actions.act_window',
            'name': _('Webhook Logs'),
            'res_model': 'ir.logging',
            'view_mode': 'tree,form',
            'domain': [('path', '=', "base_automation(%s)" % self.id)],
        }

    def _prepare_loggin_values(self, **values):
        self.ensure_one()
        defaults = {
            'name': _("Webhook Log"),
            'type': 'server',
            'dbname': self._cr.dbname,
            'level': 'INFO',
            'path': "base_automation(%s)" % self.id,
            'func': '',
            'line': ''
        }
        defaults.update(**values)
        return defaults

    def _execute_webhook(self, payload):
        """ Execute the webhook for the given payload.
        The payload is a dictionnary that can be used by the `record_getter` to
        identify the record on which the automation should be run.
        """
        self.ensure_one()
        ir_logging_sudo = self.env['ir.logging'].sudo()

        # info logging is done by the ir.http logger
        msg = "Webhook #%s triggered with payload %s"
        msg_args = (self.id, payload)
        _logger.debug(msg, *msg_args)
        if self.log_webhook_calls:
            ir_logging_sudo.create(self._prepare_loggin_values(message=msg % msg_args))

        record = self.env[self.model_name]
        if self.record_getter:
            try:
                record = safe_eval.safe_eval(self.record_getter, self._get_eval_context(payload=payload))
            except Exception as e: # noqa: BLE001
                msg = "Webhook #%s could not be triggered because the record_getter failed:\n%s"
                msg_args = (self.id, traceback.format_exc())
                _logger.warning(msg, *msg_args)
                if self.log_webhook_calls:
                    ir_logging_sudo.create(self._prepare_loggin_values(message=msg % msg_args, level="ERROR"))
                raise e

        if not record.exists():
            msg = "Webhook #%s could not be triggered because no record to run it on was found."
            msg_args = (self.id,)
            _logger.warning(msg, *msg_args)
            if self.log_webhook_calls:
                ir_logging_sudo.create(self._prepare_loggin_values(message=msg % msg_args, level="ERROR"))
            raise exceptions.ValidationError(_("No record to run the automation on was found."))

        try:
            return self._process(record)
        except Exception as e: # noqa: BLE001
            msg = "Webhook #%s failed with error:\n%s"
            msg_args = (self.id, traceback.format_exc())
            _logger.warning(msg, *msg_args)
            if self.log_webhook_calls:
                ir_logging_sudo.create(self._prepare_loggin_values(message=msg % msg_args, level="ERROR"))
            raise e

    def _update_cron(self):
        """ Activate the cron job depending on whether there exists automation rules
        based on time conditions.  Also update its frequency according to
        the smallest automation delay, or restore the default 4 hours if there
        is no time based automation.
        """
        cron = self.env.ref('base_automation.ir_cron_data_base_automation_check', raise_if_not_found=False)
        if cron:
            automations = self.with_context(active_test=True).search([('trigger', 'in', TIME_TRIGGERS)])
            cron.try_write({
                'active': bool(automations),
                'interval_type': 'minutes',
                'interval_number': self._get_cron_interval(automations),
            })

    def _update_registry(self):
        """ Update the registry after a modification on automation rules. """
        if self.env.registry.ready and not self.env.context.get('import_file'):
            # re-install the model patches, and notify other workers
            self._unregister_hook()
            self._register_hook()
            self.env.registry.registry_invalidated = True

    def _get_actions(self, records, triggers):
        """ Return the automations of the given triggers for records' model. The
            returned automations' context contain an object to manage processing.
        """
        # Note: we keep the old action naming for the method and context variable
        # to avoid breaking existing code/downstream modules
        if '__action_done' not in self._context:
            self = self.with_context(__action_done={})
        domain = [('model_name', '=', records._name), ('trigger', 'in', triggers)]
        automations = self.with_context(active_test=True).sudo().search(domain)
        return automations.with_env(self.env)

    def _get_eval_context(self, payload=None):
        """ Prepare the context used when evaluating python code
            :returns: dict -- evaluation context given to safe_eval
        """
        self.ensure_one()
        model = self.env[self.model_name]
        eval_context = {
            'datetime': safe_eval.datetime,
            'dateutil': safe_eval.dateutil,
            'time': safe_eval.time,
            'uid': self.env.uid,
            'user': self.env.user,
            'model': model,
        }
        if payload is not None:
            eval_context['payload'] = payload
        return eval_context

    def _get_cron_interval(self, automations=None):
        """ Return the expected time interval used by the cron, in minutes. """
        def get_delay(rec):
            return rec.trg_date_range * DATE_RANGE_FACTOR[rec.trg_date_range_type]

        if automations is None:
            automations = self.with_context(active_test=True).search([('trigger', 'in', TIME_TRIGGERS)])

        # Minimum 1 minute, maximum 4 hours, 10% tolerance
        delay = min(automations.mapped(get_delay), default=0)
        return min(max(1, delay // 10), 4 * 60) if delay else 4 * 60

    def _compute_least_delay_msg(self):
        msg = _("Note that this automation rule can be triggered up to %d minutes after its schedule.")
        self.least_delay_msg = msg % self._get_cron_interval()

    def _filter_pre(self, records):
        """ Filter the records that satisfy the precondition of automation ``self``. """
        self_sudo = self.sudo()
        if self_sudo.filter_pre_domain and records:
            domain = safe_eval.safe_eval(self_sudo.filter_pre_domain, self._get_eval_context())
            return records.sudo().filtered_domain(domain).with_env(records.env)
        else:
            return records

    def _filter_post(self, records):
        return self._filter_post_export_domain(records)[0]

    def _filter_post_export_domain(self, records):
        """ Filter the records that satisfy the postcondition of automation ``self``. """
        self_sudo = self.sudo()
        if self_sudo.filter_domain and records:
            domain = safe_eval.safe_eval(self_sudo.filter_domain, self._get_eval_context())
            return records.sudo().filtered_domain(domain).with_env(records.env), domain
        else:
            return records, None

    @api.model
    def _add_postmortem(self, e):
        if self.user_has_groups('base.group_user'):
            e.context = {}
            e.context['exception_class'] = 'base_automation'
            e.context['base_automation'] = {
                'id': self.id,
                'name': self.sudo().name,
            }

    def _process(self, records, domain_post=None):
        """ Process automation ``self`` on the ``records`` that have not been done yet. """
        # filter out the records on which self has already been done
        automation_done = self._context.get('__action_done', {})
        records_done = automation_done.get(self, records.browse())
        records -= records_done
        if not records:
            return

        # mark the remaining records as done (to avoid recursive processing)
        automation_done = dict(automation_done)
        automation_done[self] = records_done + records
        self = self.with_context(__action_done=automation_done)
        records = records.with_context(__action_done=automation_done)

        # modify records
        if 'date_automation_last' in records._fields:
            records.date_automation_last = fields.Datetime.now()

        # prepare the contexts for server actions
        contexts = []
        for record in records:
            # we process the automation if any watched field has been modified
            if self._check_trigger_fields(record):
                contexts.append({
                    'active_model': record._name,
                    'active_ids': record.ids,
                    'active_id': record.id,
                    'domain_post': domain_post,
                })

        # execute server actions
        for action in self.sudo().action_server_ids:
            for ctx in contexts:
                try:
                    action.with_context(**ctx).run()
                except Exception as e:
                    self._add_postmortem(e)
                    raise

    def _check_trigger_fields(self, record):
        """ Return whether any of the trigger fields has been modified on ``record``. """
        self_sudo = self.sudo()
        if not self_sudo.trigger_field_ids:
            # all fields are implicit triggers
            return True

        if not self._context.get('old_values'):
            # this is a create: all fields are considered modified
            return True

        # Note: old_vals are in the format of read()
        old_vals = self._context['old_values'].get(record.id, {})

        def differ(name):
            field = record._fields[name]
            return (
                name in old_vals and
                field.convert_to_cache(record[name], record, validate=False) !=
                field.convert_to_cache(old_vals[name], record, validate=False)
            )
        return any(differ(field.name) for field in self_sudo.trigger_field_ids)

    def _register_hook(self):
        """ Patch models that should trigger action rules based on creation,
            modification, deletion of records and form onchanges.
        """
        #
        # Note: the patched methods must be defined inside another function,
        # otherwise their closure may be wrong. For instance, the function
        # create refers to the outer variable 'create', which you expect to be
        # bound to create itself. But that expectation is wrong if create is
        # defined inside a loop; in that case, the variable 'create' is bound to
        # the last function defined by the loop.
        #

        def make_create():
            """ Instanciate a create method that processes automation rules. """
            @api.model_create_multi
            def create(self, vals_list, **kw):
                # retrieve the automation rules to possibly execute
                automations = self.env['base.automation']._get_actions(self, CREATE_TRIGGERS)
                if not automations:
                    return create.origin(self, vals_list, **kw)
                # call original method
                records = create.origin(self.with_env(automations.env), vals_list, **kw)
                # check postconditions, and execute actions on the records that satisfy them
                for automation in automations.with_context(old_values=None):
                    automation._process(automation._filter_post(records))
                return records.with_env(self.env)

            return create

        def make_write():
            """ Instanciate a write method that processes automation rules. """
            def write(self, vals, **kw):
                # retrieve the automation rules to possibly execute
                automations = self.env['base.automation']._get_actions(self, WRITE_TRIGGERS)
                if not (automations and self):
                    return write.origin(self, vals, **kw)
                records = self.with_env(automations.env).filtered('id')
                # check preconditions on records
                pre = {a: a._filter_pre(records) for a in automations}
                # read old values before the update
                old_values = {
                    old_vals.pop('id'): old_vals
                    for old_vals in (records.read(list(vals)) if vals else [])
                }
                # call original method
                write.origin(self.with_env(automations.env), vals, **kw)
                # check postconditions, and execute actions on the records that satisfy them
                for automation in automations.with_context(old_values=old_values):
                    records, domain_post = automation._filter_post_export_domain(pre[automation])
                    automation._process(records, domain_post=domain_post)
                return True

            return write

        def make_compute_field_value():
            """ Instanciate a compute_field_value method that processes automation rules. """
            #
            # Note: This is to catch updates made by field recomputations.
            #
            def _compute_field_value(self, field):
                # determine fields that may trigger an automation
                stored_fields = [f for f in self.pool.field_computed[field] if f.store]
                if not any(stored_fields):
                    return _compute_field_value.origin(self, field)
                # retrieve the action rules to possibly execute
                automations = self.env['base.automation']._get_actions(self, WRITE_TRIGGERS)
                records = self.filtered('id').with_env(automations.env)
                if not (automations and records):
                    _compute_field_value.origin(self, field)
                    return True
                # check preconditions on records
                pre = {a: a._filter_pre(records) for a in automations}
                # read old values before the update
                old_values = {
                    old_vals.pop('id'): old_vals
                    for old_vals in (records.read([f.name for f in stored_fields]))
                }
                # call original method
                _compute_field_value.origin(self, field)
                # check postconditions, and execute automations on the records that satisfy them
                for automation in automations.with_context(old_values=old_values):
                    records, domain_post = automation._filter_post_export_domain(pre[automation])
                    automation._process(records, domain_post=domain_post)
                return True

            return _compute_field_value

        def make_unlink():
            """ Instanciate an unlink method that processes automation rules. """
            def unlink(self, **kwargs):
                # retrieve the action rules to possibly execute
                automations = self.env['base.automation']._get_actions(self, ['on_unlink'])
                records = self.with_env(automations.env)
                # check conditions, and execute actions on the records that satisfy them
                for automation in automations:
                    automation._process(automation._filter_post(records))
                # call original method
                return unlink.origin(self, **kwargs)

            return unlink

        def make_onchange(automation_rule_id):
            """ Instanciate an onchange method for the given automation rule. """
            def base_automation_onchange(self):
                automation_rule = self.env['base.automation'].browse(automation_rule_id)
                result = {}
                actions = automation_rule.sudo().action_server_ids.with_context(
                    active_model=self._name,
                    active_id=self._origin.id,
                    active_ids=self._origin.ids,
                    onchange_self=self,
                )
                for action in actions:
                    try:
                        res = action.run()
                    except Exception as e:
                        automation_rule._add_postmortem(e)
                        raise

                    if res:
                        if 'value' in res:
                            res['value'].pop('id', None)
                            self.update({key: val for key, val in res['value'].items() if key in self._fields})
                        if 'domain' in res:
                            result.setdefault('domain', {}).update(res['domain'])
                        if 'warning' in res:
                            result['warning'] += res["warning"]
                return result

            return base_automation_onchange

        patched_models = defaultdict(set)

        def patch(model, name, method):
            """ Patch method `name` on `model`, unless it has been patched already. """
            if model not in patched_models[name]:
                patched_models[name].add(model)
                ModelClass = type(model)
                method.origin = getattr(ModelClass, name)
                setattr(ModelClass, name, method)

        # retrieve all actions, and patch their corresponding model
        for automation_rule in self.with_context({}).search([]):
            Model = self.env.get(automation_rule.model_name)

            # Do not crash if the model of the base_action_rule was uninstalled
            if Model is None:
                _logger.warning(
                    "Automation rule with name '%s' (ID %d) depends on model %s (ID: %d)",
                    automation_rule.id,
                    automation_rule.name,
                    automation_rule.model_name,
                    automation_rule.model_id.id)
                continue

            if automation_rule.trigger in CREATE_WRITE_SET:
                if automation_rule.trigger in CREATE_TRIGGERS:
                    patch(Model, 'create', make_create())
                if automation_rule.trigger in WRITE_TRIGGERS:
                    patch(Model, 'write', make_write())
                    patch(Model, '_compute_field_value', make_compute_field_value())

            elif automation_rule.trigger == 'on_unlink':
                patch(Model, 'unlink', make_unlink())

            elif automation_rule.trigger == 'on_change':
                # register an onchange method for the automation_rule
                method = make_onchange(automation_rule.id)
                for field in automation_rule.on_change_field_ids:
                    Model._onchange_methods[field.name].append(method)

            if automation_rule.model_id.is_mail_thread and automation_rule.trigger in MAIL_TRIGGERS:
                def _message_post(self, *args, **kwargs):
                    message = _message_post.origin(self, *args, **kwargs)
                    # Don't execute automations for a message emitted during
                    # the run of automations for a real message
                    # Don't execute if we know already that a message is only internal
                    message_sudo = message.sudo().with_context(active_test=False)
                    if "__action_done"  in self.env.context or message_sudo.is_internal or message_sudo.subtype_id.internal:
                        return message
                    if message_sudo.message_type in ('notification', 'auto_comment', 'user_notification'):
                        return message

                    # always execute actions when the author is a customer
                    mail_trigger = "on_message_received" if message_sudo.author_id.partner_share else "on_message_sent"
                    automations = self.env['base.automation']._get_actions(self, [mail_trigger])
                    for automation in automations.with_context(old_values=None):
                        records = automation._filter_pre(self)
                        automation._process(records)

                    return message

                patch(Model, "message_post", _message_post)

    def _unregister_hook(self):
        """ Remove the patches installed by _register_hook() """
        NAMES = ['create', 'write', '_compute_field_value', 'unlink', '_onchange_methods', "message_post"]
        for Model in self.env.registry.values():
            for name in NAMES:
                try:
                    delattr(Model, name)
                except AttributeError:
                    pass

    @api.model
    def _check_delay(self, automation, record, record_dt):
        if automation.trg_date_calendar_id and automation.trg_date_range_type == 'day':
            return automation.trg_date_calendar_id.plan_days(
                automation.trg_date_range,
                fields.Datetime.from_string(record_dt),
                compute_leaves=True,
            )
        else:
            delay = DATE_RANGE_FUNCTION[automation.trg_date_range_type](automation.trg_date_range)
            return fields.Datetime.from_string(record_dt) + delay

    @api.model
    def _check(self, automatic=False, use_new_cursor=False):
        """ This Function is called by scheduler. """
        if '__action_done' not in self._context:
            self = self.with_context(__action_done={})

        # retrieve all the automation rules to run based on a timed condition
        for automation in self.with_context(active_test=True).search([('trigger', 'in', TIME_TRIGGERS)]):
            _logger.info("Starting time-based automation rule `%s`.", automation.name)
            last_run = fields.Datetime.from_string(automation.last_run) or datetime.datetime.utcfromtimestamp(0)
            eval_context = automation._get_eval_context()

            # retrieve all the records that satisfy the automation's condition
            domain = []
            context = dict(self._context)
            if automation.filter_domain:
                domain = safe_eval.safe_eval(automation.filter_domain, eval_context)
            records = self.env[automation.model_name].with_context(context).search(domain)

            def get_record_dt(record):
                # determine when automation should occur for the records
                if automation.trg_date_id.name == "date_automation_last" and "create_date" in records._fields:
                    return record[automation.trg_date_id.name] or record.create_date
                else:
                    return record[automation.trg_date_id.name]

            # process action on the records that should be executed
            now = datetime.datetime.now()
            for record in records:
                record_dt = get_record_dt(record)
                if not record_dt:
                    continue
                action_dt = self._check_delay(automation, record, record_dt)
                if last_run <= action_dt < now:
                    try:
                        automation._process(record)
                    except Exception:
                        _logger.error(traceback.format_exc())

            automation.write({'last_run': now.strftime(DEFAULT_SERVER_DATETIME_FORMAT)})
            _logger.info("Time-based automation rule `%s` done.", automation.name)

            if automatic:
                # auto-commit for batch processing
                self._cr.commit()


import datetime
import glob
import io
import os
import socketserver
import tempfile
import threading
import unittest

from spaghettilogger import ChatLogger, Client

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass

class TestLogger(unittest.TestCase):
    def test_logger(self):
        thread_event = threading.Event()

        class Handler(socketserver.StreamRequestHandler):
            def handle(self):
                nick = self.rfile.readline()
                assert nick.startswith(b'NICK'), nick

                user = self.rfile.readline()
                assert user.startswith(b'USER'), user

                self.wfile.write(
                    b':tmi.twitch.tv 001 twitch_username :Welcome, GLHF!\n'
                    b':tmi.twitch.tv 002 twitch_username :Your host is tmi.twitch.tv\n'
                    b':tmi.twitch.tv 003 twitch_username :This server is rather new\n'
                    b':tmi.twitch.tv 004 twitch_username :-\n'
                    b':tmi.twitch.tv 375 twitch_username :-\n'
                    b':tmi.twitch.tv 372 twitch_username :You are in a maze of twisty passages, all alike.\n'
                    b':tmi.twitch.tv 376 twitch_username :>\n'
                )

                for dummy in range(3):
                    caps = self.rfile.readline()
                    assert caps.startswith(b'CAP'), caps

                join = self.rfile.readline()
                assert join.startswith(b'JOIN'), join

                self.wfile.write(b':twitch_username!twitch_username@twitch_username.tmi.twitch.tv JOIN #test_channel\n')
                self.wfile.write(b':justinfan28394!justinfan28394@justinfan28394.tmi.twitch.tv JOIN #test_channel\n')
                self.wfile.write(b':twitch_username!twitch_username@twitch_username.tmi.twitch.tv PART #test_channel\n')
                self.wfile.write(b':jtv MODE #test_channel +o operator_user\n')
                self.wfile.write(b'@msg-id=slow_off :tmi.twitch.tv NOTICE #test_channel :This room is no longer in slow mode.\n')
                self.wfile.write(b'@ban-duration=600;ban-reason= :tmi.twitch.tv CLEARCHAT #test_channel :naughty_user\n')
                self.wfile.write(b':tmi.twitch.tv CLEARCHAT #test_channel\n')
                self.wfile.write('@badges=;color=;display-name=Naughty_User;emotes=;mod=0;room-id=1337;subscriber=0;turbo=1;user-id=1337;user-type= :naughty_user!naughty_user@naughty_user.tmi.twitch.tv PRIVMSG #test_channel :☺\n'.encode('utf8'))
                self.wfile.write(b'@badges=;color=;display-name=Naughty_User;emotes=;mod=0;room-id=1337;subscriber=0;turbo=1;user-id=1337;user-type= :naughty_user!naughty_user@naughty_user.tmi.twitch.tv PRIVMSG #test_channel :\x01ACTION OneHand\x01\n')
                self.wfile.write(b'@badges=global_mod/1,turbo/1;color=#0D4200;display-name=TWITCH_UserNaME;emotes=25:0-4,12-16/1902:6-10;mod=0;room-id=1337;subscriber=0;turbo=1;user-id=1337;user-type=global_mod :twitch_username!twitch_username@twitch_username.tmi.twitch.tv PRIVMSG #test_channel :Kappa Keepo Kappa\n')
                self.wfile.write(b'@badges=staff/1,broadcaster/1,turbo/1;color=#008000;display-name=TWITCH_UserName;emotes=;mod=0;msg-id=resub;msg-param-months=6;room-id=1337;subscriber=1;system-msg=TWITCH_UserName\shas\ssubscribed\sfor\s6\smonths!;login=twitch_username;turbo=1;user-id=1337;user-type=staff :tmi.twitch.tv USERNOTICE #test_channel :Great stream -- keep it up!\n')
                self.wfile.write(b'@login=ronni;target-msg-id=abc-123-def :tmi.twitch.tv CLEARMSG #test_channel :HeyGuys\n')

                thread_event.set()

        server = ThreadedTCPServer(('localhost', 0), Handler)
        port = server.server_address[1]

        with tempfile.TemporaryDirectory() as temp_dir:
            log_dir = os.path.join(temp_dir, 'logs')

            os.mkdir(log_dir)

            channels_file_path = os.path.join(temp_dir, 'channels.txt')

            with open(channels_file_path, 'w') as file:
                file.write('#test_channel\n')

            chat_logger = ChatLogger(log_dir)
            client = Client(chat_logger, channels_file_path)

            client.autoconnect('localhost', port, 'justinfan28394')

            server_thread = threading.Thread(target=server.serve_forever)
            server_thread.daemon = True
            server_thread.start()

            client_thread = threading.Thread(target=client.reactor.process_forever)
            client_thread.daemon = True
            client_thread.start()

            thread_event.wait(30)

            server.shutdown()
            server.server_close()

            client.stop()

            data = io.StringIO()
            paths = sorted(glob.glob(temp_dir + '/logs/#test_channel/*.log'))

            for path in paths:
                print('reading', path)
                with open(path) as file:
                    data.write(file.read())

            log_file_data = data.getvalue()

            print(log_file_data)

            self.assertRegex(log_file_data, r'# {}.* logstart'.format(datetime.datetime.utcnow().year))
            self.assertIn('join justinfan28394', log_file_data)
            self.assertIn('part twitch_username', log_file_data)
            self.assertIn('mode jtv +o operator_user', log_file_data)
            self.assertRegex(log_file_data, r'notice .* :This room is no longer in slow mode\.')
            self.assertIn('clearchat ban-duration=600;ban-reason= :naughty_user', log_file_data)
            self.assertIn('clearchat  :', log_file_data)
            self.assertIn('☺', log_file_data)
            self.assertIn('\x01ACTION OneHand\x01', log_file_data)
            self.assertIn('TWITCH_UserNaME', log_file_data)
            self.assertIn('Kappa Keepo', log_file_data)
            self.assertRegex(log_file_data, r'usernotice .*msg-param-months.* :Great stream')
            self.assertIn('clearmsg login=ronni;target-msg-id=abc-123-def :HeyGuys', log_file_data)


from turtle import *

t = Turtle()
t.pensize(7)
t.color('#ec24fa')

for branch in range(8):
    for i in range(3):
        for j in range(3):
            t.fd(30)
            t.bk(30)
            t.rt(45)
        t.lt(90)
        t.bk(30)
        t.lt(45)
    t.rt(90)
    t.fd(90)
    t.lt(45)

done()


import random
from word_list import words
from art import logo,stages

# Get a random word
word = random.choice(words)
# create a variable to track the random word
display = []
end_of_game = False
lives = 6

# display the dashes representing the word
for _ in word:
    display.append("_")

print(logo)
while not end_of_game:
    # Display the selected word with blanks
    for dash in display:
        print(dash, end=" ")

    # get the user's guess
    guess = input("\n\nPlease guess a letter: ").lower()
        # Check if the letter has alredy been guessed
    if guess in display:
        print(f"you have already guessed the letter {guess}")
    # if the guess is correct replace the _ in diplay with the guessed letter

    for num in range(len(word)):
            if word[num] == guess:
                display[num] = guess

    # if the guess is incorrect remove a life
    if guess not in word:
        lives -= 1
        print("You guessed wrongly you lose a life")
        print(stages[lives])

        if lives == 0:
            end_of_game = True
            print("You lose!")
    print(f" You have {lives} lives remaining")
    if "_" not in display:
        end_of_game = True
        print("you win!!")


from config import ConfigFile, ConfigElement

class AppConfigMap(ConfigFile):
    UserName = ConfigElement            #Default type = str
    UserAge = ConfigElement(type=int)   #Set var type

    Version = ConfigElement(default="1.0")              #Auto detect var type
    Creators = ConfigElement(default=["Popa", "Ivan"])
    Permissions = ConfigElement(default=999)
    Links = ConfigElement(default={"Hello": "World"})

    HomePath = ConfigElement(name="HOME_PATH", default="C:\\")  #'HOME_PATH[str] = C:\;' in config file

    Display = ConfigElement(name="DISPLAY", default="AsusBLA-BLA23")    #'DISPLAY[str] = ...;'
    DisplayInt = ConfigElement(name="DISPLAY", default=1)               #'DISPLAY[int] = ...;'

    FirstStart = ConfigElement(default=True)

AppConfig = AppConfigMap("app.cfg")

input("Look at app.cfg and press Enter")

print(AppConfig.UserName)       #None
print(AppConfig.UserAge)        #None
print(AppConfig.Permissions)    #999
print(AppConfig.HomePath)       #'C:\'
print(AppConfig.FirstStart)     #True

AppConfig.UserName = "Hatsune Miku"
AppConfig.Permissions = 131
AppConfig.HomePath = "D:\\"
AppConfig.FirstStart = False

print(AppConfig.UserName)       #'Hatsune Miku'
print(AppConfig.Permissions)    #131
print(AppConfig.HomePath)       #'D:\'
print(AppConfig.FirstStart)     #False

AppConfig.HomePath = 0          #TypeError (HomePath type = str)


'''
Split the training data into subsets for cross validation, sample from the testing data, etc.
'''

from feather import read_dataframe as read_feather
import numpy as np
import pdb

from zpylib import data_path as dp
from zpylib import datatools
from zpylib import N_TEST
from zpylib import N_TRAIN

# Config
NGROUPS=3
types = datatools.FeaturesByType()
types.predictors += ['pydens_' + str(k) for k in range(10)]

def split_train():
    filepath = dp("refactored/densified_train.feather")
    print("loading " + filepath)
    df = read_feather(filepath, columns = types.predictors + [types.response])
    # Append the raw data index
    assert 'raw_data_index' not in df.columns
    assert df.shape[0] == N_TRAIN
    df['raw_data_index'] = range(N_TRAIN)

    print("Splitting into subgroups ... ")
    np.random.seed(0)
    idx_random = np.random.permutation(N_TRAIN)
    idx_groups = np.array_split(idx_random, NGROUPS)

    for k, idxs in enumerate(idx_groups):
        sk = str(k)
        print("... featherizing group " + sk + " of " + str(NGROUPS))
        fname = dp("refactored/train_split_" + sk + ".feather")
        df.iloc[idxs].reset_index(drop=True).to_feather(fname)

def test_sample():
    ''' Generate a 500k random sample of the testing data '''
    filepath = dp("refactored/densified_test.feather")
    print("loading " + filepath)
    df = read_feather(filepath, columns = types.predictors)

    # Append the raw data index
    assert 'raw_data_index' not in df.columns
    assert df.shape[0] == N_TEST
    df['raw_data_index'] = N_TRAIN + np.array(range(N_TEST))

    print("Uniformly sampling 500k rows from the testing data ... ")
    np.random.seed(0)
    idx_random = np.random.permutation(500000)

    print("Featherizing sample ... ")
    df.iloc[idx_random].reset_index(drop=True).to_feather(dp("refactored/test_sample.feather"))

if __name__=='__main__':
    split_train()
    test_sample()


#!/usr/bin/python3

from time import time
from gmpy import invert
from random import SystemRandom
from os import urandom
from binascii import hexlify
from pyprimes import miller_rabin

### Functions ###

hex = lambda s: hexlify(s)

def bytes_to_int(s):
    return int(hex(s), 16)

def get_prime(low, high):
    csprng = SystemRandom()
    n = csprng.randrange(low, high)
    if n % 2 == 0:
        n += 1
    while not miller_rabin(n):
        n += 2
    return n

def totient(a, b):
    return (a - 1) * (b - 1)

def rsa_new(size = 2**1024):
    p = get_prime(0, size)
    q = get_prime(0, size)
    n = p * q
    e = 65537
    t = totient(p, q)
    if t % e == 0:
        return rsa_new()
    d = int(invert(e, t))
    return (e, n), (d, n)

def bytes_to_int(s):
    o = 0
    for b in s:
        o <<= 8
        o += b
    return o

def int_to_bytes(n):
    c = []
    while n:
        c.append(n % 256)
        n >>= 8
    return bytes(c[::-1])

def encrypt(msg, pub):
    e, n = pub
    r = pow(bytes_to_int(msg), e, n)
    return int_to_bytes(r)

def decrypt(msg, priv):
    d, n = priv
    r = pow(bytes_to_int(msg), d, n)
    return int_to_bytes(r)

### Oracle ###

print('Setting up oracle...')

pub, priv = rsa_new()
known = set([])
secrets = [
    b'WE STRIKE AT DAWN',
    b'I ONLY TRUST HOMEBREW CRYPTO!'
]

for secret in secrets:
    c = encrypt(secret, pub)
    known.add(c)

def decryption_oracle(msg):
    if msg in known:
        return 'Nice try there pirate'
    return decrypt(msg, priv)

def get_ciphertexts():
    return list(known)

def get_pubkey():
    return pub

### Attack ###

print('Apply attack...')

texts = get_ciphertexts()
public = get_pubkey()

# Blind all messages

v = 5
blind = encrypt(int_to_bytes(v), pub)
blind = bytes_to_int(blind)
out0 = []
for t in texts:
    h = bytes_to_int(t) * blind
    out0.append(int_to_bytes(h))

# Decrypt all messages

out1 = []
for t in out0:
    out1.append(decryption_oracle(t))

# Unblind messages and print

(_, n) = public
vinv = invert(v, n)

print('Plain texts:')

for t in out1:
    print(int_to_bytes((bytes_to_int(t) * vinv) % n))


from keras.layers import Dense, Activation, Conv2D, Flatten, MaxPooling2D
from keras.models import Sequential, load_model
from keras.optimizers import Adam
import numpy as np
import random
class ReplayBuffer(object):
    def __init__(self, max_size, input_shape, n_actions, discrete=False):
        self.mem_size = max_size
        self.mem_cntr = 0
        self.discrete = discrete
        self.state_memory = np.zeros((self.mem_size, input_shape[0],  input_shape[1],  input_shape[2]))
        self.new_state_memory = np.zeros((self.mem_size, input_shape[0],  input_shape[1],  input_shape[2]))
        dtype = np.int8 if self.discrete else np.float32
        self.action_memory = np.zeros((self.mem_size, n_actions), dtype=dtype)
        self.reward_memory = np.zeros(self.mem_size)
        self.terminal_memory = np.zeros(self.mem_size, dtype=np.float32)

    def store_transition(self, current_state, action, reward, new_state, done):
        index = self.mem_cntr % self.mem_size
        self.state_memory[index] =current_state
        self.new_state_memory[index] = new_state
        self.reward_memory[index] = reward
        self.terminal_memory[index] = 1 - int(done)

        if self.discrete:
            actions = np.zeros(self.action_memory.shape[1])
            actions[action] = 1.0
            self.action_memory[index] = actions
        else:
            self.action_memory[index] = action

        self.mem_cntr +=1

    def sample_buffer(self, batch_size):
        max_mem = min(self.mem_cntr, self.mem_size)
        batch = np.random.choice(max_mem, batch_size)

        current_states= self.state_memory[batch]
        new_states = self.new_state_memory[batch]
        rewards = self.reward_memory[batch]
        actions = self.action_memory[batch]
        terminal = self.terminal_memory[batch]

        return current_states, actions, rewards, new_states, terminal

def build_dqn(LR, n_actions, input_dims, fc1_dims, fc2_dims):
    print("hey")
    model = Sequential([
        Conv2D(32, kernel_size=(3,3),activation="relu"),
        MaxPooling2D(pool_size =(2, 2), strides =(2, 2)),
        Flatten(),
        Dense(fc1_dims),
        Activation("relu"),
        Dense(fc2_dims),
        Activation("relu"),
        Dense(n_actions)])

    model.compile(optimizer=Adam(learning_rate=LR), loss="mse")

    return model

class Agent(object):
    def __init__(self, alpha, gamma, n_actions, epsilon, batch_size,
                 input_dims, epsilon_dec=0.996, epsilon_end=0.01,
                 mem_size=1000000, fname='dqn_model.h5'):
        self.action_space = [0 for i in range(n_actions)]
        self.gamma = gamma
        self.epsilon = epsilon
        self.epsilon_dec = epsilon_dec
        self.epsilon_min = epsilon_end
        self.batch_size = batch_size
        self.model_file = fname

        self.memory = ReplayBuffer(mem_size, input_dims, n_actions, discrete=False)
        self.q_eval = build_dqn(alpha, n_actions, input_dims, 256, 256)

    def remember(self, current_state, action, reward, new_state, done):
        self.memory.store_transition(current_state, action, reward, new_state, done)

    def choose_action(self, state):
        # print(state.shape)
        # exit()
        state = state[np.newaxis, :]
        rand = np.random.random()
        action = self.action_space.copy()
        # print(self.action_space)
        if rand < self.epsilon:
            action[0]=round(random.uniform(-0.7,0.7),2)
            action[1]=0.5
            action[2]=0
            # action = np.random.choice(self.action_space)
        else:
            action = self.q_eval.predict(state)[0]
            # action = np.argmax(actions)

        return action

    def learn(self):
        if self.memory.mem_cntr < self.batch_size:
            return

        state, action, reward, new_state, done = self.memory.sample_buffer(self.batch_size)
        # print(reward)
        action_values= np.array(self.action_space, dtype=np.int8)
        # action_indices = np.dot(action, action_values)

        q_eval = self.q_eval.predict(state)
        q_next = self.q_eval.predict(new_state)

        q_target = q_eval.copy()
        batch_index = np.arange(self.batch_size, dtype=np.int32)
        reward_new=[]
        done_new=[]
        for i in range(len(reward)):
            temp = [reward[i], reward[i], reward[i]]
            reward_new.append(temp)
            temp = [int(done[i]), int(done[i]), int(done[i])]
            done_new.append(temp)
        reward_new= np.array(reward_new)
        q_target[batch_index] = np.add(reward_new , self.gamma*np.multiply(q_next,done_new))
        _ = self.q_eval.fit(state, q_target, verbose = 0)
        self.epsilon = self.epsilon*self.epsilon_dec if self.epsilon > self.epsilon_min \
            else self.epsilon_min

    def save_model(self):
        self.q_eval.save(self.model_file)

    def load_model(self):
        self.q_eval = load_model(self.model_file)


#!/usr/bin/env python
# encoding:utf-8

"""
@software: PyCharm
@file: 冒泡排序.py
@time: 2017/5/3 11:53
"""

data = [10, 4, 33, 21, 54, 3, 8, 11, 5, 22, 2, 1, 17, 13, 6]

for j in range(1, len(data)):
    for i in range(len(data)-j):
        if data[i] > data[i+1]:
            tmp = data[i+1]   # 把4 保存
            data[i+1] = data[i]  # 把10 =》4
            data[i] = tmp   # 把 4 +> 10

print(data)


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

import os
from codecs import open

from setuptools import find_packages, setup

# load the package's __version__.py module as a dictionary
metadata = {}

here = os.path.abspath(os.path.dirname(__file__))

with open(os.path.join(here, "fpms", "__version__.py"), "r", "utf-8") as f:
    exec(f.read(), metadata)

def parse_requires(_list):
    requires = list()
    trims = ["#", "piwheels.org"]
    for require in _list:
        if any(match in require for match in trims):
            continue
        requires.append(require)
    requires = list(filter(None, requires))  # remove "" from list
    return requires

with open("extras.txt") as f:
    testing = f.read().splitlines()

testing = parse_requires(testing)

extras = {"testing": testing}

with open("requirements.txt") as f:
    requires = f.read().splitlines()

requires = parse_requires(requires)

setup(
    name=metadata["__title__"],
    version=metadata["__version__"],
    description=metadata["__description__"],
    long_description=metadata["__description__"],
    author=metadata["__author__"],
    author_email=metadata["__author_email__"],
    url=metadata["__url__"],
    python_requires="~=3.9,",
    license=metadata["__license__"],
    platforms=["linux"],
    packages=find_packages(),
    install_requires=requires,
    extras_require=extras,
    project_urls={
        "Documentation": "https://docs.wlanpi.com",
        "Source": metadata["__url__"],
    },
    classifiers=[
        "Natural Language :: English",
        "Development Status :: 3 - Alpha",
        "Programming Language :: Python :: 3.9",
        "Intended Audience :: System Administrators",
        "Topic :: Utilities",
    ],
    keywords="FPMS",
    include_package_data=True,
    entry_points={"console_scripts": ["fpms=fpms.__main__:main"]},
)


class Solution:
    def groupAnagrams(self, strs: List[str]) -> List[List[str]]:

        dict = {}

        answer = []
        for s in strs:
            sorted_s = "".join(sorted(s))
            if sorted_s not in dict:
                dict[sorted_s] = [s, ]

            else:
                dict[sorted_s].append(s)

        for value in dict.values():
            answer.append(value)

        return answer


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import torch.nn as nn
import torch
import config

class MapperI(nn.Module):
    def __init__(self, feat_dim, hidden_dim, out_dim):
        super(MapperI, self).__init__()
        self.fc1 = nn.Sequential(nn.Linear(feat_dim, hidden_dim),
                        nn.BatchNorm1d(hidden_dim),
                        nn.ReLU(inplace=True),
                        nn.Dropout(p=0.5))
        self.fc2 = nn.Sequential(nn.Linear(hidden_dim, out_dim))
        if config.CUDA:
            self.cuda()
    def forward(self, x):
        x = self.fc1(x)
        x = self.fc2(x)
        # L2 normalize each feature vector
        x = nn.functional.normalize(x)
        return x

class MapperT(nn.Module):
    def __init__(self, vecs, feat_dim, hidden_dim, out_dim):
        super(MapperT, self).__init__()
        self.fc = nn.Sequential(nn.Linear(feat_dim, hidden_dim),
                                nn.BatchNorm1d(hidden_dim),
                                nn.ReLU(inplace=True),
                                nn.Dropout(p=0.5),
                                nn.Linear(hidden_dim, out_dim))
        n_tokens, token_dim = vecs.shape
        self.vecs = vecs  # unique word and its embedding vector -- [#words, embed_dim]
        self.words = nn.Embedding(n_tokens, token_dim)
        self.words.weight = nn.Parameter(vecs)
        if config.CUDA:
            self.cuda()
            self.vecs = vecs.cuda()

    def forward(self, tokens):
        words = self.words(tokens)
        n_words = torch.sum(tokens > 0, 1).float() + 1e-10
        sum_words = words.sum(1).squeeze()
        sentences = sum_words / n_words.unsqueeze(1)  # take an average embed vector across all embed vectors of given caption
        x = self.fc(sentences)
        x = nn.functional.normalize(x)  # L2 normalize each feature vector
        return x


# Imports

import matplotlib.cm as cm
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.ticker import MaxNLocator
from mpl_toolkits.mplot3d import Axes3D

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn import metrics

from scipy.stats import gaussian_kde
import scipy.stats as st
import os

# Utility Functions

# Utility functions

def print_input_files():
    # Input data files are available in the "../input/" directory.
    for dirname, _, filenames in os.walk('/content/'):
        for filename in filenames:
            print(os.path.join(dirname, filename))

# Dump text files
def dump_text_file(fname):
    with open(fname, 'r') as f:
        print(f.read())

# Dump CSV files
def dump_csv_file(fname, count=5):
    # count: 0 - column names only, -1 - all rows, default = 5 rows max
    df = pd.read_csv(fname)
    if count < 0:
        count = df.shape[0]
    return df.head(count)

# Dataset related functions
ds_nbaiot = '/content/'
dn_nbaiot = ['Danmini_Doorbell', 'Ecobee_Thermostat', 'Ennio_Doorbell', 'Philips_B120N10_Baby_Monitor', 'Provision_PT_737E_Security_Camera', 'Provision_PT_838_Security_Camera', 'Samsung_SNH_1011_N_Webcam', 'SimpleHome_XCS7_1002_WHT_Security_Camera', 'SimpleHome_XCS7_1003_WHT_Security_Camera']

def fname(ds, f):
    if '.csv' not in f:
        f = f'{f}.csv'
    return os.path.join(ds, f)

def fname_nbaiot(f):
    return fname(ds_nbaiot, f)

def get_nbaiot_device_files():
    nbaiot_all_files = dump_csv_file(fname_nbaiot('data_summary'), -1)
    nbaiot_all_files = nbaiot_all_files.iloc[:,0:1].values
    device_id = 1
    indices = []
    for j in range(len(nbaiot_all_files)):
        if str(device_id) not in str(nbaiot_all_files[j]):
            indices.append(j)
            device_id += 1
    nbaiot_device_files = np.split(nbaiot_all_files, indices)
    return nbaiot_device_files

def get_nbaiot_device_data(device_id, count_norm=-1, count_anom=-1):
    if device_id < 1 or device_id > 9:
        assert False, "Please provide a valid device ID 1-9, both inclusive"
    if count_anom == -1:
        count_anom = count_norm
    device_index = device_id -1
    device_files = get_nbaiot_device_files()
    device_file = device_files[device_index]
    df = pd.DataFrame()
    y = []
    for i in range(len(device_file)):
        fname = str(device_file[i][0])
        df_c = pd.read_csv(fname_nbaiot(fname))
        count = count_anom
        if 'benign' in fname:
            count = count_norm
        rows = count if count >=0 else df_c.shape[0]
        print("processing", fname, "rows =", rows)
        y_np = np.ones(rows) if 'benign' in fname else np.zeros(rows)
        y.extend(y_np.tolist())
        df = pd.concat([df.iloc[:,:].reset_index(drop=True),
                      df_c.iloc[:rows,:].reset_index(drop=True)], axis=0)
    X = df.iloc[:,:].values
    y = np.array(y)
    return (X, y)

def get_nbaiot_devices_data():
    devices_data = []
    for i in range(9):
        device_id = i + 1
        (X, y) = get_nbaiot_device_data(device_id)
        devices_data.append((X, y))
    return devices_data

# Bilinear maps
def normalize(X, x_min=0, x_max=1):
    nom = (X-X.min(axis=0))*(x_max-x_min)
    denom = X.max(axis=0) - X.min(axis=0)
    denom[denom==0] = 1
    return x_min + nom/denom

class BilinearMap:
    def __init__(self, target_n):
        self.target_cols = target_n

    def compute_coeff(self, X, y):
        try:
            Xt = np.transpose(X)
            Xp = np.dot(Xt, X)
            Xpi = np.linalg.inv(Xp)
            XpiXt = np.dot(Xpi, Xt)
            coeff = np.dot(XpiXt, y)
            print ('coeff.shape:', coeff.shape)
        except Exception as e:
            regressor = LinearRegression(fit_intercept=False)
            regressor.fit(X, y)
            coeff = regressor.coef_
            print ('Exception:', e)

        return coeff

    def fit_transform(self, X, y):
        target_rows = X.shape[1]
        actual_rows = X.shape[0]
        required_rows = target_rows * self.target_cols

        if actual_rows < required_rows:
            assert False, f"{required_rows} rows are required, {actual_rows} are provided"

        Y = []
        for i in range(self.target_cols):
            start = i * target_rows
            end = start + target_rows
            coeff = self.compute_coeff(X[start:end,:], y[start:end])
            Y.extend(coeff.tolist())
            print("coeff.shape:", coeff.shape, "Len y:", len(Y), 'Start:', start, 'End:', end)
        Y = np.array(Y)
        Y = Y.reshape(target_rows, self.target_cols)
        print("Y.shape:", Y.shape)
        Z = np.dot(X, Y)
        return Z

 # Visualisation Utils

def plot_scatter_nbaiot_device(device_data, device_id, dim3=True):
    if device_id < 1 or device_id > 9:
        assert False, "Please provide a valid device ID 1-9, both inclusive"
    device_index = device_id-1
    print("scatter plot for", dn_nbaiot[device_index])
    (X, y) = device_data
    X_std = StandardScaler().fit_transform(X)

    bmap = PCA(n_components=2)
    X_bmap = bmap.fit_transform(X_std)

    print("X_bmap.shape:", X_bmap.shape, "X_std.shape:", X_std.shape)
    data_X = X_bmap[:,0]
    data_Y = X_bmap[:,1]
    data_Z = y
    data = np.column_stack((data_X, data_Y, data_Z))
    #if dim3:
    plot_3d_scatter(data, dn_nbaiot[device_index], 'PCA1', 'PCA2', 'Normal or Anomalous')
    #else:
    normal = mpatches.Patch(color='green', label='N')
    anomalous = mpatches.Patch(color='red', label='A')
    handles = [normal, anomalous]
    plot_2d_scatter(data, dn_nbaiot[device_index], 'PCA1', 'PCA2', handles)

def plot_surface_nbaiot_device(device_data, device_id):
    if device_id < 1 or device_id > 9:
        assert False, "Please provide a valid device ID 1-9, both inclusive"
    device_index = device_id-1
    print("scatter plot for", dn_nbaiot[device_index])
    (X, y) = device_data
    X_std = StandardScaler().fit_transform(X)

    bmap = PCA(n_components=2)
    X_bmap = bmap.fit_transform(X_std)

    print("X_bmap.shape:", X_bmap.shape, "X_std.shape:", X_std.shape)
    plot_3d_scatter_surface(X_bmap, dn_nbaiot[device_index], 'PCA1', 'PCA2', 'PCA3')

# Visualization related functions
def plot_3d_histogram(data):
    cols = data.shape[1]
    if cols < 2:
        assert False, 'The number of columns should be 2'
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    X = data[:,0]
    Y = data[:,1]
    bins = 10
    hist, xedges, yedges = np.histogram2d(X, Y, bins=bins, range=[[0, bins*0.6], [0, bins*0.6]])

    # Construct arrays for the anchor positions of the bars.
    xpos, ypos = np.meshgrid(xedges[:-1] + 0.25, yedges[:-1] + 0.25, indexing="ij")
    xpos = xpos.ravel()
    ypos = ypos.ravel()
    zpos = 0

    # Construct arrays with the dimensions for the 16 bars.
    dx = dy = 0.5 * np.ones_like(zpos)
    dz = hist.ravel()

    cmap = cm.get_cmap('cool')
    max_height = np.max(dz)
    min_height = np.min(dz)
    rgba = [cmap((k-min_height)/max_height) for k in dz]
    ax.bar3d(xpos, ypos, zpos, dx, dy, dz, zsort='average', color=rgba)

    plt.show()

def plot_3d_surface(data, func):
    cols = data.shape[1]
    if cols < 2:
        assert False, 'The number of columns should be 2'
    X = data[:,0]
    Y = data[:,1]
    X, Y = np.meshgrid(X, Y)
    Z = func(X, Y)
    ax = plt.axes(projection='3d')
    ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis', edgecolor='none')
    ax.set_title('surface');

def plot_3d_scatter(data, title=None, xlabel=None, ylabel=None, zlabel=None):
    cols = data.shape[1]
    if cols < 3:
        assert False, 'The number of columns should be 3'
    X = data[:,0]
    Y = data[:,1]
    Z = data[:,2]
    ax = plt.axes(projection='3d')
    ax.scatter(X, Y, Z, c = Z, cmap='RdYlGn')
    ax.set_title(title);
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_zlabel(zlabel)
    plt.show()

def plot_3d_scatter_trisurf(data, title=None, xlabel=None, ylabel=None, zlabel=None):
    cols = data.shape[1]
    if cols < 3:
        assert False, 'The number of columns should be 3'
    X = data[:,0]
    Y = data[:,1]
    Z = data[:,2]

    # Plot
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.set_title(title);
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_zlabel(zlabel)
    #ax.plot_surface(XX, YY, ZZ)
    surf = ax.plot_trisurf(X - X.mean(), Y - Y.mean(), Z - Z.mean(), cmap=cm.jet, linewidth=0.1)
    fig.colorbar(surf)
    ax.xaxis.set_major_locator(MaxNLocator(6))
    ax.yaxis.set_major_locator(MaxNLocator(6))
    ax.zaxis.set_major_locator(MaxNLocator(6))
    fig.tight_layout()
    plt.show()

def plot_3d_scatter_surface(data, title=None, xlabel=None, ylabel=None, zlabel=None):
    plot_3d_scatter_kde(data, title, xlabel, ylabel, zlabel)

def plot_3d_scatter_fxy(data, title=None, xlabel=None, ylabel=None, zlabel=None):
    cols = data.shape[1]
    if cols < 2:
        assert False, 'The number of columns should be 2'
    X = data[:,0]
    Y = data[:,1]
    XY = np.vstack([X,Y])
    Z = gaussian_kde(XY)(XY)

    # Sort the points by density, so that the densest points are plotted last
    idx = Z.argsort()
    x, y, z = X[idx], Y[idx], Z[idx]

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    #surf = ax.plot_trisurf(x - x.mean(), y - y.mean(), z, cmap=cm.jet, linewidth=0.1)
    surf = ax.plot_trisurf(x, y, z, cmap=cm.jet, linewidth=0.1)
    ax.scatter(x,y,z, marker='.', s=10, c="black", alpha=0.5)
    fig.colorbar(surf)
    ax.xaxis.set_major_locator(MaxNLocator(6))
    ax.yaxis.set_major_locator(MaxNLocator(6))
    ax.zaxis.set_major_locator(MaxNLocator(6))
    fig.tight_layout()

    ax.set_title(title);
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_zlabel(zlabel)
    plt.show()

    return

    # Plot
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.set_title(title);
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_zlabel(zlabel)
    ax.plot_surface(XX, YY, ZZ)
    plt.show()

def plot_3d_scatter_kde(data, title=None, xlabel=None, ylabel=None, zlabel=None):
    cols = data.shape[1]
    if cols < 2:
        assert False, 'The number of columns should be 2'
    X = data[:,0]
    Y = data[:,1]

    Xmin = int(np.floor(np.amin(X)))
    Xmax = int(np.ceil(np.amax(X)))
    Ymin = int(np.floor(np.amin(Y)))
    Ymax = int(np.ceil(np.amax(Y)))

    xmin = min(Xmin, Ymin)
    ymin = min(Xmin, Ymin)
    xmax = max(Xmax, Ymax)
    ymax = max(Xmax, Ymax)

    # Peform the kernel density estimate
    xx, yy = np.mgrid[Xmin:Xmax:100j, Ymin:Ymax:100j]
    #xx, yy = np.meshgrid(X, Y)
    positions = np.vstack([xx.ravel(), yy.ravel()])
    values = np.vstack([X, Y])
    kernel = st.gaussian_kde(values)
    f = np.reshape(kernel(positions).T, xx.shape)

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    ax.plot_surface(xx, yy, f - f.mean(), rstride=1, cstride=1, cmap='jet', edgecolor='none')

    ## Or kernel density estimate plot instead of the contourf plot
    cset = ax.contour(xx, yy, f, colors='k')
    # Label plot
    ax.clabel(cset, inline=1, fontsize=10)
    ax.set_xlabel('PCA1')
    ax.set_ylabel('PCA2')
    ax.set_title(title)
    plt.show()

def plot_2d_scatter(data, title=None, xlabel=None, ylabel=None, handles=None):
    cols = data.shape[1]
    if cols < 3:
        assert False, 'The number of columns should be 3'
    X = data[:,0]
    Y = data[:,1]
    Z = data[:,2]
    ax = plt.axes()
    scatter = ax.scatter(X, Y, c = ['green' if z > 0.5 else 'red' for z in Z], cmap='RdYlGn')
    ax.set_title(title);
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    plt.legend(handles=handles)
    plt.show()

def main():
    for i in range(9):
        device_index = i
        device_id = device_index + 1
        device_data = get_nbaiot_device_data(device_id)
        plot_surface_nbaiot_device(device_data, device_id)
        plot_scatter_nbaiot_device(device_data, device_id, False)

if __name__ == "__main__":
    main()


# Definition for singly-linked list.
class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution(object):
    def removeNthFromEnd(self, head, n):
        """
        :type head: ListNode
        :type n: int
        :rtype: ListNode
        """
        l = []
        h = head
        while True:
            l.append(h)
            h = h.next
            if h == None:
                break
        length = len(l)
        l[length-1-n].next = l[length-n+1]
        return head

def listToLinkList(l):
    dummyHead = curr = ListNode(0)
    for i in l:
        curr.next = ListNode(i)
        curr = curr.next
    return dummyHead.next

def printLinkList(listNode):
    l = []
    while True:
        l.append(listNode.val)
        if listNode.next == None:
            break
        listNode = listNode.next
    print(l)

l1 = [1,2,3,4,5]
n = 2
sln = Solution()
printLinkList(sln.removeNthFromEnd(listToLinkList(l1), n))