import abc
from collections import defaultdict
from functools import cached_property
from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Optional
from xmlrpc.client import Boolean
from zipfile import ZipFile

from django.core.files.storage import Storage
from django.db.models import Q, QuerySet
from django.db.transaction import Atomic

from rest_framework.serializers import Serializer

from baserow.contrib.database.constants import IMPORT_SERIALIZED_IMPORTING
from baserow.core.utils import ChildProgressBuilder
from baserow_enterprise.exceptions import SubjectTypeNotExist

from .exceptions import (
    ApplicationTypeAlreadyRegistered,
    ApplicationTypeDoesNotExist,
    AuthenticationProviderTypeAlreadyRegistered,
    AuthenticationProviderTypeDoesNotExist,
    ObjectScopeTypeAlreadyRegistered,
    ObjectScopeTypeDoesNotExist,
    OperationTypeAlreadyRegistered,
    OperationTypeDoesNotExist,
    PermissionDenied,
    PermissionManagerTypeAlreadyRegistered,
    PermissionManagerTypeDoesNotExist,
)
from .export_serialized import CoreExportSerializedStructure
from .registry import (
    APIUrlsInstanceMixin,
    APIUrlsRegistryMixin,
    ImportExportMixin,
    Instance,
    MapAPIExceptionsInstanceMixin,
    ModelInstanceMixin,
    ModelRegistryMixin,
    Registry,
)
from .types import ContextObject, ScopeObject

if TYPE_CHECKING:
    from django.contrib.auth.models import AbstractUser

    from baserow.core.models import Application, Group, GroupInvitation, Template

class Plugin(APIUrlsInstanceMixin, Instance):
    """
    This abstract class represents a custom plugin that can be added to the plugin
    registry. It must be extended so customisation can be done. Each plugin can register
    urls to the root and to the api.

    The added API urls will be available under the namespace 'api'. So if a url
    with name 'example' is returned by the method it will available under
    reverse('api:example').

    Example:
        from django.http import HttpResponse
        from baserow.core.registries import Plugin, plugin_registry

        def page_1(request):
            return HttpResponse('Page 2')

        class ExamplePlugin(Plugin):
            type = 'a-unique-type-name'

            # Will be added to the root.
            def get_urls(self):
                return [
                    url(r'^page-1$', page_1, name='page_1')
                ]

            # Will be added to the API.
            def get_api_urls(self):
                return [
                    path('application-type/', include(api_urls, namespace=self.type)),
                ]

        plugin_registry.register(ExamplePlugin())
    """

    def get_urls(self):
        """
        If needed root urls related to the plugin can be added here.

        Example:

            def get_urls(self):
                from . import api_urls

                return [
                    path('some-url/', include(api_urls, namespace=self.type)),
                ]

            # api_urls.py
            from django.urls import re_path

            urlpatterns = [
                url(r'some-view^$', SomeView.as_view(), name='some_view'),
            ]

        :return: A list containing the urls.
        :rtype: list
        """

        return []

    def user_created(
        self,
        user: "AbstractUser",
        group: "Group" = None,
        group_invitation: "GroupInvitation" = None,
        template: "Template" = None,
    ):
        """
        A hook that is called after a new user has been created. This is the place to
        create some data the user can start with. A group will most often be created,
        but won't be if the account has `allow_global_group_creation` set to `False`.

        :param user: The newly created user.
        :type user: User
        :param group: The newly created group for the user.
        :type group: Group or None
        :param group_invitation: Is provided if the user has signed up using a valid
            group invitation token.
        :type group_invitation: GroupInvitation or None
        :param template: The template that is installed right after creating the
            account. Is `None` if the template was not created.
        :type template: Template or None
        """

    def user_signed_in(self, user):
        """
        A hook that is called after an existing user has signed in.

        :param user: The user that just signed in.
        :type user: User
        """

class PluginRegistry(APIUrlsRegistryMixin, Registry):
    """
    With the plugin registry it is possible to register new plugins. A plugin is an
    abstraction made specifically for Baserow. It allows a plugin developer to
    register extra api and root urls.
    """

    name = "plugin"

    @property
    def urls(self):
        """
        Returns a list of all the urls that are in the registered instances. They
        are going to be added to the root url config.

        :return: The urls of the registered instances.
        :rtype: list
        """

        urls = []
        for types in self.registry.values():
            urls += types.get_urls()
        return urls

class ApplicationType(
    APIUrlsInstanceMixin, ModelInstanceMixin, ImportExportMixin, Instance
):
    """
    This abstract class represents a custom application that can be added to the
    application registry. It must be extended so customisation can be done. Each
    application will have his own model that must extend the Application model, this is
    needed so that the user can set custom settings per application instance he has
    created.

    The added API urls will be available under the namespace 'api'. So if a url
    with name 'example' is returned by the method it will available under
    reverse('api:example').

    Example:
        from baserow.core.models import Application
        from baserow.core.registries import ApplicationType, application_type_registry

        class ExampleApplicationModel(Application):
            pass

        class ExampleApplication(ApplicationType):
            type = 'a-unique-type-name'
            model_class = ExampleApplicationModel

            def get_api_urls(self):
                return [
                    path('application-type/', include(api_urls, namespace=self.type)),
                ]

        application_type_registry.register(ExampleApplication())

    """

    instance_serializer_class = None
    """This serializer that is used to serialize the instance model."""

    supports_snapshots = True

    def pre_delete(self, application):
        """
        A hook that is called before the application instance is deleted.

        :param application: The application model instance that needs to be deleted.
        :type application: Application
        """

    def export_safe_transaction_context(self, application: "Application") -> Atomic:
        """
        Should return an Atomic context (such as transaction.atomic or
        baserow.contrib.database.db.atomic.read_repeatable_single_database_atomic_transaction)
        which can be used to safely run a database transaction to export an application
        of this type.

        :param application: The application that we are about to export.
        :return: An Atomic context object that will be used to open a transaction safely
            to export an application of this type.
        """

        raise NotImplementedError(
            "Must be implemented by the specific application type"
        )

    def create_application(
        self, user, group: "Group", name: str, init_with_data: bool = False
    ) -> "Application":
        """
        Creates a new application instance of this type and returns it.

        :param user: The user that is creating the application.
        :param group: The group that the application will be created in.
        :param name: The name of the application.
        :param init_with_data: Whether the application should be created with some
            initial data. Defaults to False.
        :return: The newly created application instance.
        """

        model = self.model_class
        last_order = model.get_last_order(group)

        instance = model.objects.create(group=group, order=last_order, name=name)
        if init_with_data:
            self.init_application(user, instance)
        return instance

    def init_application(self, user, application: "Application") -> None:
        """
        This method can be called when the application is created to
        initialize it with some default data.

        :param user: The user that is creating the application.
        :param application: The application to initialize with data.
        """

    def export_serialized(
        self,
        application: "Application",
        files_zip: Optional[ZipFile] = None,
        storage: Optional[Storage] = None,
    ):
        """
        Exports the application to a serialized dict that can be imported by the
        `import_serialized` method. The dict is JSON serializable.

        :param application: The application that must be exported.
        :type application: Application
        :param files_zip: A zip file buffer where the files related to the template
            must be copied into.
        :type files_zip: ZipFile
        :param storage: The storage where the files can be loaded from.
        :type storage: Storage or None
        :return: The exported and serialized application.
        :rtype: dict
        """

        return CoreExportSerializedStructure.application(
            id=application.id,
            name=application.name,
            order=application.order,
            type=self.type,
        )

    def import_serialized(
        self,
        group: "Group",
        serialized_values: Dict[str, Any],
        id_mapping: Dict[str, Any],
        files_zip: Optional[ZipFile] = None,
        storage: Optional[Storage] = None,
        progress_builder: Optional[ChildProgressBuilder] = None,
    ) -> "Application":
        """
        Imports the exported serialized application by the `export_serialized` as a new
        application to a group.

        :param group: The group that the application must be added to.
        :param serialized_values: The exported serialized values by the
            `export_serialized` method.
        :param id_mapping: The map of exported ids to newly created ids that must be
            updated when a new instance has been created.
        :param files_zip: A zip file buffer where files related to the template can
            be extracted from.
        :param storage: The storage where the files can be copied to.
        :param progress_builder: If provided will be used to build a child progress bar
            and report on this methods progress to the parent of the progress_builder.
        :return: The newly created application.
        """

        if "import_group_id" not in id_mapping and group is not None:
            id_mapping["import_group_id"] = group.id

        if "applications" not in id_mapping:
            id_mapping["applications"] = {}

        serialized_copy = serialized_values.copy()
        application_id = serialized_copy.pop("id")
        serialized_copy.pop("type")
        application = self.model_class.objects.create(group=group, **serialized_copy)
        id_mapping["applications"][application_id] = application.id

        progress = ChildProgressBuilder.build(progress_builder, child_total=1)
        progress.increment(state=IMPORT_SERIALIZED_IMPORTING)

        return application

class ApplicationTypeRegistry(APIUrlsRegistryMixin, ModelRegistryMixin, Registry):
    """
    With the application registry it is possible to register new applications. An
    application is an abstraction made specifically for Baserow. If added to the
    registry a user can create new instances of that application via the app and
    register api related urls.
    """

    name = "application"
    does_not_exist_exception_class = ApplicationTypeDoesNotExist
    already_registered_exception_class = ApplicationTypeAlreadyRegistered

class AuthenticationProviderTypeRegistry(
    MapAPIExceptionsInstanceMixin, APIUrlsRegistryMixin, ModelRegistryMixin, Registry
):
    """
    With the authentication provider registry it is possible to register new
    authentication providers. An authentication provider is an abstraction made
    specifically for Baserow. If added to the registry a user can use that
    authentication provider to login.
    """

    name = "authentication_provider"
    does_not_exist_exception_class = AuthenticationProviderTypeDoesNotExist
    already_registered_exception_class = AuthenticationProviderTypeAlreadyRegistered

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._default = None

    def get_all_available_login_options(self):
        login_options = {}
        for provider in self.get_all():
            provider_login_options = provider.get_login_options()
            if provider_login_options:
                login_options[provider.type] = provider_login_options
        return login_options

