import logging

from kocherga.error import PublicError

logger = logging.getLogger(__name__)

import asyncio
import base64
from datetime import datetime, timedelta
from io import BytesIO
from typing import Optional

import kocherga.dateutils
import kocherga.django.models
import kocherga.mailchimp
import kocherga.telegram.utils
import kocherga.templater.models
import kocherga.vk.api
import kocherga.vk.helpers
import markdown
from django.conf import settings
from django.db import models
from django.template.loader import render_to_string
from django.utils import timezone
from kocherga.dateutils import TZ
from kocherga.email.tools import get_utmify, mjml2html
from kocherga.wagtail.utils import create_image_from_fh

from .event import Event

MAILCHIMP_IMAGE_FOLDER_NAME = 'Расписание на неделю'
MAILCHIMP_CAMPAIGN_FOLDER_NAME = 'Еженедельная рассылка'
TELEGRAM_HASHTAG = 'расписание_кочерги'
VK_HASHTAG = 'расписание_кочерги'

class WeeklyDigestManager(models.Manager):
    def current_digest(self):
        dt = datetime.now(TZ)
        if dt.weekday() < 2:
            dt = dt - timedelta(days=dt.weekday())
        else:
            dt = dt + timedelta(days=7 - dt.weekday())

        d = dt.date()
        (obj, _) = self.get_or_create(start=d)
        return obj

class WeeklyDigest(models.Model):
    start = models.DateField('Дата начала недели')
    mailchimp_id = models.CharField(
        'ID кампании в Mailchimp', max_length=100, blank=True
    )
    mailchimp_sent = models.BooleanField('Mailchimp-кампания отправлена', default=False)
    telegram_id = models.CharField(
        'ID сообщения в Telegram', max_length=100, blank=True
    )
    vk_id = models.CharField('ID поста в VK', max_length=100, blank=True)

    image = models.ForeignKey(
        'kocherga_wagtail.CustomImage',
        null=True,
        blank=True,
        on_delete=models.PROTECT,
        related_name='+',
    )

    objects = WeeklyDigestManager()

    def __str__(self):
        return f'{self.start:%Y-%m-%d}'

    @property
    def end(self):
        return self.start + timedelta(days=6)

    def events(self):
        query = Event.objects.public_only().filter_by_period(
            from_date=self.start,
            to_date=self.end,
        )
        result = list(query.all())
        logger.info(f"Schedule includes {len(result)} events")
        return result

    def get_mailchimp_content(self, text_before, text_after, image_url):
        events = self.events()

        date2events = {}
        for event in events:
            d = timezone.localtime(event.start).strftime('%Y-%m-%d')
            if d not in date2events:
                date2events[d] = []
            date2events[d].append(event)

        events_by_date = [
            {"date": d, "events": date2events[d]} for d in sorted(date2events.keys())
        ]

        start_month = kocherga.dateutils.inflected_month(self.start)
        end_month = kocherga.dateutils.inflected_month(self.end)

        title_dates = ''
        if start_month == end_month:
            title_dates = f'{self.start.day} – {self.end.day} {start_month}'
        else:
            title_dates = f'{self.start.day} {start_month} – {self.end.day} {end_month}'

        def date2day(d):
            dt = datetime.strptime(d, '%Y-%m-%d')
            weekday = kocherga.dateutils.weekday(dt).capitalize()
            month = kocherga.dateutils.inflected_month(dt)
            return f"{weekday}, {dt.day} {month}"

        mjml = render_to_string(
            'events/email/weekly_digest.mjml',
            {
                'text_before': markdown.markdown(
                    text_before, extensions=['markdown.extensions.nl2br']
                ),
                'text_after': markdown.markdown(
                    text_after, extensions=['markdown.extensions.nl2br']
                ),
                'image_url': image_url,
                'title_dates': title_dates,
                'events_by_date': events_by_date,
                'date2day': date2day,
                'utmify': get_utmify('weekly-digest', 'kocherga-newsletter'),
            },
        )

        html = mjml2html(mjml)

        return {
            'title': f'Расписание мероприятий Кочерги на {title_dates}',
            'html': html,
        }

    def create_image_if_necessary(self) -> None:
        if self.image:
            return

        start_date = self.start
        params = {
            "start_date": start_date.strftime("%Y-%m-%d"),
            "end_date": (start_date + timedelta(days=6)).strftime("%Y-%m-%d"),
        }
        template = kocherga.templater.models.Template.by_name('mailchimp')
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        image_bytes = loop.run_until_complete(template.generate_png(params))
        self.image = create_image_from_fh(
            BytesIO(image_bytes),
            title=f'WeeklyDigest cover - {start_date:%Y-%m-%d}',
            basename=f'weekly-digest-{start_date:%Y-%m-%d}.png',
            user=None,
            collection=kocherga.django.models.Settings.load().weekly_digest_images_collection,
        )
        self.save()

    def get_image_bytes(self) -> bytes:
        self.create_image_if_necessary()
        with self.image.file.open('rb') as fh:
            return fh.read()

    def upload_mailchimp_image(self):
        image_bytes = self.get_image_bytes()

        image_folder_id = kocherga.mailchimp.image_folder_by_name(
            MAILCHIMP_IMAGE_FOLDER_NAME
        )['id']

        logger.info('Uploading weekly digest image to mailchimp')
        result = kocherga.mailchimp.api_call(
            'POST',
            'file-manager/files',
            {
                'folder_id': image_folder_id,
                'name': f"weekly-image-{self.start.strftime('%Y-%m-%d')}.png",
                'file_data': base64.encodebytes(image_bytes).decode('utf-8'),
            },
        )

        return result['full_size_url']

    def post_mailchimp_draft(self, text_before='', text_after=''):
        if self.mailchimp_id:
            raise Exception("Mailchimp draft already exists")

        image_url = self.upload_mailchimp_image()

        logger.info('Generating html content')
        content = self.get_mailchimp_content(
            text_before=text_before, text_after=text_after, image_url=image_url
        )

        logger.info('Creating campaign draft')
        campaign = kocherga.mailchimp.api_call(
            'POST',
            'campaigns',
            {
                'type': 'regular',
                'recipients': {
                    'list_id': kocherga.mailchimp.MAIN_LIST_ID,
                    'segment_opts': {
                        'saved_segment_id': kocherga.mailchimp.segment_by_name(
                            'Подписаны на расписание'
                        )['id'],
                    },
                },
                'settings': {
                    'subject_line': content['title'],
                    'title': content['title'],
                    'from_name': 'Кочерга',
                    'reply_to': 'info@kocherga-club.ru',
                    'to_name': '*|FNAME|* *|LNAME|*',
                    'folder_id': kocherga.mailchimp.folder_id_by_name(
                        MAILCHIMP_CAMPAIGN_FOLDER_NAME
                    ),
                },
                'tracking': {
                    'google_analytics': f'weekly-digest-{self.start:%Y-%m-%d}',
                },
            },
        )

        campaign_id = campaign['id']

        logger.info('Filling campaign content')
        kocherga.mailchimp.api_call(
            'PUT', f'campaigns/{campaign_id}/content', {'html': content['html']}
        )
        self.mailchimp_id = campaign_id
        self.full_clean()
        self.save()

    def cancel_mailchimp_draft(self):
        if not self.mailchimp_id:
            raise PublicError("Mailchimp draft doesn't exist")
        if self.mailchimp_sent:
            raise PublicError("Mailchimp campaign is sent and can't be canceled")

        kocherga.mailchimp.api_call(
            'DELETE',
            f'campaigns/{self.mailchimp_id}',
        )
        self.mailchimp_id = ''
        self.full_clean()
        self.save()

    def send_mailchimp(self):
        if not self.mailchimp_id:
            raise PublicError("Mailchimp draft doesn't exist")
        if self.mailchimp_sent:
            raise PublicError("Mailchimp campaign is already sent")

        kocherga.mailchimp.api_call(
            'POST',
            f'campaigns/{self.mailchimp_id}/actions/send',
        )
        self.mailchimp_sent = True
        self.full_clean()
        self.save()

    def mailchimp_campaign_link(self) -> Optional[str]:
        if not self.mailchimp_id:
            return None

        try:
            response = kocherga.mailchimp.api_call(
                'GET', f'campaigns/{self.mailchimp_id}', {'fields': 'web_id'}
            )
        except kocherga.mailchimp.MailchimpException as e:
            if e.status_code == 404:
                # TODO - reset setf.mailchimp_id? implement a `fix_mailchimp` method?
                raise PublicError("Mailchimp campaign should exist but doesn't")
            raise e

        return kocherga.mailchimp.campaign_web_link(response['web_id'])

    def _telegram_message(self):
        message = f"#{TELEGRAM_HASHTAG}\nНа этой неделе в Кочерге:"
        message += "\n"

        prev_date = None
        events = self.events()
        for event in events:
            start_local = timezone.localtime(event.start)
            if start_local.date() != prev_date:
                weekday = kocherga.dateutils.weekday(start_local).capitalize()
                month = kocherga.dateutils.inflected_month(start_local)
                message += f"\n{weekday}, {start_local.day} {month}\n"
                prev_date = start_local.date()

            title = f'{event.title}'
            message += f"{start_local:%H:%M} {title}\n"

        return message

    def post_telegram(self):
        if self.telegram_id:
            raise PublicError("Telegram message is already sent")

        text = self._telegram_message()
        posted_messages = kocherga.telegram.utils.post_to_channel(text)
        self.telegram_id = posted_messages[0]['result']['message_id']
        self.full_clean()
        self.save()

    def telegram_link(self) -> Optional[str]:
        if not self.telegram_id:
            return None

        return kocherga.telegram.utils.channel_message_link_by_id(self.telegram_id)

    def post_vk(self, prefix_text):
        if self.vk_id:
            raise PublicError("VK digest already posted")

        message = f"#{VK_HASHTAG}\n"
        message += prefix_text

        events = self.events()

        prev_date = None
        for event in events:
            # if not event.vk_announcement.link:
            #     continue  # TODO - throw error?

            start_local = timezone.localtime(event.start)
            if start_local.date() != prev_date:
                weekday = kocherga.dateutils.weekday(start_local).upper()
                month = kocherga.dateutils.inflected_month(start_local)
                message += f"\n{weekday}, {start_local.day} {month}\n"
                prev_date = start_local.date()

            title = event.title
            # if event.vk_announcement.group:
            #     title = f"@{event.vk_announcement.group} ({title})"
            message += f"{start_local:%H:%M} {title} - {event.public_link()}\n"
            # message += f"{event.generate_summary()}\n\n"

        group_id = kocherga.vk.helpers.group2id(settings.KOCHERGA_VK["main_page"]["id"])

        image_bytes = self.get_image_bytes()
        photo_id = kocherga.vk.helpers.upload_wall_image(group_id, image_bytes)

        response = kocherga.vk.api.call(
            "wall.post",
            {
                "owner_id": -group_id,
                "from_group": 1,
                "message": message,
                "publish_date": int(datetime.now().timestamp()) + 86400,
                "attachments": photo_id,
            },
        )

        self.vk_id = response['post_id']
        self.full_clean()
        self.save()

    def vk_link(self) -> Optional[str]:
        if not self.vk_id:
            return None

        group_id = kocherga.vk.helpers.group2id(settings.KOCHERGA_VK["main_page"]["id"])
        return f'https://vk.com/wall-{group_id}_{self.vk_id}'


#! /usr/bin/env python3
# -*- coding: utf-8 -*-

#  Dev By : Ali B Othman

import logging
from logging.handlers import SysLogHandler
import time,os

from service import find_syslog, Service

class MyService(Service):
    def __init__(self, *args, **kwargs):
        super(MyService, self).__init__(*args, **kwargs)
        # Set logger (sys log)
        self.logger.addHandler(SysLogHandler(address=find_syslog(),
                               facility=SysLogHandler.LOG_DAEMON))
        self.logger.setLevel(logging.INFO)

    def run(self):
        # What you want from service To Do
        while not self.got_sigterm():
            self.logger.info("I'm working...")
            time.sleep(5)

if __name__ == '__main__':
    # Service name
    servicename = 'My service'
    # Pid file name
    pidname = ('%s.pid' % servicename)
    import sys

    if len(sys.argv) != 2:
        sys.exit('Syntax: %s COMMAND\n[--start] to start service.\n[--stop] to stop service.\n[--status] to check service status.' % sys.argv[0])

    cmd = sys.argv[1].lower()
    # Pid dir
    service = MyService(servicename, pid_dir='/tmp')

    if cmd == '--start':
        if service.is_running():
            print('Service is already running.\nUse (%s --stop) to stop service' % sys.argv[0])
        else:
            service.start()
    elif cmd == '--stop':
        if not service.is_running():
            print('Service is already not running.')
        else:
            try:
                while service.is_running():
                    service.stop()
                    # Don't change time.sleep(1)
                    time.sleep(1)
            except Exception as e:
                if e == '[Errno 3] No such process':
                    os.remove('/tmp/%s' % pidname)
            if pidname in os.listdir('/tmp'):
                os.remove('/tmp/%s' % pidname)
    elif cmd == '--status':
        if service.is_running():
            print("Service is running.")
        else:
            print("Service is not running.")
    else:
        sys.exit('Unknown command "%s".\n[--start] to start service.\n[--stop] to stop service.\n[--status] to check service status.' % cmd)


import random, sys, time, math, pygame
from pygame.locals import *
import pytmx
from pytmx.util_pygame import load_pygame

GRASSCOLOR = (24, 255, 0)

def initMap():
    tiled_map = load_pygame('Map/maptn5.tmx', pixelalpha=True)
    tilewidth = tiled_map.tilewidth
    tileheight = tiled_map.tileheight

    return tiled_map,tilewidth,tileheight

