import logging

from django import forms
from django.db.models.functions import ExtractYear
from django.utils import six
from django.utils.encoding import force_text
from django.utils.html import conditional_escape

from .models import Deer, Hunter, Location, Log, LogSheet, Site

from library.forms import MultipleSelectWithAdd, SelectWithAdd

logger = logging.getLogger(__name__)

DEER_PREFIX = 'deer'
HUNTER_PREFIX = 'hunter'

def _get_year_field():
    year_choices = [
        (year, year) for year in
        LogSheet.objects.dates('date', 'year')
        .annotate(year=ExtractYear('date'))
        .order_by('year')
        .values_list('year', flat=True)
    ]

    return forms.MultipleChoiceField(choices=year_choices, label='', widget=forms.SelectMultiple(attrs={'size': 20}))

class DeerForm(forms.ModelForm):
    hunter_formset_number = forms.IntegerField(widget=forms.HiddenInput())

    class Meta:
        # Sets the order the fields will be displayed
        fields = ['count', 'gender', 'points', 'tracking', 'log', 'hunter_formset_number']
        model = Deer
        widgets = {'log': forms.HiddenInput}

    def __init__(self, *args, **kwargs):
        log_required = kwargs.pop('log_required', False)
        super().__init__(*args, **kwargs)
        if not log_required:
            self.fields['log'].required = False

    def clean(self):
        cleaned_data = super().clean()
        count = cleaned_data['count']
        gender = cleaned_data['gender']
        points = cleaned_data['points']
        tracking = cleaned_data['tracking']

        if count:
            log = cleaned_data['log']

            if log.hunter is None:
                raise forms.ValidationError(
                    'Hunter needs to be specified when any deer are shot or killed. If not specified, '
                    'list first name as <unknown>.'
                )

            if gender is None:
                self.add_error('gender', 'Need to specify the deer gender when any are shot or killed')

            if gender == Deer.GENDER_MALE and points is None:
                self.add_error('points', 'Male deer need the number of points specified')

            if tracking is None:
                self.add_error(
                    'tracking', 'Need to specify if tracking was required when any deer are shot or killed'
                )
        else:
            if gender is not None:
                self.add_error('gender', 'Gender is set but the count equals 0')

            if points is not None:
                if gender == Deer.GENDER_MALE:
                    self.add_error('points', 'Points is set but the count equals 0')
                else:
                    self.add_error('points', 'Points is set but the count equals 0 and the gender is Female')

        return cleaned_data

DeerFormSet = forms.formset_factory(DeerForm, extra=0)

class HunterAnalysisForm(forms.Form):
    years = _get_year_field()

    hunter_choices = [(hunter.id, str(hunter)) for hunter in Hunter.objects.exclude(first_name='<unknown>')]

    hunters = forms.MultipleChoiceField(
        choices=hunter_choices, label='', widget=forms.SelectMultiple(attrs={'size': 20})
    )

class HunterForm(forms.ModelForm):
    hunter_formset_number = forms.IntegerField(widget=forms.HiddenInput())
    location_id = forms.IntegerField(widget=forms.HiddenInput())    # Store only the ID, so we can avoid the large list
    log_sheet_id = forms.IntegerField(widget=forms.HiddenInput())   # Store only the ID, so we can avoid the large list
    pk = forms.IntegerField(required=False, widget=forms.HiddenInput())  # Need this to add instance to form later

    class Meta:
        exclude = ['location', 'log_sheet']
        model = Log
        widgets = {
            'hunter': SelectWithAdd(),
            'incorrect_warnings': forms.CheckboxSelectMultiple,
            'missing_warnings': forms.CheckboxSelectMultiple
        }

    def as_table(self):
        return self._as_table('hunter') + \
               '
Deer
' + \
               self._as_table('comment')

    def _as_table(self, field_name):
        bf = self[field_name]
        bf_errors = self.error_class([conditional_escape(error) for error in bf.errors])

        if bf.label:
            label = conditional_escape(force_text(bf.label))
            label = bf.label_tag(label) or ''
        else:
            label = ''

        if self.fields[field_name].help_text:
            help_text = f'{force_text(self.fields[f"{field_name}"].help_text)}'
        else:
            help_text = ''

        error_row = f'
{force_text(bf_errors)}
' if bf_errors else ''

        if field_name == 'hunter':
            return f"""
                {error_row}

                    {label}

                        {six.text_type(self['pk'])}
                        {six.text_type(self['log_sheet_id'])}
                        {six.text_type(self['location_id'])}
                        {six.text_type(self['hunter_formset_number'])}
                        {six.text_type(bf)}{help_text}

                        {six.text_type(self['missing_warnings'])}
                        {six.text_type(self['incorrect_warnings'])}

            """
        else:
            return f"""
                {error_row}

                    {label}
                    {six.text_type(bf)}{help_text}

            """

HunterFormSet = forms.formset_factory(HunterForm, extra=0)

class LocationAnalysisForm(forms.Form):
    years = _get_year_field()

    location_choices = [
        (location, location)
        for location in Location.objects.distinct().values_list('label', flat=True).order_by('label')
    ]

    locations = forms.MultipleChoiceField(
        choices=location_choices, label='', widget=forms.SelectMultiple(attrs={'size': 20})
    )

class LocationForm(forms.Form):
    location = forms.ModelMultipleChoiceField(
        Location.objects.none(), label='', widget=MultipleSelectWithAdd(attrs={'size': 20})
    )

    def __init__(self, **kwargs):
        self.year = kwargs.pop('year')
        super().__init__()
        self.fields['location'].queryset = Location.objects.filter(year=self.year)
        self.initial['location'] = kwargs.get('initial', {'location': []})['location']

    def as_p(self):
        html = super().as_p()
        return html.replace('
', '
').replace(f'[{self.year}] ', '')

class LogSheetForm(forms.Form):
    log_sheet = forms.ModelChoiceField(LogSheet.objects.all(), widget=SelectWithAdd())

class SiteAnalysisForm(forms.Form):
    site_choices = [(site, site) for site in Site.objects.exclude(street='')]

    sites = forms.MultipleChoiceField(
        choices=site_choices, label='', widget=forms.SelectMultiple(attrs={'size': 20})
    )


import numpy as np
import matplotlib.pyplot as plt
import math
from numpy.polynomial import Polynomial as poly

def baseLsqm(x, y, bflag = True):
	if(np.size(x) != np.size(y)):
		raise ValueError('size of axes not identical')
	x2 = x**2
	y2 = y**2
	xy = np.array([x[i] * y[i] for i in range(np.size(x))])
	xavg = np.average(x)
	yavg = np.average(y)
	x2avg = np.average(x2)
	y2avg = np.average(y2)
	xyavg = np.average(xy)
	k = (xyavg - xavg * yavg) / (x2avg - xavg ** 2) if bflag else xyavg / x2avg
	b = yavg - k * xavg if bflag else 0
	dk = math.sqrt(((y2avg - yavg ** 2) / (x2avg - xavg**2) - k**2) / np.size(x)) if bflag else math.sqrt(((y2avg - k**2 * xavg ** 2) / (x2avg - xavg**2) - k**2) / np.size(x))
	db = dk * math.sqrt(x2avg - xavg**2) if bflag else 0
	return [k, b, dk, db]

def lsqm(x, y, dx = np.empty(0), dy = np.empty(0), bflag = True):
	if(dx.size == 0 and dy.size == 0):
		return baseLsqm(x, y)
	if(dx.size == 0):
		dx = np.zeros(np.size(x))
	if(dy.size == 0):
		dy = np.zeros(np.size(y))
	k, b, dk, db = baseLsqm(x, y, bflag)
	# Dk = math.sqrt(dk**2 + (dy[-1] / x[-1])**2 + ((y[-1] - b) * dx[-1] / x[-1]**2)**2)
	Dk = math.sqrt(dk**2 + (np.average(dy) / (np.average(x) - np.min(x)))**2 + (np.average(y) * np.average(dx) / (np.average(x) - np.min(x))**2)**2)
	# Db = math.sqrt(db**2 + dy[-1]**2 + (k * dx[-1])**2)
	# Dk = math.sqrt(dk**2 + (np.average(np.array([dy[i] / (x[i+1] - dx[i+1] - x[i] - dx[i]) for i in range(len(x) - 1)])))**2)
	Db = math.sqrt(db**2 + np.average(dy)**2 + (k * np.average(dx))**2) if bflag else 0
	return [k, b, Dk, Db]

def plot(x, y, dx = None, dy = None, filename = None, plotFmt = '.', title = None, xlabel = None, ylabel = None):
	fig, ax = plt.subplots()
	plt.minorticks_on()
	plt.grid(True, "major", "both", color = "#888888")
	plt.grid(True, "minor", "both", linestyle = '--')
	plt.title(title)
	plt.xlabel(xlabel)
	plt.ylabel(ylabel)
	ax.errorbar(x, y, dy, dx, fmt = plotFmt)
	if(filename != None):
		plt.savefig(filename)
	plt.show()

def plotLsqm(x, y, dx = np.empty(0), dy = np.empty(0), filename = None, plotFmt = '.', lsqmFmt = 'r', title = None, xlabel = None, ylabel = None, stretch = 0.1, bflag = True):
	if(dx.size == 0):
		dx = np.zeros(np.size(x))
	if(dy.size == 0):
		dy = np.zeros(np.size(y))
	k, b, dk, db = lsqm(x, y, dx, dy, bflag)
	fig, ax = plt.subplots()
	plt.minorticks_on()
	plt.grid(True, "major", "both", color = "#888888")
	plt.grid(True, "minor", "both", linestyle = '--')
	plt.title(title)
	plt.xlabel(xlabel)
	plt.ylabel(ylabel)
	xdist = np.max(x) - np.min(x)
	ax.plot([np.min(x) - stretch * xdist, np.max(x) + stretch * xdist], [k * (np.min(x) - stretch * xdist) + b, k * (np.max(x) + stretch * xdist) + b], lsqmFmt)
	ax.errorbar(x, y, dy, dx, fmt = plotFmt)
	if(filename != None):
		plt.savefig(filename)
	plt.show()
	return [k, b, dk, db]

def plotlsqm(x, y, dx = np.empty(0), dy = np.empty(0), filename = None, plotFmt = '.', lsqmFmt = 'r', title = None, xlabel = None, ylabel = None, stretch = 0.1, bflag = True):
	return plotLsqm(x, y, dx, dy, filename, plotFmt, lsqmFmt, title, xlabel, ylabel, stretch, bflag)

def plotPoly(n, x, y, dx = None, dy = None, filename = None, plotFmt = '.', polyFmt = 'r', title = None, xlabel = None, ylabel = None):
	fig, ax = plt.subplots()
	plt.minorticks_on()
	plt.grid(True, "major", "both", color = "#888888")
	plt.grid(True, "minor", "both", linestyle = '--')
	plt.title(title)
	plt.xlabel(xlabel)
	plt.ylabel(ylabel)
	approx = poly.fit(x, y, n)
	t = np.linspace(x[0], x[-1], num = 1000)
	ax.plot(t, approx(t), polyFmt)
	ax.errorbar(x, y, dy, dx, fmt = plotFmt)
	if(filename != None):
		plt.savefig(filename)
	plt.show()
	return approx

def basePlot():
	fig, ax = plt.subplots()
	plt.minorticks_on()
	plt.grid(True, 'major', 'both', color = '#888888')
	plt.grid(True, 'minor', 'both', linestyle = '--')
	return fig, ax


from pubnub.pnconfiguration import PNConfiguration
from pubnub.pubnub import PubNub, SubscribeListener
import uuid
import random
import os

def get_random_uuid_as_string():
    basename_string = 'festeban26-iot-lockers-server'
    new_uuid_random_number = random.random()
    new_uuid_string = basename_string + "-" + str(new_uuid_random_number)
    custom_uuid = uuid.uuid5(uuid.NAMESPACE_DNS, new_uuid_string)
    print("UUID generated: " + str(custom_uuid))
    return str(custom_uuid)

def get_uuid_from_default_uuid_file():
    # first check if a previous file exists, if it does not exist, generate uuid and save it to file
    try:
        with open("UUID.txt", "r") as text_file:
            return text_file.read()
    except IOError:
        print("UUID file was not found. Creating new uuid")
        new_uuid = get_random_uuid_as_string()
        save_uuid_in_default_uuid_file(new_uuid)
        with open("UUID.txt", "r") as text_file:
            return text_file.read()

def save_uuid_in_default_uuid_file(uuid_as_string):
    # first check if it exists, if it exists, delete the file
    try:
        file = open("UUID.txt", 'r')
        file.close()
        os.remove("UUID.txt")
    except IOError:
        pass
    # once the file is deleted or the file did not exists, save content
    with open("UUID.txt", "w") as text_file:
        text_file.write(uuid_as_string)

print(get_uuid_from_default_uuid_file())

'''
pub_num_iot_lockers_channel = 'iot-lockers'
pn_config = PNConfiguration()
pn_config.subscribe_key = 'INSERT API KEY'
pn_config.publish_key = 'INSERT API KEY'
pn_config.uuid = '4205e440-1258-462c-aa94-1870f1ab4755'
pub_nub = PubNub(pn_config)

print(pub_nub.uuid)
pub_nub.publish().channel(pub_num_iot_lockers_channel).message("Test").sync()
'''


"""
6083. 判断一个数的数字计数是否等于数位的值
给你一个下标从 0 开始长度为 n 的字符串 num ，它只包含数字。
如果对于 每个 0 <= i < n 的下标 i ，都满足数位 i 在 num 中出现了 num[i]次，那么请你返回 true ，否则返回 false 。
"""
import collections

def digitCount(num: str) -> bool:
    numCounter = collections.Counter(num)
    for i, char in enumerate(num):
        if numCounter[str(i)] != int(char):
            return False
    return True

digitCount(num = "1210")

Leetcode/6083. 判断一个数的数字计数是否等于数位的值.py

6083. 判断一个数的数字计数是否等于数位的值.py


import os
import pickle

import yaml

__all__ = ['save_yaml',
           'load_yaml',
           'save_obj',
           'load_obj',
           'get_dataset_tags',
           'save_experiment',
           'get_corr_pred'
           ]

def save_yaml(obj, path):
    """Save yaml object to specified path.
    """
    if not os.path.exists(os.path.dirname(path)):
        os.makedirs(os.path.dirname(path))
    with open(path, 'w') as outfile:
        yaml.dump(obj, outfile, default_flow_style=False)

def load_yaml(filepath):
    """Load yaml object from specified path.
    """
    return yaml.load(open(filepath), Loader=yaml.FullLoader)

def save_obj(obj, path):
    """Save pickle object to specified path.
    """
    if not os.path.exists(os.path.dirname(path)):
        os.makedirs(os.path.dirname(path))
    with open(path, 'wb') as f:
        pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL)