class PermissionManagerType(Instance):
    """
    A permission manager is responsible to permit or disallow a specific operation
    according to the given context.

    A permission manager is also responsible to generate the data sent to the
    frontend to make it check the permission.

    And finally, a permission manager can filter the list querysets
    to remove disallowed objects from this list.

    See each PermissionManager method and `CoreHandler` methods for more details.
    """

    def check_permissions(
        self,
        actor: "AbstractUser",
        operation_name: str,
        group: Optional["Group"] = None,
        context: Optional[Any] = None,
        include_trash: Boolean = False,
    ) -> Optional[Boolean]:
        """
        This method is called each time a permission on an operation is checked by the
        `CoreHandler().check_permissions()` method if the current permission manager is
        listed in the `settings.PERMISSION_MANAGERS` list.

        It should:
            - return `True` if the operation is permitted given the other parameters
            - raise a `PermissionDenied` exception if the operation is disallowed
            - return `None` if the condition required by the permission manager are not
              met.

        By default, this method raises a PermissionDenied exception.

        :param actor: The actor who wants to execute the operation. Generally a `User`,
            but can be a `Token`.
        :param operation_name: The operation name the actor wants to execute.
        :param group: The optional group in which  the operation takes place.
        :param context: The optional object affected by the operation. For instance
            if you are updating a `Table` object, the context is this `Table` object.
        :param include_trash: If true then also checks if the given group has been
            trashed instead of raising a DoesNotExist exception.
        :raise PermissionDenied: If the operation is disallowed a PermissionDenied is
            raised.
        :return: `True` if the operation is permitted, None if the permission manager
            can't decide.
        """

        raise PermissionDenied()

    def get_permissions_object(
        self, actor: "AbstractUser", group: Optional["Group"] = None
    ) -> Any:
        """
        This method should return the data necessary to easily check a permission from
        a client. This object can be used for instance from the frontend to hide or
        show UI element accordingly to the user permissions.
        The data set returned must contain all the necessary information to prevent and
        the client shouldn't have to get more data to decide.

        This method is called when the `CoreHandler().get_permissions()` is called,
        if the permission manager is listed in the `settings.PERMISSION_MANAGERS`.
        It can return `None` if this permission manager is not relevant for the given
        actor/group for some reason.

        By default this method returns None.

        :param actor: The actor whom we want to compute the permission object for.
        :param group: The optional group into which we want to compute the permission
            object.
        :return: The permission object or None.
        """

        return None

    def filter_queryset(
        self,
        actor: "AbstractUser",
        operation_name: str,
        queryset: QuerySet,
        group: Optional["Group"] = None,
        context: Optional[Any] = None,
    ) -> QuerySet:
        """
        This method allows a permission manager to filter a given queryset accordingly
        to the actor permissions in the specified context. The
        `CoreHandler().filter_queryset()` method calls each permission manager listed in
        `settings.PERMISSION_MANAGERS` to successively filter the given queryset.

        :param actor: The actor whom we want to filter the queryset for.
            Generally a `User` but can be a Token.
        :param operation: The operation name for which we want to filter the queryset
            for.
        :param group: An optional group into which the operation takes place.
        :param context: An optional context object related to the current operation.
        :return: The queryset potentially filtered.
        """

        return queryset

    def get_roles(self) -> List:
        """
        Get all the roles available for your permissions system
        """

        return []

class PermissionManagerTypeRegistry(Registry[PermissionManagerType]):
    """
    This registry contains all the permission manager used to handle permissions in
    Baserow. A permission manager must then be listed in the
    `settings.PERMISSION_MANAGERS` variable to be used by the `CoreHandler` methods.
    """

    name = "permission_manager"

    does_not_exist_exception_class = PermissionManagerTypeDoesNotExist
    already_registered_exception_class = PermissionManagerTypeAlreadyRegistered

class ObjectScopeType(Instance, ModelInstanceMixin):
    """
    This type describe an object scope in Baserow. This is useful if you need to know
    the object hierarchy. This hierarchy is used by the permission system, for example,
    to determine if a context object is included by a given scope.
    It can also be used to list all context object of a scope included by another scope.

    An `ObjectScopeType` must be registered for each object that can be a scope or a
    context.
    """

    def get_content_type(self):
        from django.contrib.contenttypes.models import ContentType

        return ContentType.objects.get_for_model(self.model_class)

    def get_object_for_this_type(self, **kwargs):
        return self.get_content_type().get_object_for_this_type(**kwargs)

    def get_parent_scope(self) -> Optional["ObjectScopeType"]:
        """
        Returns the parent scope of the current scope.

        :return: the parent `ObjectScopeType` or `None` if it's a root scope.
        """

        return None

    def get_parent_scopes(self) -> List["ObjectScopeType"]:
        """
        Returns the parent scope of the current scope.

        :return: the parent `ObjectScopeType` or `None` if it's a root scope.
        """

        parent_scope = self.get_parent_scope()
        if not parent_scope:
            return []

        return [parent_scope] + parent_scope.get_parent_scopes()

    def get_parent(self, context: ContextObject) -> Optional[ContextObject]:
        """
        Returns the parent object of the given context which belongs to the current
        scope.

        :param context: The context object which we want the parent for. This object
            must belong to the current scope.
        :return: the parent object or `None` if it's a root object.
        """

        return None

    def get_parents(self, context: ContextObject) -> List[ContextObject]:
        """
        Returns all ancestors of the given context which belongs to the current
        scope.

        :param context: The context object which we want the ancestors for. This object
            must belong to the current scope.
        :return: the list of parent objects if it's a root object.
        """

        parent = self.get_parent(context)

        if parent is None:
            return []

        parents = self.get_parent_scope().get_parents(parent)
        parents.append(parent)

        return parents

    def get_all_context_objects_in_scope(self, scope: ScopeObject) -> Iterable:
        """
        Returns the list of context object belonging to the current scope that are
        included in the scope object given in parameter.

        :param scope: The scope into which we want the context objects for.
        :return: An iterable containing the context objects for the given scope.
        """

        return self.get_objects_in_scopes([scope])[scope]

    def get_filter_for_scope_type(
        self, scope_type: "ObjectScopeType", scopes: List[Any]
    ) -> Q:
        """
        Returns the filter to apply to the queryset that selects all the context
        objects included in the given scopes.
        All the scopes must be members of the given scope type.

        :param scope_type: The scope type the scopes belongs to.
        :param scopes: The scopes objects we want the context object for.
        :return: A Q object that can be used in a filter operation.
        """

        raise NotImplementedError(
            f"Must be implemented by the specific type <{self.type}>"
        )

    def get_enhanced_queryset(self) -> QuerySet:
        """
        Returns the base queryset for the objects of this scope enhanced for better
        performances.
        """

        return self.model_class.objects.all()

    def get_filter_for_scopes(self, scopes: List[Any]) -> Dict[Any, Any]:
        """
        Computes the filter to apply get all the objects instance of `self.model_class`
        included in the given scopes.

        :param scopes: A list of scopes we want the object for.
        :return: A Q object filter.
        """

        # Group scope by types to use `.get_filter_for_scope_type` later
        scope_by_types = defaultdict(set)
        for s in scopes:
            scope_by_types[object_scope_type_registry.get_by_model(s)].add(s)

        union_query = Q(id__in=[])

        for scope_type, scopes in scope_by_types.items():
            if scope_type.type == self.type:
                # Simple case: the scope type is the same as this one
                # Just filter by id
                union_query |= Q(id__in=[s.id for s in scopes])
            else:
                # Otherwise it's a parent scope. We add a part to the query_parts
                union_query |= self.get_filter_for_scope_type(scope_type, scopes)

        return union_query

    def get_objects_in_scopes(self, scopes: List[Any]) -> Dict[Any, Any]:
        """
        Computes the list of all objects, instance of the model_class property
        included in the given scopes.

        :param scopes: A list of scopes we want the object for.
        :return: A dict where the keys are the given scopes and the value is a list
          of the child objects of each scope.
        """

        objects_per_scope = {}

        parent_scopes = []
        for scope in scopes:
            if object_scope_type_registry.get_by_model(scope).type == self.type:
                # Scope of the same type doesn't need to be queried
                objects_per_scope[scope] = set([scope])
            else:
                parent_scopes.append(scope)

        if parent_scopes:
            query_result = list(
                self.get_enhanced_queryset().filter(
                    self.get_filter_for_scopes(parent_scopes)
                )
            )

            # We have all the objects in the queryset, but now we want to sort them
            # into buckets per original scope they are a child of.
            for scope in scopes:
                objects_per_scope[scope] = set()
                scope_type = object_scope_type_registry.get_by_model(scope)
                for obj in query_result:
                    parent_scope = object_scope_type_registry.get_parent(
                        obj, at_scope_type=scope_type
                    )
                    if parent_scope == scope:
                        objects_per_scope[scope].add(obj)

        return objects_per_scope

    def contains(self, context: ContextObject):
        """
        Returns True if the context is one object of this context.

        :param context: The context to test
        :return: True if the ObjectScopeType of the context is the same as this one.
        """

        context_scope_type = object_scope_type_registry.get_by_model(context)
        return context_scope_type.type == self.type

    @cached_property
    def level(self) -> int:
        """
        Returns the level of this scope in the full object hierarchy. The level is the
        number of ancestor to get to the root object.

        :return: The level of the scope.
        """

        parent = self.get_parent_scope()
        if parent is None:
            return 0
        else:
            return parent.level + 1

class ObjectScopeTypeRegistry(
    Registry[ObjectScopeType], ModelRegistryMixin[Any, ObjectScopeType]
):
    """
    This registry contains all `ObjectScopeType`. It also proposes a set of methods
    useful to go through the full object/scope hierarchy.
    """

    name = "object_scope"

    def get_parent(self, context, at_scope_type=None):
        """
        Returns the parent object of the given context.

        :param context: The context object we want the parent for.
        :param at_scope_type: A parent scope at which you want the parent.
        :return: if the `at_scope_type` is not set: the parent object or `None` if it's
            a root object. If at_scope_type is set, the ancestor for which scope_type
            matches at_scope_type or None if no parents match.
        """

        context_scope_type = self.get_by_model(context)
        if at_scope_type:
            if at_scope_type.type == context_scope_type.type:
                return context
            else:
                parent_scope = context_scope_type.get_parent(context)
                if parent_scope is None:
                    return None
                else:
                    return self.get_parent(parent_scope, at_scope_type=at_scope_type)
        else:
            return context_scope_type.get_parent(context)

    def scope_includes_context(
        self,
        scope: ScopeObject,
        context: ContextObject,
        scope_type: Optional[ObjectScopeType] = None,
    ) -> Boolean:
        """
        Checks whether a scope object includes the given context.

        :param scope: The scope object.
        :param context: A context object.
        :scope_type: An optional `ObjectScopeType` that is used mainly for performance
            reason.
        :return: True if the context is included in the given scope object.
        """

        if context is None:
            return False

        scope_type = scope_type or self.get_by_model(scope)

        context_scope_type = self.get_by_model(context)

        if scope_type == context_scope_type:
            return scope.id == context.id
        else:
            return self.scope_includes_context(
                scope, context_scope_type.get_parent(context), scope_type=scope_type
            )

    def scope_type_includes_scope_type(
        self,
        parent_scope_type: ObjectScopeType,
        child_scope_type: ObjectScopeType,
    ) -> Boolean:
        """
        Checks whether the parent_scope includes the child_scope.

        :param parent_scope: The scope object or type that should includes the other
            scope.
        :param child_scope: The scope object or type that should be included by the
            other scope.
        :return: True if the parent_scope includes the children scope. False otherwise.
        """

        if child_scope_type is None:
            return False

        if parent_scope_type.type == child_scope_type.type:
            return True
        else:
            return self.scope_type_includes_scope_type(
                parent_scope_type,
                child_scope_type.get_parent_scope(),
            )

    does_not_exist_exception_class = ObjectScopeTypeDoesNotExist
    already_registered_exception_class = ObjectScopeTypeAlreadyRegistered