def renderMap(tiled_map, tilewidth, tileheight, DISPLAYSURF, camerax, cameray):
    for layer in tiled_map.layers:
        if isinstance(layer, pytmx.TiledTileLayer):

            for x, y, tile in layer.tiles():
                if (tile):
                    DISPLAYSURF.blit(tile, [( x + camerax )*tilewidth,( y + cameray )*tileheight])

def drawRectTerrain(tilewidth, tileheight, WHITE, DISPLAYSURF, camerax, cameray, SET_ALPHA):

    tiledGrass1_size = (51*tilewidth,3*tileheight)
    tiledGrass1_rect =  pygame.Surface(tiledGrass1_size)
    tiledGrass1_rect.set_alpha(SET_ALPHA)
    tiledGrass1_rect.fill(WHITE)
    tiledGrass1 = DISPLAYSURF.blit(tiledGrass1_rect, (0*tilewidth + camerax*tilewidth,13*tileheight + cameray*tileheight))

    tiledStone1_size = (11*tilewidth,1*tileheight)
    tiledStone1_rect =  pygame.Surface(tiledStone1_size)
    tiledStone1_rect.set_alpha(SET_ALPHA)
    tiledStone1_rect.fill(WHITE)
    tiledStone1 = DISPLAYSURF.blit(tiledStone1_rect, (12*tilewidth + camerax*tilewidth,12*tileheight + cameray*tileheight))

    return tiledGrass1, tiledStone1

def drawRectTerrainList(tilewidth, tileheight, WHITE, DISPLAYSURF, camerax, cameray, SET_ALPHA):

    tiledColisions = []
    tiledColisions2 = []
    tiledColisions3 = []
    tiledColisions4 = []
    tiledColisions5 = []
    tiledColisions6 = []

    # Area 1

    tileStart1 = { 'area': 1, 'type': 'ground', 'X': 0,'Y': 0, 'sizeX': 1,'sizeY': 16 }
    tileStart1_size = (tileStart1['sizeX']*tilewidth,tileStart1['sizeY']*tileheight)
    tileStart1_rect =  pygame.Surface(tileStart1_size)
    tileStart1_rect.set_alpha(SET_ALPHA)
    tileStart1_rect.fill(WHITE)
    tileStart1['rect'] = DISPLAYSURF.blit(tileStart1_rect, (tileStart1['X']*tilewidth + camerax*tilewidth,tileStart1['Y']*tileheight + cameray*tileheight))

    tiledGrass1 = { 'area':1, 'type': 'ground', 'X': 0,'Y': 13, 'sizeX': 51,'sizeY': 3   }
    tiledGrass1_size = (tiledGrass1['sizeX']*tilewidth,tiledGrass1['sizeY']*tileheight)
    tiledGrass1_rect =  pygame.Surface(tiledGrass1_size)
    tiledGrass1_rect.set_alpha(SET_ALPHA)
    tiledGrass1_rect.fill(WHITE)
    tiledGrass1['rect'] = DISPLAYSURF.blit(tiledGrass1_rect, (tiledGrass1['X']*tilewidth + camerax*tilewidth,tiledGrass1['Y']*tileheight + cameray*tileheight))

    tiledStone1={ 'area':1, 'type': 'ground', 'X': 12,'Y': 12, 'sizeX': 11,'sizeY': 1  }
    tiledStone1_size = (tiledStone1['sizeX']*tilewidth,tiledStone1['sizeY']*tileheight)
    tiledStone1_rect =  pygame.Surface(tiledStone1_size)
    tiledStone1_rect.set_alpha(SET_ALPHA)
    tiledStone1_rect.fill(WHITE)
    tiledStone1['rect'] = DISPLAYSURF.blit(tiledStone1_rect, (tiledStone1['X']*tilewidth + camerax*tilewidth,tiledStone1['Y']*tileheight + cameray*tileheight))

    tiledStone2={ 'area':1, 'type': 'ground', 'X': 13,'Y': 11, 'sizeX': 9,'sizeY': 1   }
    tiledStone2_size = (tiledStone2['sizeX']*tilewidth,tiledStone2['sizeY']*tileheight)
    tiledStone2_rect =  pygame.Surface(tiledStone2_size)
    tiledStone2_rect.set_alpha(SET_ALPHA)
    tiledStone2_rect.fill(WHITE)
    tiledStone2['rect'] = DISPLAYSURF.blit(tiledStone2_rect, (tiledStone2['X']*tilewidth + camerax*tilewidth,tiledStone2['Y']*tileheight + cameray*tileheight))

    tiledStone3={ 'area':1, 'type': 'ground', 'X': 14,'Y': 10, 'sizeX': 7,'sizeY': 1   }
    tiledStone3_size = (tiledStone3['sizeX']*tilewidth,tiledStone3['sizeY']*tileheight)
    tiledStone3_rect =  pygame.Surface(tiledStone3_size)
    tiledStone3_rect.set_alpha(SET_ALPHA)
    tiledStone3_rect.fill(WHITE)
    tiledStone3['rect'] = DISPLAYSURF.blit(tiledStone3_rect, (tiledStone3['X']*tilewidth + camerax*tilewidth,tiledStone3['Y']*tileheight + cameray*tileheight))

    tiledStone4={ 'area':1, 'type': 'ground', 'X': 15,'Y': 9, 'sizeX': 5,'sizeY': 1   }
    tiledStone4_size = (tiledStone4['sizeX']*tilewidth,tiledStone4['sizeY']*tileheight)
    tiledStone4_rect =  pygame.Surface(tiledStone4_size)
    tiledStone4_rect.set_alpha(SET_ALPHA)
    tiledStone4_rect.fill(WHITE)
    tiledStone4['rect'] = DISPLAYSURF.blit(tiledStone4_rect, (tiledStone4['X']*tilewidth + camerax*tilewidth,tiledStone4['Y']*tileheight + cameray*tileheight))

    tiledStone5={ 'area':1, 'type': 'ground', 'X': 16,'Y': 8, 'sizeX': 3,'sizeY': 1   }
    tiledStone5_size = (tiledStone5['sizeX']*tilewidth,tiledStone5['sizeY']*tileheight)
    tiledStone5_rect =  pygame.Surface(tiledStone5_size)
    tiledStone5_rect.set_alpha(SET_ALPHA)
    tiledStone5_rect.fill(WHITE)
    tiledStone5['rect'] = DISPLAYSURF.blit(tiledStone5_rect, (tiledStone5['X']*tilewidth + camerax*tilewidth,tiledStone5['Y']*tileheight + cameray*tileheight))

    tiledSnow1={ 'area':1, 'type': 'ground', 'X': 25,'Y': 8, 'sizeX': 5,'sizeY': 2   }
    tiledSnow1_size = (tiledSnow1['sizeX']*tilewidth,tiledSnow1['sizeY']*tileheight)
    tiledSnow1_rect =  pygame.Surface(tiledSnow1_size)
    tiledSnow1_rect.set_alpha(SET_ALPHA)
    tiledSnow1_rect.fill(WHITE)
    tiledSnow1['rect'] = DISPLAYSURF.blit(tiledSnow1_rect, (tiledSnow1['X']*tilewidth + camerax*tilewidth,tiledSnow1['Y']*tileheight + cameray*tileheight))

    tiledSnow2={ 'area':1, 'type': 'ground', 'X': 33,'Y': 6, 'sizeX': 6,'sizeY': 2   }
    tiledSnow2_size = (tiledSnow2['sizeX']*tilewidth,tiledSnow2['sizeY']*tileheight)
    tiledSnow2_rect =  pygame.Surface(tiledSnow2_size)
    tiledSnow2_rect.set_alpha(SET_ALPHA)
    tiledSnow2_rect.fill(WHITE)
    tiledSnow2['rect'] = DISPLAYSURF.blit(tiledSnow2_rect, (tiledSnow2['X']*tilewidth + camerax*tilewidth,tiledSnow2['Y']*tileheight + cameray*tileheight))

    tiledSnow3={ 'area':1, 'type': 'ground', 'X': 38,'Y': 12, 'sizeX': 6,'sizeY': 1   }
    tiledSnow3_size = (tiledSnow3['sizeX']*tilewidth,tiledSnow3['sizeY']*tileheight)
    tiledSnow3_rect =  pygame.Surface(tiledSnow3_size)
    tiledSnow3_rect.set_alpha(SET_ALPHA)
    tiledSnow3_rect.fill(WHITE)
    tiledSnow3['rect'] = DISPLAYSURF.blit(tiledSnow3_rect, (tiledSnow3['X']*tilewidth + camerax*tilewidth,tiledSnow3['Y']*tileheight + cameray*tileheight))

    tiledTyp1={ 'area':1, 'type': 'ground', 'X': 44,'Y': 9, 'sizeX': 3,'sizeY': 4   }
    tiledTyp1_size = (tiledTyp1['sizeX']*tilewidth,tiledTyp1['sizeY']*tileheight)
    tiledTyp1_rect =  pygame.Surface(tiledTyp1_size)
    tiledTyp1_rect.set_alpha(SET_ALPHA)
    tiledTyp1_rect.fill(WHITE)
    tiledTyp1['rect'] = DISPLAYSURF.blit(tiledTyp1_rect, (tiledTyp1['X']*tilewidth + camerax*tilewidth,tiledTyp1['Y']*tileheight + cameray*tileheight))

    # Area 2

    tiledGrass2 = { 'area':2, 'type': 'ground', 'X': 53,'Y': 10, 'sizeX': 6,'sizeY': 2   }
    tiledGrass2_size = (tiledGrass2['sizeX']*tilewidth,tiledGrass2['sizeY']*tileheight)
    tiledGrass2_rect =  pygame.Surface(tiledGrass2_size)
    tiledGrass2_rect.set_alpha(SET_ALPHA)
    tiledGrass2_rect.fill(WHITE)
    tiledGrass2['rect'] = DISPLAYSURF.blit(tiledGrass2_rect, (tiledGrass2['X']*tilewidth + camerax*tilewidth,tiledGrass2['Y']*tileheight + cameray*tileheight))

    tiledGrass3 = { 'area':2, 'type': 'ground', 'X': 61,'Y': 8, 'sizeX': 7,'sizeY': 2   }
    tiledGrass3_size = (tiledGrass3['sizeX']*tilewidth,tiledGrass3['sizeY']*tileheight)
    tiledGrass3_rect =  pygame.Surface(tiledGrass3_size)
    tiledGrass3_rect.set_alpha(SET_ALPHA)
    tiledGrass3_rect.fill(WHITE)
    tiledGrass3['rect'] = DISPLAYSURF.blit(tiledGrass3_rect, (tiledGrass3['X']*tilewidth + camerax*tilewidth,tiledGrass3['Y']*tileheight + cameray*tileheight))

    tiledSea1 = { 'area':2, 'type': 'sea', 'X': 51,'Y': 14, 'sizeX': 19,'sizeY': 2   }
    tiledSea1_size = (tiledSea1['sizeX']*tilewidth,tiledSea1['sizeY']*tileheight)
    tiledSea1_rect =  pygame.Surface(tiledSea1_size)
    tiledSea1_rect.set_alpha(SET_ALPHA)
    tiledSea1_rect.fill(WHITE)
    tiledSea1['rect'] = DISPLAYSURF.blit(tiledSea1_rect, (tiledSea1['X']*tilewidth + camerax*tilewidth,tiledSea1['Y']*tileheight + cameray*tileheight))

    tiledSand1 = { 'area':2, 'type': 'ground', 'X': 70,'Y': 13, 'sizeX': 52,'sizeY': 3   }
    tiledSand1_size = (tiledSand1['sizeX']*tilewidth,tiledSand1['sizeY']*tileheight)
    tiledSand1_rect =  pygame.Surface(tiledSand1_size)
    tiledSand1_rect.set_alpha(SET_ALPHA)
    tiledSand1_rect.fill(WHITE)
    tiledSand1['rect'] = DISPLAYSURF.blit(tiledSand1_rect, (tiledSand1['X']*tilewidth + camerax*tilewidth,tiledSand1['Y']*tileheight + cameray*tileheight))

    tiledGrass4 = { 'area':2, 'type': 'ground', 'X': 72,'Y': 5, 'sizeX': 6,'sizeY': 2   }
    tiledGrass4_size = (tiledGrass4['sizeX']*tilewidth,tiledGrass4['sizeY']*tileheight)
    tiledGrass4_rect =  pygame.Surface(tiledGrass4_size)
    tiledGrass4_rect.set_alpha(SET_ALPHA)
    tiledGrass4_rect.fill(WHITE)
    tiledGrass4['rect'] = DISPLAYSURF.blit(tiledGrass4_rect, (tiledGrass4['X']*tilewidth + camerax*tilewidth,tiledGrass4['Y']*tileheight + cameray*tileheight))

    tiledStone6={ 'area':2, 'type': 'ground', 'X': 85,'Y': 10, 'sizeX': 4,'sizeY': 1   }
    tiledStone6_size = (tiledStone6['sizeX']*tilewidth,tiledStone6['sizeY']*tileheight)
    tiledStone6_rect =  pygame.Surface(tiledStone6_size)
    tiledStone6_rect.set_alpha(SET_ALPHA)
    tiledStone6_rect.fill(WHITE)
    tiledStone6['rect'] = DISPLAYSURF.blit(tiledStone6_rect, (tiledStone6['X']*tilewidth + camerax*tilewidth,tiledStone6['Y']*tileheight + cameray*tileheight))

    tiledStone7={ 'area':2, 'type': 'ground', 'X': 85,'Y': 11, 'sizeX': 8,'sizeY': 2   }
    tiledStone7_size = (tiledStone7['sizeX']*tilewidth,tiledStone7['sizeY']*tileheight)
    tiledStone7_rect =  pygame.Surface(tiledStone7_size)
    tiledStone7_rect.set_alpha(SET_ALPHA)
    tiledStone7_rect.fill(WHITE)
    tiledStone7['rect'] = DISPLAYSURF.blit(tiledStone7_rect, (tiledStone7['X']*tilewidth + camerax*tilewidth,tiledStone7['Y']*tileheight + cameray*tileheight))

    tiledStone8={ 'area':2, 'type': 'ground', 'X': 101,'Y': 12, 'sizeX': 1,'sizeY': 1   }
    tiledStone8_size = (tiledStone8['sizeX']*tilewidth,tiledStone8['sizeY']*tileheight)
    tiledStone8_rect =  pygame.Surface(tiledStone8_size)
    tiledStone8_rect.set_alpha(SET_ALPHA)
    tiledStone8_rect.fill(WHITE)
    tiledStone8['rect'] = DISPLAYSURF.blit(tiledStone8_rect, (tiledStone8['X']*tilewidth + camerax*tilewidth,tiledStone8['Y']*tileheight + cameray*tileheight))

    tiledStone9={ 'area':2, 'type': 'ground', 'X': 104,'Y': 11, 'sizeX': 1,'sizeY': 2   }
    tiledStone9_size = (tiledStone9['sizeX']*tilewidth,tiledStone9['sizeY']*tileheight)
    tiledStone9_rect =  pygame.Surface(tiledStone9_size)
    tiledStone9_rect.set_alpha(SET_ALPHA)
    tiledStone9_rect.fill(WHITE)
    tiledStone9['rect'] = DISPLAYSURF.blit(tiledStone9_rect, (tiledStone9['X']*tilewidth + camerax*tilewidth,tiledStone9['Y']*tileheight + cameray*tileheight))

    tiledStone10={ 'area':2, 'type': 'ground', 'X': 107,'Y': 10, 'sizeX': 1,'sizeY': 3   }
    tiledStone10_size = (tiledStone10['sizeX']*tilewidth,tiledStone10['sizeY']*tileheight)
    tiledStone10_rect =  pygame.Surface(tiledStone10_size)
    tiledStone10_rect.set_alpha(SET_ALPHA)
    tiledStone10_rect.fill(WHITE)
    tiledStone10['rect'] = DISPLAYSURF.blit(tiledStone10_rect, (tiledStone10['X']*tilewidth + camerax*tilewidth,tiledStone10['Y']*tileheight + cameray*tileheight))

    tiledStone11={ 'area':2, 'type': 'ground', 'X': 110,'Y': 9, 'sizeX': 1,'sizeY': 4   }
    tiledStone11_size = (tiledStone11['sizeX']*tilewidth,tiledStone11['sizeY']*tileheight)
    tiledStone11_rect =  pygame.Surface(tiledStone11_size)
    tiledStone11_rect.set_alpha(SET_ALPHA)
    tiledStone11_rect.fill(WHITE)
    tiledStone11['rect'] = DISPLAYSURF.blit(tiledStone11_rect, (tiledStone11['X']*tilewidth + camerax*tilewidth,tiledStone11['Y']*tileheight + cameray*tileheight))

    tiledStone12={ 'area':2, 'type': 'ground', 'X': 113,'Y': 10, 'sizeX': 1,'sizeY': 3   }
    tiledStone12_size = (tiledStone12['sizeX']*tilewidth,tiledStone12['sizeY']*tileheight)
    tiledStone12_rect =  pygame.Surface(tiledStone12_size)
    tiledStone12_rect.set_alpha(SET_ALPHA)
    tiledStone12_rect.fill(WHITE)
    tiledStone12['rect'] = DISPLAYSURF.blit(tiledStone12_rect, (tiledStone12['X']*tilewidth + camerax*tilewidth,tiledStone12['Y']*tileheight + cameray*tileheight))

    tiledStone13={ 'area':2, 'type': 'ground', 'X': 116,'Y': 11, 'sizeX': 1,'sizeY': 2   }
    tiledStone13_size = (tiledStone13['sizeX']*tilewidth,tiledStone13['sizeY']*tileheight)
    tiledStone13_rect =  pygame.Surface(tiledStone13_size)
    tiledStone13_rect.set_alpha(SET_ALPHA)
    tiledStone13_rect.fill(WHITE)
    tiledStone13['rect'] = DISPLAYSURF.blit(tiledStone13_rect, (tiledStone13['X']*tilewidth + camerax*tilewidth,tiledStone13['Y']*tileheight + cameray*tileheight))

    tiledStone14={ 'area':2, 'type': 'ground', 'X': 118,'Y': 12, 'sizeX': 1,'sizeY': 1   }
    tiledStone14_size = (tiledStone14['sizeX']*tilewidth,tiledStone14['sizeY']*tileheight)
    tiledStone14_rect =  pygame.Surface(tiledStone14_size)
    tiledStone14_rect.set_alpha(SET_ALPHA)
    tiledStone14_rect.fill(WHITE)
    tiledStone14['rect'] = DISPLAYSURF.blit(tiledStone14_rect, (tiledStone14['X']*tilewidth + camerax*tilewidth,tiledStone14['Y']*tileheight + cameray*tileheight))

    # Area 3

    tiledSea2 = { 'area':3, 'type': 'sea', 'X': 122,'Y': 14, 'sizeX': 48,'sizeY': 2   }
    tiledSea2_size = (tiledSea2['sizeX']*tilewidth,tiledSea2['sizeY']*tileheight)
    tiledSea2_rect =  pygame.Surface(tiledSea2_size)
    tiledSea2_rect.set_alpha(SET_ALPHA)
    tiledSea2_rect.fill(WHITE)
    tiledSea2['rect'] = DISPLAYSURF.blit(tiledSea2_rect, (tiledSea2['X']*tilewidth + camerax*tilewidth,tiledSea2['Y']*tileheight + cameray*tileheight))

    tiledSnow4={ 'area':3, 'type': 'ground', 'X': 124,'Y': 10, 'sizeX': 5,'sizeY': 2   }
    tiledSnow4_size = (tiledSnow4['sizeX']*tilewidth,tiledSnow4['sizeY']*tileheight)
    tiledSnow4_rect =  pygame.Surface(tiledSnow4_size)
    tiledSnow4_rect.set_alpha(SET_ALPHA)
    tiledSnow4_rect.fill(WHITE)
    tiledSnow4['rect'] = DISPLAYSURF.blit(tiledSnow4_rect, (tiledSnow4['X']*tilewidth + camerax*tilewidth,tiledSnow4['Y']*tileheight + cameray*tileheight))

    tiledSnow5={ 'area':3, 'type': 'ground', 'X': 131,'Y': 7, 'sizeX': 5,'sizeY': 2   }
    tiledSnow5_size = (tiledSnow5['sizeX']*tilewidth,tiledSnow5['sizeY']*tileheight)
    tiledSnow5_rect =  pygame.Surface(tiledSnow5_size)
    tiledSnow5_rect.set_alpha(SET_ALPHA)
    tiledSnow5_rect.fill(WHITE)
    tiledSnow5['rect'] = DISPLAYSURF.blit(tiledSnow5_rect, (tiledSnow5['X']*tilewidth + camerax*tilewidth,tiledSnow5['Y']*tileheight + cameray*tileheight))

    tiledSnow6={ 'area':3, 'type': 'ground', 'X': 139,'Y': 5, 'sizeX': 6,'sizeY': 2   }
    tiledSnow6_size = (tiledSnow6['sizeX']*tilewidth,tiledSnow6['sizeY']*tileheight)
    tiledSnow6_rect =  pygame.Surface(tiledSnow6_size)
    tiledSnow6_rect.set_alpha(SET_ALPHA)
    tiledSnow6_rect.fill(WHITE)
    tiledSnow6['rect'] = DISPLAYSURF.blit(tiledSnow6_rect, (tiledSnow6['X']*tilewidth + camerax*tilewidth,tiledSnow6['Y']*tileheight + cameray*tileheight))

    tiledSnow7={ 'area':3, 'type': 'ground', 'X': 148,'Y': 11, 'sizeX': 6,'sizeY': 2   }
    tiledSnow7_size = (tiledSnow7['sizeX']*tilewidth,tiledSnow7['sizeY']*tileheight)
    tiledSnow7_rect =  pygame.Surface(tiledSnow7_size)
    tiledSnow7_rect.set_alpha(SET_ALPHA)
    tiledSnow7_rect.fill(WHITE)
    tiledSnow7['rect'] = DISPLAYSURF.blit(tiledSnow7_rect, (tiledSnow7['X']*tilewidth + camerax*tilewidth,tiledSnow7['Y']*tileheight + cameray*tileheight))

    tiledSnow8={ 'area':3, 'type': 'ground', 'X': 155,'Y': 8, 'sizeX': 6,'sizeY': 2   }
    tiledSnow8_size = (tiledSnow8['sizeX']*tilewidth,tiledSnow8['sizeY']*tileheight)
    tiledSnow8_rect =  pygame.Surface(tiledSnow8_size)
    tiledSnow8_rect.set_alpha(SET_ALPHA)
    tiledSnow8_rect.fill(WHITE)
    tiledSnow8['rect'] = DISPLAYSURF.blit(tiledSnow8_rect, (tiledSnow8['X']*tilewidth + camerax*tilewidth,tiledSnow8['Y']*tileheight + cameray*tileheight))

    tiledSnow9={ 'area':3, 'type': 'ground', 'X': 163,'Y': 6, 'sizeX': 2,'sizeY': 2   }
    tiledSnow9_size = (tiledSnow9['sizeX']*tilewidth,tiledSnow9['sizeY']*tileheight)
    tiledSnow9_rect =  pygame.Surface(tiledSnow9_size)
    tiledSnow9_rect.set_alpha(SET_ALPHA)
    tiledSnow9_rect.fill(WHITE)
    tiledSnow9['rect'] = DISPLAYSURF.blit(tiledSnow9_rect, (tiledSnow9['X']*tilewidth + camerax*tilewidth,tiledSnow9['Y']*tileheight + cameray*tileheight))

    tiledSnow10={ 'area':3, 'type': 'ground', 'X': 167,'Y': 4, 'sizeX': 2,'sizeY': 2   }
    tiledSnow10_size = (tiledSnow10['sizeX']*tilewidth,tiledSnow10['sizeY']*tileheight)
    tiledSnow10_rect =  pygame.Surface(tiledSnow10_size)
    tiledSnow10_rect.set_alpha(SET_ALPHA)
    tiledSnow10_rect.fill(WHITE)
    tiledSnow10['rect'] = DISPLAYSURF.blit(tiledSnow10_rect, (tiledSnow10['X']*tilewidth + camerax*tilewidth,tiledSnow10['Y']*tileheight + cameray*tileheight))

    # ----------------------------------------------------------

    # Area 1
    tiledColisions.append(tileStart1)
    tiledColisions.append(tiledGrass1)
    tiledColisions.append(tiledStone1)
    tiledColisions.append(tiledStone2)
    tiledColisions.append(tiledStone3)
    tiledColisions.append(tiledStone4)
    tiledColisions.append(tiledStone5)
    tiledColisions.append(tiledSnow1)
    tiledColisions.append(tiledSnow2)
    tiledColisions.append(tiledSnow3)
    tiledColisions.append(tiledTyp1)

    # Area 2
    tiledColisions2.append(tiledGrass2)
    tiledColisions2.append(tiledGrass3)
    tiledColisions2.append(tiledSea1)
    tiledColisions2.append(tiledSand1)
    tiledColisions2.append(tiledGrass4)
    tiledColisions2.append(tiledStone6)
    tiledColisions2.append(tiledStone7)
    tiledColisions2.append(tiledStone8)
    tiledColisions2.append(tiledStone9)
    tiledColisions2.append(tiledStone10)
    tiledColisions2.append(tiledStone11)
    tiledColisions2.append(tiledStone12)
    tiledColisions2.append(tiledStone13)
    tiledColisions2.append(tiledStone14)

    # Area 3

    tiledColisions3.append(tiledSea2)
    tiledColisions3.append(tiledSnow4)
    tiledColisions3.append(tiledSnow5)
    tiledColisions3.append(tiledSnow6)
    tiledColisions3.append(tiledSnow7)
    tiledColisions3.append(tiledSnow8)
    tiledColisions3.append(tiledSnow9)
    tiledColisions3.append(tiledSnow10)

    return tiledColisions, tiledColisions2, tiledColisions3