def load_obj(path):
    """Load pickle object from specified path.
    """
    with open(path, 'rb') as f:
        return pickle.load(f)

def save_experiment(d_spec, target_folder):
    """Save dataset specifications for experiment to yaml file.
    """
    if not os.path.exists(target_folder):
        os.makedirs(target_folder)
    for t in d_spec:
        save_yaml(d_spec[t], os.path.join(target_folder, str(t) + '.yml'))

def get_dataset_tags(spec):
    """Given a spec dict return a list of its tags.
    """
    tags = [mode['specification']['tag'] for mode in spec['modes']]
    return list(set(tags))

def get_corr_pred(study_name):
    """
    A study name is of the form CORR-factor1-factor2-factor3_PRED-factor1.
    If no factors are correlated, then CORR_PRED-factor1.
    This function returns lists of correlated and predicted factors from this string.
    """
    if 'CORR-' in study_name:
        # There are correlations
        corrs, preds = study_name.split('CORR-')[1].split('_PRED-')
        corrs = corrs.split('-')
        preds = preds.split('-')
    else:
        # There are no correlations
        corrs = []
        preds = study_name.split('PRED-')[1].split('-')
    return corrs, preds


from copy import copy

from dateutil.parser import parse
from django.test import TestCase

from ..models import Report

class ReportTestCase(TestCase):
    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.standard_data = {
            "subscriber": "11981094379",
            "destination": "1198109437",
            "call_started_at": parse("2016-01-01T15:00:01Z"),
            "call_ended_at": parse("2016-01-01T15:50:37Z"),
        }
        cls.reduced_data = {
            "subscriber": "11981094379",
            "destination": "1198109437",
            "call_started_at": parse("2016-01-01T22:00:00Z"),
            "call_ended_at": parse("2016-01-02T05:59:59Z"),
        }
        cls.standard_and_reduced_data = {
            "subscriber": "11981094379",
            "destination": "1198109437",
            "call_started_at": parse("2016-01-01T21:57:13Z"),
            "call_ended_at": parse("2016-01-01T22:17:53Z"),
        }
        cls.reduced_and_standard_data = {
            "subscriber": "11981094379",
            "destination": "1198109437",
            "call_started_at": parse("2017-12-12T04:57:13Z"),
            "call_ended_at": parse("2017-12-12T06:10:56Z"),
        }

        cls.report_standard_only = Report.objects.create(**cls.standard_data)
        cls.report_reduced_only = Report.objects.create(**cls.reduced_data)
        cls.report_standard_and_reduced = Report.objects.create(
            **cls.standard_and_reduced_data
        )
        cls.report_reduced_and_standard = Report.objects.create(
            **cls.reduced_and_standard_data
        )

    def test_call_price_standard_only(self):
        self.assertEqual(self.report_standard_only.price_label, "R$ 4,86")
        self.assertEqual(
            self.report_standard_only.minutes_in_each_tarrif(),
            {"standard": 50, "reduced": 0},
        )

    def test_call_price_reduced_only(self):
        self.assertEqual(self.report_reduced_only.price_label, "R$ 0,36")
        self.assertEqual(
            self.report_reduced_only.minutes_in_each_tarrif(),
            {"standard": 0, "reduced": 479},
        )

    def test_call_price_standard_and_reduced(self):
        self.assertEqual(self.report_standard_and_reduced.price_label, "R$ 0,54")
        self.assertEqual(
            self.report_standard_and_reduced.minutes_in_each_tarrif(),
            {"standard": 2, "reduced": 18},
        )

    def test_call_price_reduced_and_standard(self):
        self.assertEqual(self.report_reduced_and_standard.price_label, "R$ 1,26")
        self.assertEqual(
            self.report_reduced_and_standard.minutes_in_each_tarrif(),
            {"standard": 10, "reduced": 63},
        )

    def test_call_price_over_day(self):
        data = copy(self.standard_data)
        data["call_started_at"] = parse("2017-12-13T21:57:13Z")
        data["call_ended_at"] = parse("2017-12-14T22:10:56Z")
        report = Report.objects.create(**data)
        self.assertEqual(report.price_label, "R$ 86,85")

    def test_call_price_one_minute(self):
        data = copy(self.standard_data)
        data["call_started_at"] = parse("2017-12-13T21:57:13Z")
        data["call_ended_at"] = parse("2017-12-13T21:58:13Z")
        report = Report.objects.create(**data)
        self.assertEqual(report.price_label, "R$ 0,45")

    def test_call_price_less_than_one_minute(self):
        data = copy(self.standard_data)
        data["call_ended_at"] = parse("2016-01-01T15:00:59Z")
        report = Report.objects.create(**data)
        self.assertEqual(report.price_label, "R$ 0,36")

    def test_call_price_two_hours(self):
        data = copy(self.standard_data)
        data["call_ended_at"] = parse("2016-01-01T17:00:01Z")
        report = Report.objects.create(**data)
        self.assertEqual(report.price_label, "R$ 11,16")

    def test_call_start_date(self):
        self.assertEqual(
            self.report_standard_only.call_start_date,
            self.standard_data["call_started_at"].date(),
        )

    def test_call_start_time(self):
        self.assertEqual(
            self.report_standard_only.call_start_time,
            self.standard_data["call_started_at"].time(),
        )

    def test_call_duration(self):
        self.assertEqual(self.report_standard_only.call_duration, "0h50m36s")
        self.assertEqual(self.report_reduced_only.call_duration, "7h59m59s")
        self.assertEqual(self.report_standard_and_reduced.call_duration, "0h20m40s")


from funcionario import Funcionario
from typing import List
import datetime

class Medico(Funcionario):
  def __init__(self, data: dict):
    f = open("logs.txt", "a")
    try:
      self.__emAtendimento = data["emAtendimento"]
      self.__especialidade = data["especialidade"]
      super().__init__(data["nome"], data["idade"], data["sexo"], data["id"])
    except ValueError as e:
      print(e)
      f.write("{}: Dados incorretos de Médico, favor preencher corretamente a ficha de inscrição\n".format(datetime.datetime.now()))
    else:
      f.write("{}: Dados cadastrados com sucesso!\n".format(datetime.datetime.now()))
    f.close()

  def getEmAtendimento(self) -> bool:
    return self.__emAtendimento
  def getEspecialidade(self) -> str:
    return self.__especialidade

  def setEmAtendimento(self, atendendo: bool):
    self.__emAtendimento = atendendo
  def setEspecialidade(self, especialidade):
    self.__especialidade = especialidade