class SubjectType(abc.ABC, Instance, ModelInstanceMixin):
    """
    This type describes a subject that exists in Baserow. A subject is anything that
    can execute an operation.
    """

    @abc.abstractmethod
    def is_in_group(self, subject_id: int, group: "Group") -> bool:
        """
        This function checks if a subject belongs to a group
        :return: If the subject belongs to the group
        """

        pass

    @abc.abstractmethod
    def get_serializer(self, model_instance, **kwargs) -> Serializer:
        """
        This function can be used to generate different serializers based on the type
        of subject that is being serialized
        :param model_instance: instance of a subject
        :param kwargs: additional kwargs that are parsed to serializer
        :return: the correct serializer for the subject
        """

        pass

    @abc.abstractmethod
    def get_associated_users(self, subject) -> List["AbstractUser"]:
        """
        Returns a list of Users which are associated with this subject.
        And associated user is any user that receives permissions in Baserow based
        on their link to this subject.
        :param subject: The subject we are trying to find the associated users for
        :return: All the associated users
        """

        pass

class SubjectTypeRegistry(Registry[SubjectType], ModelRegistryMixin[Any, SubjectType]):
    """
    This registry holds all the different subject types used across Baserow.
    """

    name = "subject"
    does_not_exist_exception_class = SubjectTypeNotExist

    def get_serializer(self, model_instance, **kwargs) -> Serializer:
        """
        This function is used to get the correct serializer for a given subject model
        instance. A SubjectType has to implement the `get_serializer` method in order
        to be serialized.
        :param model_instance: Instance of a subject
        :param kwargs: Additional kwargs passed to the serializer
        :return: The correct subject serializer
        """

        instance_type = self.get_by_model(model_instance)
        return instance_type.get_serializer(model_instance, **kwargs)

class OperationType(abc.ABC, Instance):
    """
    An `OperationType` represent an `Operation` an actor can do on a `ContextObject`.

    An OperationType must define a context_scope_name which is the name of the
    `ObjectScopeType` matching the context scope type related to the `ContextObject`

    Optionally an object_scope_name can be define to for list operations to express
    the scope of listed objects sometimes necessary for queryset filtering by the
    permission manager.
    """

    @classmethod
    @property
    @abc.abstractmethod
    def type(cls) -> str:
        """
        Should be a unique lowercase string used to identify this type.
        """

        pass

    @classmethod
    @property
    @abc.abstractmethod
    def context_scope_name(cls) -> str:
        """
        An operation is executed on a context in Baserow. For example the list_fields
        operation is executed on a table as it's context. Provide the context_scope_name
        here which matches a ObjectScopeType in the object_scope_type_registry.
        """

        pass

    object_scope_name: Optional[str] = None

    @cached_property
    def context_scope(self) -> ObjectScopeType:
        """
        Returns the `ObjectScopeType` related to the context_scope_name.
        """

        return object_scope_type_registry.get(self.context_scope_name)

    @cached_property
    def object_scope(self):
        """
        Returns the `ObjectScopeType` related to the object_scope_name. If the
        object_scope_name is not defined, then the object_scope is the same as the
        context_scope.
        """

        if self.object_scope_name:
            return object_scope_type_registry.get(self.object_scope_name)
        else:
            return self.context_scope

class OperationTypeRegistry(Registry[OperationType]):
    """
    Contains all the registered operation. For each registered operation, an Operation
    object is created in the database.
    """

    name = "operation"

    does_not_exist_exception_class = OperationTypeDoesNotExist
    already_registered_exception_class = OperationTypeAlreadyRegistered

# A default plugin and application registry is created here, this is the one that is
# used throughout the whole Baserow application. To add a new plugin or application use
# these registries.
plugin_registry = PluginRegistry()
application_type_registry = ApplicationTypeRegistry()
auth_provider_type_registry = AuthenticationProviderTypeRegistry()

permission_manager_type_registry: PermissionManagerTypeRegistry = (
    PermissionManagerTypeRegistry()
)
object_scope_type_registry: ObjectScopeTypeRegistry = ObjectScopeTypeRegistry()
subject_type_registry: SubjectTypeRegistry = SubjectTypeRegistry()
operation_type_registry: OperationTypeRegistry = OperationTypeRegistry()


""" This file defines the PILQR-based algorithm. """
import copy
import logging
import numpy as np

from gps.algorithm.algorithm import Algorithm
from gps.algorithm.config import ALG_PILQR

from gps.algorithm.traj_opt.traj_opt_utils import approximated_cost

LOGGER = logging.getLogger(__name__)

class AlgorithmTrajOptPILQR(Algorithm):
    def __init__(self, hyperparams):
        config = copy.deepcopy(ALG_PILQR)
        config.update(hyperparams)
        Algorithm.__init__(self, config)

    def iteration(self, sample_lists):
        """
        Run iteration of PILQR.
        Args:
            sample_lists: List of SampleList objects for each condition.
        """
        for m in range(self.M):
            self.cur[m].sample_list = sample_lists[m]

        # Update dynamics model using all samples.
        self._update_dynamics()

        self._update_step_size()  # KL Divergence step size.

        # Run inner loop to compute new policies.
        for _ in range(self._hyperparams['inner_iterations']):
            self._update_trajectories()
            self._advance_iteration_variables()

    def _update_step_size(self):
        """ Evaluate costs on samples, and adjust the step size. """
        # Evaluate cost function for all conditions and samples.
        for m in range(self.M):
            self._eval_cost(m)

        # Adjust step size relative to the previous iteration.
        for m in range(self.M):
            if self.iteration_count >= 1 and self.prev[m].sample_list:
                self._stepadjust(m)

    def _compute_step_pi2_lqr(self, m, pdpc, pdcc, cdcc, pmc, cmc, cur_res):
        """
        Calculate new step size for pi2 and lqr.
        Args:
        m: condition
        pdpc: previous dynamics-previous controller cost
        pdcc: previous dynamics-current controller cost
        cdcc: current dynamics-current controller cost
        pmc: previous actual cost
        cmc: current actual cost
        cur_res:
        """

    def _stepadjust(self, m):
        """
        Calculate new step sizes.
        Args:
            m: Condition
        """
        # Cost estimate under prev dynamics, prev controller
        pdpc = self.traj_opt.estimate_cost(
                self.prev[m].traj_distr, self.prev[m].traj_info
        )
        # Cost estimate under prev dynamics, curr controller
        pdcc = self.traj_opt.estimate_cost(
                self.cur[m].traj_distr, self.prev[m].traj_info
        )
        # Cost estimate under curr dynamics, curr controller
        cdcc = self.traj_opt.estimate_cost(
                self.cur[m].traj_distr, self.cur[m].traj_info
        )

        ent = self._measure_ent(m)

        pmc = np.mean(np.sum(self.prev[m].cs, axis=1), axis=0)
        cmc = np.mean(np.sum(self.cur[m].cs, axis=1), axis=0)

        LOGGER.debug('Trajectory step: ent: %f cost: %f -> %f',
                     ent, pmc, cmc)

        _, cur_c_approx = approximated_cost(
                self.cur[m].sample_list, self.cur[m].traj_distr,
                self.cur[m].traj_info
        )
        cur_res = cur_c_approx - self.cur[m].cs

        LOGGER.debug('Previous cost: Laplace: %f MC: %f', np.sum(pdpc), pmc)
        LOGGER.debug('Predicted new cost: Laplace: %f MC: %f', np.sum(pdcc), cmc)
        LOGGER.debug('Actual new cost: Laplace: %f MC: %f', np.sum(cdcc), cmc)

        max_mult = self._hyperparams['max_step_mult']
        min_mult = self._hyperparams['min_step_mult']

        if self._hyperparams['step_rule'] == 'const':
            self.cur[m].step_mult = self.prev[m].step_mult
        elif self._hyperparams['step_rule'] == 'res_percent':
            cur_res_sum = np.mean(np.sum(np.abs(cur_res), axis=1))
            act_sum = np.mean(np.sum(np.abs(self.cur[m].cs), axis=1))
            # Print cost details.
            LOGGER.debug(
                    'Cur residuals: %f. Residuals percentage: %f',
                    cur_res_sum, cur_res_sum/act_sum*100
            )
            if self.traj_opt.cons_per_step:
                T = cur_res.shape[1]
                new_mult = np.ones(T)
                for t in range(T):
                    res = np.mean(np.sum(np.abs(cur_res[:, t:]), axis=1))
                    act = np.mean(np.sum(np.abs(self.cur[m].cs[:, t:]), axis=1))
                    if res / act > self._hyperparams['step_rule_res_ratio_dec']:
                        new_mult[t] = 0.5
                    elif res / act < self._hyperparams['step_rule_res_ratio_inc']:
                        new_mult[t] = 2.0
            else:
                if cur_res_sum / act_sum > self._hyperparams['step_rule_res_ratio_dec']:
                    new_mult = 0.5
                elif cur_res_sum / act_sum < self._hyperparams['step_rule_res_ratio_inc']:
                    new_mult = 2.0
                else:
                    new_mult = 1
            new_step = np.maximum(
                    np.minimum(new_mult * self.cur[m].step_mult, max_mult),
                    min_mult
            )
            self.cur[m].step_mult = new_step
        else:
            raise NotImplementedError('Unknown step adjustment rule.')

    def compute_costs(self, m, eta, augment=True):
        """ Compute cost estimates used in the LQR backward pass. """
        traj_info, traj_distr = self.cur[m].traj_info, self.cur[m].traj_distr
        if not augment:  # Whether to augment cost with term to penalize KL
            return traj_info.Cm, traj_info.cv

        multiplier = self._hyperparams['max_ent_traj']
        fCm, fcv = traj_info.Cm / (eta + multiplier), traj_info.cv / (eta + multiplier)
        K, ipc, k = traj_distr.K, traj_distr.inv_pol_covar, traj_distr.k

        # Add in the trajectory divergence term.
        for t in range(self.T - 1, -1, -1):
            fCm[t, :, :] += eta / (eta + multiplier) * np.vstack([
                np.hstack([
                    K[t, :, :].T.dot(ipc[t, :, :]).dot(K[t, :, :]),
                    -K[t, :, :].T.dot(ipc[t, :, :])
                ]),
                np.hstack([
                    -ipc[t, :, :].dot(K[t, :, :]), ipc[t, :, :]
                ])
            ])
            fcv[t, :] += eta / (eta + multiplier) * np.hstack([
                K[t, :, :].T.dot(ipc[t, :, :]).dot(k[t, :]),
                -ipc[t, :, :].dot(k[t, :])
            ])

        return fCm, fcv