# def drawRectCrystal(crystalObj, DISPLAYSURF, SET_ALPHA, camerax, cameray, tilewidth, tileheight):
#
#     # Area 1
#
#     crystal={ 'area':1, 'type': 'crytal' }
#     crystal_size = (30,30)
#     crystal_rect =  pygame.Surface(crystal_size)
#     crystal_rect.set_alpha(SET_ALPHA)
#     crystal_rect.fill(GRASSCOLOR)
#     crystal['rect'] = DISPLAYSURF.blit(crystal_rect, (crystalObj['x']*tilewidth + camerax*tilewidth,crystalObj['y']*tileheight + cameray*tileheight))
#
#
# #         print(crystalList)
#     return crystal


import json
from difflib import get_close_matches
data=json.load(open("data.json"))

def A(w):
    w=w.lower()
    if w in data:
        return data[w]
    elif len(get_close_matches(w,data.keys()))>0 :
        yn=input("Doyoumean %s  ?.Type Y/N:" % get_close_matches(w,data.keys())[0])
        if yn=="Y":
            return data[get_close_matches(w,data.keys())[0]]
        elif yn=="N":
            return ("Word not exist")

            if Q in data:
                return data[w]

        else:
            return ("Word does not exit")

    else:
        return ("safasf")

word=input("Enter Word: ")

output = A(word)

if type(output)==list:

    for P in output:
        print(P)
else:

    print(output)


from config import *
import os
import copy
import datetime
import numpy as np
import pandas as pd
import joblib
from typing import List
import seaborn as sns
from matplotlib import pyplot as plt
import scipy.stats as stats
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split, GridSearchCV, LeaveOneOut, cross_val_score, cross_val_predict
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.pipeline import Pipeline
from sklearn.metrics import roc_curve, plot_confusion_matrix, roc_auc_score, mean_absolute_error
from imblearn.over_sampling import SVMSMOTE
from imblearn.metrics import sensitivity_specificity_support
from multiprocessing import cpu_count
import shap
from alibi.explainers import KernelShap, TreeShap
from openpyxl import load_workbook

