from django.core.urlresolvers import reverse
from django.utils.translation import ugettext

from soc.logic import dicts
from soc.logic.exceptions import AccessViolation
from soc.logic.exceptions import BadRequest
from soc.logic.exceptions import NotFound
from soc.logic.exceptions import RedirectRequest
from soc.models.org_app_record import OrgAppRecord
from soc.views.helper import access_checker
from soc.views.helper import request_data

from soc.modules.gci.models.profile import GCIProfile
from soc.modules.gci.models.task import GCITask
from soc.modules.gci.models.task import UNPUBLISHED

DEF_ALREADY_PARTICIPATING_AS_NON_STUDENT = ugettext(
    'You cannot register as a student since you are already a '
    'mentor or organization administrator in %s.')

DEF_ALL_WORK_STOPPED = ugettext(
    'All work on tasks has stopped. You can no longer place comments, '
    'submit work or make any changes to existing tasks.')

DEF_COMMENTING_NOT_ALLOWED = ugettext(
    "No more comments can be placed at this time.")

DEF_NO_TASK_CREATE_PRIV = ugettext(
    'You do not have sufficient privileges to create a new task for %s.' )

DEF_NO_TASK_EDIT_PRIV = ugettext(
    'You do not have sufficient privileges to edit a new task for %s.' )

DEF_NO_PREV_ORG_MEMBER = ugettext(
    'To apply as an organization for GCI you must have been a member of an '
    'organization in Google Summer of Code or Google Code In.')

DEF_TASK_UNEDITABLE_STATUS = ugettext(
    'This task is already published and published tasks cannot be edited.')

DEF_TASK_MUST_BE_IN_STATES = ugettext(
    'The task must be in one of the followings states %s')

DEF_TASK_MAY_NOT_BE_IN_STATES = ugettext(
    'The task may not be in one of the followings states %s')

DEF_ORG_APP_REJECTED = ugettext(
    'This org application has been rejected')

class Mutator(access_checker.Mutator):
  """Helper class for access checking.

  Mutates the data object as requested.
  """

  def unsetAll(self):
    self.data.task = access_checker.unset
    self.data.comments = access_checker.unset
    self.data.work_submissions = access_checker.unset
    self.data.is_visible = access_checker.unset
    self.data.full_edit = access_checker.unset
    super(Mutator, self).unsetAll()

  def profileFromKwargs(self):
    """Retrieves profile from the kwargs for GCI.
    """
    super(Mutator, self).profileFromKwargs(GCIProfile)

  def taskFromKwargs(self, comments=False, work_submissions=True):
    """Sets the GCITask entity in RequestData object.

    The entity that is set will always be in a valid state and for the program
    that is set in the RequestData.

    Args:
      comments: If true the comments on this task are added to RequestData
      work_submissions: If true the work submissions on this task are added to
                        RequestData
    """
    id = long(self.data.kwargs['id'])
    task = GCITask.get_by_id(id)

    if not task or (task.program.key() != self.data.program.key()) or \
        task.status == 'invalid':
      error_msg = access_checker.DEF_ID_BASED_ENTITY_NOT_EXISTS % {
          'model': 'GCITask',
          'id': id,
          }
      raise NotFound(error_msg)

    self.data.task = task

    if comments:
      self.data.comments = task.comments()

    if work_submissions:
      self.data.work_submissions = task.workSubmissions()

  def taskFromKwargsIfId(self):
    """Sets the GCITask entity in RequestData object if ID exists or None.
    """
    if not 'id' in self.data.kwargs:
      self.data.task = None
      return

    self.taskFromKwargs()

  def orgAppFromOrgId(self):
    org_id = self.data.GET.get('org_id')

    if not org_id:
      raise BadRequest('Missing org_id')

    q = OrgAppRecord.all()
    q.filter('survey', self.data.org_app)
    q.filter('org_id', org_id)

    self.data.org_app_record = q.get()

    if not self.data.org_app_record:
      raise NotFound("There is no org_app for the org_id %s" % org_id)

    if self.data.org_app_record.status != 'accepted':
      raise AccessViolation(DEF_ORG_APP_REJECTED)

  def fullEdit(self, full_edit=False):
    """Sets full_edit to True/False depending on the status of the task.
    """
    self.data.full_edit = full_edit

class DeveloperMutator(access_checker.DeveloperMutator,
                       Mutator):
  pass

class AccessChecker(access_checker.AccessChecker):
  """Access checker for GCI specific methods.
  """

  def isTaskVisible(self):
    """Checks if the task is visible to the public.

    Returns: True if the task is visible, if the task is not visible
        but the user can edit the task, False.
    """
    assert access_checker.isSet(self.data.task)

    can_edit = False
    try:
      self.checkCanUserEditTask()
      self.checkHasTaskEditableStatus()
      self.checkTimelineAllowsTaskEditing()
      can_edit = True
    except AccessViolation:
      pass

    if not self.data.timeline.tasksPubliclyVisible():
      if can_edit:
        return False
      period = self.data.timeline.tasksPubliclyVisibleOn()
      raise AccessViolation(
          access_checker.DEF_PAGE_INACTIVE_BEFORE % period)

    if not self.data.task.isPublished():
      if can_edit:
        return False
      error_msg = access_checker.DEF_PAGE_INACTIVE
      raise AccessViolation(error_msg)

    return True

  def isTaskInState(self, states):
    """Checks if the task is in any of the given states.

    Args:
      states: List of states in which a task may be for this check to pass.
    """
    assert access_checker.isSet(self.data.task)

    if self.data.task.status not in states:
      raise AccessViolation(DEF_TASK_MUST_BE_IN_STATES %states)

  def isTaskNotInStates(self, states):
    """Checks if the task is not in any of the given states.

    Args:
      states: List of states in which a task may not be for this check to pass.
    """
    assert access_checker.isSet(self.data.task)

    if self.data.task.status in states:
      raise AccessViolation(DEF_TASK_MAY_NOT_BE_IN_STATES %states)

  def canApplyStudent(self, edit_url):
    """Checks if a user may apply as a student to the program.
    """
    if self.data.profile:
      if self.data.profile.student_info:
        raise RedirectRequest(edit_url)
      else:
        raise AccessViolation(
            DEF_ALREADY_PARTICIPATING_AS_NON_STUDENT %
            self.data.program.name)

    self.studentSignupActive()

    # custom pre-registration age check for GCI students
    age_check = self.data.request.COOKIES.get('age_check', None)
    if not age_check or age_check == '0':
      # no age check done or it failed
      kwargs = dicts.filter(self.data.kwargs, ['sponsor', 'program'])
      age_check_url = reverse('gci_age_check', kwargs=kwargs)
      raise RedirectRequest(age_check_url)
    else:
      self.isLoggedIn()

  def canTakeOrgApp(self):
    """A user can take the GCI org app if he/she participated in GSoC or GCI
    as a non-student.
    """
    from soc.modules.gsoc.models.profile import GSoCProfile

    self.isUser()

    q = GSoCProfile.all()
    q.filter('is_student', False)
    q.filter('status', 'active')
    q.filter('user', self.data.user)
    gsoc_profile = q.get()

    q = GCIProfile.all()
    q.filter('is_student', False)
    q.filter('status', 'active')
    q.filter('user', self.data.user)
    gci_profile = q.get()

    if not (gsoc_profile or gci_profile):
      raise AccessViolation(DEF_NO_PREV_ORG_MEMBER)

  def canCreateNewOrg(self):
    """A user can create a new org if they have an accepted org app.
    """
    assert self.data.org_app

    if not self.data.profile:
      org_id = self.data.GET['org_id']
      profile_url = self.data.redirect.createProfile('org_admin').urlOf(
          'create_gci_profile')
      raise RedirectRequest(profile_url + '?new_org=' + org_id)

  def isBeforeAllWorkStopped(self):
    """Raises AccessViolation if all work on tasks has stopped.
    """
    if not self.data.timeline.allWorkStopped():
      return

    raise AccessViolation(DEF_ALL_WORK_STOPPED)

  def isCommentingAllowed(self):
    """Raises AccessViolation if commenting is not allowed.
    """
    if not self.data.timeline.allWorkStopped() or (
        not self.data.timeline.allReviewsStopped() and
        self.data.mentorFor(self.data.task.org)):
      return

    raise AccessViolation(DEF_COMMENTING_NOT_ALLOWED)

  def canCreateTask(self):
    """Checks whether the currently logged in user can edit the task.
    """
    assert access_checker.isSet(self.data.organization)
    assert access_checker.isSet(self.data.mentor_for)

    valid_org_keys = [o.key() for o in self.data.mentor_for]
    if self.data.organization.key() not in valid_org_keys:
      raise AccessViolation(DEF_NO_TASK_CREATE_PRIV % (
          self.data.organization.name))

    if (request_data.isBefore(self.data.timeline.orgsAnnouncedOn()) \
        or self.data.timeline.tasksClaimEnded()):
      raise AccessViolation(access_checker.DEF_PAGE_INACTIVE)

  def canUserEditTask(self):
    """Returns True/False depending on whether the currently logged in user
    can edit the task.
    """
    assert access_checker.isSet(self.data.task)
    assert access_checker.isSet(self.data.mentor_for)

    task = self.data.task

    valid_org_keys = [o.key() for o in self.data.mentor_for]
    if task.org.key() not in valid_org_keys:
      return False

    return True

  def checkCanUserEditTask(self):
    """Checks whether the currently logged in user can edit the task.
    """
    assert access_checker.isSet(self.data.task)

    if not self.canUserEditTask():
      raise AccessViolation(DEF_NO_TASK_EDIT_PRIV % (self.data.task.org.name))

  def hasTaskEditableStatus(self):
    """Returns True/False depending on whether the task is in one of the
    editable states.
    """
    assert access_checker.isSet(self.data.task)

    task = self.data.task

    if task.status not in (UNPUBLISHED + ['Open']):
      return False

    return True

  def checkHasTaskEditableStatus(self):
    """Checks whether the task is in one of the editable states.

    We specifically do not allow editing of tasks which are already claimed.
    """
    if not self.hasTaskEditableStatus():
      raise AccessViolation(DEF_TASK_UNEDITABLE_STATUS)

  def timelineAllowsTaskEditing(self):
    """Returns True/False depending on whether orgs can edit task depending
    on where in the program timeline we are currently in.
    """
    if (request_data.isBefore(self.data.timeline.orgsAnnouncedOn()) \
        or self.data.timeline.tasksClaimEnded()):
      return False

    return True

  def checkTimelineAllowsTaskEditing(self):
    """Checks if organizations can edit tasks at the current time in
    the program.
    """
    if not self.timelineAllowsTaskEditing():
      raise AccessViolation(access_checker.DEF_PAGE_INACTIVE)