import os,logging,json
from common import opmysql
from public import config
operation_db = opmysql.OperationDbInterface()
#封装接口返回值校验
class CompareParam(object):

    def __init__(self, interface_params):
        self.interface_params = interface_params
        self.id_case = interface_params['id']
        self.result_list_response = []
        self.params_to_compare = interface_params['params_to_compare']

    # 定义关键字值参数比较
    def compare_code(self, result_interface):
        try:
            if result_interface.startswith('{') and isinstance(result_interface, str):
                temp_result_interface = json.loads(result_interface)
                temp_code_to_compare = self.interface_params['code_to_compare']
                if temp_code_to_compare in temp_result_interface.keys():
                    if str(temp_result_interface[temp_code_to_compare]) == self.interface_params['code_expect']:
                        result = {'code': '0', 'message': 'pass', 'data': []}
                        operation_db.op_sql("update case_interface set code_actual= '%s',result_code_compare=%s where "
                                            "id=%s" % (temp_result_interface[temp_code_to_compare], 1, self.id_case))

                    elif str(temp_result_interface[temp_code_to_compare]) != str(self.interface_params['code_except']):
                        result = {'code': '1', 'message': 'fail', 'data': []}
                        operation_db.op_sql("update case_interface set code_actual=%s,result_code_compare=%s where id=%s"
                                          % (temp_result_interface[temp_code_to_compare], 0, self.id_case))
                    else:
                        result = {'code':'2','message':'compare keyvalue error','data':[]}
                        operation_db.op_sql("update case_interface set code_actual=%s,result_code_compare=%s where id=%s"
                                          % (temp_result_interface[temp_code_to_compare], 3, self.id_case))
                else:
                    result = {'code':'3','message':'no keyvalue','data':[]}
                    operation_db.op_sql("updated case_interface set result_code_compare=%s where id=%s" %(2,self.id_case))
            else:
                result = {'code':'4','message':'reponse package error','data':[]}
                operation_db.op_sql("updated case_interface set result_code_compare=%s where id=%s" %(4,self.id_case))
        except Exception as error:
            result = {'code': '5', 'message': '关键字参数比较异常', 'data': []}
            operation_db.op_sql(" update case_interface set result_code_compare=%s where id = %s " % (9, self.id_case))
            logging.basicConfig(filename=config.src_path + '/log/syserror.log',
                                level= logging.DEBUG,
                                format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
            logger = logging.getLogger(__name__)
            logger.exception(error)
        finally:
            return result

if __name__ == "__main__":
    sen_sql = "select * from case_interface where name_interface = '获取手机号信息' and id = 1"
    params_interface = operation_db.select_one(sen_sql)
    result_interface = params_interface['data']['result_interface']
    print(result_interface)
    # 实例
    test_compare_param = CompareParam(params_interface['data'])
    #关键字参数值比较
    result_compare_code = test_compare_param.compare_code(result_interface)
    print(result_compare_code)


# -*- encoding: utf-8 -*-
"""
"""

from youpy.tools import extended_sys_path
from youpy.engine import Engine
from youpy.engine import Simulation
from youpy.project import Project
from youpy.project import get_project_dir
from youpy import logging
LOGGER = logging.getLogger(__name__)

def run(path,
        show_fps=False, fps=30,
        log_level=None, syslog_level=None, log_context=False):
    project_dir = get_project_dir(path)
    project = Project(project_dir)
    logging.init_logger(project, log_level=log_level, syslog_level=syslog_level,
                        log_context=log_context)
    LOGGER.info("=" * 60)
    LOGGER.info(f"running {project.name}")
    simu = Simulation(project, show_fps=show_fps)
    engine = Engine(simu, target_fps=fps)
    with extended_sys_path(project_dir.parent):
        return engine.run()


#!/usr/bin/env python

import scipy.stats
import numpy as np
import astropy.io.fits as fits
import emcee
import matplotlib.pyplot as p
from matplotlib import rc
from astropy.table import Table, Column
rc('text',usetex=True)

execfile('logprob.py')

s = fits.getdata('colira_subset.fits')
hdr = fits.getheader('colira_subset.fits')
GalNames = np.unique(s['GALNAME'])

nTest = 20
nPoints = 100
Signal = np.linspace(1,20,nTest)
t = Table(names=('Name','theta','theta+','theta-','phi','phi+','phi-',\
                     'R21-','R21','R21+','R32-','R32','R32+','sigx',\
                     'R31-','R31','R31+','sigy'),\
              dtypes=('S7','f8','f8','f8','f8','f8','f8','f8','f8','f8',\
                          'f8','f8','f8','f8','f8','f8','f8','f8'))
r21True = 0.7
r32True = 0.5
scatter =2
badfrac = 0.15
BadMult = 5

for ctr in np.arange(nTest):
    x = np.abs((np.random.randn(nPoints)))*Signal[ctr]
    y = r21True*x
    z = r21True*r32True*x
# Add noise
    x = x+np.random.randn(nPoints)*5+np.random.randn(nPoints)*scatter/np.sqrt(2)
    y = y+np.random.randn(nPoints)+np.random.randn(nPoints)*scatter/np.sqrt(2)
    z = z+np.random.randn(nPoints)
    nBad = len(x)*badfrac

    x_err = np.ones((nPoints))*5
    y_err = np.ones((nPoints))
    z_err = np.ones((nPoints))

    x[0:nBad] = x[0:nBad]+np.random.randn(nBad)*BadMult*x_err[0:nBad]
    y[0:nBad] = y[0:nBad]+np.random.randn(nBad)*BadMult*y_err[0:nBad]
    z[0:nBad] = z[0:nBad]+np.random.randn(nBad)*BadMult*z_err[0:nBad]

    ndim, nwalkers = 7,50

    p0 = np.zeros((nwalkers,ndim))
    p0[:,0] = np.random.rand(nwalkers)*np.pi/2
    p0[:,1] = (np.random.randn(nwalkers))**2
    p0[:,2] = (np.random.randn(nwalkers))**2
    p0[:,3] = (np.random.randn(nwalkers))
    p0[:,4] = (np.random.randn(nwalkers))**2*0.05
    p0[:,5] = (np.random.randn(nwalkers))
    p0[:,6] =(np.random.randn(nwalkers))**2*10
    sampler = emcee.EnsembleSampler(nwalkers, ndim,
                                    logprob2d_xoff_scatter_mixture,
                                    args=[x,y,x_err,y_err])
    pos, prob, state = sampler.run_mcmc(p0, 400)
    sampler.reset()
    sampler.run_mcmc(pos, 1000)

    print(ctr,np.mean(sampler.acceptance_fraction),np.tan(np.median(sampler.flatchain[:,0])))
    t.add_row()
    t['theta'][ctr] = np.median(sampler.flatchain[:,0])
    t['theta+'][ctr] = scipy.stats.scoreatpercentile(\
        sampler.flatchain[:,0],85) - t['theta'][ctr]
    t['theta-'][ctr] = t['theta'][ctr] - \
        scipy.stats.scoreatpercentile(\
        sampler.flatchain[:,0],15)

    r32 = np.tan(sampler.flatchain[:,0])
    t['R32'][ctr] = np.median(r32)
    t['R32+'][ctr] = scipy.stats.scoreatpercentile(r32,85)
    t['R32-'][ctr] = scipy.stats.scoreatpercentile(r32,15)

    # r31 = 1/np.tan(sampler.flatchain[:,0])/np.cos(sampler.flatchain[:,1])
    # t['R31'][ctr] = np.median(r31)
    # t['R31+'][ctr] = scipy.stats.scoreatpercentile(r31,85)
    # t['R31-'][ctr] = t['R31'][ctr] =scipy.stats.scoreatpercentile(r31,1)
    # t['var'][ctr] = np.median(sampler.flatchain[:,2])

p.figure(1)
# p.subplot(121)
# p.errorbar(r21True,t['R21'],yerr=[t['R21']-t['R21-'],t['R21+']-t['R21']],linestyle='none')
# p.plot(r21True,r21True)
# p.xlabel(r'$R_{21}$ True')
# p.ylabel(r'$R_{21}$ Estimate')

p.subplot(111)
p.errorbar(Signal,t['R32'],yerr=[t['R32']-t['R32-'],t['R32+']-t['R32']],linestyle='none')

p.xlabel(r'$R_{32}$ True')
p.ylabel(r'$R_{32}$ Estimate')

p.show()


'''
Nombre: cambios.
Input: un array arr y su tamaño n.
Return: void.
Descripcion: la funcion recorre el array, tomando pequeños segmentos de este (0..1,0..2,etc) y en la lista aux va registrando
la cantidad de elementos que estan en orden considerando que el elemento en la ultima posicion (posicion cont) no se mueve.
Si el numero en dicha posicion es el menor de la secuencia, se contara como que no hay elementos ordenados, para que cuenten
los ordenados, el elemento en la posicion cont tiene que estar dentro de alguna secuencia de orden. Por ejemplo, si se cuenta
al segmento [2,3,1], el elemento en la posicion cont es 1, y a pesar de que 2 y 3 si estan en orden, 1 no es parte, por lo que
en la lista aux iria un 1 en la posicion cont.
'''

def cambios(arr,n):
    mayor=1
    #Genera una lista de tamaño n, con 1 dentro de cada casilla
    aux = []
    for i in range(0,n):
        aux.append(1)

    for cont in range (1,n):
        for cont2 in range(cont):

            #Compara si el elemento en cont es mayor que el de cont2, el signo = para numero repetidos.
            if (arr[cont] >= arr[cont2]) and ((aux[cont2] + 1) > aux[cont]) :
                aux[cont] = aux[cont2]+1

    #Busca el mayor numero en la lista aux.
    for elem in aux:
        mayor = max(mayor,elem)

    #Se resta n al numero mayor ya que este representa la mayor secuencia de numero ordenados, por ende el la resta
    #representaria el minimi numero de cambios que hacer en el arreglo.
    valor = n-mayor
    print(valor)

flag = True

while flag==True:
    try:

        line = input().split()
        n=int(line[0])
        lista=list(map(int,line[1:]))
        cambios(lista,n)

    except EOFError:
        flag= False


from django.urls import path, include
from .views import HomeView, SignUpView, DashboardView, LoginView, admin_dashboard, upload_xml, JobListView, JobDetailView, JobListXMLView
from django.contrib.auth.views import LogoutView
from django.conf import settings
from django.conf.urls.static import static

app_name = 'UserApp'  # Updated to 'UserApp'

urlpatterns = [
    path('', HomeView.as_view(), name='home'),
    path('dashboard/', DashboardView.as_view(), name='dashboard'),
    path('login/', LoginView.as_view(), name='login'),
    path('signup/', SignUpView.as_view(), name='signup'),
    path('admin-dashboard/', admin_dashboard, name='admin_dashboard'),
    path('upload-xml/', upload_xml, name='upload_xml'),
    path('jobs/', JobListView.as_view(), name='job_list'),
    path('jobs/<int:pk>/', JobDetailView.as_view(), name='job_detail'),
    path('job-list-xml/', JobListXMLView.as_view(), name='job_list_xml'),
    path('logout/', LogoutView.as_view(next_page='UserApp:home'), name='logout'),  # Updated to use 'UserApp' namespace
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)


#Loop over the different values of C_value, creating and fitting a LogisticRegression model each time.
#Save the error on the training set and the validation set for each model.
#Create a plot of the training and testing error as a function of the regularization parameter, C.
#Looking at the plot, what's the best value of C?

# Train and validaton errors initialized as empty list
train_errs = list()
valid_errs = list()

# Loop over values of C_value
for C_value in [0.001, 0.01, 0.1, 1, 10, 100, 1000]:
    # Create LogisticRegression object and fit
    lr = LogisticRegression(C= C_value)
    lr.fit(X_train, y_train)

    # Evaluate error rates and append to lists
    train_errs.append( 1.0 - lr.score(X_train, y_train) )
    valid_errs.append( 1.0 - lr.score(X_valid, y_valid) )

# Plot results
plt.semilogx(C_values, train_errs, C_values, valid_errs)
plt.legend(("train", "validation"))
plt.show()

#As we  see, too much regularization (small C) doesn't work well - due to underfitting - and too little regularization (large C

#we'll perform feature selection on the movie review sentiment data set using L1 regularization. The features and targets are already loaded for you in X_train and y_train.

#We'll search for the best value of C using scikit-learn's GridSearchCV()

# Specify L1 regularization
lr = LogisticRegression(penalty = 'l1')

# Instantiate the GridSearchCV object and run the search
searcher = GridSearchCV(lr, {'C':[0.001, 0.01, 0.1, 1, 10]})
searcher.fit(X_train, y_train)

# Report the best parameters
print("Best CV params", searcher.best_params_)

# Find the number of nonzero coefficients (selected features)
best_lr = searcher.best_estimator_
coefs = best_lr.coef_
print("Total number of features:", coefs.size)
print("Number of selected features:", np.count_nonzero(coefs))


import asyncio
import os
import random
import re
from PIL import Image
import aiohttp
import datetime
from bs4 import BeautifulSoup

import nonebot
from nonebot.adapters.cqhttp.bot import Bot
from nonebot.adapters.cqhttp.event import GroupMessageEvent, MessageEvent, PrivateMessageEvent
from nonebot.plugin import on_command, on_regex
from nonebot.rule import to_me
from nonebot.typing import T_State
from utils import NAME, image
from utils.image import ImgDraw
from utils.message_builder import image

from utils.message_builder import face, text
from utils.zhongguose import *

__usage__ = f'您可以使用任何正常状态下的文字请{NAME}算命'
__help__version__ = '0.1.4'
__help__plugin_name__ = "智能化八字算命"

bg = os.path.join('.', 'res', 'ft', 'bg.png')
font = os.path.join('.', 'res', 'fonts','LXGWWenKai-Regular.ttf')
Image.new('RGB', (1200, 3000), (255, 255, 255)).save(bg)

ft = on_regex(
    '.*(算.{0,2}[命卦]|卜.{0,2}卦).*', rule=to_me()
)

headers = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
    'Accept-Encoding': 'gzip, deflate',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Content-Type': 'application/x-www-form-urlencoded',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36'
}