class ModelBase:
	"""模型基类"""

	def __init__(self, feat_data: pd.DataFrame, model_name: str = "", shuffle=True, random_state=rs, opt_gs=False):
		"""
		初始化
		:param feat_data: 特征数据,pd.DataFrame格式
		:param model_name: 模型名称
		:param shuffle: 是否打乱数据集，默认打乱
		:param random_state: 随机种子
		:param opt_gs: 网格搜索中是否使用已找到的最优超参数
		"""
		self.reg = False
		if model_name.split('_')[-1] == '0-12':
			feat_data_clf = feat_data.replace({"Dysarthria Severity": {2: 1}})  # 分组2替换成组1
		elif model_name.split('_')[-1] == '1-2':
			feat_data_clf = feat_data[feat_data['Dysarthria Severity'].isin([1, 2])]
			feat_data_clf = feat_data_clf.replace({"Dysarthria Severity": {1: 0, 2: 1}})
		elif model_name.split('_')[-1] == '0-1':
			feat_data_clf = feat_data[feat_data['Dysarthria Severity'].isin([0, 1])]
		elif model_name.split('_')[-1] == 'reg':
			self.reg = True
			feat_data_clf = feat_data
		else:
			raise ValueError(f'{model_name}后半段分组表示错误，仅在0-12、1-2、0-1、reg中选取')
		x_data_clf, y_label = feat_data_clf.drop(columns=['Dysarthria Severity', 'm-FDA score']), \
							  feat_data_clf['Dysarthria Severity']
		x_data_reg = feat_data.dropna(subset=['m-FDA score']).drop(columns=['Dysarthria Severity', 'm-FDA score'])
		y_score = feat_data['m-FDA score'].dropna()
		self.feat_name_list = x_data_reg.columns.tolist()
		x_data_clf, y_label = x_data_clf.to_numpy(), y_label.to_numpy()
		x_data_reg, y_score = x_data_reg.to_numpy(), y_score.to_numpy()
		# 分层抽样，避免数据不平衡问题
		self.rs = random_state
		train_data_clf, test_data_clf, train_label, test_label = \
			train_test_split(x_data_clf, y_label, random_state=self.rs, test_size=0.3, shuffle=shuffle, stratify=y_label)
		train_data_reg, test_data_reg, train_score, test_score = \
			train_test_split(x_data_reg, y_score, random_state=self.rs, test_size=0.3, shuffle=shuffle)
		self.train_label, self.test_label = train_label, test_label
		self.train_score, self.test_score = train_score, test_score
		# 划分数据后再进行数据处理，避免测试集数据泄露
		ss = StandardScaler()  # 标准化特征
		pipe = Pipeline([('ss', ss)])
		self.train_data_clf = pipe.fit_transform(train_data_clf)
		self.test_data_clf = pipe.transform(test_data_clf)
		self.train_data_reg = pipe.fit_transform(train_data_reg)
		self.test_data_reg = pipe.transform(test_data_reg)
		if model_name.split('_')[-1] != '0-12':
			oversample = SVMSMOTE(random_state=self.rs, n_jobs=-1)  # 重采样，弥补不平衡数据（仅对训练集进行）
			self.train_data_clf, self.train_label = oversample.fit_resample(self.train_data_clf, self.train_label)
			# 绘制重采样平衡样本数量之后的各类分布情况
			# from collections import Counter
			# counter = Counter(self.train_label)
			# for k, v in counter.items():
			# 	per = v / len(self.train_label) * 100
			# 	print('Class=%d, n=%d (%.3f%%)' % (k, v, per))
			# plt.bar(counter.keys(), counter.values())
			# plt.show()
		self.model_clf_save_dir = os.path.join('./models', f'clf')  # 保存模型路径
		self.model_reg_save_dir = os.path.join('./models', f'reg')
		self.fig_save_dir = os.path.join('./results/model_results', f'{model_name}')  # 保存结果曲线图片路径
		if not os.path.exists(self.model_clf_save_dir):
			os.makedirs(self.model_clf_save_dir)
		if not os.path.exists(self.model_reg_save_dir):
			os.makedirs(self.model_reg_save_dir)
		if not os.path.exists(self.fig_save_dir):
			os.makedirs(self.fig_save_dir)
		self.model_name = model_name
		self.model_file_clf = os.path.join(self.model_clf_save_dir, f'clf_{self.model_name}.m')
		self.model_file_reg = os.path.join(self.model_reg_save_dir, f'reg_{self.model_name}.m')
		self.perform_comp_f = os.path.join(current_path, "results/PerformanceComparison.xlsx")
		self.wb = load_workbook(self.perform_comp_f)  # 模型性能比较的EXCEL文件
		self.sheet = self.wb['Sheet1']
		self.loo = LeaveOneOut()  # 留一验证
		self.opt_gs = opt_gs

	def model_train(self):
		"""
		模型训练
		"""
		pass

	def model_cv(self, clf_model, cv, n_jobs=-1, fit_params=None, isprint=True):
		"""
		模型交叉验证
		:param clf_model: 分类模型
		:param cv: 交叉验证方式
		:param n_jobs: 并行处理核数，默认-1全部CPU核数
		:param fit_params: fit参数
		:param isprint: 是否打印结果，默认打印
		:return: 评估测试集的ROC曲线下面积roc_auc、敏感性、特异性
		"""
		y_pred_proba = cross_val_predict(clf_model, self.train_data_clf, self.train_label,
										 method='predict_proba', cv=cv, n_jobs=n_jobs, fit_params=fit_params)
		roc_auc = roc_auc_score(self.train_label, y_pred_proba[:, 1])
		y_preds = np.argmax(y_pred_proba, axis=-1)
		sen, spec, sup = sensitivity_specificity_support(self.train_label, y_preds, average='binary')
		if isprint:
			print(f'LOOCV ROC-AUC: {roc_auc:.4f}')
			print(f'LOOCV Sensitivity (Recall): {sen:.4f}')
			print(f'LOOCV Specificity: {spec:.4f}\n')
		return roc_auc, sen, spec

	def model_evaluate(self, fig=False, sen_spe=False):
		"""
		模型评估
		:param fig: 显示测试集的ROC曲线以及预测曲线
		:param sen_spe: ROC曲线标题中是否显示敏感性和特异性值
		:return: 评估测试集的指标
		"""
		index = 0
		for column_cell in self.sheet.iter_rows():  # 遍历行
			index += 1
			if column_cell[0].value == self.model_name:  # 每行的首个为对应的Features
				break
		if not self.reg:
			if os.path.exists(self.model_file_clf):  # 存在已训练模型且设置加载，
				print("----------加载分类模型：{}----------".format(self.model_file_clf))
				clf_model = joblib.load(self.model_file_clf)  # 加载已训练模型
			else:
				print("分类模型不存在，无法评估，请先训练")
				return None
			y_preds = clf_model.predict(self.test_data_clf)
			y_pred_proba = clf_model.predict_proba(self.test_data_clf)
			roc_auc = roc_auc_score(self.test_label, y_pred_proba[:, 1])
			sen, spec, sup = sensitivity_specificity_support(self.test_label, y_preds, average='binary')
			print('Test set ROC-AUC: %.4f\nTest set Sensitivity (Recall): %.4f\nTest set Specificity: %.4f\n' %
				  (roc_auc, sen, spec))
			self.sheet['F' + str(index)] = round(roc_auc, 4)
			self.sheet['G' + str(index)] = round(sen, 4)
			self.sheet['H' + str(index)] = round(spec, 4)
			self.wb.save(self.perform_comp_f)  # 将结果写入模型性能比较的EXCEL文件
			if fig:  # 显示测试集的ROC曲线与混淆矩阵
				if sen_spe:
					title_t = f'ROC Curve of Detecting WD ({self.model_name})\n' \
					          f'Sensitivity = {sen:.2f}, Specificity = {spec:.2f}'
				else:
					title_t = f'ROC Curve of Detecting WD ({self.model_name})'
				plt.figure(figsize=(8, 6), dpi=300, tight_layout=True)
				plt.title(title_t, fontdict={'family': font_family, 'size': 16})
				plt.xlabel('False Positive Rate', fontdict={'family': font_family, 'size': 14})
				plt.ylabel('True Positive Rate', fontdict={'family': font_family, 'size': 14})
				plt.plot([0, 1], [0, 1], c='gray', lw=1.2, ls='--')
				fpr, tpr, thresholds = roc_curve(self.test_label, y_pred_proba[:, 1])
				plt.plot(fpr, tpr, label=f'ROC curve (AUC = {roc_auc:.2f})', c='r', ls='-', lw=2)
				plt.legend(loc="lower right", prop={'family': font_family, 'size': 12})
				plt.xlim(0.0, 1.0)
				plt.ylim(0.0, 1.0)
				for sp in plt.gca().spines:
					plt.gca().spines[sp].set_color('black')
					plt.gca().spines[sp].set_linewidth(1)
				plt.gca().tick_params(direction='out', color='black', length=5, width=1)
				plt.grid(False)
				fig_file = os.path.join(self.fig_save_dir, f"ROC_{self.model_name}.png")
				if not os.path.exists(os.path.dirname(fig_file)):
					os.makedirs(os.path.dirname(fig_file))
				plt.savefig(fig_file, dpi=600, bbox_inches='tight', pad_inches=0.2)
				plt.savefig(fig_file.replace('.png', '.svg'), bbox_inches='tight', pad_inches=0.2)
				plt.show()
				plt.close('all')
				# 绘制混淆矩阵
				plt.figure(figsize=(8, 6), dpi=300, tight_layout=True)
				disp = plot_confusion_matrix(clf_model, self.test_data_clf, self.test_label, normalize='true',
				                             colorbar=False, cmap='Blues')
				disp.ax_.set_title(f'Confusion Matrix of Detecting WD ({self.model_name})',
				                   fontdict={'family': font_family, 'size': 16})
				disp.ax_.set_xlabel('Predicted Class', fontdict={'family': font_family, 'size': 14})
				disp.ax_.set_ylabel('True Class', fontdict={'family': font_family, 'size': 14})
				for sp in plt.gca().spines:
					plt.gca().spines[sp].set_color('black')
					plt.gca().spines[sp].set_linewidth(1)
				plt.gca().tick_params(direction='out', color='black', length=5, width=1)
				plt.grid(False)
				save_fig_path = fig_file.replace(f'ROC_', 'confusion_mat_')
				plt.savefig(save_fig_path, dpi=600, bbox_inches='tight', pad_inches=0.2)
				plt.savefig(save_fig_path.replace('.png', '.svg'), bbox_inches='tight', pad_inches=0.2)
				plt.show()
				plt.close('all')
			return roc_auc, sen, spec
		else:
			if os.path.exists(self.model_file_reg):  # 存在已训练模型且设置加载，
				print("----------加载回归模型：{}----------".format(self.model_file_reg))
				reg_model = joblib.load(self.model_file_reg)  # 加载已训练模型
			else:
				print("回归模型不存在，无法评估，请先训练")
				return None
			y_preds = reg_model.predict(self.test_data_reg)
			mae = mean_absolute_error(self.test_score, y_preds)
			print('Test set MAE: %.4f\n' % mae)
			self.sheet['I' + str(index)] = round(mae, 4)
			self.wb.save(self.perform_comp_f)  # 将结果写入模型性能比较的EXCEL文件
			if fig:  # 显示测试集的预测误差曲线与预测/真实值比较
				plt.figure(figsize=(8, 6), dpi=300, tight_layout=True)
				plt.title(f"Frenchay Predict ({self.model_name})",
						  fontdict={'family': font_family, 'size': 16})
				plt.xlabel('Test set samples', fontdict={'family': font_family, 'size': 14})
				plt.ylabel('m-FDA score', fontdict={'family': font_family, 'size': 14})
				plt.plot(range(1, len(self.test_score) + 1), self.test_score, 'r-', lw=1, marker=".")
				plt.plot(range(1, len(self.test_score) + 1), y_preds, 'b-.', lw=1, marker="x")
				plt.xticks(range(1, len(self.test_score) + 1), fontproperties=font_family, fontsize=12)
				plt.yticks(fontproperties=font_family, fontsize=12)
				plt.xlim(0, len(self.test_score) + 1)
				plt.legend(["True", "Predict"], loc='upper left')
				fig_file = os.path.join(self.fig_save_dir, f"reg_{self.model_name}.png")
				plt.savefig(fig_file, dpi=600, bbox_inches='tight', pad_inches=0.2)
				plt.savefig(fig_file.replace('.png', '.svg'), bbox_inches='tight', pad_inches=0.2)
				plt.show()
				plt.close('all')
				plt.figure(figsize=(8, 6), dpi=300, tight_layout=True)
				# plt.title(f"Frenchay Predict ({self.model_name})",
				# 		  fontdict={'family': font_family, 'size': 16})
				plt.xlabel('True m-FDA score', fontdict={'family': font_family, 'size': 16})
				plt.ylabel('Predicted m-FDA score', fontdict={'family': font_family, 'size': 16})
				plt.plot([0, 150], [0, 150], c='red', lw=1.2, ls='--')
				plt.scatter(self.test_score, y_preds, s=50, c='b')
				plt.xticks(fontproperties=font_family, fontsize=12)
				plt.yticks(fontproperties=font_family, fontsize=12)
				plt.xlim(50, 130)
				plt.ylim(50, 130)
				r_p = stats.spearmanr(self.test_score, y_preds)  # 计算r和p值
				plt.text(53, 120, f'MAE={mae:.2f}\n$r={r_p[0]:.2f}, p={r_p[1]:.3g}$',
						 fontdict={'family': font_family, 'size': 16})
				fig_file = os.path.join(self.fig_save_dir, f"reg_scatter_{self.model_name}.png")
				plt.savefig(fig_file, dpi=600, bbox_inches='tight', pad_inches=0.2)
				plt.savefig(fig_file.replace('.png', '.tif'), dpi=600, bbox_inches='tight', pad_inches=0.2,
				            pil_kwargs={"compression": "tiff_lzw"})
				plt.savefig(fig_file.replace('.png', '.svg'), bbox_inches='tight', pad_inches=0.2)
				plt.show()
				plt.close('all')
			return mae

	def model_explain_reg(self, shap_type='kernel'):
		"""
		回归模型解释（利用SHAP）
		:param shap_type: SHAP类型，基于树的模型采用'tree',其他以及还未支持的模型用'kernel'
		"""
		assert shap_type in ['kernel', 'tree'], \
			'参数model_output输入错误（["kernel", "tree"]）'
		if os.path.exists(self.model_file_reg):  # 存在已训练模型且设置加载，
			print("----------加载回归模型：{}----------".format(self.model_file_reg))
			reg_model = joblib.load(self.model_file_reg)  # 加载已训练模型
		else:
			print("回归模型不存在，无法评估，请先训练")
			return None
		if shap_type == 'tree':
			explainer = TreeShap(reg_model, feature_names=self.feat_name_list, task='regression', seed=self.rs)
			explainer.fit()
			explanation = explainer.explain(self.test_data_reg)
			shap_values = copy.deepcopy(explanation.data['shap_values'])  # 深拷贝防止调用绘图时shap_values改变
		else:
			explainer = KernelShap(reg_model.predict, feature_names=self.feat_name_list, task='regression',
			                       seed=self.rs, distributed_opts={'n_cpus': cpu_count() // 2})  # 并行运行KernelShap
			explainer.fit(self.train_data_reg)
			explanation = explainer.explain(self.test_data_reg)
			shap_values = copy.deepcopy(explanation.data['shap_values'])  # 深拷贝防止调用绘图时shap_values改变
			# 也可以用SHAP包串行运行KernelShap，很慢
			# explainer = shap.KernelExplainer(reg_model.predict, self.train_data_reg,
			#                                  feature_names=self.feat_name_list)
			# shap_values = [copy.deepcopy(explainer.shap_values(self.test_data_reg))]
		# print(type(shap_values), len(shap_values), shap_values[0].shape, self.test_data_reg.shape, shap_values)
		# Global explanation summary plot: explain all the predictions in the test set
		plt.figure(figsize=(8, 6), dpi=300, tight_layout=True)
		shap.summary_plot(shap_values[-1], self.test_data_reg, max_display=10,
		                  feature_names=self.feat_name_list, axis_color='black', show=False, color_bar=False)
		plt.xlabel('SHAP value (impact on model output)', fontdict={'family': font_family, 'size': 14})
		plt.xticks(fontproperties=font_family, size=12)
		plt.yticks(fontproperties=font_family, size=14)
		plt.axvline(x=0, color='black', lw=1.5)
		plt.gca().spines['bottom'].set_color('black')
		plt.gca().tick_params(axis='x', direction='out', color='black', length=5, width=1)
		m = plt.cm.ScalarMappable(cmap=shap.plots.colors.red_blue)
		m.set_array(np.array([0, 1]))
		cb = plt.colorbar(m, ticks=[0, 1], aspect=1000)
		cb.ax.tick_params(labelsize=12, length=0)
		cb.set_ticklabels(['Low', 'High'])
		cb.set_label('Feature value', labelpad=0, fontdict={'family': font_family, 'size': 14})
		cb.outline.set_visible(False)
		bbox = cb.ax.get_window_extent().transformed(plt.gcf().dpi_scale_trans.inverted())
		cb.ax.set_aspect((bbox.height - 0.9) * 20)
		plt.grid(False)
		plt.tight_layout()
		save_fig_path = os.path.join(self.fig_save_dir, f"summary_plot_{self.model_name}.png")
		if not os.path.exists(os.path.dirname(save_fig_path)):
			os.makedirs(os.path.dirname(save_fig_path))
		plt.savefig(save_fig_path, dpi=600, bbox_inches='tight', pad_inches=0.2)
		plt.savefig(save_fig_path.replace('.png', '.tif'), dpi=600, bbox_inches='tight', pad_inches=0.2,
		            pil_kwargs={"compression": "tiff_lzw"})
		plt.savefig(save_fig_path.replace('.png', '.svg'), bbox_inches='tight', pad_inches=0.2)
		plt.show()
		plt.close()

	def model_explain_clf(self, shap_type='kernel'):
		"""
		分类模型解释（利用SHAP）
		:param shap_type: SHAP类型，基于树的模型采用'tree',其他以及还未支持的模型用'kernel'
		"""
		assert shap_type in ['kernel', 'tree'], \
			'参数model_output输入错误（["kernel", "tree"]）'
		if os.path.exists(self.model_file_clf):  # 存在已训练模型且设置加载，
			print("----------加载分类模型：{}----------".format(self.model_file_clf))
			clf_model = joblib.load(self.model_file_clf)  # 加载已训练模型
		else:
			print("分类模型不存在，无法评估，请先训练")
			return None
		if shap_type == 'tree':
			explainer = TreeShap(clf_model, feature_names=self.feat_name_list, seed=rs)
			explainer.fit()
			explanation = explainer.explain(self.test_data_clf)
			shap_values = copy.deepcopy(explanation.data['shap_values'])  # 深拷贝防止调用绘图时shap_values改变
		else:
			explainer = KernelShap(clf_model.predict_proba, feature_names=self.feat_name_list,
			                       seed=rs, distributed_opts={'n_cpus': cpu_count() // 2})  # 并行运行KernelShap
			explainer.fit(self.train_data_clf)
			explanation = explainer.explain(self.test_data_clf)
			shap_values = copy.deepcopy(explanation.data['shap_values'])  # 深拷贝防止调用绘图时shap_values改变
			# 也可以用SHAP包串行运行KernelShap，很慢
			# explainer = shap.KernelExplainer(clf_model.predict_proba, self.train_data_clf,
			#                                  feature_names=self.feat_name_list)
			# shap_values = copy.deepcopy(explainer.shap_values(self.test_data_clf))
		# print(type(shap_values), len(shap_values), shap_values[0].shape, self.test_data_clf.shape, shap_values)
		# 绘制类1的Global explanation summary plot
		plt.figure(figsize=(8, 6), dpi=300, tight_layout=True)
		plt.title(f'Global Explanation for Class 1 ({self.model_name})\n',
		          fontdict={'family': font_family, 'size': 16})
		shap.summary_plot(shap_values[-1], self.test_data_clf, max_display=10,
		                  feature_names=self.feat_name_list, axis_color='black', show=False, color_bar=False)
		plt.xlabel('SHAP value (impact on model output)', fontdict={'family': font_family, 'size': 14})
		plt.xticks(fontproperties=font_family, size=12)
		plt.yticks(fontproperties=font_family, size=14)
		plt.axvline(x=0, color='black', lw=1.5)
		plt.gca().spines['bottom'].set_color('black')
		plt.gca().tick_params(axis='x', direction='out', color='black', length=5, width=1)
		m = plt.cm.ScalarMappable(cmap=shap.plots.colors.red_blue)
		m.set_array(np.array([0, 1]))
		cb = plt.colorbar(m, ticks=[0, 1], aspect=1000)
		cb.ax.tick_params(labelsize=12, length=0)
		cb.set_ticklabels(['Low', 'High'])
		cb.set_label('Feature value', labelpad=0, fontdict={'family': font_family, 'size': 14})
		cb.outline.set_visible(False)
		bbox = cb.ax.get_window_extent().transformed(plt.gcf().dpi_scale_trans.inverted())
		cb.ax.set_aspect((bbox.height - 0.9) * 20)
		plt.grid(False)
		plt.tight_layout()
		save_fig_path = os.path.join(self.fig_save_dir, f"summary_plot_{self.model_name}.png")
		if not os.path.exists(os.path.dirname(save_fig_path)):
			os.makedirs(os.path.dirname(save_fig_path))
		plt.savefig(save_fig_path, dpi=600, bbox_inches='tight', pad_inches=0.2)
		plt.savefig(save_fig_path.replace('.png', '.svg'), bbox_inches='tight', pad_inches=0.2)
		plt.show()
		plt.close()

class RFModel(ModelBase):
	"""同质集成学习Bagging：使用随机森林"""

	def __init__(self, model_name='RF_reg', **kwargs):
		"""
		初始化
		:param **kwargs: SvmModel类__init__参数
		"""
		super().__init__(model_name=model_name, **kwargs)

	def model_train(self):
		"""
		模型训练
		:return: 模型与指标
		"""
		param_grid = {'n_estimators': range(100, 600, 100),  # 决策树的个数
		              'min_samples_split': range(2, 6),  # 分割内部节点所需要的最小样本数量
		              'min_samples_leaf': range(1, 5),  # 需要在叶子结点上的最小样本数量
		              }
		if self.opt_gs:
			if self.model_name.split('_')[-1] == '0-12':
				param_grid = {'min_samples_leaf': [1], 'min_samples_split': [4], 'n_estimators': [300]}
			elif self.model_name.split('_')[-1] == '1-2':
				param_grid = {'min_samples_leaf': [2], 'min_samples_split': [2], 'n_estimators': [300]}
			elif self.model_name.split('_')[-1] == '0-1':
				param_grid = {'min_samples_leaf': [1], 'min_samples_split': [3], 'n_estimators': [400]}
			else:
				param_grid = {'min_samples_leaf': [4], 'min_samples_split': [2], 'n_estimators': [200]}
		index = 0
		for column_cell in self.sheet.iter_rows():  # 遍历行
			index += 1
			if column_cell[0].value == self.model_name:
				break
		if not self.reg:  # 分类
			_model_clf = RandomForestClassifier(n_jobs=1, random_state=self.rs)
			grid_clf = GridSearchCV(_model_clf, param_grid, cv=5, scoring='roc_auc', n_jobs=-1)
			grid_clf.fit(self.train_data_clf, self.train_label)  # 训练模型
			model_clf = grid_clf.best_estimator_
			joblib.dump(model_clf, self.model_file_clf)  # 保存模型
			print("------ The best Classification params -------")
			print(grid_clf.best_params_)
			roc_auc, sen, spec = self.model_cv(model_clf, self.loo, n_jobs=-1)
			self.sheet['B' + str(index)] = f'{roc_auc:.4f}'
			self.sheet['C' + str(index)] = f'{sen:.4f}'
			self.sheet['D' + str(index)] = f'{spec:.4f}'
			self.sheet['J' + str(index)] = str(grid_clf.best_params_)
			self.wb.save(self.perform_comp_f)  # 将结果写入模型性能比较的EXCEL文件
			return model_clf, roc_auc, sen, spec
		else:  # Regression
			_model_reg = RandomForestRegressor(n_jobs=1, random_state=self.rs)
			grid_reg = GridSearchCV(_model_reg, param_grid, cv=5, scoring='neg_mean_absolute_error', n_jobs=-1)
			grid_reg.fit(self.train_data_reg, self.train_score)
			model_reg = grid_reg.best_estimator_  # 最优模型
			joblib.dump(model_reg, self.model_file_reg)  # 保存模型
			print("------ The best Regression params -------")
			print(grid_reg.best_params_)
			mae = get_cv_res(model_reg, self.train_data_reg, self.train_score,
							 "neg_mean_absolute_error", cv=self.loo)
			print(f'LOOCV MAE: {-mae[0]}±{mae[1]}')
			self.sheet['E' + str(index)] = f'{-mae[0]}±{mae[1]}'
			self.sheet['J' + str(index)] = str(grid_reg.best_params_)
			self.wb.save(self.perform_comp_f)  # 将结果写入模型性能比较的EXCEL文件
			return model_reg, -mae[0], mae[1]

	def model_explain(self):
		"""
		模型解释（利用SHAP）
		"""
		if self.model_name.split('_')[-1] == 'reg':
			self.model_explain_reg('tree')
		else:
			self.model_explain_clf('tree')

def get_cv_res(model, features, labels, score, cv=None, n_jobs=-1, fit_params=None):
	"""
	交叉验证并获取评价指标的均值、标准差
	:param model: 模型：最优参数对应的分类或回归
	:param features: 特征
	:param labels: 标签
	:param score: 评价指标
	:param cv: 交叉验证拆分策略，默认10折交叉验证
	:param n_jobs: 并行运行的作业数
	:param fit_params: fit参数
	:return: 评价指标的均值、标准差
	"""
	if cv is None:
		cv = 10
	res = cross_val_score(model, features, labels, cv=cv, scoring=score, n_jobs=n_jobs, fit_params=fit_params)
	return round(res.mean(), 4), round(res.std(), 4)

def roc_plot(feat_data: pd.DataFrame, model_name_list: List[str], grp_name=None):
	"""
	绘制全部模型的ROC曲线至同一张图上
	:param feat_data: 特征数据,pd.DataFrame格式
	:param model_name_list: 分类模型名称列表
    :param grp_name: None或dict类型，{'0':'first', '1':'second', '2':'third'}，将键替换为值
	:return: None
	"""
	color_list = sns.color_palette('muted', len(model_name_list))
	lw_list = ['-', '--', '-.', ':'] * len(model_name_list)
	plt.figure(figsize=(8, 6), dpi=300, tight_layout=True)
	for i_mn in model_name_list:
		model = RFModel(feat_data=feat_data, model_name=i_mn)
		test_data_clf, test_label = model.test_data_clf, model.test_label
		model_file_clf = os.path.join('models/clf', f'clf_{i_mn}.m')
		if os.path.exists(model_file_clf):  # 存在已训练模型且设置加载，
			print("----------加载分类模型：{}----------".format(model_file_clf))
			clf_model = joblib.load(model_file_clf)  # 加载已训练模型
		else:
			print(f"{model_file_clf}分类模型不存在，无法评估，请先训练")
			return None
		y_preds = clf_model.predict(test_data_clf)
		y_pred_proba = clf_model.predict_proba(test_data_clf)
		roc_auc = roc_auc_score(test_label, y_pred_proba[:, 1])
		sen, spec, sup = sensitivity_specificity_support(test_label, y_preds, average='binary')
		# title_t = f'ROC Curve of Detecting WD'
		# plt.title(title_t, fontdict={'family': font_family, 'size': 16})
		fpr, tpr, thresholds = roc_curve(test_label, y_pred_proba[:, 1])
		if isinstance(grp_name, dict):
			comp = grp_name[i_mn.split('_')[-1].split('-')[0]] + ' vs. ' + grp_name[i_mn.split('_')[-1].split('-')[-1]]
		else:
			comp = i_mn.split('_')[-1].replace('-', ' vs. ')
		plt.plot(fpr, tpr, label=f'ROC curve of {comp}\n(AUC = {roc_auc:.2f}, sen = {sen:.2f}, spe = {spec:.2f})',
		         c=color_list[model_name_list.index(i_mn)], ls=lw_list[model_name_list.index(i_mn)], lw=2)
	plt.xlabel('False Positive Rate', fontdict={'family': font_family, 'size': 16})
	plt.ylabel('True Positive Rate', fontdict={'family': font_family, 'size': 16})
	plt.xticks(fontproperties=font_family, fontsize=12)
	plt.yticks(fontproperties=font_family, fontsize=12)
	plt.plot([0, 1], [0, 1], c='gray', lw=1.2, ls='--')
	plt.legend(loc="lower right", prop={'family': font_family, 'size': 14})
	plt.xlim(0.0, 1.0)
	plt.ylim(0.0, 1.0)
	for sp in plt.gca().spines:
		plt.gca().spines[sp].set_color('black')
		plt.gca().spines[sp].set_linewidth(1)
	plt.gca().tick_params(direction='out', color='black', length=5, width=1)
	plt.grid(False)
	fig_file = os.path.join(res_path, "model_results/ROC_all.png")
	if not os.path.exists(os.path.dirname(fig_file)):
		os.makedirs(os.path.dirname(fig_file))
	plt.savefig(fig_file, dpi=600, bbox_inches='tight', pad_inches=0.2)
	plt.savefig(fig_file.replace('.png', '.tif'), dpi=600, bbox_inches='tight', pad_inches=0.2,
	            pil_kwargs={"compression": "tiff_lzw"})
	plt.savefig(fig_file.replace('.png', '.svg'), bbox_inches='tight', pad_inches=0.2)
	plt.show()

if __name__ == "__main__":
	start_time = datetime.datetime.now()
	print("---------- Running %s ----------" % os.path.basename(__file__))
	print("---------- Start Time: %s ----------" % start_time.strftime("%Y-%m-%d %H:%M:%S"))
	current_path = os.getcwd()
	feat_file = os.path.join(current_path, "data/features_used_model.csv")
	res_path = os.path.join(current_path, r"results")
	feat_data_all = pd.read_csv(feat_file).drop(columns=['SubjectID', 'Age', 'Gender', 'Edu', 'Group'])
	feat_data_all.columns = feat_data_all.columns.str.replace(r"^.*-|\(.*$", '', regex=True)  # 删除特征名中-之前字符和(之后字符
	feat_data_all.rename(columns={'Frenchay Score': 'm-FDA score'}, inplace=True)
	feat_data_all.fillna(feat_data_all.groupby('Dysarthria Severity').transform('mean'), inplace=True)
	model_l = ['RF_0-12', 'RF_0-1', 'RF_1-2', 'RF_reg', ]
	for task in model_l:
		print(f"------- {task} model -------\n")
		_model = RFModel(feat_data=feat_data_all, model_name=f'{task}', opt_gs=True)
		_model.model_train()
		_model.model_evaluate(fig=True)
		_model.model_explain()
	roc_plot(feat_data_all, model_l[:-1],
	         grp_name={'0': 'healthy', '1': 'mild', '2': 'moderate-severe', '12': 'dysarthria'})

	end_time = datetime.datetime.now()
	print("---------- End Time: %s ----------" % end_time.strftime("%Y-%m-%d %H:%M:%S"))
	print("---------- Time Used: %s ----------" % (end_time - start_time))
	with open(os.path.join(current_path, "results/finished.txt"), "w") as ff:
		ff.write("------------------ Started at %s -------------------\r\n" % start_time.strftime("%Y-%m-%d %H:%M:%S"))
		ff.write("------------------ Finished at %s -------------------\r\n" % end_time.strftime("%Y-%m-%d %H:%M:%S"))
		ff.write("------------------ Time Used %s -------------------\r\n" % (end_time - start_time))


#!/usr/bin/env python
"""
The 'templates' directory contains two Jinja2 templates for rendering the
graph:

* `index.html` - the skeleton which only loads the CSS files, and then includes
  the output of the second template:
* `graph.html` - this is the template which actually renders a graph.

This module is responsible for preparing and rendering the templates.
"""

from collections import namedtuple, defaultdict
import datetime
from flask import render_template
from WateringSite.contributionchart import dateutils
from WateringSite.contributionchart import parser
from WateringSite.contributionchart import statistics
from WateringSite.contributionchart import bp
GridCell = namedtuple('GridCell', ['date', 'contributions'])

def create_graph(devices, eventsdict):
    """
    Prepare the `index.html` template.
    """
    graphs = []
    contributions = defaultdict(int)
    for Device in devices:
        contributions = parser.parse_events(Device.watering_events)

        graph = {"data": gridify_contributions(contributions), "cell_class": _cell_class(contributions.values()),
                 "longest_streak": statistics.longest_streak(
                     [key for key, val in contributions.items() if val > 0]
                 ), "current_streak": statistics.current_streak(
                [key for key, val in contributions.items() if val > 0]
            ), "sum": sum(contributions.values()), "device_name": Device.device_name, "last_date": (
                    [""] + sorted([key for key, v in contributions.items() if v])
            )[-1]}

        graphs.append(graph)

    weekdays = dateutils.weekday_initials()
    for idx in [0, 2, 4, 6]:
        weekdays[idx] = ""

    months = [
        cell.date.strftime("%b")
        for cell in gridify_contributions(contributions)[0]
    ]
    months = filter_months(months)

    return render_template('index.html', graphs=graphs, devices=devices,
                           today=dateutils.today(),
                           start=dateutils.start(),
                           weekdays=weekdays,
                           months=months, events=eventsdict)

def gridify_contributions(contributions):
    """
    The contributions graph has seven rows (one for each day of the week).
    It spans a year. Given a dict of date/value pairs, rearrange these results
    into seven rows of "cells", where each cell records a date and a value.
    """
    start = dateutils.start()
    today = dateutils.today()

    graph_entries = []

    # The first row is a Sunday, so go back to the last Sunday before the start
    if start.weekday() == 6:
        first_date = start
    else:
        first_date = start - datetime.timedelta(start.weekday() + 1 % 7)
    next_date = first_date

    first_row_dates = [first_date]
    while (next_date <= today) and (next_date + datetime.timedelta(7) <= today):
        next_date += datetime.timedelta(7)
        first_row_dates.append(next_date)

    # Now get contribution counts for each of these dates, and save the row
    first_row = [
        GridCell(date, contributions[date]) for date in first_row_dates
    ]
    graph_entries.append(first_row)

    # For each subsequent day of the week, use the first row as a model: add
    # the appropriate number of days and count the contributions
    for i in range(1, 7):
        row_dates = [day + datetime.timedelta(i) for day in first_row_dates]
        next_row = [
            GridCell(date, contributions[date]) for date in row_dates
        ]
        graph_entries.append(next_row)

    return graph_entries

@bp.app_template_filter('tooltip')
def tooltip_text(cell):
    """
    Returns the tooltip text for a cell.
    """
    if cell.contributions == 0:
        count = "No waterings"
    elif cell.contributions == 1:
        count = "1 watering"
    else:
        count = "%d waterings" % cell.contributions
    date_str = dateutils.display_date(cell.date)
    return "%s on %s" % (count, date_str)

def _cell_class(values):
    """
    Returns a function which determines how a cell is highlighted.
    """
    quartiles = statistics.quartiles(values)

    def class_label(cell):
        if cell.date > dateutils.today() or cell.date < dateutils.start():
            return "empty"
        elif cell.contributions == 0:
            return "grad0"
        elif cell.contributions <= quartiles[1]:
            return "grad1"
        elif cell.contributions <= quartiles[2]:
            return "grad2"
        elif cell.contributions <= quartiles[3]:
            return "grad3"
        else:
            return "grad4"
    return class_label

def filter_months(months):
    """
    We only want to print each month heading once, over the first column
    which contains days only from that month. This function filters a list of
    months so that only the first unique month heading is shown.
    """
    for idx in reversed(range(len(months))):
        if months[idx] == months[idx - 1]:
            months[idx] = ""

    # If the same month heading appears at the beginning and end of the year,
    # then only show it at the end of the year
    if months.count(months[0]) > 1:
        months[0] = ""
    if months.count(months[-1]) > 1:
        months[-1] = ""

    # Since each month takes up cells, we delete an empty space for each month
    # heading
    indices = [idx for idx, month in enumerate(months) if month]
    for idx in reversed(indices):
        if idx != len(months) - 1:
            del months[idx+1]

    return months


import os
import sys
import argparse
from torch import nn
from fedlab.core.network import DistNetwork
from fedlab.contrib.algorithm.basic_server import AsyncServerHandler
from fedlab.core.server.manager import AsynchronousServerManager
from fedlab.models import MLP
import torch
from torchvision import datasets, models, transforms
import torchvision
import torchvision.transforms as transforms
import random
import torch
from torchvision import datasets, models, transforms
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader, random_split

def load_dataset():
    # Suas duas strings
    string1 = "biglycan"
    string2 = "breakhis"

    # Escolha aleatoriamente entre as duas strings
    name = random.choice([string1, string2])

    # Caminho para o diretório do conjunto de dados
    data_dir = "./dataset/"+str(name)

    # Transformações de pré-processamento que você deseja aplicar às imagens
    transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor(),  # Converte a imagem para tensor
        transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])  # Normaliza os valores dos pixels
    ])

    # Carregar o conjunto de dados
    dataset = datasets.ImageFolder(root=data_dir, transform=transform)

    # Dividir o conjunto de dados em treinamento e validação
    train_size = int(0.8 * len(dataset))
    val_size = len(dataset) - train_size
    train_dataset, val_dataset = random_split(dataset, [train_size, val_size])

    # Criar DataLoader para treinamento e validação
    batch_size = 32
    train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
    val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=True)
    print("Train Loader: "+str(type(train_loader)))
    return train_loader, val_loader