class DeveloperAccessChecker(access_checker.DeveloperAccessChecker):
  """Developer access checker for GCI specific methods.
  """
  def isTaskVisible(self):
    return True


from setuptools import find_packages, setup

with open("README.md", "r", encoding="utf-8") as f:
    readme = f.read()

setup(
    name='dtweb',
    version='0.0.1',
    author='Juuso Autiosalo',
    author_email="juuso.autiosalo@iki.fi",
    description='Python library for accessing the Digital Twin Web',
    long_description=readme,
    long_description_content_type="text/markdown",
    url="https://github.com/juusoautiosalo/dtweb-python",
    classifiers=[
        "Development Status :: 1 - Planning",
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    license='MIT',
    packages=find_packages(),
    install_requires=['requests'],
    python_requires='>=3.8',
)


from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
driver=webdriver.Chrome(executable_path="C:\\python\\Python_Drivers\\chromedriver.exe")
driver.get("https://krninformatix.com/sample.html")
driver.implicitly_wait(2)
# Method1 drop=Select(driver.find_element_by_id("city"))
element=driver.find_element_by_id("city")
drp=Select(element)

# we can identify in 3 methods

# select by visible text
drp.select_by_visible_text('Chennai')

# select by index. Index start from 0
drp.select_by_index(0) # select by bangalore

# select by value
drp.select_by_value('3') # select by mumbai

# count all drop down options
print(len(drp.options))

allOptions=drp.options

for option in allOptions:
    print(option.text)


'''
This program prints numerical derivative convergance
'''
import representation_ZRN as ZRNrep
from jax.config import config
config.update("jax_enable_x64", True) #increase precision from float32 to float64
import jax_representation as jrep
import numpy as np
from numpy import linalg
import jax_derivative as jder
import database_preparation as datprep
import matplotlib.pyplot as plt
import numerical_derivative as numder

#give xyz coordinates of H2O
path = "./TEST/H2O.xyz"

#read xyz file and create compound instances
Z_orig, R_orig, N, e_orig = datprep.read_xyzfile(path)
Z_orig = np.array(Z_orig, dtype = np.float64)

M, order = jrep.CM_full_sorted(Z_orig, R_orig, N)
which_derivative = 1
#reorder your initial data. otherwise numerical differentiation will fail
#needs to be performed with preordered data, otherwise sorting can change
Z = Z_orig[order.tolist()]
R = R_orig[order]

R = np.asarray(R, dtype = np.float64)
Z = np.asarray(Z, dtype = np.float64)
functionnames = ['CM_unsrt', 'CM_EV_unsrt', 'OM', 'OM_EV']
functions = [ZRNrep.Coulomb_Matrix, ZRNrep.Eigenvalue_Coulomb_Matrix, ZRNrep.Overlap_Matrix, ZRNrep.Eigenvalue_Overlap_Matrix]

def get_first_order_errors(fun, fname, hlist, d1):

    if d1[0] == 0:
        name = "dZ%i" % (d1[1] + 1)
        dx = "Z"
        exact = jder.sort_derivative(fname, Z, R, N, 1, dx)[d1[1]]
    if d1[0] == 1:
        xyz = ['x', 'y', 'z']
        name = "d%s%i" %(xyz[d1[1]], d1[2]+1)
        dx = 'R'
        exact = jder.sort_derivative(fname, Z, R, N, 1, dx)[d1[1]][d1[2]]
    print("\n ------------------- \n Derivative %s \n ------------------ \nAnalytical derivative" %name)
    print(exact)

    ylist = []
    #calculate numerical derivatives for increasingly small h
    for h in hlist:
        derf = numder.derivative(fun, [Z,R,N], 'numerical', 1, d1, [0,0], h)
        exf = exact.flatten()
        print("derf")
        print(derf)
        print("exf")
        print(exf)
        error = linalg.norm(derf - exf)
        ylist.append(error)

    return(ylist, name)

def get_second_order_errors(fun, fname, hlist, d1, d2):
    '''
    fun: function from representation_ZRN.py
    fname: string, 'CM', 'CM_EV', 'CM_unsrt', 'OM', 'OM_EV'
    '''
    #calculate exact result
    if d1[0] == 0:
        name1 = "dZ%i" % (d1[1] + 1)
        dx1 = "Z"
        if d2[0] == 0:
            name = name1 + "dZ%i" % (d2[1] + 1)
            dx2 = "Z"
            exact = jder.sort_derivative(fname, Z, R, N, 2, dx1, dx2)[d1[1]][d2[1]]

    if d1[0] == 1:
        xyz = ['x', 'y', 'z']
        name1 = "d%s%i" %(xyz[d1[1]], d1[2])
        dx1 = 'R'
        if d2[0] == 1:
            name = name1 + "d%s%i" %(xyz[d2[1]], (d2[2] + 1))
            dx2 = 'R'
            exact = jder.sort_derivative(fname, Z, R, N, 2, dx1, dx2)[d1[1]][d1[2]][d2[1]][d2[2]]

    if (d1[0] == 0 and d2[0] == 1) or (d1[0] == 1 and d2[0] == 0):
        xyz = ['x', 'y', 'z']
        #translate dx1 and dx2 to numbers for functions and names
        dx1 = "Z"
        dx2 = "R"

        if d1[0] == 0:
            ZR_order = True
            name1 = "dZ%i" % (d1[1]+1)
            name2 = "d%s%i" %(xyz[d2[1]], d2[2]+1)

        else:
            ZR_order = False
            name1 = "dZ%i" % (d2[1]+1)
            name2 = "d%s%i" %(xyz[d1[1]], d1[2]+1)
        name = name1 + name2

        if ZR_order:
            exact = jder.sort_derivative(fname, Z, R, N, 2, dx1, dx2)[d1[1]][d2[1]][d2[2]]
        else:
            exact = jder.sort_derivative(fname, Z, R, N, 2, dx1, dx2)[d2[1]][d1[1]][d1[2]]

    print("\n ------------------- \n Derivative %s \n ------------------ \nAnalytical derivative" %name)
    print(exact)

    ylist = []
    #calculate numerical derivatives for increasingly small h
    for h in hlist:
        derf = numder.derivative(fun, [Z,R,N], 'numerical', 2, d1, d2, h)
        exf = exact.flatten()
        error = linalg.norm(derf - exf)
        print("derf")
        print(derf)
        print("exf")
        print(exf)

        #print("h", h, "error", error)
        #print(derf.reshape(3,3))
        ylist.append(error)

    return(ylist, name)

def plot_numeric_errors(fun, fname, do_first_order = True, do_second_order = True, show_legend = False):
    '''
    fun : function from representation_ZRN.py file
    fname : function name: 'CM', 'CM_unsrt', 'CM_EV', 'OM' or 'OM_EV'

    '''
    #prepare panels for plots
    fig = plt.figure(tight_layout = True, figsize=(8, 6))

    #create subfigure spacing as needed
    if do_first_order:
        if do_second_order:
            '''create 5 panel image'''
            ax1 = plt.subplot2grid((2,6),(0,0), rowspan=1, colspan=3)
            ax2 = plt.subplot2grid((2,6), (0,3), rowspan=1, colspan=3)
            ax3 = plt.subplot2grid((2,6), (1,0), rowspan=1, colspan=2)
            ax4 = plt.subplot2grid((2,6), (1,2), rowspan=1, colspan=2)
            ax5 = plt.subplot2grid((2,6), (1,4), rowspan=1, colspan=2)

        else:
            '''create 2 panel image for dZ and dR'''
            ax1 = fig.add_subplot(121)
            ax2 = fig.add_subplot(122)
    elif do_second_order:
        '''create 3 panel image for dZdZ, dRdR and dZdR'''
        ax3 = fig.add_subplot(131)
        ax4 = fig.add_subplot(132)
        ax5 = fig.add_subplot(133)

    #derivatives: standard parameters by which to derive.
    '''
    [0, ...] is dZ
    [1, ...] is dR
    [...,0,...] is dZ1 or dR1
    for dR, [...,...,0] is dx, [...,...,1] dy and [...,...,2] dz
    '''

    derivative_1 =  [[0,0], [0,1], [0,2],[1,0,0], [1,0,1],[1,0,2],[1,1,0], [1,1,1],[1,1,2],[1,2,0], [1,2,1], [1,2,2]]

    derivative_2 = [[0,0], [0,1], [0,2], [1, 1, 2], [1,0, 1], [1,0,0], [1,2,2], [1,1,2]]

    #hlist: small steps that are taken for numerical derivation
    hlist = np.logspace(-1, -5, 10)

    #plot 1st order if needed
    if do_first_order:
        for d1 in derivative_1:
            ylist, name = get_first_order_errors(fun, fname, hlist, d1)

            #plot errors in correct subplot
            if d1[0] == 0:
                ax1.loglog(hlist, ylist, 'o-', label = name)
            else:
                ax2.loglog(hlist, ylist, 'o-', label = name)

        if show_legend:
            ax1.legend()
            ax2.legend()

        ax1.title.set_text('dZ (nuclear charge)')
        ax2.title.set_text('dR (nuclear coordinate)')
        ax1.set_xlabel("dh")
        ax2.set_xlabel("dh")

    if do_second_order:

        for d1 in derivative_1:
            for d2 in derivative_2:

                ylist, name = get_second_order_errors(fun, fname, hlist, d1, d2)

                #plot errors in correct subplot
                if d1[0] == 0:
                    if d2[0] == 0:
                        ax3.loglog(hlist, ylist, 'o-', label = name)
                    else:
                        ax5.loglog(hlist, ylist, 'o-', label = name)
                if d1[0] == 1:
                    if d2[0] == 1:
                        ax4.loglog(hlist, ylist, 'o-', label = name)
                    else:
                        ax5.loglog(hlist, ylist,'o-', label = name)

        if show_legend:
            ax3.legend()
            ax4.legend()
            ax5.legend()
        ax3.title.set_text("dZdZ")
        ax4.title.set_text("dRdR")
        ax5.title.set_text("dZdR")
        ax3.set_xlabel("dh")
        ax4.set_xlabel("dh")
        ax5.set_xlabel("dh")

    fig.suptitle('Finite Central Difference Derivative on %s of H2O Molecule' %fname, fontsize = 15)
    #ad title to overall y axis

    fig.text(0.5, 0.0004, '(dh is the small change introduced in numerical differentiation)', ha='center', fontsize = 13)
    fig.text(0.0004, 0.5, 'Absolute Error w.r.t. Analytical Derivative [a.u.]', rotation = 'vertical', va='center', fontsize =13)

    filename = "./Images/numerical_errors_%s.png" % fname
    plt.savefig(filename, bbox_inches = 'tight')

    return()

fun = functions[1]
fname = functionnames[1]

print("function:", fun)
print("function name:", fname)
plot_numeric_errors(fun, fname)


from datetime import date
# from [file] import [Class]
from food import Food

class Cat:
    def __init__(self,
                 n: str,
                 a: int,
                 color: str):
        self.name = n
        self.__age = a
        self.color = color
        self.energy = 100

    @property
    def age(self):
        if self.__age < 1:
            return 'Es un minino'
        elif 1 < self.__age < 4:
            return 'Es joven'
        elif 4 < self.__age < 10:
            return 'Es adulto'

        return 'Es anciano'

    @age.setter
    def age(self, value: int):
        if value < 0:
            raise Exception('Edad inválida')
        else:
            self.__age = value

    def meow(self):
        print(self.name)
        print('Meoooooooooooooow')

    def run(self):
        if self.energy > 0:
            self.energy -= 10
            print(f"{self.name}: Estoy corriendo...")
        else:
            print(f"{self.name}: No puedo... estoy cansadito")

    def eat(self, food: Food):
        today = date.today()
        if food.exp_date > today:
            self.energy += food.energy
        else:
            days = (today - food.exp_date).days
            consumed_energy = days * food.energy

            self.energy = max(self.energy - consumed_energy, 0)

    def __str__(self):
        result = 'Información del gato: \n'
        result += 'Nombre: ' + self.name + '\n'
        result += 'Edad: ' + str(self.__age) + '\n'
        result += 'Energía: ' + str(self.energy)

        return result


import random

with open('words.txt', 'r') as f:
    WORDS = f.read().splitlines()

def valid_guess(guess):
    return guess in WORDS

def next_word(user_name, i, prev_word):
    """For now I want a deterministic randomness.

    The combination of user_name, which word they are on and previous word should suffice for now.

    This should ensure that words are deteministic, random and will not get stuck in cycles.
    """
    random.seed(f"{user_name}{i}{prev_word}")
    return random.choice(WORDS)

if __name__ == "__main__":
    print('Evaluation of how we generate the random words!')
    print()
    user_name = f'alex{random.randint(0, 10_000)}'

    from collections import Counter
    from itertools import count as count_from
    from statistics import mean
    import statistics

    counter = Counter()
    never_picked = set(WORDS)

    _90 = False

    word = ""
    for i in count_from(0):
        word = next_word(user_name, i, prev_word=word)
        counter[word] += 1

        if word in never_picked:
            never_picked.remove(word)

        if not _90 and len(never_picked) / len(WORDS) <= 0.1:
            print(f"{i} iterations until 90% of words have been picked")
            print('Stats about how often words have appeared:')
            print(f"Average {mean(counter.values()):.3f}")
            print(f"Median  {statistics.median(counter.values())}")
            print(f"Max     {max(counter.values())}")
            print()
            _90 = True

        if not never_picked:
            print(f"{i} iterations until 100% of words have been picked ({len(WORDS)} words)")
            print('Stats about how often words have appeared:')
            print(f"Average {mean(counter.values()):.3f}")
            print(f"Median  {statistics.median(counter.values())}")
            print(f"Max     {max(counter.values())}")
            print()
            break


from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains

class Base:
    '''
    selenium api 封装
    '''
    def __init__(self, logger, driver=None):
        self.driver = driver
        self.timeout = 6
        self.t = 0.5
        self.logger = logger

    def _get_driver(self, driver):
        '''获取driver，判断用初始化的driver还是传入的driver，传入的优先级的driver优先级最高'''
        return driver or self.driver

    def _get_element(self, locator, element, driver):
        '''判断传入的是locator还是element；返回element; 只传其中一个'''
        if element:
            return element
        return self.find_element(locator, driver)

    def find_element(self, locator, driver=None):
        '''
        定位元素
        返回定位到的元素，没定位到则抛timeout异常
        locator： ('id', 'kw')/('css selector', 'input#kw')
         ID = "id"
        XPATH = "xpath"
        LINK_TEXT = "link text"
        PARTIAL_LINK_TEXT = "partial link text"
        NAME = "name"
        TAG_NAME = "tag name"
        CLASS_NAME = "class name"
        CSS_SELECTOR = "css selector"
        '''
        if not isinstance(locator, tuple):
            self.logger.error('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
            raise Exception('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
        driver = self._get_driver(driver)
        try:
            element = WebDriverWait(driver, self.timeout, self.t).until(EC.presence_of_element_located(locator))
            self.logger.info("定位元素信息：定位方式->%s, value值->%s"%(locator[0], locator[1]))
        except Exception:
            self.logger.error("定位方式报错->%s, value值->%s"%(locator[0], locator[1]), exc_info=True)
            #raise Exception("定位方式报错->%s, value值->%s"%(locator[0], locator[1]))
            return False
        else:
            return element

    def find_elements(self, locator, driver=None):
        # 定位元素, 返回元素[]
        if not isinstance(locator, tuple):
            self.logger.error('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
            raise Exception('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
        driver = self._get_driver(driver)
        try:
            elements = WebDriverWait(driver, self.timeout, self.t).until(EC.presence_of_all_elements_located(locator))
            self.logger.info("定位元素信息：定位方式->%s, value值->%s"%(locator[0], locator[1]))
            return elements
        except Exception:
            self.logger.error("查找元素报错->%s, value值->%s"%(locator[0], locator[1]), exc_info=True)

    def send_keys(self, locator=None, text='', element=None, driver=None):
        driver = self._get_driver(driver)
        element = self._get_element(locator, element, driver)
        element.send_keys(text)
        self.logger.info("输入信息：%s"% text)

    def click(self, locator=None, element=None, driver=None):
        driver = self._get_driver(driver)
        element = self._get_element(locator, element, driver)
        element.click()
        self.logger.info("点击元素")

    def clear(self, locator=None, element=None, driver=None):
        driver = self._get_driver(driver)
        element = self._get_element(locator, element, driver)
        element.clear()
        self.logger.info("清空输入框")

    def is_selected(self, locator=None, element=None, driver=None):
        '''判断元素是否被选中，返回bool值'''
        driver = self._get_driver(driver)
        element = self._get_element(locator, element, driver)
        r = element.is_selected()
        self.logger.info("元素是否被选中:%s" %r)
        return r

    def is_enabled(self, locator=None, element=None, driver=None):
        '''判断input\select等元素是否可编辑状态，返回bool值'''
        driver = self._get_driver(driver)
        element = self._get_element(locator, element, driver)
        r = element.is_enabled()
        self.logger.info("元素是否可编辑：%s" %r)
        return r

    def is_element_exist(self, locator=None, element=None, driver=None):
        '''判断元素是否存在，返回bool值'''
        driver = self._get_driver(driver)
        try:
            self._get_element(locator, element, driver)
            self.logger.info("元素存在")
            return True
        except:
            self.logger.info("元素不存在")
            return False

    def is_title(self, title='', driver=None):
        '''判断标题是否相同，返回bool'''
        driver = self._get_driver(driver)
        try:
            result = WebDriverWait(driver, self.timeout, self.t).until(EC.title_is(title))
            self.logger.info('标题相同：%s'%title)
            return result
        except:
            self.logger.info('标题不相同：%s' % title)
            return False

    def is_title_contains(self, title='', driver=None):
        '''判断标题是否包含，返回bool'''
        driver = self._get_driver(driver)
        try:
            result = WebDriverWait(driver, self.timeout, self.t).until(EC.title_contains(title))
            self.logger.info('标题包含：%s' % title)
            return result
        except:
            self.logger.info('标题没有包含：%s' % title)
            return False

    def is_element_contains_text(self, locator, text='', driver=None):
        '''判断元素是否包含预期的字符串，返回bool'''
        if not isinstance(locator, tuple):
            self.logger.error('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
            raise Exception('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
        driver = self._get_driver(driver)
        try:
            result = WebDriverWait(driver, self.timeout, self.t).until(EC.text_to_be_present_in_element(locator, text))
            self.logger.info('元素文本值包含：%s'%text)
            return result
        except:
            self.logger.info('元素文本值没有包含：%s' % text)
            return False

    def is_elementValue_contains_value(self, locator, value='', driver=None):
        '''判断元素的value属性值是否包含预期的value，返回bool'''
        if not isinstance(locator, tuple):
            self.logger.error('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
            raise Exception('locator参数类型错误，必须传元祖类型：loc = ("id", "value1")')
        driver = self._get_driver(driver)
        try:
            result = WebDriverWait(driver, self.timeout, self.t).until(EC.text_to_be_present_in_element_value(locator, value))
            self.logger.info('元素的value属性值包含：%s'%value)
            return result
        except:
            self.logger.info('元素的value属性值没有包含：%s' % value)
            return False

    def is_alert(self, timeout=3, driver=None):
        '''判断alert,存在返回alert实例，不存在，返回false'''
        driver = self._get_driver(driver)
        try:
            result = WebDriverWait(driver, timeout, self.t).until(EC.alert_is_present())
            self.logger.info('有alert弹框')
            return result
        except:
            self.logger.info('没有alert弹框')
            return False

    def get(self, url, driver=None):
        '''打开地址'''
        driver = self._get_driver(driver)
        driver.get(url)
        self.logger.info('打开url：%s'%url)

    def refresh(self, driver=None):
        '''刷新页面'''
        driver = self._get_driver(driver)
        driver.refresh()
        self.logger.info('刷新页面')

    def forward(self, driver=None):
        '''跳转到下一页'''
        driver = self._get_driver(driver)
        driver.forward()
        self.logger.info('跳转下一页')

    def back(self, driver=None):
        '''跳转到下一页'''
        driver = self._get_driver(driver)
        driver.back()
        self.logger.info('跳转上一页')

    def maximize_window(self, driver=None):
        '''最大化浏览器'''
        driver = self._get_driver(driver)
        driver.maximize_window()
        self.logger.info('最大化浏览器')

    def set_window_size(self, x=100, y=50, driver=None):
        '''设置窗口大小'''
        driver = self._get_driver(driver)
        driver.set_window_size(x, y)
        self.logger.info('设置窗口大小%s，%s'%(x, y))

    def set_page_load_timeout(self, timeout=15, driver=None):
        '''设置页面加载超时时间'''
        driver = self._get_driver(driver)
        driver.set_page_load_timeout(timeout)
        self.logger.info('设置页面加载超时时间：%s' %timeout)

    def close(self, driver=None):
        '''关闭浏览器'''
        driver = self._get_driver(driver)
        driver.quit()
        self.logger.info('关闭浏览器')

    def get_screenshot(self, file_path, driver=None):
        '''截图;  file_path="/pictures/test.png" '''
        driver = self._get_driver(driver)
        driver.get_screenshot_as_file(file_path)
        self.logger.info('截图：%s'%file_path)

    def get_cookies(self, driver=None):
        '''获取所有cookies'''
        driver = self._get_driver(driver)
        cookies = driver.get_cookies()
        self.logger.info('获取所有cookies信息：%s' %cookies)
        return cookies

    def get_cookie(self, name, driver=None):
        '''获取cookies的某个值'''
        driver = self._get_driver(driver)
        cookie = driver.get_cookie(name)
        self.logger.info('获取%s的cookie信息：%s' %(name, cookie))
        return cookie

    def delete_cookie(self, name, driver=None):
        '''删除指定的cookie信息'''
        driver = self._get_driver(driver)
        driver.delete_cookie(name)
        self.logger.info('删除%s的cookie信息' %name)

    def delete_cookies(self, driver=None):
        '''删除所有的cookies信息'''
        driver = self._get_driver(driver)
        driver.delete_all_cookies()
        self.logger.info('删除所有的cookie信息')

    def add_cookie(self, cookie_dict, driver=None):
        '''添加cookies信息'''
        driver = self._get_driver(driver)
        driver.add_cookie(cookie_dict)
        self.logger.info('添加cookies信息：%s' %cookie_dict)

    def get_title(self, driver=None):
        '''获取title标题'''
        driver = self._get_driver(driver)
        title = driver.title
        self.logger.info('获取标题：%s'%title)
        return title

    def get_url(self, driver=None):
        '''获取当前url'''
        driver = self._get_driver(driver)
        url = driver.current_url
        self.logger.info('获取当前url：%s' % url)
        return url

    def get_element_text(self, locator=None, element=None, driver=None):
        '''获取元素的文本'''
        try:
            driver = self._get_driver(driver)
            element = self._get_element(locator, element, driver)
            text = element.text
            self.logger.info('获取元素的文本：%s'%text)
            return text
        except:
            self.logger.info("获取text失败，返回''")
            return ""

    def get_element_attribute(self, locator, name, element=None, driver=None):
        '''获取元素的属性'''
        driver = self._get_driver(driver)
        try:
            element = self._get_element(locator, element, driver)
            attribute = element.get_attribute(name)
            self.logger.info('获取元素的%s属性：' %(name,attribute))
            return attribute
        except:
            self.logger.info("获取元素的%s属性失败，返回''")
            return ""

    def js_focus_element(self, locator=None, element=None, driver=None):
        '''聚焦元素'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        driver.execute_script("arguments[0].scrollIntoView();", target_element)
        self.logger.info("聚焦元素")

    def js_scroll_top(self, driver=None):
        '''滚动到顶部'''
        driver = self._get_driver(driver)
        js = "window.scrollTo(0,0)"
        driver.execute_script(js)
        self.logger.info("滚动到顶部")

    def js_scroll_end(self, x=0, driver=None):
        '''滚动到底部'''
        driver = self._get_driver(driver)
        js = "window.scrollTo(%s,document.body.scrollHeight)" % x
        driver.execute_script(js)
        self.logger.info("滚动到底部")

    def js_play_video(self, locator=None, element=None, driver=None):
        '''播放视频'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        js = "arguments[0].play();"
        driver.execute_script(js, target_element)
        self.logger.info("播放视频")

    def select_by_index(self, locator, index=0, element=None, driver=None):
        '''下拉框，通过索引选择。index是索引第几个，从0开始，默认选第一个'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        Select(target_element).select_by_index(index)
        self.logger.info("下拉框选择索引%s的值"%index)

    def select_by_value(self, locator, value, element=None, driver=None):
        '''下拉框，通过value选择'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        Select(target_element).select_by_value(value)
        self.logger.info("下拉框选择value:%s的值"%value)

    def select_by_text(self, locator, text, element=None, driver=None):
        '''下拉框，通过文本值选择'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        Select(target_element).select_by_visible_text(text)
        self.logger.info("下拉框选择文本:%s的值" % text)

    def deselect_by_index(self, locator, index=0, element=None, driver=None):
        '''下拉框，通过索引,取消选择。index是索引第几个，从0开始，默认取消第一个'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        Select(target_element).deselect_by_index(index)
        self.logger.info("下拉框取消选择索引%s的值" % index)

    def deselect_by_value(self, locator, value, element=None, driver=None):
        '''下拉框，通过value，取消选择'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        Select(target_element).deselect_by_index(value)
        self.logger.info("下拉框取消选择value:%s的值"%value)

    def deselect_by_text(self, locator, text, element=None, driver=None):
        '''下拉框，通过文本值，取消选择'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        Select(target_element).deselect_by_visible_text(text)
        self.logger.info("下拉框取消选择文本:%s的值" % text)

    def deselect_all(self, locator=None, element=None, driver=None):
        '''下拉框，取消选择'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        Select(target_element).deselect_all()
        self.logger.info("下拉框取消选择")

    def get_select_options(self, locator=None, element=None, driver=None):
        '''获取下拉框的所有options ； 遍历之后 .text可以获取该文本值'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        options = Select(target_element).options()
        self.logger.info("获取下拉框的所有options")
        return options

    def get_first_selected_option(self, locator=None, element=None, driver=None):
        '''获取第一个被选中的option'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        first_option = Select(target_element).first_selected_option()
        self.logger.info("获取下拉框的第一个被选中的option")
        return first_option

    def get_selected_options(self, locator=None, element=None, driver=None):
        '''获取所有被选中的option'''
        driver = self._get_driver(driver)
        target_element = self._get_element(locator, element, driver)
        options = Select(target_element).all_selected_options()
        self.logger.info("获取下拉框的所有被选中的option")
        return options

    def switch_iframe(self, id_name_index_locator, driver=None):
        '''切换iframe ； 可以传入id、name、index以及selenium的WebElement对象'''
        driver = self._get_driver(driver)
        try:
            if isinstance(id_name_index_locator, tuple):
                element = self.find_element(id_name_index_locator)
                driver.switch_to.frame(element)
            driver.switch_to.frame(id_name_index_locator)
            self.logger.info("切换iframe")
        except:
            self.logger.error("切换iframe异常", exc_info=True)
            raise Exception("切换iframe异常")

    def switch_default_content(self, driver=None):
        '''释放iframe'''
        driver = self._get_driver(driver)
        driver.switch_to.default_content()
        self.logger.info("释放iframe")

    def switch_parent_iframe(self, driver=None):
        '''回到父级的iframe'''
        driver = self._get_driver(driver)
        driver.switch_to.parent_frame()
        self.logger.info("回到父级的iframe")

    def get_current_handle(self, driver=None):
        '''获取当前句柄窗口'''
        driver = self._get_driver(driver)
        handle = driver.current_window_handle()
        self.logger.info("获取当前句柄窗口")
        return handle

    def get_handles(self, driver=None):
        '''获取所有的句柄窗口'''
        driver = self._get_driver(driver)
        handles = driver.window_handles()
        self.logger.info("获取所有的句柄窗口")
        return handles

    def switch_handle(self, handle, driver=None):
        '''切换句柄窗口'''
        driver = self._get_driver(driver)
        driver.switch_to.window(handle)
        self.logger.info("切换句柄窗口")

    def switch_alert(self, driver=None):
        '''切换到alert ；'''
        driver = self._get_driver(driver)
        alert = driver.switch_to_alert()
        self.logger.info("切换到alert")
        return alert

    def accept_alert(self, driver=None):
        '''切换到alert ； 并且点击确认'''
        self.switch_alert(driver).accept()
        self.logger.info("点击确认alert")

    def dismiss_alert(self, driver=None):
        '''切换到alert ； 并且点击取消'''
        self.switch_alert(driver).dismiss()
        self.logger.info("点击取消alert")

    def execute_js(self, js, driver=None):
        '''执行js脚本'''
        driver = self._get_driver(driver)
        driver.execute_script(js)
        self.logger.info("执行js脚本--> %s"%js)

    def move_to_element(self, locator=None, element=None, driver=None):
        '''鼠标悬停到某个元素上'''
        driver = self._get_driver(driver)
        element = self._get_element(locator, element, driver)
        ActionChains(driver).move_to_element(element).perform()
        self.logger.info("鼠标悬停到元素上")

    def double_click(self, locator=None, element=None, driver=None):
        '''双击鼠标'''
        driver = self._get_driver(driver)
        element = self._get_element(locator, element, driver)
        ActionChains(driver).double_click(element).perform()
        self.logger.info("双击鼠标")

    def context_click(self, locator=None, element=None, driver=None):
        '''点击鼠标右键'''
        driver = self._get_driver(driver)
        element = self._get_element(locator, element, driver)
        ActionChains(driver).context_click(element).perform()
        self.logger.info("点击鼠标右键")

    def drag_element(self, from_locator, to_locator, driver=None):
        '''拖动元素（原元素--->目标元素）'''
        driver = self._get_driver(driver)
        from_element = self.find_element(from_locator, driver)
        to_element = self.find_element(to_locator, driver)
        ActionChains(driver).drag_and_drop(from_element, to_element).perform()
        self.logger.info("拖动元素")

if __name__ == '__main__':
    from selenium import webdriver
    from time import sleep
    from selenium.webdriver.chrome.options import Options
    from logger import Logger

    logger = Logger()

    chrome_options = Options()
    # 设置chrome浏览器无界面模式
    chrome_options.add_argument('--headless')
    driver = webdriver.Chrome(chrome_options=chrome_options)

    handle = Base(logger=logger, driver=driver) # logger是必传的，driver可以不传
    handle.maximize_window()
    handle.set_page_load_timeout()
    handle.get('http://www.baidu.com')
    handle.send_keys(('id', 'kw'), '哦哦哦哦')  # 初始化传了driver的，默认用初始化的driver
    sleep(1)

    handle.clear(('id', 'kw'), driver=driver)   # 初始化没有传driver，或者传了driver的；调方法时，传driver会优先用方法传的driver

    sleep(2)
    search_btn_ele = handle.find_element(('id', 'su'))
    handle.double_click(element=search_btn_ele) # 可以传locator或者element，其中一个，去操作
    sleep(2)
    handle.close()


#! /usr/bin/python3

import rospy
import tf
import tf2_ros
from turtlesim.msg import Pose
from turtlesim.srv import Spawn
from geometry_msgs.msg import TransformStamped
import sys

def callback(msg, turtlename):
    # tf下broadcaster需要发送坐标
    tw = TransformStamped()
    '''
    rosmsg show geometry_msgs/TransformStamped
    std_msgs/Header header
    uint32 seq
    time stamp
    string frame_id
    string child_frame_id
    geometry_msgs/Transform transform
    geometry_msgs/Vector3 translation
        float64 x
        float64 y
        float64 z
    geometry_msgs/Quaternion rotation
        float64 x
        float64 y
        float64 z
        float64 w

    '''
    tw.header.stamp = rospy.Time.now()
    tw.header.frame_id = "world"
    tw.child_frame_id = turtlename
    tw.transform.translation.x = msg.x
    tw.transform.translation.y = msg.y
    tw.transform.translation.z = 0
    # TransformStamped中姿态是以四元数形式组织，quaternion_from_euler(ai, aj, ak, axes='sxyz')，需要在空间中理解...
    qtn = tf.transformations.quaternion_from_euler(0, 0, msg.theta)
    tw.transform.rotation.x = qtn[0]
    tw.transform.rotation.y = qtn[1]
    tw.transform.rotation.z = qtn[2]
    tw.transform.rotation.w = qtn[3]
    # tf2和tf下TransformBroadcaster功能不相同
    br = tf2_ros.TransformBroadcaster()
    br.sendTransform(tw)

if __name__ == "__main__":
    rospy.init_node("turtle_trace_pub")
    print(len(sys.argv))
    if len(sys.argv) != 4:
        rospy.loginfo("参数错误")
        sys.exit(1)
    else:
        turtlename = sys.argv[1]

    # rospy.wait_for_service("/spawn")
    # try:
    #     spawn = rospy.ServiceProxy("/spawn", Spawn)
    #     spawn.call(1, 1, 0, 'turtle2')
    # except:
    #     pass

    rate = rospy.Rate(10)

    sub = rospy.Subscriber('/%s/pose' % turtlename, Pose,
                           callback, callback_args=turtlename)
    rospy.spin()
    # while not rospy.is_shutdown():
    #     sub1 = rospy.Subscriber('/turtle1/pose', Pose,
    #                             callback, callback_args='turtle1')
    #     sub2 = rospy.Subscriber('/turtle2/pose', Pose,
    #                             callback, callback_args='turtle2')
    #     rate.sleep()
    '''

    Type: geometry_msgs/Twist
    rosmsg show geometry_msgs/Twist
    geometry_msgs/Vector3 linear
    float64 x
    float64 y
    float64 z
    geometry_msgs/Vector3 angular
    float64 x
    float64 y
    float64 z

    Type: turtlesim/Pose
    rostopic type /turtle1/pose | rosmsg show
    float32 x
    float32 y
    float32 theta
    float32 linear_velocity
    float32 angular_velocity
    '''


import re
import os
from pytgcalls import GroupCallFactory
from pyrogram import Client, filters
from pyrogram.types import Message
from pyyoutube import ytdl

API_ID = os.environ.get("API_ID",12345)
API_HASH = os.environ.get("API_HASH","")
SESSION_NAME = os.environ.get("SESSION_NAME","")
CHAT = os.environ.get("CHAT","")
ADMIN = int(os.environ.get("ADMIN", 1950281200))

app = Client(SESSION_NAME, API_ID, API_HASH)

group_call_factory = GroupCallFactory(app, GroupCallFactory.MTPROTO_CLIENT_TYPE.PYROGRAM)
VIDEO_CALL = {}

@Client.on_message(filters.private & filters.user(ADMIN) & filters.command(["start"]))
async def start(client, m: Message):
	await m.reply("مرحبًا ابدأ تشغيل الفيديو باستخدام الأمر /play(مع الرد) او /stop لي اقافه")

@Client.on_message(filters.private & filters.user(ADMIN) & filters.command(["play"]))
async def play(client, m: Message):
	if (m.reply_to_message):
			link = m.reply_to_message.text
			youtube_regex = r"^(https?\:\/\/)?(www\.youtube\.com|youtu\.?be)\/.+"
			youtube_regex_match = re.match(youtube_regex, link)
			if youtube_regex_match:
				             try:
				             	video_url = ytdl(link).besturl()
				             except Exception as e:
				             	await m.reply(f"**حدث خطأ** -- `{e}`")
				             	return
				             try:
				             	group_call = group_call_factory.get_group_call()
				             	await group_call.join(CHAT)
				             	await group_call.start_video(video_url,enable_experimental_lip_sync=True)
				             	VIDEO_CALL[CHAT] = group_call
				             	await m.reply("**بدء التشغيل!**")
				             except Exception as e:
				             	await m.reply(f"**حدث خطأ** -- `{e}`")

			else:
			         	try:
			         		group_call = group_call_factory.get_group_call()
			         		await group_call.join(CHAT)
			         		await group_call.start_video(link,enable_experimental_lip_sync=True)
			         		VIDEO_CALL[CHAT] = group_call
			         		await m.reply("**بدء التشغيل!**")
			         	except Exception as e:
			         	    	await m.reply(f"**حدث خطأ** -- `{e}`")

@Client.on_message(filters.private & filters.user(ADMIN) & filters.command(["stop"]))
async def stop (client, m: Message):
	try:
	       await VIDEO_CALL[CHAT].stop()
	       await m.reply("**توقف البث!**")
	except Exception as e:
		await m.reply(f"**حدث خطأ** - `{e}`")


import re

from cryptography import x509
from cryptography.exceptions import UnsupportedAlgorithm, InvalidSignature
from cryptography.hazmat.backends import default_backend
from cryptography.x509 import NameOID
from flask import current_app
from marshmallow.exceptions import ValidationError

from lemur.auth.permissions import SensitiveDomainPermission
from lemur.common.utils import check_cert_signature, is_weekend
from lemur.plugins.base import plugins

def common_name(value):
    """If the common name could be a domain name, apply domain validation rules."""
    # Common name could be a domain name, or a human-readable name of the subject (often used in CA names or client
    # certificates). As a simple heuristic, we assume that human-readable names always include a space.
    # However, to avoid confusion for humans, we also don't count spaces at the beginning or end of the string.
    value = value.strip()
    if value and " " not in value:
        return sensitive_domain(value)

def sensitive_domain(domain):
    """
    Checks if user has the admin role, the domain does not match sensitive domains and allowed domain patterns.
    :param domain: domain name (str)
    :return:
    """
    if SensitiveDomainPermission().can():
        # User has permission, no need to check anything
        return

    allowlist = current_app.config.get("LEMUR_ALLOWED_DOMAINS", [])
    if allowlist and not any(re.match(pattern, domain) for pattern in allowlist):
        raise ValidationError(
            "Domain {} does not match allowed domain patterns. "
            "Contact an administrator to issue the certificate.".format(domain)
        )

    # Avoid circular import.
    from lemur.domains import service as domain_service

    if domain_service.is_domain_sensitive(domain):
        raise ValidationError(
            "Domain {} has been marked as sensitive. "
            "Contact an administrator to issue the certificate.".format(domain)
        )

def encoding(oid_encoding):
    """
    Determines if the specified oid type is valid.
    :param oid_encoding:
    :return:
    """
    valid_types = ["b64asn1", "string", "ia5string"]
    if oid_encoding.lower() not in [o_type.lower() for o_type in valid_types]:
        raise ValidationError(
            "Invalid Oid Encoding: {} choose from {}".format(
                oid_encoding, ",".join(valid_types)
            )
        )

def sub_alt_type(alt_type):
    """
    Determines if the specified subject alternate type is valid.
    :param alt_type:
    :return:
    """
    valid_types = [
        "DNSName",
        "IPAddress",
        "uniFormResourceIdentifier",
        "directoryName",
        "rfc822Name",
        "registrationID",
        "otherName",
        "x400Address",
        "EDIPartyName",
    ]
    if alt_type.lower() not in [a_type.lower() for a_type in valid_types]:
        raise ValidationError(
            "Invalid SubAltName Type: {} choose from {}".format(
                type, ",".join(valid_types)
            )
        )

def csr(data):
    """
    Determines if the CSR is valid and allowed.
    :param data:
    :return:
    """
    try:
        request = x509.load_pem_x509_csr(data.encode("utf-8"), default_backend())
    except Exception:
        raise ValidationError("CSR presented is not valid.")

    # Validate common name and SubjectAltNames
    try:
        for name in request.subject.get_attributes_for_oid(NameOID.COMMON_NAME):
            common_name(name.value)
    except ValueError as err:
        current_app.logger.info("Error parsing Subject from CSR: %s", err)
        raise ValidationError("Invalid Subject value in supplied CSR")

    try:
        alt_names = request.extensions.get_extension_for_class(
            x509.SubjectAlternativeName
        )

        for name in alt_names.value.get_values_for_type(x509.DNSName):
            sensitive_domain(name)
    except x509.ExtensionNotFound:
        pass

def dates(data):
    if not data.get("validity_start") and data.get("validity_end"):
        raise ValidationError("If validity start is specified so must validity end.")

    if not data.get("validity_end") and data.get("validity_start"):
        raise ValidationError("If validity end is specified so must validity start.")

    if data.get("validity_start") and data.get("validity_end"):
        if not current_app.config.get("LEMUR_ALLOW_WEEKEND_EXPIRATION", True):
            if is_weekend(data.get("validity_end")):
                raise ValidationError("Validity end must not land on a weekend.")

        if not data["validity_start"] < data["validity_end"]:
            raise ValidationError("Validity start must be before validity end.")

        if data.get("authority"):
            if (
                data.get("validity_start").date()
                < data["authority"].authority_certificate.not_before.date()
            ):
                raise ValidationError(
                    "Validity start must not be before {}".format(
                        data["authority"].authority_certificate.not_before
                    )
                )

            if (
                data.get("validity_end").date()
                > data["authority"].authority_certificate.not_after.date()
            ):
                raise ValidationError(
                    "Validity end must not be after {}".format(
                        data["authority"].authority_certificate.not_after
                    )
                )

    return data

def verify_private_key_match(key, cert, error_class=ValidationError):
    """
    Checks that the supplied private key matches the certificate.

    :param cert: Parsed certificate
    :param key: Parsed private key
    :param error_class: Exception class to raise on error
    """
    if key.public_key().public_numbers() != cert.public_key().public_numbers():
        raise error_class("Private key does not match certificate.")

def verify_cert_chain(certs, error_class=ValidationError):
    """
    Verifies that the certificates in the chain are correct.

    We don't bother with full cert validation but just check that certs in the chain are signed by the next, to avoid
    basic human errors -- such as pasting the wrong certificate.

    :param certs: List of parsed certificates, use parse_cert_chain()
    :param error_class: Exception class to raise on error
    """
    cert = certs[0]
    for issuer in certs[1:]:
        # Use the current cert's public key to verify the previous signature.
        # "certificate validation is a complex problem that involves much more than just signature checks"
        try:
            check_cert_signature(cert, issuer.public_key())

        except InvalidSignature:
            # Avoid circular import.
            from lemur.common import defaults

            raise error_class(
                "Incorrect chain certificate(s) provided: '%s' is not signed by '%s'"
                % (
                    defaults.common_name(cert) or "Unknown",
                    defaults.common_name(issuer),
                )
            )

        except UnsupportedAlgorithm as err:
            current_app.logger.warning("Skipping chain validation: %s", err)

        # Next loop will validate that *this issuer* cert is signed by the next chain cert.
        cert = issuer

def is_valid_owner(email):
    user_membership_provider = None
    if current_app.config.get("USER_MEMBERSHIP_PROVIDER") is not None:
        user_membership_provider = plugins.get(current_app.config.get("USER_MEMBERSHIP_PROVIDER"))
    if user_membership_provider is None:
        # nothing to check since USER_MEMBERSHIP_PROVIDER is not configured
        return True

    # expecting owner to be an existing team DL
    return user_membership_provider.does_group_exist(email)


import asyncio
import logging
from datetime import datetime
from json import JSONDecodeError

import requests

from chitanda.config import config
from chitanda.database import database
from chitanda.decorators import args, auth_only, register
from chitanda.errors import BotError

logger = logging.getLogger(__name__)
API_URL = "https://ws.audioscrobbler.com/2.0/"

@register("lastfm")
@args(r"$")
@auth_only
async def call(message):
    """Relay your currently playing Last.FM track."""
    lastfm = _get_lastfm_nick(message.username, message.listener)
    response = await _get_now_playing(lastfm, message.formatted_author)
    track, album, artist = (
        response["name"],
        response["album"]["#text"],
        response["artist"]["#text"],
    )
    time_since = _calculate_time_since_played(response, message.formatted_author)
    tags = await _get_track_tags(track, album, artist)
    return _format_response(
        message.formatted_author,
        track,
        album,
        artist,
        tags,
        time_since,
    )

def _get_lastfm_nick(username, listener):
    with database() as (conn, cursor):
        cursor.execute(
            """
            SELECT lastfm
            FROM lastfm
            WHERE user = ?  AND listener = ?
            """,
            (username, str(listener)),
        )
        row = cursor.fetchone()
        if not row:
            raise BotError("No Last.FM name set.")
        return row["lastfm"]

async def _get_now_playing(lastfm, formatted_author=None):
    try:
        response = (
            await asyncio.get_event_loop().run_in_executor(
                None,
                lambda: requests.get(
                    API_URL,
                    headers={"User-Agent": config["user_agent"]},
                    params={
                        "method": "user.getrecenttracks",
                        "api_key": config["lastfm"]["api_key"],
                        "user": lastfm,
                        "limit": 1,
                        "format": "json",
                    },
                    timeout=5,
                ),
            )
        ).json()
    except JSONDecodeError as e:
        logger.error(f"Failed to query Last.FM API: {e}.")
        raise BotError("Failed to query Last.FM API.")
    if "error" in response:
        message = (
            f'Failed to query Last.FM API: {response["error"]} '
            f'{response["message"]}.'
        )
        logger.error(message)
        raise BotError(message)
    if not response["recenttracks"]["track"]:
        raise BotError(f"{formatted_author} has never scrobbled before.")
    return response["recenttracks"]["track"][0]

def _calculate_time_since_played(track, formatted_author=None):
    if "@attr" in track:
        return

    scrobble_time = datetime.strptime(track["date"]["#text"], "%d %b %Y, %H:%M")
    diff = datetime.utcnow() - scrobble_time
    hours, seconds = divmod(diff.seconds, 3600)
    minutes = divmod(seconds, 60)[0]

    time_since = []

    if diff.days > 0:
        time_since.append(f"{diff.days} day(s)")
    if hours > 0:
        time_since.append(f"{hours} hour(s)")
    if minutes > 0:
        time_since.append(f"{minutes} minute(s)")

    if diff.days > 0 or hours > 0:
        raise BotError(
            f"{formatted_author} is not playing anything (last seen: "
            f'{" ".join(time_since)} ago)'
        )

    return f"{minutes} minutes"

async def _get_track_tags(track, album, artist):
    params = [
        {"method": "track.gettoptags", "track": track, "artist": artist},
        {"method": "album.gettoptags", "album": album, "artist": artist},
        {"method": "artist.gettoptags", "artist": artist},
    ]

    executors = [
        asyncio.get_event_loop().run_in_executor(
            None,
            lambda: requests.get(
                API_URL,
                headers={"User-Agent": config["user_agent"]},
                params={
                    "api_key": config["lastfm"]["api_key"],
                    "format": "json",
                    **p,
                },
                timeout=5,
            ),
        )
        for p in params
    ]

    tags = []
    for response in await asyncio.gather(*executors):
        data = response.json()
        try:
            tags += [t["name"] for t in data["toptags"]["tag"]]
        except KeyError:
            pass

    return tags[:3]

def _format_response(author, track, album, artist, tags, time_since):
    response = f"{author} is now playing {track}"
    if artist:
        response += f" by {artist}"
    if album:
        response += f" from {album}"
    if tags:
        response += f' [tags: {" / ".join(tags)}]'
    if time_since:
        response += f" (scrobbled {time_since} ago)"
    return response


import re
from datetime import date
from db import DataBase
from exceptions import *

def add_expense_to_db(user_id: int, raw_message: str) -> tuple:
    """Parse message, add raw in db and return info about category and money spent on it"""
    pattern = re.compile(r'(.+) (\d+)')
    match = pattern.search(raw_message.lower())
    # if not found return False
    if not match:
        raise BadMessage
    category = match.group(1)

    # here is params
    money = match.group(2)
    category_name = check_the_category(category)
    db = DataBase('db.db')

    # if there isn't user table: create new table
    if not db.check_if_the_user_in_db(user_id):
        db.create_user_table(user_id)

    # add expense to the table
    db.add_expense(user_id, category_name, money, date.today())
    return category_name, money

def show_month_expense(user_id: int) -> str:
    """Show month expense"""
    db = DataBase('db.db')

    # get all expenses as a dict
    result = db.get_month_expenses(user_id)
    if not result:
        raise NoInformationException
    # get total sum of all expenses
    total = sum(result.values())

    # sort dict by money spent on category and get sorted list of tuples (category, money)
    result = sorted(result.items(), key=lambda item: item[1], reverse=True)

    # convert every tuple to string
    result = [f'{el[0].capitalize()}: {el[1]} руб.' for el in result]
    # make final message of all expences and return it
    final_message = '\n'.join(result) + f'\n*Всего*: {total} руб.'

    return final_message

def show_daily_expense(user_id: int) -> str:
    """Show daily expense"""
    db = DataBase('db.db')

    # get all expenses as a dict
    result = db.get_daily_expenses(user_id)
    if not result:
        raise NoInformationException
    # get total sum of all expenses
    total = sum(result.values())

    # sort dict by money spent on category and get sorted list of tuples (category, money)
    result = sorted(result.items(), key=lambda item: item[1], reverse=True)

    # convert every tuple to string
    result = [f'{el[0].capitalize()}: {el[1]} руб.' for el in result]
    # make final message of all expences and return it
    final_message = '\n'.join(result) + f'\n*Всего*: {total} руб.'

    return final_message

def delete_expense(user_id: int) -> tuple:
    """Delete expense and return info about deleted row"""
    # type error
    db = DataBase('db.db')
    # get deleted row
    deleted_row = db.del_last_expense(user_id)
    return deleted_row

def check_the_category(category_name) -> str:
    available_categories = ['продукты', 'коммунальные услуги', 'такси', 'кофе', 'транспорт', 'обслуживание машины',
                            'здоровье', 'кредит', 'книги', 'связь', 'покупка техники', 'интернет', 'подписки', 'кафе',
                            'прочее']
    # check the category name, if it is not found add to category others
    if category_name not in available_categories:
        category_name = 'прочее'

    return category_name

if __name__ == '__main__':
    check_the_category('такси')


import datetime

class BicECPriceGapAnalysisSetting(object):
    OUTPUT_DIR = './data/OUTPUT/' + 'BicEC_PriceGapAnalysis' + '/'
    TGT_FLOOR_DATE = datetime.date(2018,6,1)
    TGT_UPPER_DATE = datetime.date(2018,8,31)

class PreprocessSetting(object):
    RAW_DATA_DIR = './data/Input/raw_data/'
    # DATA_FILES_TO_FETCH = ['売上データ詳細_' + TGT_STORE + '_20180401-0630.csv', ]
    # DATA_FILES_TO_FETCH = ['定楽屋 金山店2018-04-01-2018-06-30_before_grouping.csv', ]
    # PROCESSED_DATA_DIR = './data/Input/processed_data/'+ TGT_STORE +'/'


import os
import json
import simpy
import numpy as np

import resources

from collections import defaultdict

from util import Loggable, floor, ceil

class Meter(Loggable):

  def __init__(self, env):
    assert isinstance(env, simpy.Environment)
    self.__meta = resources.ResourceMetadata()
    self.__env = env
    self.__hosts = defaultdict(list)
    self.__routes = defaultdict(dict)
    self.__usage = defaultdict(dict)
    self.__data_transfers = []
    self.__sched_turnovers = []
    self.__n_sched_ops = 0

  @property
  def runtime(self):
    return self.__env.now

  @property
  def cumulative_instance_hours(self):
    return sum([sum([v[1] - v[0] for v in vals]) for h, vals in self.__hosts.items()])/3600

  @property
  def total_network_traffic_cost(self):
    routes, meta = self.__routes, self.__meta
    cost = 0
    for r, pkts in routes.items():
      data_size = sum([sum(pkt_size for _, _, pkt_size in trans) for trans in pkts.values()])
      cost += meta.calc_network_traffic_cost(r.src, r.dst, data_size)
    return cost

  @property
  def average_congestion_delay(self):
    delay, n_pkts = 0, 0
    for pkts in self.__routes.values():
      n_pkts += len(pkts)
      for p in pkts.values():
        for i, (start, end, _) in enumerate(p):
          if i > 0:
            delay += start - p[i - 1][1]
    return n_pkts and delay/n_pkts

  def initialize(self):
    self.__hosts = defaultdict(list)
    self.__routes = defaultdict(dict)
    self.__meta = resources.ResourceMetadata()

  def host_check_in(self, h):
    env, hosts = self.__env, self.__hosts
    last_chkpt = hosts[h][-1] if hosts[h] else None
    self._track_resource_usage(h)
    if last_chkpt is None:
      hosts[h] += [env.now],
    elif len(last_chkpt) == 2:
      if env.now > last_chkpt[-1]:
        hosts[h] += [env.now],
      else:
        last_chkpt.pop()

  def host_check_out(self, h):
    env, hosts = self.__env, self.__hosts
    last_chkpt = hosts[h][-1] if hosts[h] else None
    self._track_resource_usage(h)
    if last_chkpt is None:
      raise Exception('Check-out occurs before any check-in')
    if len(last_chkpt) == 1:
      last_chkpt += env.now,
    elif len(last_chkpt) == 2:
      if env.now > last_chkpt[-1]:
        last_chkpt[-1] = env.now

  def route_check_in(self, r, pkt_id):
    self.__routes[r].setdefault(pkt_id, []).append([self.__env.now])

  def route_check_out(self, r, pkt_id, pkt_size):
    self.__routes[r][pkt_id][-1] += [self.__env.now, pkt_size]

  def add_data_transfer(self, timepoint, srcs, dst, data_amt, duration, prop_delay, avg_bw, avg_egress_cost):
    data_transfers = self.__data_transfers
    data_transfers += {
      'timestamp': timepoint,
      'from': [[s.cloud.value, s.region.value, s.zone.value] for s in srcs],
      'to': [dst.cloud.value, dst.region.value, dst.zone.value],
      'data_amt': data_amt,
      'total_delay': duration,
      'propagation_delay': prop_delay,
      'avg_bw': avg_bw,
      'avg_egress_cost': avg_egress_cost
    },

  def add_scheduling_turnover(self, timepoint):
    self.__sched_turnovers += timepoint,

  def increment_scheduling_ops(self, n_ops):
    self.__n_sched_ops += n_ops

  def save(self, data_dir):
    if not os.path.exists(data_dir):
      os.makedirs(data_dir, exist_ok=True)
    # host_usage = open('%s/host_usage.json' % data_dir, 'w')
    # cpu_usage = open('%s/cpu_usage.json' % data_dir, 'w')
    # mem_usage = open('%s/mem_usage.json' % data_dir, 'w')
    # disk_usage = open('%s/disk_usage.json' % data_dir, 'w')
    # gpu_usage = open('%s/gpu_usage.json' % data_dir, 'w')
    with open('%s/general.json'%data_dir, 'w') as f:
      json.dump({
        'egress_cost': self.total_network_traffic_cost,
        'cum_instance_hours': self.cumulative_instance_hours
      }, f)
    with open('%s/transfers.json'%data_dir, 'w') as f:
      json.dump([t for t in self.__data_transfers], f)
    with open('%s/scheduler.json'%data_dir, 'w') as f:
      json.dump({
        'turnovers': self.__sched_turnovers,
        'total_scheduling_ops': self.__n_sched_ops
      }, f)
    with open('%s/host_usage.json'%data_dir, 'w') as f:
      x, y = self.plot_host_usage()
      json.dump({
        'timestamps': x,
        'n_hosts': y
      }, f)

  def plot_host_usage(self, sample_size=100):
    counter = {}
    for h, durations in self.__hosts.items():
      for start, end in durations:
        start, end = floor(start, sample_size), ceil(end, sample_size)
        cur_end = min(start + sample_size, end)
        while cur_end < end:
          timerange = (cur_end - sample_size, cur_end)
          if h not in counter.get(timerange, set()):
            counter.setdefault(timerange, set()).add(h)
          cur_end += sample_size
    x = list(sorted(counter.keys()))
    y = [len(counter[k]) for k in x]
    return x, y

  def plot_resource_usage(self, resource, sample_size=100):
    counter, usage = {}, self.__usage[resource]
    for h, recs in usage.items():
      for timepoint, amt in recs:
        counter.setdefault(floor(timepoint, sample_size), {}).setdefault(h, []).append(amt)
    for timepoint, hosts in dict(counter).items():
      counter[timepoint] = np.mean([np.mean(vals) for vals in hosts.values()])
    x = list(sorted(counter.keys()))
    y = [counter[k] for k in x]
    return x, y

  def get_avg_host_usage(self, sample_size=100):
    _, n_hosts = self.plot_host_usage(sample_size=sample_size)
    return np.mean(n_hosts)

  def get_avg_cpu_usage(self, sample_size=100):
    _, n_cpus = self.plot_resource_usage('cpus', sample_size=sample_size)
    return np.mean(n_cpus)

  def get_avg_mem_usage(self, sample_size=100):
    _, mem = self.plot_resource_usage('mem', sample_size=sample_size)
    return np.mean(mem)

  def get_avg_disk_usage(self, sample_size=100):
    _, disks = self.plot_resource_usage('disk', sample_size=sample_size)
    return np.mean(disks)

  def get_avg_gpus_usage(self, sample_size=100):
    _, n_gpus = self.plot_resource_usage('gpus', sample_size=sample_size)
    return np.mean(n_gpus)

  def _track_resource_usage(self, h):
    assert isinstance(h, resources.Host)
    usage, now, resc = self.__usage, self.__env.now, h.resource
    usage['cpus'].setdefault(h, []).append((now, resc.cpus_used/resc.total_cpus))
    usage['mem'].setdefault(h, []).append((now, resc.mem_used/resc.total_mem))
    usage['disk'].setdefault(h, []).append((now, resc.disk_used/resc.total_disk))
    usage['gpus'].setdefault(h, []).append((now, resc.gpus_used/resc.total_gpus))


from openravepy import *
import os
import errno
import signal
from functools import wraps
import StringIO
import numpy as np
from math import atan2

def base_pose_to_mat(pose):
    # x, y, rot = pose
    x = pose[0,0]
    y = pose[1,0]
    rot = pose[2,0]
    q = quatFromAxisAngle((0, 0, rot)).tolist()
    pos = [x, y, 0]
    # pos = np.vstack((x,y,np.zeros(1)))
    matrix = matrixFromPose(q + pos)
    return matrix

def mat_to_base_pose(mat):
    pose = poseFromMatrix(mat)
    x = pose[4]
    y = pose[5]
    rot = axisAngleFromRotationMatrix(mat)[2]
    return np.array([[x],[y],[rot]])
    # return [x, y, rot]

def obj_pose_from_transform(transform):
    trans = transform[:3,3]
    rot_matrix = transform[:3,:3]
    yaw, pitch, roll = _ypr_from_rot_matrix(rot_matrix)
    # ipdb.set_trace()
    return np.array((yaw, pitch, roll, trans[0], trans[1], trans[2]))

def set_transparency(body, transparency):
    for link in body.GetLinks():
        for geom in link.GetGeometries():
            geom.SetTransparency(transparency)

def set_color(body, color):
    for link in body.GetLinks():
        for geom in link.GetGeometries():
            geom.SetDiffuseColor(color)

def transform_from_obj_pose(pose):
    alpha, beta, gamma, x, y, z = pose
    Rz, Ry, Rx = _axis_rot_matrices(pose)
    rot_mat = np.dot(Rz, np.dot(Ry, Rx))
    transform = np.eye(4)
    transform[:3,:3] = rot_mat
    transform[:3,3] = [x,y,z]
    return transform

def _axis_rot_matrices(pose):
    from math import cos, sin
    alpha, beta, gamma, x, y, z = pose
    Rz_2d = np.array([[cos(alpha), -sin(alpha)], [sin(alpha), cos(alpha)]])
    Ry_2d = np.array([[cos(beta), sin(beta)], [-sin(beta), cos(beta)]])
    Rx_2d = np.array([[cos(gamma), -sin(gamma)], [sin(gamma), cos(gamma)]])
    I = np.eye(3)
    Rz = I.copy()
    Rz[:2,:2] = Rz_2d
    Ry = I.copy()
    Ry[[[0],[2]],[0,2]] = Ry_2d
    Rx = I.copy()
    Rx[1:3,1:3] = Rx_2d
    # ipdb.set_trace()
    return Rz, Ry, Rx

def _ypr_from_rot_matrix(r):
    # alpha
    yaw = atan2(r[1,0], r[0,0])
    # beta
    pitch = atan2(-r[2,0],np.sqrt(r[2,1]**2+r[2,2]**2))
    # gamma
    roll = atan2(r[2,1], r[2,2])
    # ipdb.set_trace()
    return (yaw, pitch, roll)

class TimeoutError(Exception):
    pass

def timeout(seconds=10, error_message=os.strerror(errno.ETIME)):
    def decorator(func):
        def _handle_timeout(signum, frame):
            raise TimeoutError(error_message)
        def wrapper(*args, **kwargs):
            signal.signal(signal.SIGALRM, _handle_timeout)
            signal.alarm(seconds)
            try:
                result = func(*args, **kwargs)
            finally:
                signal.alarm(0)
            return result
        return wraps(func)(wrapper)
    return decorator

def getStringFile(str):
    strPlanFileH = StringIO.StringIO()
    strPlanFileH.write(str)
    strPlanFileH.seek(0)
    return strPlanFileH

def get_object_limits(obj):
    """Returns the bounding box of an object.

    Returns: min_x, max_x, min_y, max_y, z
    """

    ab = obj.ComputeAABB()
    max_x = ab.pos()[0] + ab.extents()[0]
    min_x = ab.pos()[0] - ab.extents()[0]

    max_y = ab.pos()[1] + ab.extents()[1]
    min_y = ab.pos()[1] - ab.extents()[1]
    z = ab.pos()[2] + ab.extents()[2]

    return min_x, max_x, min_y, max_y, z

def get_object_limits_2(obj):
    """
    Returns the bounding box of an object.
    Returns: min_x, max_x, min_y, max_y, min_z, max_z
    """

    ab = obj.ComputeAABB()
    max_x = ab.pos()[0] + ab.extents()[0]
    min_x = ab.pos()[0] - ab.extents()[0]

    max_y = ab.pos()[1] + ab.extents()[1]
    min_y = ab.pos()[1] - ab.extents()[1]

    max_z = ab.pos()[2] + ab.extents()[2]
    min_z = ab.pos()[2] - ab.extents()[2]

    return min_x, max_x, min_y, max_y, min_z, max_z

def get_object_center(obj):
    min_x, max_x, min_y, max_y, z = get_object_limits(obj)
    return [(min_x + max_x) / 2, (min_y + max_y) / 2, z]

def get_object_height(obj):
    ab = obj.ComputeAABB()
    max_z = ab.pos()[2] + ab.extents()[2]
    min_z = ab.pos()[2] - ab.extents()[2]
    return max_z - min_z

def create_body_at(env, T, name = "crash-test-dummy"):
    body = openravepy.RaveCreateKinBody(env, "")
    body.SetName(name)
    body.InitFromBoxes(np.array([[0,0,0, 0.04, 0.04, 0.1]]), True)
    p = openravepy.poseFromMatrix(T)
    p[-1] += 0.101


import inspect

from discord import Embed

from chitanda.config import config
from chitanda.decorators import args, register
from chitanda.listeners import DiscordListener

@register("help")
@args(r"$")
async def call(message):
    """Sends a private message detailing the available commands."""
    if isinstance(message.listener, DiscordListener):
        embed = Embed(title="Help!")
        for trigger, command in sorted(message.bot.commands.items()):
            if _applicable_listener(message.listener, command.call):
                embed.add_field(
                    name=f'{config["trigger_character"]}{trigger}',
                    value=_generate_help_text(command.call),
                    inline=False,
                )

        yield {
            "target": message.author,
            "message": embed,
            "private": True,
            "embed": True,
        }
    else:
        longest = max(len(t) for t in message.bot.commands)
        yield {"target": message.author, "message": "Help:"}
        for trigger, command in sorted(message.bot.commands.items()):
            if _applicable_listener(message.listener, command.call):
                yield {
                    "target": message.author,
                    "message": (
                        f'{config["trigger_character"]}'
                        f"{trigger.ljust(longest)} | "
                        f"{_generate_help_text(command.call)}"
                    ),
                }

def _applicable_listener(listener, call):
    return not hasattr(call, "listeners") or type(listener) in call.listeners

def _generate_help_text(call):
    doc = inspect.getdoc(call)
    if hasattr(call, "admin_only"):
        doc += " (admin only)"
    if hasattr(call, "auth_only"):
        doc += " (requires authentication)"
    if hasattr(call, "channel_only"):
        doc += " (channel only)"
    if hasattr(call, "private_message_only"):
        doc += " (PM only)"
    return doc


def export_to_excel(uploaded_files, date_str, encoding_str, separator_str):
    """
    Create a folder where to put the created files.
    Creates all layouts that could be matched.
    Creates a zip file with the layouts.
    Return the zip filepath so it can be downloaded.
    """
    from datetime import datetime
    import pandas as pd
    import shutil
    import os
    from .helpers import read_uploaded_csv

    # Variables
    LAYOUT_POSTFIX = ".csv"
    LAYOUT_TAIL = len(LAYOUT_POSTFIX)
    # Create the filename and folder
    now_str = datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
    folder_to_zip = f"tmp/exported_to_excel_{now_str}"
    os.mkdir(folder_to_zip)
    # Read the files
    expander_dict = {}
    df_dict = {}
    for csv_file in sorted(uploaded_files, key=lambda x: x.name):
        try:
            basename = csv_file.name[:-LAYOUT_TAIL]
            df, read_csv_params = read_uploaded_csv(csv_file)
            expander_content_list = []
            n = 5
            expander_content_list.append(f"* Encoding: {read_csv_params['encoding']}")
            expander_content_list.append(f"* Separator: {read_csv_params['sep']}")
            expander_content_list.append(f"* Rows: {df.shape[0]}")
            expander_content_list.append(f"* Columns: {df.shape[1]}")
            expander_content_list.append(df.head(n))
            expander_content_list.append(f"First {n} rows:")
            expander_dict[basename] = expander_content_list
            df_dict[basename[:30]] = df
        except Exception as e:
            expander_dict["ERROR!!!:" + basename] = ["Could not read the file", "File skipped", f"ERROR: {e}"]
    # Create the excel file
    excel_filepath = folder_to_zip + f"/excel_from_csv_{date_str}.xlsx"
    with pd.ExcelWriter(excel_filepath) as ew:
        for sheet_name, df in df_dict.items():
            df.to_excel(ew, sheet_name=sheet_name, index=False)

    # Zip the folder
    executed_outputs = f"tmp/exported_to_excel_{date_str}.zip"
    filename_without_extension = executed_outputs.split(".")[0]
    shutil.make_archive(filename_without_extension, "zip", folder_to_zip)
    return executed_outputs, expander_dict