def is_all_zh(s):
    for c in s:
        if not ('\u4e00' <= c <= '\u9fa5'):
            return False
    return True

def processing_text(text: str, deletions: list = [], delete_space: bool = False, line_feed: bool = False):
    text = text.replace(u'\xa0', u' ').replace(u'\u3000', '  ')
    if line_feed:
        text = text.replace('\r\n', u'\n').replace(
            '\n', u'\n').replace('\r', u'\n')
    else:
        text = text.replace('\r', '').replace('\n', '')
    if delete_space:
        text = text.replace(' ', '').strip()
    else:
        text = text.strip()
    text = text.replace(',', '，')
    for i in deletions:
        text = text.replace(i, '')
    return text

async def fortuneTelling(
        ln: str,
        fn: str,
        sex: int,
        date: datetime,
        bt: str = ''
):
    """
    说明：
        八字算命(来源 http://www.dajiazhao.com/sm )
        字典数据说明：
        - info str		基本信息
        - name str		姓名
        - birthday
            - gc str    公历生日
            - lc str    农历生日
        - wxsx str      五行属相
        - bz list       八字
        - wx list       五行
        - ny list       纳音
        - bzwxgs list   八字五行个数, [金, 木, 水, 火, 土]
        - sjys str      四季用神参考
        - qtbjdhys str  穷通宝鉴调候用神参考
        - rgxx str      日干心性
        - rgzcs str     日干支层次
        - rgzfx str     日干支分析
        - wxskzhyj str  五行生克制化宜忌
        - wxzx str      五行之性
        - szwxskxb str  四柱五行生克中对应需补的脏腑和部位
        - syfw str      宜从事行业与方位
        - sxgx str      生肖个性
        - smth str      三命通会
        - yrsml         月日时命理
            - y list    月 |
            - r list    日 | => [标题, 文本]
            - s list    时 |

    参数：
        :param ln: Lastname 姓，汉字
        :param fn: Firstname 名，汉字
        :param sex: 性别，[int]: 1.男, 2.女
        :param date: 出生日期，精确到分钟
        :param bt: 血型，[A, B, O, AB, ]
    """
    if not is_all_zh(ln):
        raise ValueError('ln must be a string.')
    if len(ln) > 2 or len(ln) <= 0:
        raise ValueError('1 <= len(ln) <= 2')
    if not is_all_zh(fn):
        raise ValueError('fn must be a string.')
    if isinstance(sex, int):
        if sex not in [1, 2]:
            raise ValueError('sex must be 1 or 2')
    else:
        raise TypeError('sex must be a int')
    async with aiohttp.ClientSession() as c:
        r = await c.post(
            'http://www.dajiazhao.com/sm/scbz.asp',
            data=f'''xing={ln}&ming={fn
			}&xingbie={('男' if (sex == 1) else '女')
			}&xuexing={bt
			}&nian={date.year}&yue={date.month}&ri={date.day
			}&hh={date.hour}&mm={date.minute}'''.encode('gb2312'),
            headers=headers
        )
        soup = BeautifulSoup((await r.text('gb2312')), features="lxml")
    if len(soup.select('.ttop2 dl dd font')) == 0:
        # 判定无结果
        raise RuntimeError('Unable to get data.')
    # 开始分析处理
    result = {
        'info': '',		# 基本信息
        'name': '',		# 姓名
        'birthday':
        {
            'gc': '',   # 公历生日
            'lc': ''    # 农历生日
        },
        'wxsx': '',		# 五行属相
        'bz': [],		# 八字
        'wx': [],		# 五行
        'ny': [],       # 纳音
        'bzwxgs': [],   # 八字五行个数, [金, 木, 水, 火, 土]
        'sjys': '',     # 四季用神参考
        'qtbjdhys': '',  # 穷通宝鉴调候用神参考
        'rgxx': '',     # 日干心性
        'rgzcs': '',    # 日干支层次
        'rgzfx': '',    # 日干支分析
        'wxskzhyj': '',  # 五行生克制化宜忌
        'wxzx': '',     # 五行之性
        'szwxskxb': '',  # 四柱五行生克中对应需补的脏腑和部位
        'syfw': '',     # 宜从事行业与方位
        'sxgx': '',     # 生肖个性
        'smth': '',     # 三命通会
        'yrsml': {      # 月日时命理
            'y': [],    # 月    \
            'r': [],    # 日    | => [标题, 文本]
            's': []     # 时    /
        },
    }
    # region main
    # 获得当前算命者信息
    ttopp2 = soup.select_one('.ttop2 dl dd')
    result['info'] = processing_text(ttopp2.text)
    # 分析表格
    tables = soup.select('table tbody')
    # table1
    t1 = tables[0]
    trs = t1.select('tr')
    tr1 = trs[0]
    # 名字
    result['name'] = processing_text(
        tr1.select('td')[0].text, delete_space=True
    )
    # 公历生日
    gc = tr1.select('td:nth-child(n+4):not(:last-child)')
    for i in gc:
        result['birthday']['gc'] += i.text
    # 五行属相
    tfes = tr1.select_one('td:last-child').text.split('重要说明')[0]
    result['wxsx'] = processing_text(tfes, delete_space=True)
    tr2 = trs[1]
    # 农历生日
    gc = tr2.select('td:nth-child(n+2)')
    for i in gc:
        result['birthday']['lc'] += i.text
    tr3 = trs[2]
    # 八字
    for i in tr3.select('td')[1:]:
        result['bz'].append(i.text.strip())
    tr4 = trs[3]
    # 五行
    for i in tr4.select('td')[1:]:
        result['wx'].append(i.text.strip())
    tr5 = trs[4]
    # 纳音
    for i in tr5.select('td')[1:]:
        result['ny'].append(i.text.strip())
    # 八字五行个数
    ecfln = processing_text(tables[1].select_one('tr td').text, ['八字五行个数 : '])
    # 正则开始匹配
    for i in ['金', '木', '水', '火', '土']:
        result['bzwxgs'].append(int(re.search(f'(\d+)个{i}', ecfln).group(1)))
    # 四季用神参考
    result['sjys'] = processing_text(
        tables[2].select_one('tr:first-child td').text, ['四季用神参考 : '], delete_space=True
    )
    # 穷通宝鉴调候用神参考
    result['qtbjdhys'] = processing_text(
        tables[2].select_one('tr:last-child td').text, ['穷通宝鉴调候用神参考 : '], delete_space=True
    )
    # 日干心性
    result['rgxx'] = processing_text(
        tables[3].select_one('tr td:last-child').text, delete_space=True
    )
    # 日干支层次
    result['rgzcs'] = processing_text(
        tables[4].select_one('tr td:last-child').text, delete_space=True
    )
    # 日干支分析
    tables[5].select_one('tr td:last-child font').decompose()
    result['rgzfx'] = processing_text(
        tables[5].select_one('tr td:last-child').text
    )
    trs = tables[6].select('tr')
    # 五行生克制化宜忌
    result['wxskzhyj'] = processing_text(
        trs[0].select_one('td:last-child').text, delete_space=True, line_feed=True
    )
    # 五行之性
    result['wxzx'] = processing_text(
        trs[1].select_one('td:last-child').text, delete_space=True
    )
    # 四柱五行生克中对应需补的脏腑和部位
    result['szwxskxb'] = processing_text(
        trs[2].select_one('td:last-child').text, delete_space=True
    )
    # 宜从事行业与方位
    result['syfw'] = processing_text(
        trs[3].select_one('td:last-child').text, delete_space=True
    )
    # 生肖个性
    sxgx = tables[7].select_one('tr td:last-child')
    # url = sxgx.select_one('script').get('src')
    # async with aiohttp.ClientSession() as c:
    #     r = await c.get('http://www.dajiazhao.com/'+url, headers=headers)
    #     t = re.search('<p>(.+)<a', (await r.text())).group(1)
    em = sxgx.select_one('em')
    emt = em.text
    em.decompose()
    result['sxgx'] = processing_text(
        f'''
        {sxgx.text}

        {emt}
        ''', delete_space=True, line_feed=True
    )
    # 三命通会
    result['smth'] = processing_text(
        tables[8].select_one('tr td:last-child').text, delete_space=True, line_feed=True
    )
    # 月日时命理
    trs = tables[9].select('tr')
    y = trs[0].select('td')
    result['yrsml']['y'].append(y[1].text)
    result['yrsml']['y'].append(
        processing_text(y[2].text, delete_space=True, line_feed=True)
    )
    r = trs[1].select('td')
    result['yrsml']['r'].append(r[0].text)
    result['yrsml']['r'].append(
        processing_text(r[1].text, delete_space=True, line_feed=True)
    )
    s = trs[2].select('td')
    result['yrsml']['s'].append(s[0].text)
    result['yrsml']['s'].append(
        processing_text(s[1].text, delete_space=True, line_feed=True)
    )
    # endregion
    return result