def evaluate_model(val_loader, model):
    with torch.no_grad():
        model.eval()

        for inputs, labels in val_loader:
            outputs = model(inputs)
            _, predicted = torch.max(outputs, 1)  # Supondo classificação binária
            total += labels.size(0)
            correct += (predicted == labels).sum().item()

    accuracy = correct / total
    print(f'Acurácia no conjunto de validação: {100 * accuracy:.2f}%')

def select_model(name):
    # ShuffleNet
    if name == 'ShuffleNet':
        model_ft = models.shufflenet_v2_x1_0(pretrained=True)
        # Congele todos os parâmetros do modelo (ou deixe-os descongelados, dependendo de sua escolha)
        for param in model_ft.parameters():
            param.requires_grad = True  # Defina como False para congelar
        num_ftrs = model_ft.fc.in_features
        model_ft.fc = nn.Linear(num_ftrs, 2)
        input_size = 224
        return model_ft

    elif name == 'AlexNet':
        ###AlexNet###
        feature_extract = True
        model_ft = models.alexnet(pretrained=True)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.classifier[6].in_features
        model_ft.classifier[6] = nn.Linear(num_ftrs, 4)
        input_size = 224
        return model_ft

    elif name == 'Resnet18':
        ###Resnet18###
        feature_extract = True
        model_ft = models.resnet18(pretrained=True)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.fc.in_features
        model_ft.fc = nn.Linear(num_ftrs, 4)
        input_size = 224

    elif name == 'SqueezeNet':
        ###SqueezeNet###
        model_ft = models.squeezenet1_0(pretrained=True)
        set_parameter_requires_grad(model_ft, True)
        model_ft.classifier[1] = nn.Conv2d(512, 4, kernel_size=(1,1), stride=(1,1))
        model_ft.num_classes = 4
        input_size = 224
        return model_ft

    elif name == 'VGG11_b':
        ###VGG11_b###
        use_pretrained = True
        feature_extract = True
        model_ft = models.vgg11_bn(pretrained=use_pretrained)
        set_parameter_requires_grad(model_ft, feature_extract)
        num_ftrs = model_ft.classifier[6].in_features
        model_ft.classifier[6] = nn.Linear(num_ftrs, 4)
        input_size = 224
        return model_ft

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='Distbelief training example')

    parser.add_argument('--ip', type=str, default='127.0.0.1')
    parser.add_argument('--port', type=str, default='3002')
    parser.add_argument('--world_size', type=int)
    args = parser.parse_args()

    #model = MLP(784, 10)
    model = select_model('ShuffleNet')

    handler = AsyncServerHandler(model, global_round=5)
    handler.setup_optim(0.5)

    network = DistNetwork(address=(args.ip, args.port),
                          world_size=args.world_size,
                          rank=0)
    Manager = AsynchronousServerManager(handler=handler, network=network)

    Manager.run()

    train_loader, val_loader = load_dataset()
    evaluate_model(val_loader, model)