class Interno(Medico):
  def __init__(self, data: dict):
    try:
      if data["tempoDeInternato"] >= datetime.datetime.now():
        self.__tempoDeInternato = data["tempoDeInternato"] - datetime.datetime.now()
      else:
        raise Exception("Favor fornecer uma data válida")
      self.__supervisor = data["supervisor"]
      self.__rotacao = data["rotacao"]
      super().__init__(data)
    except :
      print("Dados incorretos de Interno, favor preencher corretamente a ficha de inscrição")
    else:
      f = open("logs.txt", "a")
      f.write("{}: Dados cadastrados com sucesso!\n".format(datetime.datetime.now()))
      f.close()

  def getTempoDeInternato(self) -> datetime:
    return self.__tempoDeInternato
  def getSupervisor(self):
    return self.__supervisor
  def getRotacao(self):
    return self.__rotacao

  def setTempoDeInternato(self, tempoInternato):
    if tempoInternato >= datetime.datetime.now():
      self.__tempoDeInternato = tempoInternato - datetime.datetime.now()
    else:
      raise Exception("Favor fornecer uma data válida")

class Titular(Medico):
  def __init__(self, data: dict):
    try:
      f = open("logs.txt", "a")
      super().__init__(data)
      if (type(data["crm"]) and type(data["areaAtuacao"])) is str:
        self.__crm = data["crm"]
        self.__areaAtuacao = data["areaAtuacao"]

      else:
        raise Exception("dados enviados estão com tipos incorretos")
    except:
      f.write("{}: Dados incorretos de Médico Titular, favor preencher corretamente a ficha de inscrição\n".format(datetime.datetime.now()))
    else:
      f.write("{}: Dados cadastrados com sucesso!\n".format(datetime.datetime.now()))

    f.close()

  def getCrm(self) -> str:
    return self.__crm
  def getAreaAtuacao(self) -> str:
    return self.__areaAtuacao

  def reCadastroTitular(self, data: dict):
    self.__crm = data["crm"]
    self.__areaAtuacao = data["areaAtuacao"]

  def setAreaAtuacao(self, areaAtuacao):
    self.__areaAtuacao = areaAtuacao

class Residente(Titular):
  def __init__(self, data: dict):
    try:
      f = open("logs.txt", "a")
      super().__init__(data)
      self.__supervisor = data["supervisor"]
    except:
      f.write("{}: Dados incorretos de Residente, favor preencher corretamente a ficha de inscrição\n".format(datetime.datetime.now()))
    else:
      f.write("{}: Dados cadastrados com sucesso!\n".format(datetime.datetime.now()))

    f.close()

  def getSupervisor(self) -> Titular:
    return self.__supervisor

  def reCadastroTitular(self, data: dict):
    f = open("logs.txt", "a")
    try:
      # utiliza o método da superclasse para cadastrar dados gerais, para depois cadastrar dados especificos
      self.__supervisor = data["supervisor"]
      super().reCadastroTitular(data)
    except:
      f.write("{}: Recadastro de Residente incompletos, favor preenchê-los integralmente\n".format(datetime.datetime.now()))
    else:
      f.write("{}: Dados cadastrados com sucesso!\n".format(datetime.datetime.now()))
    f.close()

class Cirurgiao(Titular):
  def __init__(self, data: dict):
    try:
      f = open("logs.txt", "a")
      self.__inicioDeCarreira = data["inicioDeCarreira"] #data de início do profissional na área de atuação
      self.__equipeCirurgica = data["equipeCirurgica"]
      super().__init__(data)
    except:
      f.write("{}: Dados incorretos de Residente, favor preencher corretamente a ficha de inscrição\n".format(datetime.datetime.now()))
    else:
      f.write("{}: Dados cadastrados com sucesso!\n".format(datetime.datetime.now()))

    f.close()

  def getInicioDeCarreira(self) -> datetime:
    return self.__inicioDeCarreira
  def getEquipeCirurgica(self) -> List[Titular]:
    try:
      return self.__equipeCirurgica
    except:
      raise Exception("Dados não são listas de Médicos titulares, favor preencher corretamente")

  def addMembroEquipe(self, membro: Titular):
    try:
      f = open("logs.txt", "a")
      self.__equipeCirurgica.append(membro)
    except:
      f.write("{}: Favor inserir dados corretamente\n".format(datetime.datetime.now()))
    else:
      f.write("{}: Membro cadastrado com sucesso!\n".format(datetime.datetime.now()))

    f.close()

  def removeMembroEquipe(self, membro: Titular):
    try:
      f = open("logs.txt", "a")
      self.__equipeCirurgica.remove(membro)
    except:
      f.write("{}: Médico não encontrado, favor colocar cadastro válido\n".format(datetime.datetime.now()))
    else:
      f.write("{}: Membro removido com sucesso.\n".format(datetime.datetime.now()))
    f.close()

  def reCadastroTitular(self, data: dict):
    try:
      # utiliza o método da superclasse para cadastrar dados gerais, para depois cadastrar dados especificos
      f = open("logs.txt", "a")
      self.__inicioDeCarreira = data["inicioDeCarreira"]
      self.__equipeCirurgica = data["equipeCirurgica"]
      super().reCadastroTitular(data)
    except:
      f.write("{}: Recadastro de Cirurgião incompletos, favor preenchê-los integralmente".format(datetime.datetime.now()))
    else:
      f.write("{}: Dados cadastrados com sucesso!\n".format(datetime.datetime.now()))

    f.close()

class MedicoClinico(Titular):
  def __init__(self, data: dict):
    f = open("logs.txt", "a")
    try:
      super().__init__(data)
      self.__endereco = data["enderecoClinica"]
    except:
      f.write("{}: Dados incorretos de Médico Geral, favor preencher corretamente a ficha de inscrição\n".format(datetime.datetime.now()))
    else:
      f.write("{}: Dados cadastrados com sucesso!\n".format(datetime.datetime.now()))
    f.close()

  def getEndereco(self):
    return self.__endereco
  def reCadastroTitular(self, data: dict):
    f = open("logs.txt", "a")
    try:
      # utiliza o método da superclasse para cadastrar dados gerais, para depois cadastrar dados especificos

      super().reCadastroTitular(data)
      self.__endereco = data["enderecoClinica"]

    except:
      f.write("{}: Recadastro de Médico Geral incompletos, favor preenchê-los integralmente\n".format(datetime.datetime.now()))
    else:
      f.write("{}: Dados cadastrados com sucesso!\n".format(datetime.datetime.now()))
    f.close()


import sys
import glob
import scipy.io as sio
import numpy as np

import torch
from torch.utils.data.dataloader import DataLoader

from framework_pytorch.core import Trainer, BasicDataset
from framework_pytorch.modelnet.model_vnet_nointer import VNet
from framework_pytorch.modelnet.model import SegModel
from framework_pytorch.utils import loss_fuctions as LF, process_methods as P, utils as U
from framework_pytorch.utils.data_augmentation import RandomAugmentation

# training parameters
epochs = 500
learning_rate = 1e-4
train_batch_size = 1
eval_batch_size = 1
loss_function = {LF.CrossEntropy(): 1.0, LF.SoftDice(): 1.0} # loss functions {method: weight}
aug_rate = 0.8
output_path = './results/cmr_unet'

# training setttings
test_only = False

log_train_image = False
log_validation_image = True

data_path = './CMRxMotion/training/'

data_list = np.load(data_path + '/train_list.npy', allow_pickle=True).item()

train = data_list['d1_train'] + data_list['d2_train']
valid = data_list['d1_valid'] + data_list['d2_valid']

train = [data_path + '/data/' + x.split('-E')[0] + '/' + x + '.nii.gz' for x in train]
valid = [data_path + '/data/' + x.split('-E')[0] + '/' + x + '.nii.gz' for x in valid]

# set suffix for image and label (the difference between image path and label path) for data loading
img_suffix = '.nii.gz'
lab_suffix = '-label.nii.gz'

# set pre-process functions for image and label
pre = {img_suffix: [np.squeeze,
                    P.CenterCrop([256,256,10]),
                    P.min_max,
                    P.ExpandDim(0),
                    ],
        lab_suffix: [P.CenterCrop([256,256,10]),
                    #  lambda x: np.array(x>0.5, np.uint8),
                     P.OneHot([0,1,2,3])]
        }

aug = RandomAugmentation(img_suffix, lab_suffix, aug_rate=aug_rate)

# set device to gpu if gpu is available, otherwise use cpu
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# build pytorch dataset, see core/basic_dataset
train_set = BasicDataset(train, [img_suffix, lab_suffix], pre, aug)
valid_set = BasicDataset(valid, [img_suffix, lab_suffix], pre)

# build pytorch data loader, shuffle train set while training
trainloader = DataLoader(train_set, batch_size=train_batch_size, shuffle=True)
validloader = DataLoader(valid_set, batch_size=eval_batch_size)

# get a random image for graph draw
random_img = torch.tensor([train_set[0][img_suffix]]).to(device)

# build 3D unet for 5 classes segmentation, the input channel is 1
net = VNet(input_channel=1, class_number=4)
# init optimizer, adam is used here
optimizer = torch.optim.Adam(net.parameters(), lr=learning_rate)
# init the model, #TODO: more details see modelnet/model
model = SegModel(net, optimizer, device, img_suffix, lab_suffix, dropout_rate=0, loss_functions=loss_function)

# init train and start train
trainer = Trainer(model)
if not test_only:
    trainer.train(trainloader, validloader,
        epochs=epochs,
        output_path=output_path,
        log_train_image=log_train_image,
        log_validation_image=log_validation_image,
        log_graph_input=random_img)

# test on test set
trainer.restore(output_path+'/ckpt/model_final.pt')

# test set 1
test1 = data_list['d1_valid'] + data_list['d2_valid']
test1 = [data_path + '/data/' + x.split('-E')[0] + '/' + x + '.nii.gz' for x in test1]
test1_set = BasicDataset(test1, [img_suffix, lab_suffix], pre)
test1loader = DataLoader(test1_set, batch_size=eval_batch_size)

test_results = trainer.test(test1loader, output_path, log_image=True, logsuffix='1')
sio.savemat(f'{output_path}/test1_results.mat', test_results)
with open(f'{output_path}/test1_results.txt', 'a+') as f:
    f.write(U.dict_to_str(test_results) + '\n')


from typing import Union

from pipenv.vendor.plette.models import Package, PackageCollection
from pipenv.vendor.tomlkit.container import Container
from pipenv.vendor.tomlkit.items import AoT, Array, Bool, InlineTable, Item, String, Table