def linefeed(string, length):
    return re.sub(
        '(.{'+str(length)+'}[，。？！]?)',
        (lambda i: i.group(0)+'\n'),
        string
    )

TESTWIDTH = 45

replies = {
    'group': {
        'beginning': [
            text('乐意至极，但您真的要在这大庭广众之下做这种事情？')
            + face(0)
            + text('万一算出什么不好的东西怎么办...')
            + face(13),

        ],
        'cancel': [
            text('好吧好吧，如果你还是想算，可以悄悄的来找我哦')
            + face(20) + face(20)
        ]
    },
    'start':[
        '开始了！如果你中间不想算了，可以随时告诉我'
    ],
    'not understand': [
        face(32)
        + text('您在说什么呐，我怎么听不懂呢？')
    ],
    'ln': [
        '您贵姓？',
    ],
    'wrong ln': [
        '请认真一点，这明显就不是一个姓氏！',
        '请不要在这件事上开玩笑！'
    ],
    'fn': [
        '您的名字是什么？不要包括姓氏'
    ],
    'wrong fn': [
        '请认真一点，这明显就不是您的名！',
        '请不要在这件事上开玩笑！'
    ],
    'sex': [
        '您的性别是？'
    ],
    'wrong sex': [
        '不要开玩笑了！您到底是男是女？'
    ],
    'birthday': [
        '请问您是哪一天出生的？公历哦'
    ],
    'wrong birthday': [
        '我无法理解您的生日，请再说一次'
    ],
    'btime': [
        '请问您是{:%Y-%m-%d}几点几分出生的？可以只精确到小时'
    ],
    'wrong btime': [
        '您...这时间不太对啊，请认真一点！'
    ],
    'bt': [
        '好，下一个。您的血型是什么，如果不知道的话请跟我说「不知道」'
    ],
    'wrong bt': [
        '您这，有点不对啊！我所知道的血型只有 A, B, O, AB 四种，您是哪一种？'
    ],
    'confirm': [
        '''请确认您的数据：
姓：「{}」
名：「{}」
性别：「{}」
生日：「{:%Y-%m-%d %H:%M}」
血型：「{}」'''
    ],
    'cancel': [
        '好吧，您可真无聊...那不弄了'
    ],
    'getting': [
        '正在帮您进行八字算命...'
    ],
}
msg_false = ['不', '否']
msg_true = ['确定', '当然', '没错', '没关系', '可以', '是']
msg_cancel = ['取消', '算了', '不弄了']

def has(text, words: list):
    for i in words:
        if i in text:
            return True
    return False

@ft.handle()
async def _(bot: Bot, event: MessageEvent, state: T_State):
    if isinstance(event, GroupMessageEvent):
        await bot.send(
            event, random.choice(replies['group']['beginning'])
        )
    elif isinstance(event, PrivateMessageEvent):
        state['allow'] = True
        await bot.send(event, random.choice(replies['start']))
        await asyncio.sleep(1)
        await bot.send(event, random.choice(replies['ln']))

@ft.got('allow')
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
    if has(event.raw_message, msg_false) or has(event.raw_message, msg_cancel):
        await ft.finish(random.choice(replies['group']['cancel']))
    elif has(event.raw_message, msg_true):
        state['allow'] = True
        await bot.send(event, random.choice(replies['start']))
        await asyncio.sleep(1)
        await bot.send(event, random.choice(replies['ln']))
    else:
        state['allow'] = None
        await ft.reject(random.choice(replies['not understand']))

@ft.got('ln')
async def _(bot: Bot, event: MessageEvent, state: T_State):
    if has(event.raw_message, msg_cancel):
        await ft.finish(random.choice(replies['cancel']))
    ln = state['ln']
    if not is_all_zh(ln) or 0 == len(ln) or len(ln) > 2:
        await ft.reject(random.choice(replies['wrong ln']))
    else:
        await bot.send(event, random.choice(replies['fn']))

@ft.got('fn')
async def _(bot: Bot, event: MessageEvent, state: T_State):
    if has(event.raw_message, msg_cancel):
        await ft.finish(random.choice(replies['cancel']))
    fn = state['fn']
    if not is_all_zh(fn) or 0 == len(fn) or len(fn) > 3:
        await ft.reject(random.choice())
    else:
        await bot.send(event, random.choice(replies['sex']))

@ft.got('sex')
async def _(bot: Bot, event: MessageEvent, state: T_State):
    if has(event.raw_message, msg_cancel):
        await ft.finish(random.choice(replies['cancel']))
    sex = state['sex']
    if sex == '男':
        state['sex'] = 1
    elif sex == '女':
        state['sex'] = 2
    else:
        await ft.reject(random.choice(replies['wrong sex']))
        return
    await bot.send(event, random.choice(replies['birthday']))

@ft.got('birthday')
async def _(bot: Bot, event: MessageEvent, state: T_State):
    if has(event.raw_message, msg_cancel):
        await ft.finish(random.choice(replies['cancel']))
    birthday = state['birthday']
    rb = re.match('(\d{4})[-./年](\d{1,2})[-./月](\d{1,2})', birthday)
    if rb is None:
        await ft.reject(random.choice(replies['wrong birthday']))
    else:
        state['year'] = int(rb.group(1))
        state['month'] = int(rb.group(2))
        state['day'] = int(rb.group(3))
    await bot.send(
        event, random.choice(
            replies['btime']
        ).format(
            datetime.datetime(state['year'], state['month'], state['day'])
        )
    )

@ft.got('btime')
async def _(bot: Bot, event: MessageEvent, state: T_State):
    if has(event.raw_message, msg_cancel):
        await ft.finish(random.choice(replies['cancel']))
    btime = state['btime']
    rbt = re.match('(\d{1,2})[点时:](\d{0,2})', btime)
    if rbt is None:
        await ft.reject(random.choice(replies['wrong btime']))
    else:
        state['hour'] = int(rbt.group(1))
        try:
            state['minute'] = int(rbt.group(2))
        except:
            state['minute'] = 0
    await bot.send(event, random.choice(replies['bt']))

@ft.got('bt')
async def _(bot: Bot, event: MessageEvent, state: T_State):
    if has(event.raw_message, msg_cancel):
        await ft.finish(random.choice(replies['cancel']))
    bt = state['bt']
    if bt == '不知道':
        state['bt'] = ''
    elif bt not in ['A', 'B', 'O', 'AB']:
        await ft.reject(random.choice(replies['wrong bt']))
    await bot.send(
        event,
        text(random.choice(replies['confirm']).format(
            state['ln'],
            state['fn'],
            '男' if(state['sex'] == 1) else '女',
            datetime.datetime(
                state['year'],
                state['month'],
                state['day'],
                state['hour'],
                state['minute']
            ),
            '未知' if(state['bt'] == '') else state['bt']
        ))
    )
    await asyncio.sleep(1)
    await bot.send(event, '如果资料正确，请发送「确定」开始，否则发送「取消」退出')