from __future__ import annotations

import asyncio

import aiohttp
from cisco_opentelemetry_specifications import SemanticAttributes
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.test.test_base import TestBase

from cisco_telescope.configuration import Configuration
from instrumentation.telescope_instrumentation_aiohttp import AiohttpInstrumentorWrapper
from instrumentation.telescope_instrumentation_utils.base_http_tests_util import (
    BaseHttpTest,
)

class TestAiohttpWrapper(BaseHttpTest, TestBase):
    def setUp(self):
        super().setUp()
        AiohttpInstrumentorWrapper().instrument()

    def tearDown(self):
        super().tearDown()
        AiohttpInstrumentorWrapper().uninstrument()
        Configuration().reset_to_default()

    async def test_get_request_sanity(self):
        async with aiohttp.client.request(
            method="GET",
            url=self.http_url_sanity,
            headers=self.request_headers(),
            chunked=True,
        ) as resp:
            self._assert_basic_attributes_and_headers(resp)

    async def test_post_request_sanity(self):
        Configuration().payloads_enabled = True
        async with aiohttp.client.request(
            method="POST",
            url=self.http_url_sanity,
            headers=self.request_headers(),
            chunked=True,
            data=self.request_body(),
        ) as resp:
            self._assert_basic_attributes_and_headers(resp)
            spans = self.memory_exporter.get_finished_spans()
            span = spans[0]

            self.assertEqual(
                span.attributes[SemanticAttributes.HTTP_REQUEST_BODY],
                self.request_body(),
            )
            self.assertEqual(
                span.attributes[SemanticAttributes.HTTP_RESPONSE_BODY],
                self.response_body(),
            )

    async def test_post_request_invalid_utf8(self):
        Configuration().payloads_enabled = True
        with self.assertLogs() as logs_written:
            data_to_send = b'/">Miso\xdfNoContinuation'
            async with aiohttp.client.request(
                method="POST",
                url=self.http_url_sanity,
                headers=self.request_headers(),
                chunked=True,
                data=data_to_send,
            ) as resp:
                self._assert_basic_attributes_and_headers(resp)
                spans = self.memory_exporter.get_finished_spans()
                span = spans[0]

                self.assertEqual(
                    span.attributes[SemanticAttributes.HTTP_REQUEST_BODY],
                    str(data_to_send),
                )
                self.assertEqual(
                    span.attributes[SemanticAttributes.HTTP_RESPONSE_BODY],
                    self.response_body(),
                )
                self.assertEqual(len(logs_written.records), 1)

    async def test_get_request_error(self):
        Configuration().payloads_enabled = True
        async with aiohttp.client.request(
            method="GET", url=self.http_url_error, headers=self.request_headers()
        ) as resp:
            self.assertEqual(resp.status, 404)
            spans = self.memory_exporter.get_finished_spans()
            self.assertEqual(len(spans), 1)
            request_span = spans[0]
            self.assert_captured_headers(
                request_span,
                SemanticAttributes.HTTP_REQUEST_HEADER,
                self.request_headers(),
            )

    async def test_post_request_error(self):
        Configuration().payloads_enabled = True
        async with aiohttp.client.request(
            method="POST", url=self.http_url_error, headers=self.request_headers()
        ) as resp:
            self.assertEqual(resp.status, 404)
            spans = self.memory_exporter.get_finished_spans()
            self.assertEqual(len(spans), 1)
            request_span = spans[0]
            self.assert_captured_headers(
                request_span,
                SemanticAttributes.HTTP_REQUEST_HEADER,
                self.request_headers(),
            )

    async def test_post_request_payloads_not_enabled(self):
        Configuration().payloads_enabled = False
        async with aiohttp.client.request(
            method="POST",
            url=self.http_url_sanity,
            headers=self.request_headers(),
            data=self.request_body(),
        ) as resp:
            self.assertEqual(resp.status, 200)
            spans = self.memory_exporter.get_finished_spans()
            self.assertEqual(len(spans), 1)
            request_span = spans[0]
            self.assertNotIn(
                f"{SemanticAttributes.HTTP_REQUEST_HEADER}.test-header-key",
                request_span.attributes,
            )
            self.assertNotIn(
                f"{SemanticAttributes.HTTP_RESPONSE_HEADER}.server-response-header",
                request_span.attributes,
            )

    async def test_response_content_unharmed(self):
        Configuration().payloads_enabled = True
        async with aiohttp.client.request(
            method="POST",
            url=self.http_url_sanity,
            headers=self.request_headers(),
            chunked=True,
            data=self.request_body(),
        ) as resp:
            self.assertEqual(resp.status, 200)
            spans = self.memory_exporter.get_finished_spans()
            self.assertEqual(len(spans), 1)
            span = spans[0]
            self.assertEqual(hasattr(resp, "content"), True)
            self.assertIsNotNone(resp.content)
            resp_body = ""
            while not resp.content.at_eof():
                response_chunk = b""
                response_chunk = await asyncio.wait_for(resp.content.read(), 0.1)
                resp_body += response_chunk.decode()
            self.assertEqual(self.response_body(), resp_body)
            self.assertEqual(
                span.attributes[SemanticAttributes.HTTP_RESPONSE_BODY],
                resp_body,
            )

    def _assert_basic_attributes_and_headers(self, resp):
        self.assertEqual(resp.status, 200)
        spans = self.memory_exporter.get_finished_spans()
        self.assertEqual(len(spans), 1)
        span = spans[0]
        self.assertEqual(span.attributes[SpanAttributes.HTTP_METHOD], resp.method)
        self.assertEqual(span.attributes[SpanAttributes.HTTP_URL], str(resp.url))
        self.assert_captured_headers(
            span,
            SemanticAttributes.HTTP_REQUEST_HEADER,
            self.request_headers(),
        )
        self.assert_captured_headers(
            span,
            SemanticAttributes.HTTP_RESPONSE_HEADER,
            self.response_headers(),
        )