try:
    import tomllib as toml
except ImportError:
    from pipenv.vendor import tomli as toml

from pipenv.vendor import tomlkit

TOML_DICT_TYPES = Union[Container, Package, PackageCollection, Table, InlineTable]
TOML_DICT_OBJECTS = (Container, Package, Table, InlineTable, PackageCollection)
TOML_DICT_NAMES = [o.__class__.__name__ for o in TOML_DICT_OBJECTS]

def cleanup_toml(tml):
    # Remove all empty lines from TOML.
    toml = "\n".join(line for line in tml.split("\n") if line.strip())
    new_toml = []
    # Add newlines between TOML sections.
    for i, line in enumerate(toml.split("\n")):
        # Skip the first line.
        if line.startswith("[") and i > 0:
            # Insert a newline before the heading.
            new_toml.append("")
        new_toml.append(line)
    # adding new line at the end of the TOML file
    new_toml.append("")
    toml = "\n".join(new_toml)
    return toml

def convert_toml_outline_tables(parsed, project):
    """Converts all outline tables to inline tables."""

    def convert_tomlkit_table(section):
        result = section.copy()
        if isinstance(section, tomlkit.items.Table):
            body = section.value._body
        elif isinstance(section, tomlkit.container.OutOfOrderTableProxy):
            body = section._internal_container._body
        else:
            body = section._body
        for key, value in body:
            if not key:
                continue
            if hasattr(value, "keys") and not isinstance(
                value, tomlkit.items.InlineTable
            ):
                table = tomlkit.inline_table()
                table.update(value.value)
                result[key.key] = table
        return result

    def convert_toml_table(section):
        result = section.copy()
        for package, value in section.items():
            if hasattr(value, "keys") and not isinstance(
                value, toml.decoder.InlineTableDict
            ):
                table = toml.TomlDecoder().get_empty_inline_table()
                table.update(value)
                result[package] = table
        return result

    is_tomlkit_parsed = isinstance(parsed, tomlkit.container.Container)
    for section in project.get_package_categories():
        table_data = parsed.get(section, {})
        if not table_data:
            continue
        if is_tomlkit_parsed:
            result = convert_tomlkit_table(table_data)
        else:
            result = convert_toml_table(table_data)

        parsed[section] = result
    return parsed

def tomlkit_value_to_python(toml_value):
    # type: (Union[Array, AoT, TOML_DICT_TYPES, Item]) -> Union[List, Dict]
    value_type = type(toml_value).__name__
    if (
        isinstance(toml_value, TOML_DICT_OBJECTS + (dict,))
        or value_type in TOML_DICT_NAMES
    ):
        return tomlkit_dict_to_python(toml_value)
    elif isinstance(toml_value, AoT) or value_type == "AoT":
        return [tomlkit_value_to_python(val) for val in toml_value._body]
    elif isinstance(toml_value, Array) or value_type == "Array":
        return [tomlkit_value_to_python(val) for val in list(toml_value)]
    elif isinstance(toml_value, String) or value_type == "String":
        return f"{toml_value!s}"
    elif isinstance(toml_value, Bool) or value_type == "Bool":
        return toml_value.value
    elif isinstance(toml_value, Item):
        return toml_value.value
    return toml_value

def tomlkit_dict_to_python(toml_dict):
    # type: (TOML_DICT_TYPES) -> Dict
    value_type = type(toml_dict).__name__
    if toml_dict is None:
        raise TypeError("Invalid type NoneType when converting toml dict to python")
    converted = None  # type: Optional[Dict]
    if isinstance(toml_dict, (InlineTable, Table)) or value_type in (
        "InlineTable",
        "Table",
    ):
        converted = toml_dict.value
    elif isinstance(toml_dict, (Package, PackageCollection)) or value_type in (
        "Package, PackageCollection"
    ):
        converted = toml_dict._data
        if isinstance(converted, Container) or type(converted).__name__ == "Container":
            converted = converted.value
    elif isinstance(toml_dict, Container) or value_type == "Container":
        converted = toml_dict.value
    elif isinstance(toml_dict, dict):
        converted = toml_dict.copy()
    else:
        raise TypeError(
            f"Invalid type for conversion: expected Container, Dict, or Table, got {toml_dict}"
        )
    if isinstance(converted, dict):
        return {k: tomlkit_value_to_python(v) for k, v in converted.items()}
    elif isinstance(converted, (TOML_DICT_OBJECTS)) or value_type in TOML_DICT_NAMES:
        return tomlkit_dict_to_python(converted)
    return converted


import torch.nn as nn
import torch.optim as optim
from Models.Resnet_Model.BasicBlock import BasicBlock
from Models.Resnet_Model.Bottle_neck import Bottle_neck
from Utils import conv_1x1

class ResNet(nn.Module):
    def __init__(self, input_channel, layers, num_classes=10, zero_init_residual=True):
        super(ResNet, self).__init__()
        self.in_planes = 64

        self.inputLayer = nn.Sequential(nn.Conv2d(input_channel, 64, kernel_size=7, stride=2, padding=3, bias=False),
                                        nn.BatchNorm2d(64),
                                        nn.ReLU(inplace=True),
                                        nn.MaxPool2d(kernel_size=3, stride=2, padding=1))

        self.inputLayer2 = nn.Sequential(self.make_layer(Bottle_neck, 64, layers[0]),
                                         self.make_layer(Bottle_neck, 128, layers[1], stride=2),
                                         self.make_layer(Bottle_neck, 256, layers[2], stride=2),
                                         self.make_layer(Bottle_neck, 512, layers[3], stride=2))

        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(512 * Bottle_neck.expansion, num_classes)

        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
            elif isinstance(m, nn.BatchNorm2d):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

        if zero_init_residual:
            for m in self.modules():
                if isinstance(m, Bottle_neck):
                    nn.init.constant_(m.bn3.weight, 0)
                elif isinstance(m, BasicBlock):
                    nn.init.constant_(m.bn2.weight, 0)

        self.optimizer = optim.Adam(self.parameters(), lr=0.001)
        self.cross_entropy = nn.CrossEntropyLoss()

    def make_layer(self, block, planes, blocks, stride=1):
        down_sample = None

        if stride != 1 or self.in_planes != planes * block.expansion:
            down_sample = nn.Sequential(
                conv_1x1(self.in_planes, planes * block.expansion, stride),
                nn.BatchNorm2d(planes * block.expansion),
            )

        layers = []
        layers.append(block(self.in_planes, planes, stride, down_sample))

        self.in_planes = planes * block.expansion

        for _ in range(1, blocks):
            layers.append(block(self.in_planes, planes))

        final_layers = nn.Sequential(*layers)
        return final_layers

    def forward(self, x):
        x = self.inputLayer(x)
        x = self.inputLayer2(x)
        x = self.avgpool(x)

        x = x.view(x.size(0), -1)
        x = self.fc(x)

        return x

    def Learn(self, inputs, labels):
        predict = self.forward(inputs)
        loss = self.cross_entropy(predict, labels)

        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

        return loss.item()


"""
by Rabbit White.
2019/12/1

Package used: cv2

Mark that
1. the file will be stored into the folder which is the
same as the source .jpg file.
2. each 12-bit data of the coe file is organized
as the order of BGR. That is the bit [11:8] is for Blue,
[7:4] for Green and [3:0] for Red. You should to pay
attention for this when using in VGA displaying.
"""

import cv2

# Describe: use this function to generate coe file.
# Parameters:
#   img_filename: the name of the source file.
#   resize_shape: the shape you want to resize your image to.
#   scale_percent: set the proportion to zoom your picture out or in.
def img_transformer(img_filename, resize_shape):
    # read in img, default as BGR
    img = cv2.imread(img_filename, 1)

    # resize image
    # resized = cv2.resize(img, None, fx=scale_percent, fy=scale_percent, interpolation=cv2.INTER_AREA)
    resized = cv2.resize(img, resize_shape, interpolation=cv2.INTER_AREA)
    # resized = img
    print(resized.shape)

    img = cv2.imwrite('sources/test.resized.jpg', resized)
    # generate the coe list
    coe = []
    for row in resized:
        for pixel in row:
            coe.append(hex(pixel[2] >> 4)[2:] + hex(pixel[1] >> 4)[2:] + hex(pixel[0] >> 4)[2:])

    # Write the coe list into the coe file.
    print()
    f = open(img_filename[:img_filename.rfind('.')+1]+'coe', 'w')
    fmem = open(img_filename[:img_filename.rfind('.')+1]+'mem', 'w')
    f.write('memory_initialization_radix=16;\n')
    f.write('memory_initialization_vector=\n')
    f.write(coe[0])
    fmem.write(coe[0])
    for c in coe[1:]:
        f.write(',\n' + c)
        fmem.write(f'\n{c}')
    f.write(';\n')
    fmem.write('\n')
    f.close()

    # show the img resized
    cv2.imshow("hebe", resized)
    cv2.waitKey(0)

def main():
    img_filename = "sources/test.jpg"
    img_transformer(img_filename=img_filename, resize_shape=(216, 216))

if __name__ == '__main__':
    main()


#!/usr/bin/env python3

import sys
sys.path.append('../Lib/')

import torch
import torch.nn as nn

from torch.utils.data import DataLoader

import random, argparse
import wikidata

from transformers import (
    AdamW,
    T5ForConditionalGeneration,
    T5Tokenizer,
    get_linear_schedule_with_warmup)

# deterministic determinism
torch.manual_seed(2020)
# torch.backends.cudnn.deterministic = True
# torch.backends.cudnn.benchmark = False
random.seed(2020)

class T5FineTuner(nn.Module):
  """A transformative experience"""

  def __init__(self):
    """Some of the best constructors in the world"""

    super(T5FineTuner, self).__init__()
    self.model = T5ForConditionalGeneration.from_pretrained(
      hparams.model_name)

  def forward(
   self,
   input_ids,
   attention_mask,
   decoder_input_ids,
   decoder_attention_mask,
   labels):
    """Forwarding"""

    output = self.model(
      input_ids=input_ids,
      attention_mask=attention_mask,
      decoder_input_ids=decoder_input_ids,
      decoder_attention_mask=decoder_attention_mask,
      labels=labels)

    return output