@ft.got('st')
async def _(bot: Bot, event: MessageEvent, state: T_State):
    if state['st'] == '取消':
        await ft.finish(random.choice(replies['cancel']))
    elif state['st'] == '确定':
        await bot.send(event, random.choice(replies['getting']))
        data = (await fortuneTelling(
            state['ln'],
            state['fn'],
            state['sex'],
            datetime.datetime(
                state['year'],
                state['month'],
                state['day'],
                state['hour'],
                state['minute']
            ),
            state['bt']
        ))
        draw = ImgDraw(bg)
        await draw.init()
        draw.pos = (30, 30)
        await draw.openfont(font)
        #
        await draw.putText(data['name'], 0, 30, fontsize=48, fill=JIANJINGYUHONG)
        await draw.putText('出生\n时间', 1, 15, fontsize=24, fill=CAOMOLIHONG)
        await draw.putText(
            f"公历：{data['birthday']['gc']}\n农历：{data['birthday']['lc']}",
            0, 30, fontsize=24
        )
        draw.x = 30
        # await draw.putText('基本信息', 0, 10, fontsize=36)
        # await draw.putText(data['info'], 0, 30, fontsize=24)

        await draw.putText('五行属相', 0, 5, fontsize=30)
        await draw.putText(data['wxsx'], 0, 15, fontsize=24)

        await draw.putText('''八字：\n五行：\n纳音：''', 1, 10, fontsize=24)
        await draw.putText(f'''{data['bz'][0]}\n{data['wx'][0]}\n{data['ny'][0]}''', 1, 10, fontsize=24)
        await draw.putText(f'''{data['bz'][1]}\n{data['wx'][1]}\n{data['ny'][1]}''', 1, 10, fontsize=24)
        await draw.putText(f'''{data['bz'][2]}\n{data['wx'][2]}\n{data['ny'][2]}''', 1, 10, fontsize=24)
        await draw.putText(f'''{data['bz'][3]}\n{data['wx'][3]}\n{data['ny'][3]}''', 0, 30, fontsize=24)
        draw.x = 30
        await draw.putText(
            f'''八字五行个数：{data['bzwxgs'][0]}个金，{data['bzwxgs'][1]}个木，{
                data['bzwxgs'][2]}个水，{data['bzwxgs'][3]}个火，{data['bzwxgs'][4]}个土。''',
            0, 15, fontsize=24
        )
        await draw.putText('四季用神参考', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['sjys'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('穷通宝鉴调候用神参考', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['qtbjdhys'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('日干心性', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['rgxx'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('日干支层次', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['rgzcs'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('日干支分析', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['rgzfx'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('五行生克制化宜忌', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['wxskzhyj'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('五行之性', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['wxzx'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('四柱五行生克中对应需补的脏腑和部位', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['szwxskxb'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('宜从事行业与方位', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['syfw'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('生肖个性', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['sxgx'], TESTWIDTH), 0, 50, fontsize=24)
        await draw.putText('三命通会', 0, 10, fontsize=36)
        await draw.putText(linefeed(data['smth'], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText('月日时命理', 0, 10, fontsize=36)
        draw.x += 15
        await draw.putText(data['yrsml']['y'][0], 0, 10, fontsize=28)
        await draw.putText(linefeed(data['yrsml']['y'][1], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText(data['yrsml']['r'][0], 0, 10, fontsize=28)
        await draw.putText(linefeed(data['yrsml']['r'][1], TESTWIDTH), 0, 30, fontsize=24)
        await draw.putText(data['yrsml']['s'][0], 0, 10, fontsize=28)
        await draw.putText(linefeed(data['yrsml']['s'][1], TESTWIDTH), 0, 30, fontsize=24)

        draw.y += 50
        await draw.putText('此图片乃临时格式，正式格式正在设计...', 0, 5, fill=LUHUI)
        await draw.putText('By Hanjin in Sakuyark@2021', 0, 5, fill=LUHUI)
        await draw.putText('From www.dajiazhao.com', 0, 5, fill=LUHUI)
        out_path = os.path.join(
            '.', 'res', 'ft', 'cards',
            datetime.datetime.now().__format__('%Y%m%d%H%M%S') +
            str(random.randint(10, 99))+'.jpg'
        )
        await draw.save(out_path)
        await bot.send(event, image(out_path), at_sender=True)
        os.remove(out_path)
    else:
        await ft.reject('指令发送有误！')


from django.conf import settings
from django.contrib.admin.widgets import AdminTextareaWidget
from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.db import models
from django.forms import MultipleChoiceField
from django.utils.text import capfirst
from django.utils.translation import gettext_lazy as _

from mezzanine.core.forms import OrderWidget
from mezzanine.utils.html import escape
from mezzanine.utils.importing import import_dotted_path

class OrderField(models.IntegerField):
    def formfield(self, **kwargs):
        kwargs.update({"widget": OrderWidget, "required": False})
        return super().formfield(**kwargs)

class RichTextField(models.TextField):
    """
    TextField that stores HTML.
    """

    def formfield(self, **kwargs):
        """
        Apply the widget class defined by the
        ``RICHTEXT_WIDGET_CLASS`` setting.
        """
        default = kwargs.get("widget", None) or AdminTextareaWidget
        if default is AdminTextareaWidget:
            from mezzanine.conf import settings

            richtext_widget_path = settings.RICHTEXT_WIDGET_CLASS
            try:
                widget_class = import_dotted_path(richtext_widget_path)
            except ImportError:
                raise ImproperlyConfigured(
                    _(
                        "Could not import the value of "
                        "settings.RICHTEXT_WIDGET_CLASS: "
                        "%s" % richtext_widget_path
                    )
                )
            kwargs["widget"] = widget_class()
        kwargs.setdefault("required", False)
        formfield = super().formfield(**kwargs)
        return formfield

    def clean(self, value, model_instance):
        """
        Remove potentially dangerous HTML tags and attributes.
        """
        return escape(value)

class MultiChoiceField(models.CharField):
    """
    Charfield that stores multiple choices selected as a comma
    separated string. Based on http://djangosnippets.org/snippets/2753/
    """

    def formfield(self, *args, **kwargs):
        from mezzanine.core.forms import CheckboxSelectMultiple

        defaults = {
            "required": not self.blank,
            "label": capfirst(self.verbose_name),
            "help_text": self.help_text,
            "choices": self.choices,
            "widget": CheckboxSelectMultiple,
            "initial": self.get_default() if self.has_default() else None,
        }
        defaults.update(kwargs)
        return MultipleChoiceField(**defaults)

    def get_db_prep_value(self, value, connection, **kwargs):
        if isinstance(value, (tuple, list)):
            value = ",".join(str(i) for i in value)
        return value

    def from_db_value(self, value, expression, connection):
        return self.to_python(value)

    def to_python(self, value):
        if isinstance(value, str):
            value = value.split(",")
        return value

    def validate(self, value, instance):
        choices = [str(choice[0]) for choice in self.choices]
        if set(value) - set(choices):
            error = self.error_messages["invalid_choice"] % {"value": value}
            raise ValidationError(error)

    def value_to_string(self, obj):
        value = self.value_from_object(obj)
        return ",".join(value)

# Define a ``FileField`` that maps to filebrowser's ``FileBrowseField``
# if available, falling back to Django's ``FileField`` otherwise.
try:
    FileBrowseField = import_dotted_path(
        "%s.fields.FileBrowseField" % settings.PACKAGE_NAME_FILEBROWSER
    )
except ImportError:

    class FileField(models.FileField):
        def __init__(self, *args, **kwargs):
            for fb_arg in ("format", "extensions"):
                kwargs.pop(fb_arg, None)
            super().__init__(*args, **kwargs)

else:

    class FileField(FileBrowseField):
        def __init__(self, *args, **kwargs):
            kwargs.setdefault("directory", kwargs.pop("upload_to", None))
            kwargs.setdefault("max_length", 255)
            super().__init__(*args, **kwargs)


import re

from datetime import date

from dateutil.relativedelta import relativedelta

from odoo import api, fields, models

from odoo.exceptions import ValidationError

class ProfesorRecord(models.Model):

    _name = "profesor.record"
    _rec_name = "profesor_id"
    _description = "profesor"

    profesor_id = fields.Many2one('res.partner', string="Professor Name", required=True)
    profesor_department = fields.Char(string="Department", required=True)
    profesor_dob = fields.Date(string="Date of Birth", required=True)
    profesor_phone_number = fields.Char(string="Phone Number")
    profesor_age = fields.Integer(string="Age", compute='get_dob', store=True)
    profesor_email = fields.Char(string="E-mail")
    profesor_gender = fields.Selection(
        [('m', 'Male'), ('f', 'Female'), ('o', 'Other')], string='Gender')
    profesor_blood_group = fields.Selection([('A+', 'A+ve'), ('B+', 'B+ve'), (
        'O+', 'O+ve'), ('AB+', 'AB+ve'), ('A-', 'A-ve'), ('B-', 'B-ve'), (
        'O-', 'O-ve'), ('AB-', 'AB-ve')], string='Blood Group')
    subject_id = fields.Many2one('subject.record', string="Subject")
    subject_description = fields.Char(related="subject_id.subject_details",
                                      readonly=True)
    student_ids = fields.One2many(
        'student.record', 'profesor_id', string="Student")

    # Name get method to get phone number with profesor name.
    @api.model
    def name_get(self):
        profesor_list = []
        for i in self:
            if self._context.get('profesor_id'):
                name = i.profesor_phone_number + ' ' + i.profesor_id.name
                profesor_list.append((i.id, name))
        return profesor_list

    # onchange function to get age through entered dob

    @api.depends('profesor_dob')
    def get_dob(self):
        for record in self:
            if record.profesor_dob:
                date_record = record.profesor_dob
                current_date = date.today()
                result = relativedelta(current_date, date_record)
                age = int(result.years)
                self.profesor_age = age

    # constraint to input valid mobile number
    @api.constrains('profesor_phone_number')
    def check_number(self):
        if self.profesor_phone_number:
            match = re.match(r'[789]\d{9}$', self.profesor_phone_number)
            if match is None:
                raise ValidationError('Not a valid mobile number')

    # constrain for not allowing user to enter current date or future date
    @api.constrains('profesor_dob')
    def validate_dob(self):
        if self.profesor_dob >= (date.today()):
            raise ValidationError("Date you have enter is not valid..!")

    @api.constrains('profesor_age')
    def check_age(self):
        if self.profesor_age < 25:
            raise ValidationError(
                'Can not ragister as a professor.Age must be above 25.!')

    @api.constrains('profesor_email')
    def validate_mail(self):
        if self.profesor_email:
            match = re.match('^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$',
                             self.profesor_email)
            if match is None:
                raise ValidationError('Not a valid E-mail ID')

    def create_student(self):
        student_record = self.env['student.record'].search([])
        email_list = []
        for i in student_record:
            email_list.append(i.student_email)
        student = self.env['student.record'].create({
            'student_id': 'Kinjal',
            'last_name': 'Lalani',
            'student_dob': '2005-12-10',
            'student_email': 'kinjal123@gmail.com'
        })
        pop_up = {
            'name': 'Message',
            'type': 'ir.actions.act_window',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': 'msg.wizard',
            'target': 'new',
            'context': {'default_name': "Student created successfully."}
        }
        for i in student:
            for j in email_list:
                if j == i.student_email:
                    raise ValidationError(
                        'Email alredy exist..!You can not create student with \
                        this email..!')
            else:
                return pop_up


print("Initiating... please wait.\n")

import requests
import csv
import re
import random
import time
import pandas as pd
from sys import exit
from bs4 import BeautifulSoup

# ===== DEFINE FUNCTIONS =====

search_from, URL_edit = "", ""

def wait():
    print("Waiting for a few secs...")
    time.sleep(random.randrange(1, 6))
    print("Waiting done. Continuing...\n")

def checkPage():
    global search_from
    if "scholar.google.com" in URL_input:
        search_from = "Google Scholar"
        print("Input is from: Google Scholar.\n")
    elif "pubmed" in URL_input:
        search_from = "Pubmed"
        print("Input is from: PubMed.\n")
    elif "sciencedirect.com" in URL_input:
        search_from = "Science Direct"
        print("Input is from: Science Direct.\n")
    elif "plos.org" in URL_input:
        search_from = "PLOS"
        print("Input is from: PLOS.\n")

    else:
        print("Page URL undefined.\n")

# ===== GETTING AND SETTING THE URL =====

URL_input = input("Please paste search URL and press Enter:")
URL_ori = URL_input
headers = requests.utils.default_headers()
headers.update(
    {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36",
    }
)
checkPage()

# ===== MAIN FRAMEWORK =====

try:

    # SETTING UP THE CSV FILE

    outfile = open("scrapped_science_direct.csv", "w", newline="", encoding="utf-8")
    writer = csv.writer(outfile)
    df = pd.DataFrame(
        columns=["Title", "Article Rype", "Year", "Source", "Authors", "Link"]
    )

    # SETTING & GETTING PAGE NUMBER

    page_num = 0
    offset = 0
    page_view = 100
    URL_edit = str(URL_ori + "&show=" + str(page_view) + "&offset=" + str(offset))

    page = requests.get(URL_edit, headers=headers, timeout=None)
    soup = BeautifulSoup(page.content, "html.parser")
    wait()

    search_results = soup.find_all("span", class_="search-body-results-text").text
    print(search_results)

    if "About" in search_results:
        search_results_split = search_results.split("results")[0].split("About")[1]
    elif "results" in search_results:
        search_results_split = search_results.split("results")[0]
    else:
        seascrapped_science_directrch_results_split = search_results.split("result")[0]

    search_results_num = int("".join(filter(str.isdigit, search_results_split)))
    page_total_num = int(search_results_num / 10) + 1
    print(f"Total page number: {page_total_num}")
    print(f"Total search results: {search_results_num}.\n")

except AttributeError:

    print("Opss! ReCaptcha is probably preventing the code from running.")
    print("Please consider running in another time.\n")
    # exit()
    wait()

wait()

# # EXTRACTING INFORMATION

for i in range(page_total_num):

    # SETTING UP URL SECOND TIME

    page_num_up = page_num + i
    print(f"Going to page {page_num_up}.\n")
    URL_edit = str(URL_ori + "&show=" + str(page_view) + "&offset=" + str(page_num_up))
    wait()
    # if page_num_up != 2 and page_num_up != 1:
    # 	# time.sleep(2)
    headers = requests.utils.default_headers()
    headers.update(
        {
            "User-Agent": "Mozilla/15.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20210916 Firefox/95.0",
        }
    )

    page = requests.get(URL_edit, headers=headers, timeout=None)
    soup = BeautifulSoup(page.content, "html.parser")
    wait()
    response = requests.get(URL_edit, headers=headers)

    # check successful response
    if response.status_code != 200:
        print("Status code:", response.status_code)
        raise Exception("Failed to fetch web page ")

    results = soup.find("div", id="srp-results-list")
    paper_doc = BeautifulSoup(response.text, "html.parser")

    # EXTRACTING INFORMATION

    try:

        job_elements = results.find_all("div", class_="result-item-content")
        for job_element in job_elements:
            try:
                article_type = job_element.find(
                    "span", class_="article-type u-clr-grey8"
                ).text
                article_title = job_element.find("span", class_="anchor-text").text
                article_src = job_element.find(
                    "span", class_="srctitle-date-fields"
                ).text
                article_authors = job_element.find(
                    "ol", class_="Authors hor reduce-list"
                ).text
                article_date = article_src.split(",")[-2]
                source = article_src.split(",")[:-1]
                links = job_element.find(
                    "a", class_="anchor download-link anchor-default"
                )
                link_url = links["href"]

            except Exception as e:
                print(e)
                continue
            print(article_title)

            df2 = pd.DataFrame(
                [
                    [
                        article_title,
                        article_type,
                        article_date,
                        source,
                        article_authors,
                        link_url,
                    ]
                ],
                columns=[
                    "Title",
                    "Cites",
                    "Year",
                    "Reference",
                    "Publisher",
                    "Tag",
                    "Link",
                    "Description",
                ],
            )
            df = pd.concat([df, df2], ignore_index=True)
            headers = ["Title", "Article Rype", "Year", "Source", "Authors", "Link"]

            with open(
                "scrapped_science_direct.csv", "a", newline="", encoding="utf-8"
            ) as f:
                writer = csv.writer(f)
                writer.writerow(
                    [
                        article_title,
                        article_type,
                        article_date,
                        source,
                        article_authors,
                        link_url,
                    ]
                )

            # time.sleep(1)
    except AttributeError:
        print("Opss! ReCaptcha is probably preventing the code from running.")
        print("Please consider running in another time.\n")
        # exit()
        wait()

df.index += 1
df.to_csv("scrapped_gscholar.csv", encoding="utf-8")
outfile.close()

# ===== CODE FOR SCIENCE  =====

# END OF PROGRAM

print("Job finished, Godspeed you! Cite us.")


#!/usr/bin/python3
import time

def main():
    answer = 0
    temp = 0 #Keeps every calculated side, so when it is greater than answer, answer = temp
    matrix = ConvertToMatrix('LargestProductInGrid.txt') #The grid is kept in a txt file

    for row in range(0, len(matrix)):
        for column in range(0, len(matrix[0])):
            temp = CalculateLeftSide(row, column, matrix)
            if temp > answer:
                answer = temp
                print(row, column,"on LeftSide is the greatest product",answer)

            temp = CalculateRightSide(row, column, matrix)
            if temp > answer:
                answer = temp
                print(row, column,"on RightSide is the greatest product",answer)

            temp = CalculateTopSide(row, column, matrix)
            if temp > answer:
                answer = temp
                print(row, column,"on TopSide is the greatest product",answer)

            temp = CalculateBottomSide(row, column, matrix)
            if temp > answer:
                answer = temp
                print(row, column,"on BottomSide is the greatest product",answer)

            temp = CalculateDiagonalBottomLeft(row, column, matrix)
            if temp > answer:
                answer = temp
                print(row, column,"on DiagonalBottomLeftSide is the greatest product",answer)

            temp = CalculateDiagonalBottomRight(row, column, matrix)
            if temp > answer:
                answer = temp
                print(row, column,"on DiagonalBottomRightSide is the greatest product",answer)

            temp = CalculateDiagonalTopLeft(row, column, matrix)
            if temp > answer:
                answer = temp
                print(row, column,"on DiagonalTopLeftSide is the greatest product",answer)

            temp = CalculateDiagonalTopRight(row, column, matrix)
            if temp > answer:
                answer = temp
                print(row, column,"on DiagonalTopRightSide is the greatest product",answer)

    print("\n-------------------------\nFinal Answer is", answer,"\n-------------------------")

#The grid is kept in a txt File
def ConvertToMatrix(filename = 'LargestProductInGrid.txt'): #This function puts numbers into rows and columns like a matrix
    row = []
    file = open(filename)
    for line in file.readlines():
        column = []
        newLine = line.replace(' ','').replace('\n','')
        counter = 0
        while counter < len(newLine):
            cuttedText = newLine[counter: 2 + counter]
            if len(cuttedText) == 2:
                column.append(int(cuttedText))
            counter += 2
        else:
            row.append(column)

    else:
        return row

def CalculateLeftSide(row, column, matrix):
    if column - 3 >= 0:
        return matrix[row][column] * matrix[row][column - 1] * matrix[row][column - 2] * matrix[row][column - 3]
    else:
        return 0

def CalculateRightSide(row, column, matrix):
    if column + 3 < len(matrix):
        return matrix[row][column] * matrix[row][column + 1] * matrix[row][column + 2] * matrix[row][column + 3]
    else:
        return 0

def CalculateBottomSide(row, column, matrix):
    if row + 3 < len(matrix[0]):
        return matrix[row][column] * matrix[row + 1][column] * matrix[row + 2][column] * matrix[row + 3][column]
    else:
        return 0

def CalculateTopSide(row, column, matrix):
    if row - 3 > 0:
        return matrix[row][column] * matrix[row - 1][column] * matrix[row - 2][column] * matrix[row - 3][column]
    else:
        return 0

def CalculateDiagonalTopRight(row, column, matrix):
    if row - 3 >= 0 and column + 3 < len(matrix):
        return matrix[row][column] * matrix[row - 1][column + 1] * matrix[row - 2][column + 2] * matrix[row - 3][column + 3]
    else:
        return 0

def CalculateDiagonalBottomRight(row, column, matrix):
    if row + 3 < len(matrix[0]) and column + 3 < len(matrix):
        return matrix[row][column] * matrix[row + 1][column + 1] * matrix[row + 2][column + 2] * matrix[row + 3][column + 3]
    else:
        return 0

def CalculateDiagonalTopLeft(row, column, matrix):
    if row - 3 >= 0 and column - 3 >= 0:
        return matrix[row][column] * matrix[row - 1][column - 1] * matrix[row - 2][column - 2] * matrix[row - 3][column - 3]
    else:
        return 0

def CalculateDiagonalBottomLeft(row, column, matrix):
    if row + 3 < len(matrix[0]) and column - 3 >= 0:
        return matrix[row][column] * matrix[row + 1][column - 1] * matrix[row + 2][column - 2] * matrix[row + 3][column - 3]
    else:
        return 0

if __name__ == '__main__': main()


from tkinter import *

from tkinter import messagebox as msg_box
from tkinter.filedialog import askopenfilename
import os
import configparser
import subprocess
import threading

# 初始化Tk
window = Tk()
window.title('tk_command_gui')

# 设置窗口大小
width = 540
height = 500

# 获取屏幕尺寸以计算布局参数，使窗口居屏幕中央
screenwidth = window.winfo_screenwidth()
screenheight = window.winfo_screenheight()
align = '%dx%d+%d+%d' % (width, height, (screenwidth-width)/2, (screenheight-height)/2)
window.geometry(align)

# 设置窗口是否可变长
window.resizable(width=False, height=False)

video_path = StringVar()
model_path = StringVar()
rate = StringVar()
rate.set(0.8)

cf = configparser.ConfigParser()

def add_video_path():
    cf.read("gui.ini")
    value = askopenfilename(title='add_video_path', filetypes=[("videos", "*.mp4")], initialdir=cf.get("path", "video"))
    video_path.set(value)

def add_model_path():
    cf.read("gui.ini")
    value = askopenfilename(title='add_model_path', filetypes=[("model", "*.xml")],  initialdir=cf.get("path", "model"))
    model_path.set(value)

# video path
Label(window, text="video path").place(x=20, y=36, width=100, height=40)
Entry(window, textvariable=video_path).place(x=123, y=36, width=270, height=40)
Button(window, text="add", command=add_video_path).place(x=405, y=36, width=70, height=40)

# model path
Label(window, text="model path").place(x=20, y=85, width=100, height=40)
Entry(window, textvariable=model_path).place(x=123, y=85, width=270, height=40)
Button(window, text="add", command=add_model_path).place(x=405, y=85, width=70, height=40)

# value
Label(window, text="rate").place(x=20, y=125, width=100, height=40)
Entry(window, textvariable=rate).place(x=123, y=130, width=60, height=40)

def on_run():
    path1 = video_path.get()
    path2 = model_path.get()
    if path1 == "":
        msg_box.showerror("Error", "please select video path")
        return
    if path2 == "":
        msg_box.showerror("Error", "please select model path")
        return

    d = os.path.dirname(__file__)  # 返回当前文件所在的目录
    parent_path = os.path.dirname(d)  # 获得d所在的目录,即d的父级目录
    parent_path = parent_path.replace('/', '\\')

    # 创建空文件
    batch_path = "./run.bat"
    cf.read("gui.ini")
    fp = open(batch_path, 'w')
    fp.write('cd ' + cf.get("path", "sdk") + '\n')
    fp.write('call setupvars.bat' + '\n')
    fp.write('cd ' + parent_path + '\\lib' + '\n')
    fp.write('object_detection_demo_ssd_async.exe -i ' + path1 + ' -m ' + path2 + ' -d CPU -t ' + rate.get() + '\n')
    fp.close()

    p = subprocess.Popen("cmd.exe /c" + "run.bat", stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

def on_stop():
    cmd = "taskkill /F /IM object_detection_demo_ssd_async.exe"
    p = subprocess.Popen("cmd.exe /c" + cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    curline = p.stdout.readline()
    while(curline != b''):
        # unicode_text = u"".join(curline)
        print(curline.decode("gbk"))
        curline = p.stdout.readline()

    p.wait()
    print(p.returncode)

def thread_it(func, *args):
    # 创建
    t = threading.Thread(target=func, args=args)
    # 守护 !!!
    t.setDaemon(True)
    # 启动
    t.start()

Button(window, text="run", bg="white", command=lambda: thread_it(on_run)).place(x=260, y=130, width=100, height=40)

Button(window, text="stop", bg="yellow", command=lambda: thread_it(on_stop)).place(x=370, y=130, width=100, height=40)

Button(window, bg="black").place(x=0, y=190, width=550, height=3)

# 进入消息循环
window.mainloop()


#%%
import tensorflow as tf
import tensorflow_probability as tfp
tfd = tfp.distributions

import matplotlib.pyplot as plt
import numpy as np

print("TF version:", tf.__version__)
print("TFP version:", tfp.__version__)
# %%
normal_diag = tfd.MultivariateNormalDiag(loc = [0,5], scale_diag=[10,15])
normal_diag
# %%
normal_diag.sample(10)
# %%
plt_sample = normal_diag.sample(10000)
plt.scatter(plt_sample[:,0], plt_sample[:,1],marker='.',alpha=0.1)
plt.axis('equal')
plt.show()

# %%
normal_diag2 = tfd.MultivariateNormalDiag(loc = [[0,5],[0,5],[0,5]], scale_diag=[[1,2],[2,1],[2,2]])
normal_diag2
# %%
samples = normal_diag2.sample(5)
samples
# %%
normal_diag2.log_prob(samples)
# %%
plt_sample_batch = normal_diag2.sample(10000)
plt_sample_batch.shape
# %%
fig, ax = plt.subplots(1, 3, sharex=True, sharey=True, figsize=(10,3))
titles = ['Cov1', 'Cov2', 'Cov3']

for i, (ax, title) in enumerate(zip(ax, titles)):
    samples = plt_sample_batch[:,i,:]
    ax.scatter(samples[:,0],samples[:,1], marker='.', alpha=0.1)
    ax.set_title(title)
# %%