import spacy
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker

# Load the spaCy model
nlp = spacy.load("de_core_news_sm")

# Read the CSV file
df = pd.read_csv("C:/Users/ASUS/OneDrive/Desktop/articleInfo/image_data.csv", encoding="utf8")

# Perform named entity recognition on the "image_titels" column
entities = []
for text in df["image_titels"].astype(str):
    doc = nlp(text)
    for ent in doc.ents:
        entities.append(ent.text)

# Get the top 10 entities
top_entities = pd.Series(entities).value_counts().nlargest(10)

# Create a bar chart of the top 10 entities
fig, ax = plt.subplots()
ax.bar(top_entities.index, top_entities.values)
ax.set_xlabel("Entities")
ax.set_ylabel("Frequency")
ax.set_title("Top 10 Entities in Image Titles")

# Rotate the x-axis labels
plt.xticks(rotation=45, ha="right")

# Adjust the y-axis to show only whole numbers
ax.yaxis.set_major_locator(ticker.MaxNLocator(integer=True))

# Save the chart as a PNG file
plt.savefig("C:/Users/ASUS/OneDrive/Desktop/articleInfo/entity_chart.png", bbox_inches="tight")


from dataclasses import dataclass
from io import TextIOWrapper
import json
from models import Element, GameElementState

@dataclass
class RapidReactGameElementState(GameElementState):
    '''Represents the current state of the rapid react game'''
    red_cargo: list[Element]
    blue_cargo: list[Element]
    misc: list[Element]

    @staticmethod
    def read(file: TextIOWrapper) -> 'RapidReactGameElementState':
        '''Returns the current state of the game'''
        raw = file.read()
        raw = raw.strip()
        raw = json.loads(raw)
        elements: list[Element] = []
        for raw_object in raw['objects']:
            elements.append(Element.from_json(raw_object))
        red_cargo: list[Element] = []
        blue_cargo: list[Element] = []
        misc: list[Element] = []
        for element in elements:
            if element.element_type is None:
                misc.append(element)
            elif 'Ball_Red' in element.name:
                red_cargo.append(element)
            elif 'Ball_Blue' in element.name:
                blue_cargo.append(element)
            else:
                misc.append(element)
        red_cargo.sort(key=lambda i: i.identifier)
        blue_cargo.sort(key=lambda i: i.identifier)
        return RapidReactGameElementState(red_cargo, blue_cargo, misc)

    def __str__(self) -> str:
        return f"{[str(item) for item in self.red_cargo]}\n" \
            f"{[str(item) for item in self.blue_cargo]}\n" \
            f"{[str(item) for item in self.misc]}"


from django.conf import settings
from utils import restful
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
import os

@csrf_exempt  # 防止post时引发403错误
def upload_view(request):
    if request.method == 'POST':
        f = request.FILES.get('file')
        name = f.name
        path = os.path.join(settings.MEDIA_ROOT, name)

        with open(path, 'wb') as fp:
            for chunk in f.chunks():
                fp.write(chunk)
        return JsonResponse({
            'location': settings.MEDIA_URL + name
        })
    else:
        return restful.methoderror('请求方式错误！')


#!/usr/bin/env python
# -*- coding: utf-8 -*-

from collections import Counter

class Solution:
    def checkInclusion(self, s1: str, s2: str) -> bool:
        if len(s1) > len(s2):
            return False
        s1_counter = Counter(s1)
        s2_counter = Counter(s2[:len(s1)])
        if s1_counter == s2_counter:
            return True
        for i in range(len(s2) - len(s1)):
            s2_counter[s2[i]] -= 1
            if s2_counter[s2[i]] == 0:
                del s2_counter[s2[i]]
            s2_counter[s2[i + len(s1)]] += 1
            if s1_counter == s2_counter:
                return True
        return False

def test_checkInclusion():
    s = Solution()
    assert s.checkInclusion("ab", "eidbaooo") == True
    assert s.checkInclusion("ab", "eidboaoo") == False
    assert s.checkInclusion("adc", "dcda") == True
    assert s.checkInclusion("abcd", "abcdabcdabcdabcdabcdabcdabcdabc") == True
    assert s.checkInclusion("hello", "ooolleoooleh") == False
    print("========================== Test Passed ==========================")

if __name__ == '__main__':
    test_checkInclusion()


import torch
import numpy as np
import gudhi as gd
from gudhi.weighted_rips_complex import WeightedRipsComplex
import seaborn as sns
import pandas as pd
from scipy.spatial.distance import cdist
from scipy.optimize import linear_sum_assignment
import ot

def PI_value(grid, points, h=1, h_list=None, PI_weight="linear", density="gaussian"):
    if type(grid).__module__ != "torch":
        grid = torch.tensor(grid).to(torch.float32)
    ret = torch.zeros_like(grid[:, :, 0])

    if density == "gaussian":
        # for p, q in _points:
        for p, q in points:
            dist = torch.tensor([[torch.linalg.norm(grid[i, j, :] - torch.tensor([p, q])) for j in range(grid.shape[1])] for i in range(grid.shape[0])])
            if PI_weight == "linear":
                ret += (1 / (h**2)) * q * torch.exp(- (dist**2 / (2*(h**2))))
            elif PI_weight == "quad":
                ret += (1 / (h**2)) * (q ** 2)* torch.exp(- (dist**2 / (2*(h**2))))
            elif PI_weight == "tanh":
                ret += (1 / (h**2)) * torch.tanh(q) * torch.exp(- (dist**2 / (2*(h**2))))
    elif density == "cone":
        for p, q in points:
            dist = torch.tensor([[torch.linalg.norm(grid[i, j, :] - torch.tensor([p, q])) for j in range(grid.shape[1])] for i in range(grid.shape[0])])
            if PI_weight == "linear":
                ret += (6 / (np.pi * (h**2))) * q * torch.fmin(h - dist, torch.zeros_like(dist))
            elif PI_weight == "quad":
                ret += (6 / (np.pi * (h**2))) * (q ** 2)* torch.fmin(h - dist, torch.zeros_like(dist))
            elif PI_weight == "tanh":
                ret += (6 / (np.pi * (h**2))) * torch.tanh(q) * torch.fmin(h - dist, torch.zeros_like(dist))
    else:
        raise NotImplementedError
    return ret

def MLP_solver(heatmap_tensors, **kwargs):
    value = heatmap_tensors.reshape(heatmap_tensors.shape[0],heatmap_tensors.shape[1]*heatmap_tensors.shape[2])
    return MLP_network(value, **kwargs)

def MLP_network(value, weight, bias, activation=torch.tanh, task="cls", last_linear=False):
    for i in range(len(weight)-1):
        value = activation(torch.einsum("bi,ik->bk", value, weight[i]) + bias[i])
    if last_linear:
        value = torch.einsum("bi,ik->bk", value, weight[-1]) + bias[-1]
    else:
        value = activation(torch.einsum("bi,ik->bk", value, weight[-1]) + bias[-1])

    if "cls" in task:
        m = torch.nn.Softmax(dim=1)
        value = m(value)
    return value

def tensor_list_detach(tensor_list):
    return [x.detach() for x in tensor_list]

def get_simplex_tree(X, delay=None, base_filt="rips", input_format="coord", max_dim=2):
    if delay is None:
        delay = torch.zeros_like(X[:, 0])

    if input_format == "coord":
        dist = torch.cdist(X, X)
    elif input_format == "dist":
        dist = X.clone()
    else:
        raise NotImplementedError

    if base_filt == "rips":
        dist = dist.detach()
        dist += torch.stack([delay for _ in range(X.shape[0])], dim=0)
        dist += torch.stack([delay for _ in range(X.shape[0])], dim=1)
        filt = gd.RipsComplex(
            distance_matrix = dist,
            max_edge_length = np.inf
        )
    elif base_filt == "weighted":
        filt = WeightedRipsComplex(
            distance_matrix = dist,
            weights = delay
        )
    else:
        raise NotImplementedError

    return filt.create_simplex_tree(max_dimension=max_dim)