def fit(model, train_loader, val_loader, tokenizer):
  """Training routine"""

  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  model.to(device)

  optimizer = AdamW(model.parameters())

  for epoch in range(1, hparams.n_epochs + 1):
    train_loss, num_train_steps = 0, 0
    model.train()

    for batch in train_loader:
      optimizer.zero_grad()

      batch = tuple(t.to(device) for t in batch)
      source_ids, source_mask, target_ids, target_mask = batch

      labels = target_ids
      labels[labels[:, :] == tokenizer.pad_token_id] = -100

      outputs = model(
        input_ids=source_ids,
        attention_mask=source_mask,
        decoder_input_ids=None,
        decoder_attention_mask=target_mask,
        labels=labels)
      loss = outputs[0]

      loss.backward()

      torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
      optimizer.step()

      train_loss += loss.item()
      num_train_steps += 1

    av_loss = train_loss / num_train_steps
    val_loss = evaluate(model, val_loader, tokenizer)
    print('ep: %d, steps: %d, tr loss: %.3f, val loss: %.3f' % \
          (epoch, num_train_steps, av_loss, val_loss))

def evaluate(model, data_loader, tokenizer):
  """Evaluation routine"""

  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  model.to(device)

  total_loss, num_steps = 0, 0
  model.eval()

  for batch in data_loader:
    batch = tuple(t.to(device) for t in batch)
    source_ids, source_mask, target_ids, target_mask = batch

    labels = target_ids
    labels[labels[:, :] == tokenizer.pad_token_id] = -100

    with torch.no_grad():
      outputs = model(
        input_ids=source_ids,
        attention_mask=source_mask,
        decoder_input_ids=None,
        decoder_attention_mask=target_mask,
        labels=labels)
      loss = outputs[0]

    total_loss += loss.item()
    num_steps += 1

  average_loss = total_loss / num_steps
  return average_loss

def generate(model, data_loader, tokenizer):
  """Need to add 'summarize' if run before training"""

  device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
  model.to(device)
  model.eval()

  for batch in data_loader:
    batch = tuple(t.to(device) for t in batch)
    source_ids, source_mask, target_ids, target_mask = batch

    inputs = tokenizer.batch_decode(source_ids)
    targets = tokenizer.batch_decode(target_ids)

    predictions = model.model.generate(
      input_ids=source_ids,
      max_length=hparams.max_output_length,
      early_stopping=True,
      num_beams=2,
      attention_mask=source_mask)
    predictions = tokenizer.batch_decode(
      predictions,
      skip_special_tokens=True,
      clean_up_tokenization_spaces=True)

    print('input:', inputs[0])
    print('target:', targets[0])
    print('prediction:', predictions[0])
    print()

def main():
  """Fine-tune on summarization data"""

  tokenizer = T5Tokenizer.from_pretrained(hparams.model_name)

  train_dataset = wikidata.WikiHow(
    tokenizer=tokenizer,
    split='train',
    input_length=hparams.max_input_length,
    output_length=hparams.max_output_length)
  train_data_loader = DataLoader(
    train_dataset,
    batch_size=hparams.batch_size)

  val_dataset = wikidata.WikiHow(
    tokenizer=tokenizer,
    split='validation',
    input_length=hparams.max_input_length,
    output_length=hparams.max_output_length)
  val_data_loader = DataLoader(
    val_dataset,
    batch_size=hparams.batch_size)

  model = T5FineTuner()

  fit(model, train_data_loader, val_data_loader, tokenizer)
  generate(model, val_data_loader, tokenizer)

if __name__ == "__main__":
  "My kind of street"

  hparam_dict = dict(
    model_name='t5-small',
    max_input_length=512,
    max_output_length=150,
    batch_size=32,
    n_epochs=5)
  hparams = argparse.Namespace(**hparam_dict)
  print('hyper-parameters:', hparams)

  main()


# python3

from abc import ABC
from collections import namedtuple
from sys import setrecursionlimit, stdin
from threading import stack_size, Thread
from typing import AnyStr, IO, List
from unittest import TestCase

setrecursionlimit(10 ** 6)
stack_size(2 ** 27)

test = namedtuple('test', 'input expected')

class TreeOrders:
    def __init__(self):
        self.n = 0
        self.key = self.left = self.right = None

    def read(self, src: IO):
        self.n = int(src.readline())
        self.key = [0 for _ in range(self.n)]
        self.left = [0 for _ in range(self.n)]
        self.right = [0 for _ in range(self.n)]
        for i in range(self.n):
            [self.key[i], self.left[i], self.right[i]] = map(int, src.readline().split())
        return self

    def walk(self) -> List[List[int]]:
        result, buf = [], [0 for _ in range(self.n)]

        for walk in [self.in_order, self.pre_order, self.post_order]:
            walk(buf)
            result.append(buf[:])

        return result

    def in_order(self, buf: List[int], node: int = 0, position: int = 0) -> int:
        if self.left[node] != -1:
            position = self.in_order(buf, self.left[node], position)

        buf[position] = self.key[node]
        position += 1

        if self.right[node] != -1:
            position = self.in_order(buf, self.right[node], position)

        return position

    def pre_order(self, buf: List[int], node: int = 0, position: int = 0) -> int:
        buf[position] = self.key[node]
        position += 1

        if self.left[node] != -1:
            position = self.pre_order(buf, self.left[node], position)

        if self.right[node] != -1:
            position = self.pre_order(buf, self.right[node], position)

        return position

    def post_order(self, buf: List[int], node: int = 0, position: int = 0) -> int:
        if self.left[node] != -1:
            position = self.post_order(buf, self.left[node], position)

        if self.right[node] != -1:
            position = self.post_order(buf, self.right[node], position)

        buf[position] = self.key[node]
        position += 1

        return position

class Fake(IO, ABC):
    def __init__(self, rows: List[str]):
        self.__i = -1
        self.__rows = [str(len(rows))] + rows

    def readline(self, limit: int = -1) -> AnyStr:
        self.__i += 1
        return self.__rows[self.__i]

class Test(TestCase):
    def test_tree_orders(self):
        tests = [
            # samples
            test([
                '4 1 2',
                '2 3 4',
                '5 -1 -1',
                '1 -1 -1',
                '3 -1 -1',
            ], [
                '1 2 3 4 5',
                '4 2 1 3 5',
                '1 3 2 5 4',
            ]),
            test([
                '0 7 2',
                '10 -1 -1',
                '20 -1 6',
                '30 8 9',
                '40 3 -1',
                '50 -1 -1',
                '60 1 -1',
                '70 5 4',
                '80 -1 -1',
                '90 -1 -1',
            ], [
                '50 70 80 30 90 40 0 20 10 60',
                '0 70 50 40 30 80 90 20 60 10',
                '50 80 90 30 40 70 10 60 20 0',
            ]),
        ]
        for i, t in enumerate(tests):
            src = Fake(t.input)
            self.assertEqual(t.expected,
                             list(map(lambda way: ' '.join(str(step) for step in way), TreeOrders().read(src).walk())),
                             msg='at {} position'.format(i))

def main():
    for way in TreeOrders().read(stdin).walk():
        print(' '.join(str(step) for step in way))

if __name__ == '__main__':
    Thread(target=main).start()


"""
An all around bot for discord
loosely based on functionality provided by leaf for Patch Gaming
written by isometricramen
"""

import asyncio
import random
import re
import time
from datetime import datetime, timedelta
from difflib import unified_diff
from itertools import dropwhile
from traceback import format_exc
from typing import (
    AsyncGenerator,
    AsyncIterator,
    Callable,
    Coroutine,
    Generator,
    Iterator,
    List,
    Optional,
    Set,
)

import discord
from requests import post

from petal import grasslands
from petal.commands import CommandRouter as Commands
from petal.commands.core import CommandPending
from petal.config import cfg
from petal.dbhandler import DBHandler
from petal.etc import mash, filter_members_with_role, timestr
from petal.exceptions import TunnelHobbled, TunnelSetupError
from petal.tunnel import Tunnel
from petal.types import PetalClientABC, Src
from petal.util import questions
from petal.util.cdn import get_avatar
from petal.util.embeds import Color, membership_card
from petal.util.fmt import escape, mask, mono, mono_block, userline
from petal.util.grammar import pluralize
from petal.util.minecraft import Minecraft
from petal.util.numbers import word_number

short_time: timedelta = timedelta(seconds=10)
log = grasslands.Peacock()

version = "<UNSET>"
with open("version_info.sh", "r") as f:
    for _line in f:
        if _line.startswith("VERSION="):
            version = _line.split("=", 1)[1].split("#")[0].strip(" \n\"'")

grasslands.version = version

def first_role_named(name: str, guild: discord.Guild):
    for role in guild.roles:
        if role.name == name:
            return role

class Petal(PetalClientABC):
    logLock = False

    def __init__(self, devmode=False):
        try:
            super().__init__(intents=discord.Intents.all())
        except Exception as e:
            log.err(f"Could not initialize client object: {e}")
        else:
            log.info("Client object initialized")

        self.config = cfg
        self.startup = datetime.utcnow()
        self.startup_unix = self.startup.timestamp()

        self.db = DBHandler(self.config)

        self.minecraft = Minecraft(self)
        self.commands = Commands(self)
        self.commands.version = version

        self.loop_tasks: List[asyncio.Future] = []
        self.potential_typo = {}
        self.session_id = hex(mash(datetime.utcnow(), digits=5, base=16)).upper()
        self.tempBanFlag = False
        self.tunnels = []

        self.dev_mode = devmode
        log.info("Configuration object initalized")

    def run(self):
        try:
            super().run(self.config.token, bot=not self.config.get("selfbot"))
        except AttributeError as e:
            log.err(f"Could not connect using the token provided: {e}")
            return 1

        except discord.errors.LoginFailure as e:
            log.err(
                f"Authenication Failure. Your auth:\n{self.config.token}"
                f"\nis invalid: {e}"
            )
            return 401

        else:
            return 0

    @property
    def uptime(self):
        return datetime.utcnow() - self.startup

    @staticmethod
    def is_pm(message):
        if message.channel.is_private:
            return True
        else:
            return False

    @staticmethod
    def remove_prefix(content):
        return content[len(content.split()[0]) :]

    @property
    def main_guild(self) -> discord.Guild:
        if len(self.guilds) == 0:
            log.err("This client is not a member of any guilds")
            exit(404)
        return self.get_guild(self.config.get("mainServer"))

    def register_loop(
        self, coro: Callable[..., Coroutine], name: str, *, restart: bool = False
    ):
        async def run():
            log.ready(f"{name} coroutine running...")

            while True:
                try:
                    await coro()

                except asyncio.CancelledError:
                    log.err(f"{name} coroutine cancelled.")
                    break

                except Exception as e:
                    log.err(f"{name} coroutine FAILED: {type(e).__name__}: {e}")

                    if restart:
                        log.ready(f"{name} coroutine RESTARTING in 10 seconds...")
                        await asyncio.sleep(10)
                        continue
                    else:
                        break

                else:
                    log.info(f"{name} coroutine finished.")
                    break

        self.loop_tasks.append(self.loop.create_task(run()))

    async def status_loop(self):
        interv = 32
        # times = {"start": self.startup.timestamp() * 1000}
        g_ses = discord.Activity(
            name=f"Session: {self.session_id[2:]}",
            # timestamps=times,
            type=discord.ActivityType.playing,
        )
        g_ver = discord.Activity(
            name=f"Version: {version}",
            # timestamps=times,
            type=discord.ActivityType.playing,
        )
        await self.change_presence(
            activity=discord.Activity(
                name=f"Starting: {self.session_id[2:]}",
                type=discord.ActivityType.playing,
            )
        )
        while True:
            try:
                await asyncio.sleep(interv)
                await self.change_presence(activity=g_ses)
                await asyncio.sleep(interv)
                await self.change_presence(
                    activity=discord.Game(name=f"Uptime: {str(self.uptime)[:-10]}")
                )
                await asyncio.sleep(interv)
                await self.change_presence(activity=g_ver)
                await asyncio.sleep(interv)
                await self.change_presence(
                    activity=discord.Game(name=self.config.prefix + "info")
                )

            except asyncio.CancelledError:
                raise

            except Exception as e:
                log.warn(f"Failed to change Status: {type(e).__name__}: {e}")
                await asyncio.sleep(interv * 4)

    async def save_loop(self):
        if self.dev_mode:
            return
        interval = self.config.get("autosaveInterval")
        while True:
            self.config.save()
            await asyncio.sleep(interval)

    async def ask_patch_loop(self):
        if self.dev_mode:
            return
        if self.config.get("motdInterval") is None:
            log.f("PA", "not using MOTD stuff...")
            return
        interval = self.config.get("motdInterval")
        while True:
            await self.commands.check_pa_updates()
            await asyncio.sleep(interval)

    async def ban_loop(self):
        # if self.dev_mode:
        #     return
        mainguild: discord.Guild = self.get_guild(self.config.get("mainServer"))
        interval = self.config.get("unbanInterval")
        log.f("BANS", f"Checking for temp unbans (Interval: {interval})")
        await asyncio.sleep(interval)
        while True:
            epoch = int(time.time())
            log.f("BANS", "Now Timestamp: " + str(epoch))

            for entry in await mainguild.bans():
                user = entry.user
                # log.f("UNBANS", m.name + "({})".format(m.id))
                ban_expiry: int = int(
                    self.db.get_attribute(user, "banExpires", verbose=False) or 0
                )

                if not ban_expiry or not self.db.get_attribute(user, "tempBanned"):
                    continue

                if ban_expiry <= int(epoch):
                    log.f(f"{ban_expiry} compared to {epoch}")
                    print(flush=True)
                    try:
                        await mainguild.unban(user, reason="Tempban Expired")
                    except discord.Forbidden:
                        log.f("BANS", f"Lacking permission to unban {user.id}.")
                    except discord.HTTPException as e:
                        log.f("BANS", f"FAILED to unban {user.id}: {e}")
                    else:
                        self.db.update_member(user, {"banned": False})
                        log.f("BANS", f"Unbanned {user.name} ({user.id}) ")
                else:
                    log.f(
                        "BANS",
                        f"{user.name} ({user.id}) has {int(ban_expiry) - epoch} seconds left",
                    )
                await asyncio.sleep(0.5)

            await asyncio.sleep(interval)

    async def member_stats_update_loop(self):
        interval = 30
        while True:
            d = datetime.utcnow()
            if not (d.hour == 0 and d.minute == 00):
                log.debug(f"UTC Time is actually: {d.hour}:{d.minute} which is wrong")
                await asyncio.sleep(60)
                continue
            guild: discord.Guild = self.get_guild(self.config.get("mainServer"))
            member_role: discord.Role = guild.get_role(self.config.get("mainRoleId"))
            log.debug(f"Member role for guild: {guild.name} is {member_role.name}")
            body = {
                "value1": guild.member_count,
                "value2": len(filter_members_with_role(guild.members, member_role)),
            }
            url = self.config.get("iftttEvent")
            response = post(url, body)
            if response.status_code == 200:
                log.debug("Successfully pushed data to IFTTT")
            else:
                log.err(
                    f"An error occurred while trying to push to IFTTT:"
                    f" ({response.status_code})[{response.text}]"
                )
            await asyncio.sleep(interval)

    async def close_tunnels_to(self, channel):
        """Given a Channel, remove it from any/all Tunnels connecting to it."""
        for t in self.tunnels:
            await t.drop(channel)

    async def dig_tunnel(self, origin, *channels: List[int], anon=False):
        """Create a new Tunnel.
        The first Positional Argument is the Origin Channel, the Channel to
        which to report back in case of problems. All subsequent Positional
        Arguments are Integer IDs.
        """
        new = Tunnel(self, origin, *channels, anon)
        try:
            tunnel_coro = new.activate()
        except TunnelSetupError:
            return False
        else:
            self.tunnels.append(new)
            await tunnel_coro

    def get_tunnel(self, channel):
        """Given a Channel, return the first Tunnel connected to it, if any."""
        for t in self.tunnels:
            if channel in t.connected:
                return t

    async def kill_tunnel(self, t: Tunnel):
        """Given a Tunnel, kill it. Duh."""
        if t:
            await t.kill()

    def remove_tunnel(self, t: Tunnel):
        """Given a dead Tunnel, remove it from the Client Tunnels."""
        if t.active:
            raise TunnelHobbled("Cannot Remove a Tunnel which is still active.")
        while t in self.tunnels:
            self.tunnels.remove(t)

    async def on_member_ban(self, member):
        print("Giving database a chance to sync...")
        await asyncio.sleep(1)

        with self.minecraft.db(str(member.id)) as mc:
            for entry in mc:
                if not entry["suspended"]:
                    entry["suspended"] = 403

        if not self.db.member_exists(member):
            return
        banstate = self.db.get_attribute(member, "tempBanned")
        if banstate:
            print(f"Member{member.name}({member.id}) tempbanned, ignoring")
            return

        self.db.update_member(member, {"tempBanned": False})
        print(f"Member {member.name} ({member.id}) was banned manually")

    async def on_ready(self):
        """
        Called once a connection has been established
        """
        log.ready(f"Running discord.py version: {discord.__version__}")
        log.ready("Connected to Discord!")
        log.info(
            f"Logged in as {self.user.name}#{self.user.discriminator} ({self.user.id})"
        )
        log.info(f"Prefix: {self.config.prefix}")
        log.info(f"SelfBot: {not bool(self.config.useToken)}")

        self.register_loop(self.status_loop, "Gamestatus", restart=True)
        self.register_loop(self.save_loop, "Autosave", restart=True)
        self.register_loop(self.ban_loop, "Auto-unban", restart=True)
        self.register_loop(
            self.member_stats_update_loop, "Daily Stats Update", restart=True
        )

        if self.config.get("dbconf") is not None:
            self.register_loop(self.ask_patch_loop, "MOTD", restart=True)
        else:
            log.warn(
                "No dbconf configuration in config.yml, motd features are disabled"
            )

    async def print_response(self, src: Src, response, to_edit: discord.Message = None):
        """Use a discrete method for this, so that it can be used recursively if
            needed.

        Return Types of Command Methods:
        def, return         ->  Any             - send(value)
        def, yield          ->  Generator       - for x in value: send(x)
        async def, return   ->  Coroutine       - send(await value)
        async def, yield    ->  AsyncGenerator  - async for x in value: send(x)
        """
        # print("Outputting Response:", repr(response))
        while isinstance(response, Coroutine):
            # Ensure that the Response is actually final.
            response = await response
            # print("Awaited Response:", repr(response))

        if response is None:
            # Ignore Void Responses.
            return

        elif isinstance(response, BaseException):
            await src.channel.send(
                f"Command Yielded an Exception: {type(response).__name__}"
                + (f": {response}" if str(response) else "")
            )

        elif isinstance(
            response, (AsyncGenerator, AsyncIterator, Generator, Iterator, list, tuple)
        ):
            # Response is a Generator, indicating the method used Yielding, or a
            #   List or Tuple, which should be treated the same. Yield command
            #   returns support flushing and clearing the List of Buffered
            #   Lines, with True and False, respectively.
            # print("Iterating Type:", type(response))
            if to_edit:
                # Due to the ability to chain multiple messages by yielding, we
                #   cannot cleanly take advantage of editing. Delete it.
                await to_edit.delete()

            buffer: list = []

            async def push(line):
                # print("  Reading Line:", repr(line))
                if line is True:
                    # Upon reception of True, "flush" the current "buffer" by
                    #   posting a Message.
                    # print("    Printing Buffer:", repr(buffer))
                    if buffer:
                        await self.print_response(src, "\n".join(map(str, buffer)))
                        buffer.clear()

                elif line is False:
                    # Upon reception of False, discard the buffer.
                    # print("    Discarding Buffer:", repr(buffer))
                    buffer.clear()

                elif isinstance(line, questions.Question):
                    # Upon reception of a Question, ask() the Question in the
                    #   original Channel. Then, [a]send() the Response to the
                    #   Command Generator.
                    res2 = await line.ask(self, src.channel, src.author)

                    if isinstance(response, AsyncGenerator):
                        try:
                            await push(await response.asend(res2))
                        except StopAsyncIteration:
                            pass

                    elif isinstance(response, Generator):
                        try:
                            await push(response.send(res2))
                        except StopIteration:
                            pass

                    # Do not Raise anything if the Command is not a Generator,
                    #   because the Question Class is purposefully open-ended.
                    #   While it is unlikely anyone would go to the effort, it
                    #   is possible that ask()ing the Question handles its own
                    #   Response.

                elif isinstance(line, (BaseException, dict, discord.Embed)):
                    # Upon reception of a Dict or an Embed, send it in a Message
                    #   immediately.
                    await self.print_response(src, line)

                elif isinstance(line, (Generator, Iterator, list, tuple)):
                    # Upon reception of any Sequence, treat it the same as
                    #   reception of its elements in sequence.
                    for elem in line:
                        while isinstance(elem, Coroutine):
                            elem = await elem
                        await push(elem)

                else:
                    # Anything else is added to the buffer.
                    # print("    Appending to Buffer.")
                    buffer.append(line)

            try:
                if isinstance(response, (AsyncGenerator, AsyncIterator)):
                    async for y in response:
                        while isinstance(y, Coroutine):
                            y = await y
                        await push(y)

                else:
                    for y in response:
                        while isinstance(y, Coroutine):
                            y = await y
                        await push(y)

            finally:
                if buffer:
                    await push(True)

                del buffer

        elif isinstance(response, dict):
            # If the response is a Dict, it is a series of keyword arguments
            #   intended to be passed directly to `Channel.send()`.
            # print("Building from Dict.")
            if to_edit:
                vals = {"content": None, "embed": None}
                vals.update(response)
                await to_edit.edit(**vals)
            else:
                await src.channel.send(**response)

        elif isinstance(response, discord.Embed):
            # If the response is an Embed, simply show it as normal.
            # print("Sending Embed.")
            if to_edit:
                await to_edit.edit(content=None, embed=response)
            else:
                await src.channel.send(embed=response)

        elif isinstance(response, str):
            # Same with String.
            # print("Sending String.")
            if to_edit:
                await to_edit.edit(content=response, embed=None)
            else:
                await self.send_message(src.author, src.channel, str(response))

        else:
            # And everything else.
            # print("Sending Other.")
            if to_edit:
                await to_edit.edit(content=repr(response), embed=None)
            else:
                await self.send_message(src.author, src.channel, str(response))

    async def execute_command(self, message):
        command = self.potential_typo.get(message.id) or CommandPending(
            self.potential_typo, self.print_response, self.commands, message
        )
        try:
            return await command.run()

        except Exception as e:
            if isinstance(command.channel, discord.TextChannel):
                await self.log_moderation(
                    embed=discord.Embed(
                        title="Unhandled Exception in Command",
                        description=escape(command.src.content),
                    )
                    .add_field(
                        name="Author",
                        value=f"{command.invoker.mention}"
                        f"\n`{command.invoker.name}#{command.invoker.discriminator}`"
                        f"\n`{command.invoker.id}`",
                    )
                    .add_field(
                        name="Location",
                        value=f"{command.channel.mention}"
                        f"\n`#{command.channel.name}`"
                        f"\n`{command.channel.guild.name}`",
                    )
                    .add_field(
                        name=type(e).__name__,
                        value=escape(str(e) or "<No Details>"),
                        inline=False,
                    )
                    .add_field(
                        name="Exception Traceback",
                        value=mono_block(format_exc()),
                        inline=False,
                    )
                )

    async def send_message(
        self,
        author: discord.Member = None,
        channel: discord.abc.Messageable = None,
        message: str = None,
        *,
        embed: discord.Embed = None,
        **_,
    ) -> Optional[discord.Message]:
        """
        Overload on the send_message function
        """
        if not message and not embed:
            # Without a message to send, dont even try; it would just error
            return None

        afters = set(" ,.…:¿?¡!")

        if (
            author is not None
            and not set(message) <= afters & {"`"}
            and self.db.get_member(author) is not None
            and self.db.get_attribute(author, "ac")
        ):
            try:
                ac = random.choice(list(self.db.ac.find()))["ending"]
                blocks = ""
                msg = message

                while msg.endswith("```"):
                    segment = msg.rsplit("```", 2)
                    if len(segment) == 3 and segment[0] and not segment[2]:
                        msg = segment[0]
                        blocks = f"```{segment[1]}```{blocks}"

                i = 0
                while msg[-(i + 1)] in afters:
                    i += 1

                msg, end = (msg[:-i], msg[-i:]) if i > 0 else (msg, "")
            except:
                pass
            else:
                if msg:
                    message = f"{msg}, {ac}{end}{blocks}"

        if self.dev_mode:
            message = f"[DEV]  {message}  [DEV]"
        try:
            return await channel.send(content=message, embed=embed)
        except (discord.errors.Forbidden, discord.errors.InvalidArgument) as e:
            log.err(
                f"Message could not be sent in #{channel.name}/{channel.id}:"
                f"\n    {message!r}\n    ({type(e).__name__}) {e}"
            )

    async def log_membership(
        self, content: str = None, *, embed: discord.Embed = None
    ) -> Optional[discord.Message]:
        channel: discord.abc.Messageable = self.get_channel(
            self.config.get("logChannel", 0)
        )
        if not channel:
            log.err("Cannot post message to 'logChannel'.")
            return None
        else:
            if content is not None and embed is not None:
                return await channel.send(content, embed=embed)
            elif content is not None:
                return await channel.send(content)
            elif embed is not None:
                return await channel.send(embed=embed)
            else:
                return None

    async def log_moderation(
        self, content: str = None, *, embed: discord.Embed = None
    ) -> Optional[discord.Message]:
        channel: discord.abc.Messageable = self.get_channel(
            self.config.get("modChannel", 0)
        )
        if not channel:
            log.err("Cannot post message to 'modChannel'.")
            return None
        else:
            if content is not None and embed is not None:
                return await channel.send(content, embed=embed)
            elif content is not None:
                return await channel.send(content)
            elif embed is not None:
                return await channel.send(embed=embed)
            else:
                return None

    async def embed(
        self,
        channel: discord.abc.Messageable,
        embedded: discord.Embed,
        content: str = None,
    ):
        if self.dev_mode:
            embedded.add_field(name="DEV", value="DEV")

        if not channel:
            raise RuntimeError("Channel not provided.")

        if content is not None:
            return await channel.send(content=content, embed=embedded)
        else:
            return await channel.send(embed=embedded)

    async def on_member_join(self, member):
        """To be called When a new member joins the server"""
        card = membership_card(member, colour=Color.user_join)

        if self.db.useDB:
            if self.db.member_exists(member):
                # This User has been here before.
                card.set_author(name="Returning Member")
                aliases = self.db.get_attribute(member, "aliases")
                if aliases:
                    first, last = aliases[0], aliases[1]
                    card.insert_field_at(
                        0,
                        name="Originally seen as",
                        value=f"{escape(first)}\n{escape(ascii(first))}",
                    )
                    if first != last:
                        card.insert_field_at(
                            1,
                            name="Last seen as",
                            value=f"{escape(last)}\n{escape(ascii(last))}",
                        )
            else:
                # We have no previous record of this User.
                if not isinstance(member, discord.Member):
                    member = self.main_guild.get_member(member.id)
                self.db.add_member(member)
                card.set_author(name="New Member")

            self.db.update_member(
                member, {"aliases": [member.name], "guilds": [member.guild.id]}
            )
        else:
            card.set_author(name="Joining Member")

        welcome = self.config.get("welcomeMessage", None)
        if welcome and welcome != "null":
            try:
                await member.send(welcome)
            except Exception as e:
                card.add_field(
                    name="Welcome",
                    value=f"User could not be sent a Welcome Message:"
                    f" {type(e).__name__}: {e}",
                    inline=False,
                )
            else:
                card.add_field(
                    name="Welcome",
                    value="User was sent a Welcome Message in DM.",
                    inline=False,
                )
        else:
            card.add_field(
                name="Welcome", value="No Welcome Message is configured.", inline=False
            )

        with self.minecraft.db(str(member.id)) as mc:
            to_okay: list = [entry for entry in mc if entry["suspended"] == 104]

            if to_okay:
                names: List[str] = []

                for entry in to_okay:
                    names.append(entry["name"])
                    entry["suspended"] = 0

                card.add_field(
                    name="Unsuspending from Minecraft",
                    value="\n".join(names),
                    inline=False,
                )

        await self.log_membership(embed=card)

    async def on_member_remove(self, member):
        """To be called when a member leaves"""
        card = membership_card(member, colour=Color.user_part)
        card.set_author(
            name="Member Left", icon_url="https://puu.sh/tB7bp/f0bcba5fc5.png"
        )

        with self.minecraft.db(str(member.id)) as mc:
            to_suspend: list = [entry for entry in mc if not entry["suspended"]]

            if to_suspend:
                names: List[str] = []

                for entry in to_suspend:
                    names.append(entry["name"])
                    entry["suspended"] = 104

                card.add_field(
                    name="Suspending from Minecraft",
                    value="\n".join(names),
                    inline=False,
                )

        await self.log_membership(embed=card)

    async def on_message_delete(self, message: discord.Message):
        try:
            if message.channel.id in self.config.get(
                "ignoreChannels", []
            ) or not isinstance(message.channel, discord.TextChannel):
                return

            now = datetime.utcnow()
            guild: discord.Guild = message.guild

            executor = None
            reason = None
            try:
                async for record in guild.audit_logs(
                    limit=10,
                    after=now - short_time,
                    oldest_first=False,
                    action=discord.AuditLogAction.message_delete,
                ):
                    if (
                        record.target == message.author
                        and record.extra.channel.id == message.channel.id
                    ):
                        executor = record.user
                        reason = record.reason
                        break
            except (discord.Forbidden, discord.HTTPException):
                can_audit = False
            else:
                can_audit = True

            em = (
                discord.Embed(
                    title="Message Deleted",
                    description=f"A Message by {message.author.mention} was deleted.",
                    colour=Color.message_delete,
                )
                .set_author(
                    name=message.author.display_name,
                    icon_url=get_avatar(message.author),
                )
                .set_footer(text=userline(message.author))
            )

            if message.content:
                em.add_field(
                    name="Content", value=escape(message.content), inline=False
                )
            if message.embeds:
                n = len(message.embeds)
                em.add_field(
                    name="Embeds",
                    value=f"Message had {word_number(n)} ({n})"
                    f" {pluralize(n, 'Embed')}.",
                    inline=False,
                )
            if message.attachments:
                n = len(message.attachments)
                em.add_field(
                    name="Attachments",
                    value=f"Message had {word_number(n)} ({n})"
                    f" {pluralize(n, 'Attachment')}.",
                    inline=False,
                )

            if can_audit:
                if executor is None:
                    em.add_field(
                        name="Deleter",
                        value="Message was probably deleted by __the Author__.",
                        inline=False,
                    )
                else:
                    em.add_field(
                        name="Deleter",
                        value=f"Message was probably deleted by:"
                        f"\n`{escape(userline(executor))}`"
                        f"\n{executor.mention}",
                        inline=False,
                    )
            else:
                em.add_field(
                    name="Deleter",
                    value="Insufficient Permissions to find Deleter.",
                    inline=False,
                )

            if reason is not None:
                em.add_field(name="Reason for Deletion", value=reason, inline=False)

            em.add_field(
                name="Channel",
                value=f"`#{message.channel.name}`\n{message.channel.mention}",
            )
            em.add_field(name="Guild", value=guild.name)
            em.add_field(
                name="────────────",
                value=mask(message.jump_url, "Jump to Message"),
                inline=False,
            )

            em.add_field(name="Time of Creation", value=timestr(message.created_at))

            last = message.edited_at
            if last is not None:
                em.add_field(name="Last Edited", value=timestr(last))

            em.add_field(name="Time of Deletion", value=timestr(now))

            await self.log_moderation(embed=em)
        except discord.errors.HTTPException:
            return

    async def on_message_edit(self, before: Src, after: Src):
        if (
            Petal.logLock
            or before.content == ""
            or not isinstance(before.channel, discord.TextChannel)
            or after.content == ""
            or before.content == after.content
            or before.guild.id in self.config.get("ignoreServers")
            or before.channel.id in self.config.get("ignoreChannels")
        ):
            return

        # If the message was marked as a possible typo by the command router,
        #   try running it again.
        executed = before.id in self.potential_typo and await self.execute_command(
            after
        )

        em = (
            discord.Embed(
                title="Message Edit (with command execution)"
                if executed
                else "Message Edit",
                description=f"{before.author.mention} edited their message.",
                colour=Color.message_edit,
            )
            .add_field(
                name="Original Content", value=escape(before.content), inline=False
            )
            .add_field(name="Edited Content", value=escape(after.content), inline=False)
        )

        if max(before.content.count("\n"), after.content.count("\n")) >= 3:
            diff = dropwhile(
                (lambda s: not s.startswith("@")),
                unified_diff(
                    before.content.splitlines(), after.content.splitlines(), n=2
                ),
            )
            em.add_field(
                name="Unified Diff",
                value="```diff\n{}```".format("\n".join(diff)),
                inline=False,
            )

        em.add_field(
            name="Channel",
            value=f"`#{before.channel.name}`\n{before.channel.mention}",
        ).add_field(name="Guild", value=before.guild.name).add_field(
            name="────────────",
            value=mask(after.jump_url, "Jump to Message"),
            inline=False,
        ).set_author(
            name=before.author.display_name, icon_url=get_avatar(before.author)
        ).set_footer(
            text=userline(before.author)
        ).add_field(
            name="Time of Creation", value=timestr(before.created_at)
        )

        last = before.edited_at
        if last is not None:
            em.add_field(name="Previous Edit", value=timestr(last))

        em.add_field(name="Time of Edit", value=timestr())

        try:
            await self.log_moderation(embed=em)
        except discord.errors.HTTPException:
            log.warn(
                "HTTP 400 error from the edit statement. "
                + "Usually it's safe to ignore it"
            )

    async def on_member_update(self, before: discord.Member, after: discord.Member):
        if Petal.logLock:
            return

        r_aft: Set[discord.Role] = set(after.roles)
        r_bef: Set[discord.Role] = set(before.roles)

        if r_aft ^ r_bef:
            em = (
                discord.Embed(
                    title=f"User Roles on {after.guild.name!r} updated",
                    colour=Color.user_promote,
                )
                .set_author(
                    name=self.user.name, icon_url="https://puu.sh/tBpXd/ffba5169b2.png"
                )
                .set_thumbnail(url=get_avatar(after))
                .set_footer(text=userline(after))
            )

            gain = r_aft - r_bef
            if gain:
                em.add_field(
                    name="Gained",
                    value="\n".join(
                        "{} (`{}`#{})".format(role.mention, escape(role.name), role.id)
                        for role in gain
                    ),
                    inline=False,
                )

            lost = r_bef - r_aft
            if lost:
                em.add_field(
                    name="Lost",
                    value="\n".join(
                        "{} (`{}`#{})".format(role.mention, escape(role.name), role.id)
                        for role in lost
                    ),
                    inline=False,
                )

            await self.log_moderation(
                embed=em.add_field(name="Timestamp", value=timestr(), inline=False)
            )

        if before.nick != after.nick:
            em = (
                discord.Embed(
                    title="Nickname Change",
                    description=f"{after.mention} changed their nickname.",
                    colour=Color.user_update,
                )
                .add_field(name="Before", value=escape(before.nick))
                .add_field(name="After", value=escape(after.nick))
                .add_field(name="Timestamp", value=timestr(), inline=False)
                .set_thumbnail(url=get_avatar(after))
                .set_footer(text=userline(after))
            )

            await self.log_moderation(embed=em)

    async def on_user_update(self, before: discord.User, after: discord.User):
        if before.name != after.name:
            em = (
                discord.Embed(
                    title="Username Change",
                    description=f"{after.mention} changed their username.",
                    colour=Color.user_update,
                )
                .add_field(name="Before", value=escape(before.name))
                .add_field(name="After", value=escape(after.name))
                .add_field(name="Timestamp", value=timestr(), inline=False)
                .set_thumbnail(url=get_avatar(after))
                .set_footer(text=userline(after))
            )

            await self.log_moderation(embed=em)

        if before.avatar_url != after.avatar_url:
            em = (
                discord.Embed(
                    title="Avatar Change",
                    description=f"{after.mention} changed their Avatar.",
                    colour=Color.user_update,
                )
                .add_field(name="Timestamp", value=timestr(), inline=False)
                .set_thumbnail(url=get_avatar(before))
                .set_footer(text=userline(after))
                .set_image(url=get_avatar(after))
            )

            await self.log_moderation(embed=em)

    # async def on_voice_state_update(self, before, after):
    #
    #     # FIXME: This needs to have a limiter. Use at own risk of spam.
    #     if self.config.tc is None:
    #         return
    #     else:
    #         return
    #     tc = self.config.tc
    #     trackedChan = self.get_channel(tc["monitoredChannel"])
    #     postChan = self.get_channel(tc["destinationChannel"])
    #     if trackedChan is None:
    #         log.err("Invalid tracking channel. Function disabled")
    #         self.config.tc = None
    #         return
    #     if postChan is None:
    #         log.err("Invalid posting channel. Function disabled")
    #         self.config.tc = None
    #         return
    #     if before.voice_channel != trackedChan and after.voice_channel == trackedChan:
    #         try:
    #             await self.send_message(None, after, tc["messageToUser"])
    #         except discord.errors.HTTPException:
    #             log.warn("Unable to PM {}".format(before.name))
    #         else:
    #             msg = await self.wait_for_message(
    #                 author=after, check=self.is_pm, timeout=200
    #             )
    #             if msg is None:
    #                 return
    #             else:
    #                 if msg.content.lower() in [
    #                     "yes",
    #                     "confirm",
    #                     "please",
    #                     "yeah",
    #                     "yep",
    #                     "mhm",
    #                 ]:
    #                     await self.send_message(
    #                         None,
    #                         postChan,
    #                         tc["messageFormat"].format(
    #                             user=after, channel=after.voice_channel
    #                         ),
    #                     )
    #                 else:
    #                     await self.send_message(
    #                         None,
    #                         channel,
    #                         "Alright, just to let"
    #                         + "you know. If you "
    #                         + "have a spotty "
    #                         + "connection, you may"
    #                         + " get PM'd more than "
    #                         + "once upon joining"
    #                         + " this channel",
    #                     )
    #                 return

    async def on_message(self, message: Src):
        await self.wait_until_ready()
        content = message.content.strip()
        try:
            if isinstance(message.channel, discord.TextChannel):
                self.db.update_member(
                    message.author,
                    {
                        "aliases": message.author.name,
                        "guilds": message.guild.id,
                        "last_message_channel": message.channel.id,
                        "last_active": message.created_at,
                        "last_message": message.created_at,
                    },
                    type=1,
                )
                m_count = self.db.get_attribute(message.author, "message_count")
                if m_count is not None:
                    self.db.update_member(
                        message.author,
                        {"message_count": m_count + 1},
                    )
        except Exception as e:
            log.err("{} on Message: {}".format(type(e).__name__, str(e)))

        if (
            message.author == self.user
            or message.content == self.config.prefix
            or message.author.id in self.config.blacklist
        ):
            return

        if len(message.mentions) >= 10:
            # Potential here to autoban tag spammers.
            pass

        for word in message.content.split():
            if message.channel.id in self.config.get("ignoreChannels"):
                break
            if word in self.config.wordFilter:
                embed = discord.Embed(
                    title="Word Filter Hit",
                    description="At least one filtered word was detected",
                    colour=Color.alert,
                )

                embed.add_field(
                    name="Author",
                    value=message.author.name + "#" + message.author.discriminator,
                )
                embed.add_field(name="Channel", value=message.channel.name)
                embed.add_field(name="Guild", value=message.guild.name)
                embed.add_field(name="Content", value=message.content)
                embed.add_field(name="Detected word", value=word, inline=False)
                embed.add_field(name="Timestamp", value=timestr(), inline=False)
                embed.set_thumbnail(url=message.author.avatar_url)
                await self.log_moderation(embed=embed)
                break

        role_member = first_role_named(
            self.config.get("roleGrant")["role"], self.main_guild
        )
        if (
            role_member
            and message.channel.id == self.config.get("roleGrant")["chan"]
            and role_member not in message.author.roles
        ):
            try:
                if self.config.get("roleGrant")["ignorecase"]:
                    check = re.compile(
                        self.config.get("roleGrant")["regex"], re.IGNORECASE
                    )
                else:
                    check = re.compile(self.config.get("roleGrant")["regex"])

                if check.match(message.content):
                    await self.send_message(
                        None, message.channel, self.config.get("roleGrant")["response"]
                    )
                    await message.author.add_roles(
                        role_member, reason="Message matched the Agreement regex."
                    )
                    log.member(
                        message.author.name
                        + " (id: "
                        + str(message.author.id)
                        + ") was given access"
                    )
                    return

            except Exception as e:
                await self.send_message(
                    None,
                    message.channel,
                    "Something went wrong with granting"
                    + " your role. Pm a member of staff "
                    + str(e),
                )
                raise e

        if not self.config.pm and isinstance(
            message.channel, discord.abc.PrivateChannel
        ):
            if not message.author == self.user:
                # noinspection PyTypeChecker
                await self.send_message(
                    None,
                    message.channel,
                    "Petal has been configured by staff"
                    + " to not respond to PMs right now",
                )
            return

        replies = self.config.get("autoreplies") or {}
        if content in replies:
            if not message.author == self.user:
                reply = replies.get(content, "").format(
                    user=message.author, self=self.user
                )
                if reply:
                    await self.send_message(None, message.channel, reply)
            return

        # For now, do all the above checks and then run/route it.
        # This may result in repeating some checks, but these checks should
        #     eventually be moved into the commands module itself.
        if message.content.startswith(self.config.prefix):
            await self.execute_command(message)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date  : 2019/2/21 14:24
# @Author: PythonVampire
# @email : vampire@ivamp.cn
# @File  : test_json.py
import json

my_dict = {"img": "hjaksdgbasjkbdnasjkn"}
my_json = json.dumps(my_dict)
print(type(my_json))
print(my_json)