def get_one_heatmap_tensor(X, delay=None, base_filt="rips", input_format="coord", PI_weight="linear", pers_pair=None, h=1, pi_range_x=2.0, pi_range_y=2.0, grid_num_x=10, grid_num_y=10):
    if type(X).__module__ != "torch":
        X = torch.tensor(X).to(torch.float32)
    if (delay is not None) and (type(delay).__module__ != "torch"):
        delay = torch.tensor(delay).to(torch.float32)
    if delay is None:
        delay = torch.zeros_like(X[:, 0])

    if pers_pair is None:
        simplex_tree = get_simplex_tree(X, delay, base_filt=base_filt, input_format=input_format, max_dim=2)
        barcode = simplex_tree.persistence()
        pers_pair = simplex_tree.persistence_pairs()

    points = []
    if input_format == "coord":
        dist = torch.cdist(X, X)
    elif input_format == "dist":
        dist = X.clone()
    else:
        raise NotImplementedError
    dist += torch.stack([delay for _ in range(X.shape[0])], dim=0)
    dist += torch.stack([delay for _ in range(X.shape[0])], dim=1)
    for x in pers_pair:
        if len(x[0]) == 2:
            points.append(
                (
                    dist[x[0][0], x[0][1]],
                    torch.max(dist[x[1][0], x[1][1]], torch.max(dist[x[1][1], x[1][2]], dist[x[1][2], x[1][0]])) - dist[x[0][0], x[0][1]]
                )
            )
        elif len(x[0]) == 3:
            raise NotImplementedError

    grid = torch.tensor([[[x, y] for y in list(np.linspace(0, pi_range_y, grid_num_y))] for x in list(np.linspace(0, pi_range_x, grid_num_x))]).to(torch.float32)
    return PI_value(grid, points, h=0.5)

def get_heatmap_tensors(X, delay=None, base_filt="rips", input_format="coord", PI_weight="linear", h=1, pi_range_x=2.0, pi_range_y=2.0, grid_num_x=10, grid_num_y=10):
    if delay is None:
        delay = torch.zeros_like(X[:, :, 0])

    pers_pair_list = [None for _ in range(X.shape[0])]

    heatmap_tensor_list = []
    for i in range(X.shape[0]):
        heatmap_tensor = get_one_heatmap_tensor(
            X=X[i, :, :], delay=delay[i, :],
            base_filt=base_filt, input_format=input_format, PI_weight=PI_weight,
            pers_pair=pers_pair_list[i],
            h=h, pi_range_x=pi_range_x, pi_range_y=pi_range_y, grid_num_x=grid_num_x, grid_num_y=grid_num_y
        )
        heatmap_tensor_list.append(heatmap_tensor)

    return torch.stack(heatmap_tensor_list, dim=0)

def deepsets(X, weight1, weight2, bias1, bias2, activation=torch.tanh, ds_op="sum"):
    """
    -----------
    ## parameters

    X: input tensor with shape (# data, # points, dim)
    ds_op: "sum" or "max" or "concat"

    ## output
    tensor with shape (# data, feature vector dim)
    """
    value = X.clone()
    for i in range(len(weight1)):
        value = activation(torch.einsum("bij,jk->bik", value, weight1[i]) + bias1[i])
    if ds_op == "max":
        value = torch.max(value, axis=1).values
    elif ds_op == "sum":
        value = torch.sum(value, axis=1)
    elif ds_op == "concat":
        value = torch.cat([torch.max(value, axis=1).values, torch.sum(value, axis=1)], dim=1)
    else:
        raise NotImplementedError
    for i in range(len(weight2)):
        value = activation(torch.einsum("bj,jk->bk", value, weight2[i]) + bias2[i])
    return value

def MLP_layers_init(neuron_num_list, loc=0, bias=0.1):
    weight_list = []
    bias_list = []
    for i in range(len(neuron_num_list)-1):
        weight_list.append(torch.normal(loc, bias, size=(neuron_num_list[i], neuron_num_list[i+1]), requires_grad=True))
        bias_list.append(torch.normal(loc, bias, size=(neuron_num_list[i+1], ), requires_grad=True))
    return weight_list, bias_list

def draw_heatmap(heatmap_array, ax, vmin=0.0, vmax=2.0):
    if type(heatmap_array).__module__ == "torch":
        heatmap_array = heatmap_array.detach().numpy()

    sns.heatmap(
        pd.DataFrame(heatmap_array.T).iloc[::-1],
        cmap="jet",
        xticklabels=True,
        yticklabels=True,
        vmin = vmin,
        vmax = vmax,
        ax = ax
    )

def wasserstein_loss(x1, x2):
    """
    x1, x2: torch.tensor  (# data, # points, dim)
    """
    dist = torch.cdist(x1, x2)
    ret_list = []
    for i in range(x1.shape[0]):
        a = np.ones(x1.shape[1]) / x1.shape[1]
        b = np.ones(x2.shape[1]) / x2.shape[1]
        P = torch.tensor(ot.emd(a, b, dist[i, :, :].detach().numpy())).to(torch.float32)
        ret_list.append(torch.einsum("ij,ij", P, dist[i, :, :]))
    return sum(ret_list) / len(ret_list)

def random_rotation(X):
    """
    X: torch.tensor  (# data, # points, dim)
    """
    # creating a list of rotation matrix
    theta = 2 * np.pi * np.random.rand(X.shape[0])
    if X.shape[2] == 3:
        rot_mat_list = [
            torch.tensor([
                [np.cos(theta[k]), -np.sin(theta[k]), 0.],
                [np.sin(theta[k]),  np.cos(theta[k]), 0.],
                [0., 0., 1.]
            ]).to(torch.float32) for k in range(X.shape[0])
        ]
    else:
        rot_mat_list = [
            torch.tensor([
                [np.cos(theta[k]), -np.sin(theta[k])],
                [np.sin(theta[k]),  np.cos(theta[k])],
            ]).to(torch.float32) for k in range(X.shape[0])
        ]
    rotated_X = torch.stack([torch.einsum("ij,kj->ki", A, X[l, :, :]) for l, A in enumerate(rot_mat_list)], dim=0)
    return rotated_X

class DeepSets:
    def __init__(self, input_dim, output_dim, init_params=None):
        if init_params is None:
            self.ds_weight1, self.ds_bias1 = MLP_layers_init([input_dim, 128, 128, 128, 64])
            self.ds_weight2, self.ds_bias2 = MLP_layers_init([64, 128, 128, 128, output_dim])
        else:
            if len(init_params[4].shape) == 1:
                self.ds_weight1, self.ds_bias1 = init_params[:4], init_params[4:8]
                self.ds_weight2, self.ds_bias2 = init_params[8:12], init_params[12:16]
            else:
                self.ds_weight1, self.ds_bias1 = init_params[:4], init_params[8:12]
                self.ds_weight2, self.ds_bias2 = init_params[4:8], init_params[12:16]

    def output(self, X):
        return deepsets(X, self.ds_weight1, self.ds_weight2, self.ds_bias1, self.ds_bias2, activation=torch.relu)

class MultiLayerPerceptron:
    def __init__(self, width_list, activation=torch.tanh, task="cls", last_linear=False, loc=0.0, bias=0.1, weight_init=None, bias_init=None):
        if (weight_init is not None) and (bias_init is not None):
            self.weight, self.bias = weight_init, bias_init
        else:
            self.weight, self.bias = MLP_layers_init(width_list, loc=loc, bias=bias)
        self.activation = activation
        self.task = task
        self.last_linear = last_linear

    def output(self, value):
        return MLP_network(value, weight=self.weight, bias=self.bias, activation=self.activation, task=self.task, last_linear=self.last_linear)


import numpy as np
from django.db.models import Count
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import AllowAny
from rrhh.models import MonedaLink, EntregaMoneda, CanjeMonedas
from users.serializers import linkcoins_serializers

class LinkcoinsViewset(viewsets.GenericViewSet):
    queryset = MonedaLink.objects.all()
    permission_classes = (AllowAny,)

    @action(detail=True, methods=["GET"])
    def reporte(self, request, pk=None):
        quey_canje = CanjeMonedas.objects.filter(usuario__identificacion=pk)
        monedas_generadas = MonedaLink.objects.filter(usuario_portador__identificacion=pk).count()
        monedas_recibidas = EntregaMoneda.objects.filter(usuario_recibe__identificacion=pk).count()
        monedas_canjeadas = sum(np.array(quey_canje.values_list('monedas', flat=True)))
        canjes_pendientes = quey_canje.filter(entregado='NO').count()
        ultimo_canje = quey_canje.order_by('-fecha').first()
        ultimo_canje = ultimo_canje.premio if ultimo_canje else ""

        response = {
            'monedas_generadas' : monedas_generadas,
            'monedas_recibidas' : monedas_recibidas,
            'monedas_canjeadas' : monedas_canjeadas,
            'canjes_pendientes' : canjes_pendientes,
            'ultimo_canje' : ultimo_canje,
        }

        return Response(response, status=status.HTTP_200_OK)

    @action(detail=True, methods=["GET"])
    def reporte_entrega(self, request, pk=None):
        quey_canje = EntregaMoneda.objects.filter(moneda__usuario_portador__identificacion=pk).annotate(cantidad=Count('mensaje')).order_by('-fecha')
        serializer = linkcoins_serializers.EntregasSerializer(quey_canje, many=True)
        response = {'entrega':serializer.data}
        return Response(response, status=status.HTTP_200_OK)


# -*- coding: utf-8 -*-
"""
Created on Sun Aug 28 18:05:18 2022

@author: timve
"""

import numpy as np
from datetime import datetime
import pathlib
import matplotlib.pyplot as plt

def load(filename):
        data = np.load(str(pathlib.Path().resolve()) + '/data/' + str(filename) + '.npz')
        return data

n = 7
data_kin = []
data_tel = []

for i in range(1,n+1):
    try:
        data_kin.append(load(f"dual_{i}_kin_dem"))
    except:
        data_kin.append(0)

    try:
        data_tel.append(load(f"dual_{i}_tel_dem"))
    except:
        data_tel.append(0)

plt.figure(0)
print('d')
for i ,d in enumerate(data_kin):
    plt.plot(d['recorded_traj_dual'][0], label=i)
    print(len(d['recorded_traj_dual'][0]))
plt.legend()

plt.figure(1)
print('t')
for i, t in enumerate(data_tel):
    if t != 0:
        plt.plot(t['recorded_traj_dual'][0], label=i)
        print(len(t['recorded_traj_dual'][0]))
    else:
        print(0)
        pass
plt.legend()

#%%
import scipy.stats as st
a=[965, 1795, 1180, 1205]#1795, 1180 , 1205]
b=[8028,4730, 5294, 9430] #my idea is to put 10000 for the example that failed
(stat, p) = st.ttest_rel(a,b)

#%%
import scipy.stats as st
a=[1116, 965, 1188, 827, 1795, 1180, 1205]#1795, 1180 , 1205]
b=[1.5*9430, 8028, 1.5*9430, 1.5*9430, 4730, 5294, 9430] #my idea is to put 10000 for the example that failed
(stat, p) = st.ttest_rel(a,b)

#%%

import plotly.graph_objects as go

categories = ['Mental Demand', 'Physical Demand', 'Temporal Demand', 'Effort', 'Frustration']

fig = go.Figure()

fig.add_trace(go.Scatterpolar(
      r=[3.57, 9, 6.86, 4.29, 2.86],
      theta=categories,
      fill='toself',
      name='Kinesthetic Teaching'
))
fig.add_trace(go.Scatterpolar(
      r=[16.43, 3.86, 10.57, 16.57, 11.71],
      theta=categories,
      fill='toself',
      name='Teleoperated Teaching'
))

fig.update_layout(
  polar=dict(
    radialaxis=dict(
      visible=True,
      range=[0, 20]
    )),
  showlegend=False
)

fig.show()
# %%


from django.shortcuts import render, redirect, get_object_or_404
from main.models import User
from main.forms import CustomUserForm
from django.contrib.auth.models import Group
from django.contrib import messages
from django.contrib.auth.decorators import login_required

@login_required
def user_lists(request):
    group = request.user.groups.all()[0].name
    users = User.objects.all().exclude(groups__name='client')
    # usergroup = request.user.groups.all()[0].name
    context = {
        'users': users,'group': group, 'title': 'Users List',
    }
    return render(request, 'employee/users_list.html', context)

@login_required
def user_add(request):
    group = request.user.groups.all()[0].name
    form = CustomUserForm()
    if request.method == 'POST':
        form = CustomUserForm(request.POST)
        if form.is_valid():
            instance = form.save(commit=False)
            gp = request.POST.get('groups')
            group = Group.objects.get(id=gp)
            instance.set_password('edtl#2022')
            instance.save()
            instance.groups.add(group)
            messages.success(
                request, f'User {instance.email} Successfully Created')
            return redirect('admin-user-lists')
    context = {
        'form': form,'group': group, 'title': 'Add User',
    }
    return render(request, 'employee/add.html', context)

@login_required
def user_update(request, hashid):
    group = request.user.groups.all()[0].name
    object = get_object_or_404(User, hashed=hashid)
    if request.method == 'POST':
        form = CustomUserForm(request.POST, instance=object)
        if form.is_valid():
            instance = form.save(commit=False)
            instance.groups.clear()
            gp = request.POST.get('groups')
            group = Group.objects.get(id=gp)
            instance.groups.add(group)
            instance.save()
            messages.success(
                request, f'User {instance.email} Successfully Update')
            return redirect('admin-user-lists')
    else:
        form = CustomUserForm(instance=object)
    context = {
        'form': form,'group': group, 'title': 'Update User',
    }
    return render(request, 'employee/add.html', context)

@login_required
def user_activate(request, hashid):
    object = get_object_or_404(User, hashed=hashid)
    object.is_active = True
    object.save()
    messages.success(request, f'User {object.email} Successfully Activate')
    return redirect('admin-user-lists')

@login_required
def user_deactivate(request, hashid):
    object = get_object_or_404(User, hashed=hashid)
    object.is_active = False
    object.save()
    messages.warning(request, f'User {object.email} Successfully Deactivate')
    context = {}
    return redirect('admin-user-lists')