from volttron.platform.vip.agent import BasicAgent, Core
from volttron.platform.agent import utils
import logging
import random
import gevent
import traceback
from volttron.platform.messaging import headers as headers_mod
from volttron.platform.messaging.topics import (DRIVER_TOPIC_BASE,
                                                DRIVER_TOPIC_ALL,
                                                DEVICES_VALUE,
                                                DEVICES_PATH)

from volttron.platform.vip.agent.errors import VIPError, Again
from .driver_locks import publish_lock
import datetime

utils.setup_logging()
_log = logging.getLogger(__name__)

class DriverAgent(BasicAgent):
    def __init__(self, parent, config, time_slot, driver_scrape_interval, device_path,
                 group, group_offset_interval,
                 default_publish_depth_first_all=True,
                 default_publish_breadth_first_all=True,
                 default_publish_depth_first=True,
                 default_publish_breadth_first=True,
                 **kwargs):
        super(DriverAgent, self).__init__(**kwargs)
        self.heart_beat_value = 0
        self.device_name = ''
        #Use the parent's vip connection
        self.parent = parent
        self.vip = parent.vip
        self.config = config
        self.device_path = device_path

        self.update_publish_types(default_publish_depth_first_all ,
                                 default_publish_breadth_first_all,
                                 default_publish_depth_first,
                                 default_publish_breadth_first)

        try:
            interval = int(config.get("interval", 60))
            if interval < 1.0:
                raise ValueError
        except ValueError:
            _log.warning("Invalid device scrape interval {}. Defaulting to 60 seconds.".format(config.get("interval")))
            interval = 60

        self.interval = interval
        self.periodic_read_event = None

        self.update_scrape_schedule(time_slot, driver_scrape_interval, group, group_offset_interval)

    def update_publish_types(self, publish_depth_first_all,
                                   publish_breadth_first_all,
                                   publish_depth_first,
                                   publish_breadth_first):
        """Setup which publish types happen for a scrape.
           Values passed in are overridden by settings in the specific device configuration."""
        self.publish_depth_first_all = bool(self.config.get("publish_depth_first_all", publish_depth_first_all))
        self.publish_breadth_first_all = bool(self.config.get("publish_breadth_first_all", publish_breadth_first_all))
        self.publish_depth_first = bool(self.config.get("publish_depth_first", publish_depth_first))
        self.publish_breadth_first = bool(self.config.get("publish_breadth_first", publish_breadth_first))

    def update_scrape_schedule(self, time_slot, driver_scrape_interval, group, group_offset_interval):
        self.time_slot_offset = (time_slot * driver_scrape_interval) + (group * group_offset_interval)
        self.time_slot = time_slot
        self.group = group

        _log.debug("{} group: {}, time_slot: {}, offset: {}".format(self.device_path, group,
                                                                    time_slot, self.time_slot_offset))

        if self.time_slot_offset >= self.interval:
            _log.warning(
                "Scrape offset exceeds interval. Required adjustment will cause scrapes to double up with other devices.")
            while self.time_slot_offset >= self.interval:
                self.time_slot_offset -= self.interval

        #check weather or not we have run our starting method.
        if not self.periodic_read_event:
            return

        self.periodic_read_event.cancel()

        next_periodic_read = self.find_starting_datetime(utils.get_aware_utc_now())

        self.periodic_read_event = self.core.schedule(next_periodic_read, self.periodic_read, next_periodic_read)

    def find_starting_datetime(self, now):
        midnight = now.replace(hour=0, minute=0, second=0, microsecond=0)
        seconds_from_midnight = (now - midnight).total_seconds()
        interval = self.interval

        offset = seconds_from_midnight % interval

        if not offset:
            return now

        previous_in_seconds = seconds_from_midnight - offset
        next_in_seconds = previous_in_seconds + interval

        from_midnight = datetime.timedelta(seconds=next_in_seconds)
        return midnight + from_midnight + datetime.timedelta(seconds=self.time_slot_offset)

    def get_interface(self, driver_type, config_dict, config_string):
        """Returns an instance of the interface"""
        module_name = "platform_driver.interfaces." + driver_type
        module = __import__(module_name,globals(),locals(),[], 0)
        interfaces = module.interfaces
        sub_module = getattr(interfaces, driver_type)
        klass = getattr(sub_module, "Interface")
        interface = klass(vip=self.vip, core=self.core, device_path=self.device_path)
        interface.configure(config_dict, config_string)
        return interface

    @Core.receiver('onstart')
    def starting(self, sender, **kwargs):
        self.setup_device()

        # interval = self.config.get("interval", 60)
        # self.core.periodic(interval, self.periodic_read, wait=None)

        next_periodic_read = self.find_starting_datetime(utils.get_aware_utc_now())

        self.periodic_read_event = self.core.schedule(next_periodic_read, self.periodic_read, next_periodic_read)

        self.all_path_depth, self.all_path_breadth = self.get_paths_for_point(DRIVER_TOPIC_ALL)

    def setup_device(self):

        config = self.config
        driver_config = config["driver_config"]
        driver_type = config["driver_type"]
        registry_config = config.get("registry_config")

        self.heart_beat_point = config.get("heart_beat_point")

        self.interface = self.get_interface(driver_type, driver_config, registry_config)
        self.meta_data = {}

        for point in self.interface.get_register_names():
            register = self.interface.get_register_by_name(point)
            if register.register_type == 'bit':
                ts_type = 'boolean'
            else:
                if register.python_type is int:
                    ts_type = 'integer'
                elif register.python_type is float:
                    ts_type = 'float'
                elif register.python_type is str:
                    ts_type = 'string'

            self.meta_data[point] = {'units': register.get_units(),
                                     'type': ts_type,
                                     'tz': config.get('timezone', '')}

        self.base_topic = DEVICES_VALUE(campus='',
                                        building='',
                                        unit='',
                                        path=self.device_path,
                                        point=None)

        self.device_name = DEVICES_PATH(base='',
                                        node='',
                                        campus='',
                                        building='',
                                        unit='',
                                        path=self.device_path,
                                        point='')

        # self.parent.device_startup_callback(self.device_name, self)

    def periodic_read(self, now):
        #we not use self.core.schedule to prevent drift.
        next_scrape_time = now + datetime.timedelta(seconds=self.interval)
        # Sanity check now.
        # This is specifically for when this is running in a VM that gets
        # suspended and then resumed.
        # If we don't make this check a resumed VM will publish one event
        # per minute of
        # time the VM was suspended for.
        test_now = utils.get_aware_utc_now()
        if test_now - next_scrape_time > datetime.timedelta(seconds=self.interval):
            next_scrape_time = self.find_starting_datetime(test_now)

        _log.debug("{} next scrape scheduled: {}".format(self.device_path, next_scrape_time))

        self.periodic_read_event = self.core.schedule(next_scrape_time, self.periodic_read, next_scrape_time)

        _log.debug("scraping device: " + self.device_name)

        self.parent.scrape_starting(self.device_name)

        try:
            results = self.interface.scrape_all()
            register_names = self.interface.get_register_names_view()
            for point in (register_names - results.keys()):
                depth_first_topic = self.base_topic(point=point)
                _log.error("Failed to scrape point: "+depth_first_topic)
        except (Exception, gevent.Timeout) as ex:
            tb = traceback.format_exc()
            _log.error('Failed to scrape ' + self.device_name + ':\n' + tb)
            return

        # XXX: Does a warning need to be printed?
        if not results:
            return

        utcnow = utils.get_aware_utc_now()
        utcnow_string = utils.format_timestamp(utcnow)
        sync_timestamp = utils.format_timestamp(now - datetime.timedelta(seconds=self.time_slot_offset))

        headers = {
            headers_mod.DATE: utcnow_string,
            headers_mod.TIMESTAMP: utcnow_string,
            headers_mod.SYNC_TIMESTAMP: sync_timestamp
        }

        if self.publish_depth_first or self.publish_breadth_first:
            for point, value in results.items():
                depth_first_topic, breadth_first_topic = self.get_paths_for_point(point)
                message = [value, self.meta_data[point]]

                if self.publish_depth_first:
                    self._publish_wrapper(depth_first_topic,
                                          headers=headers,
                                          message=message)

                if self.publish_breadth_first:
                    self._publish_wrapper(breadth_first_topic,
                                          headers=headers,
                                          message=message)

        message = [results, self.meta_data]
        if self.publish_depth_first_all:
            self._publish_wrapper(self.all_path_depth,
                                  headers=headers,
                                  message=message)

        if self.publish_breadth_first_all:
            self._publish_wrapper(self.all_path_breadth,
                                  headers=headers,
                                  message=message)

        self.parent.scrape_ending(self.device_name)

    def _publish_wrapper(self, topic, headers, message):
        while True:
            try:
                with publish_lock():
                    _log.debug("publishing: " + topic)
                    self.vip.pubsub.publish('pubsub',
                                            topic,
                                            headers=headers,
                                            message=message).get(timeout=10.0)

                    _log.debug("finish publishing: " + topic)
            except gevent.Timeout:
                _log.warning("Did not receive confirmation of publish to "+topic)
                break
            except Again:
                _log.warning("publish delayed: " + topic + " pubsub is busy")
                gevent.sleep(random.random())
            except VIPError as ex:
                _log.warning("driver failed to publish " + topic + ": " + str(ex))
                break
            else:
                break

    def heart_beat(self):
        if self.heart_beat_point is None:
            return

        self.heart_beat_value = int(not bool(self.heart_beat_value))

        _log.debug("sending heartbeat: " + self.device_name + ' ' + str(self.heart_beat_value))

        self.set_point(self.heart_beat_point, self.heart_beat_value)

    def get_paths_for_point(self, point):
        depth_first = self.base_topic(point=point)

        parts = depth_first.split('/')
        breadth_first_parts = parts[1:]
        breadth_first_parts.reverse()
        breadth_first_parts = [DRIVER_TOPIC_BASE] + breadth_first_parts
        breadth_first = '/'.join(breadth_first_parts)

        return depth_first, breadth_first

    def get_point(self, point_name, **kwargs):
        return self.interface.get_point(point_name, **kwargs)

    def set_point(self, point_name, value, **kwargs):
        return self.interface.set_point(point_name, value, **kwargs)

    def scrape_all(self):
        return self.interface.scrape_all()

    def get_multiple_points(self, point_names, **kwargs):
        return self.interface.get_multiple_points(self.device_name,
                                                  point_names,
                                                  **kwargs)

    def set_multiple_points(self, point_names_values, **kwargs):
        return self.interface.set_multiple_points(self.device_name,
                                                  point_names_values,
                                                  **kwargs)

    def revert_point(self, point_name, **kwargs):
        self.interface.revert_point(point_name, **kwargs)

    def revert_all(self, **kwargs):
        self.interface.revert_all(**kwargs)

    def publish_cov_value(self, point_name, point_values):
        """
        Called in the platform driver agent to publish a cov from a point
        :param point_name: point which sent COV notifications
        :param point_values: COV point values
        """
        utcnow = utils.get_aware_utc_now()
        utcnow_string = utils.format_timestamp(utcnow)
        headers = {
            headers_mod.DATE: utcnow_string,
            headers_mod.TIMESTAMP: utcnow_string,
        }
        for point, value in point_values.items():
            results = {point_name: value}
            meta = {point_name: self.meta_data[point_name]}
            all_message = [results, meta]
            individual_point_message = [value, self.meta_data[point_name]]

            depth_first_topic, breadth_first_topic = self.get_paths_for_point(
                point_name)

            if self.publish_depth_first:
                self._publish_wrapper(depth_first_topic,
                                      headers=headers,
                                      message=individual_point_message)
            #
            if self.publish_breadth_first:
                self._publish_wrapper(breadth_first_topic,
                                      headers=headers,
                                      message=individual_point_message)

            if self.publish_depth_first_all:
                self._publish_wrapper(self.all_path_depth,
                                      headers=headers,
                                      message=all_message)

            if self.publish_breadth_first_all:
                self._publish_wrapper(self.all_path_breadth,
                                      headers=headers,
                                      message=all_message)


import json
from flask import Blueprint, abort, current_app as app
from app_utils import row2dict
from app_const import CACHE_FOUR_WEEK

genres_api = Blueprint('genres', __name__, url_prefix='/api/v1/genres')

@genres_api.route('/')
def get_all_genres():
    """
    Get all genres
    :return: list of genres
    """
    json_data = app.wiring.cache_db.get_value('genres')
    if json_data:
        response = app.response_class(
            response=json_data,
            status=200,
            mimetype='application/json'
        )
        return response

    dataset = app.wiring.genre_dao.get_all()
    genres = [row2dict(row) for row in dataset]
    if not genres:
        return abort(404)
    json_data = json.dumps(genres, ensure_ascii=False).encode('utf8')
    app.wiring.cache_db.set_value('genres', json_data, CACHE_FOUR_WEEK)  # four week cache
    response = app.response_class(
        response=json_data,
        status=200,
        mimetype='application/json'
    )
    return response


from server.schemas import AuthenticatorModelInput, AuthenticatorModelOutput
from pydantic import Field, BaseModel, EmailStr
from uuid import UUID as GUID
from typing import List, Optional

class EntidadeExternaInput(AuthenticatorModelInput):
    """
    Schema de input da entidade externa
    """
    nome: str = Field(example='Tipo Tag Exemplo')

    def convert_to_dict(self):
        return self.dict()

    class Config:
        orm_mode = True
        arbitrary_types_allowed = True

class EntidadeExternaOutput(AuthenticatorModelOutput):
    """
    Schema de output da entidade externa

    retorna também id e guid criados
    """
    id: int = Field(example="1")
    nome: str = Field(example='Tipo Entidade Externa Exemplo')
    guid: GUID = Field(example='78628c23-aae3-4d58-84a9-0c8d7ea63672')

    def convert_to_dict(self):
        return self.dict()

    class Config:
        orm_mode = True
        arbitrary_types_allowed = True


import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import plotly.graph_objs as go

years = [i for i in range(1987, 2023, 4)]

# Getting data
first_points = [126, 68, 112, 102, 113, 105, 62, 97, 69]

plt.bar(x=years,
        height=first_points,
        width=2.5,
        align="center",
        color=["black", "green", "blue", "lightblue", "red", "darkgreen", "darkgreen", "lightblue", "darkgreen"],
        edgecolor=["black", "orange", "red", "yellow", "blue", "gold", "gold", "yellow", "gold"]
)
plt.xticks(years)
plt.title("Top point scorer in each tournament")
plt.xlabel("RWC Year")
plt.ylabel("Points")
plt.show()


#!/usr/bin/env python

from typing import Tuple, List
import torch
from torchvision import transforms
from PIL import Image
import cv2
import numpy as np
from matplotlib import pyplot as plt

class GlimpseSensor:
    def __init__(self, patch_width: int, num_scales: int)->None:
        """
        patch_width: int width of patch
        num_scales: int number of glimpses for img
        """
        self.patch_width = patch_width
        self.num_scales = num_scales

    @staticmethod
    def _get_ctr(w: int, h: int)->Tuple[int, int]:
        return int(w - w * 0.5), int(h - h * 0.5)

    def get_coords(self, img_size:Tuple[int, int], location: Tuple[int, int])->List[Tuple[float, float, float, float]]:
        height, width = img_size
        w = self.patch_width
        coords = []
        for _ in range(self.num_scales):
            x, y = location  # assume the location is between(-1, -1) to (+1, +1)
            x_ctr, y_ctr = self._get_ctr(height, width)
            x = x + x_ctr
            y = y + y_ctr
            half_width = w/2
            x0 = max(0, x - half_width)
            y0 = max(0, y - half_width)
            x1 = min(width, x + half_width)
            y1 = min(height, y + half_width)
            coords.append((x0, y0, x1, y1))
            w *= 4

        return coords

    def glimpse(self, image: torch.tensor, location: torch.tensor)-> torch.tensor:
        image = transforms.ToPILImage()(image)
        location = tuple(location.detach().numpy())
        coords = self.get_coords(image.size, location)
        crops = []
        for coord in coords:
            crop = image.crop(coord)
            crop = crop.resize((self.patch_width, self.patch_width))
            crops.append(crop)

        crops = [transforms.ToTensor()(x) for x in crops]
        crops = torch.stack(crops, dim=0)
        return crops

    def forward(self, images: torch.tensor, location: torch.tensor)->torch.tensor:
        crops_batch = []
        for idx in range(0, images.shape[0]):
            # import ipdb; ipdb.set_trace()
            img = images[idx].squeeze(0)
            loc = location[idx].squeeze(0)
            assert loc[0] >= -1.0 and loc[0] <= 1.0
            assert loc[1] >= -1.0 and loc[1] <= 1.0
            crops = self.glimpse(img, loc)
            crops_batch.append(crops)
        crops_batch = torch.stack(crops_batch, dim=0)
        return crops_batch

def visualize_patch(img: Image, loc: Tuple[float, float], sensor: GlimpseSensor)-> None:
    coords = sensor.get_coords(img.size, loc)
    canvas = np.array(img)
    # plt.axis('tight')
    # plt.axis('off')
    for coord in coords:
        x0, y0, x1, y1 = list(map(lambda x: int(x), coord))
        print("Coords", x0, y0, x1, y1)
        canvas = cv2.rectangle(canvas, (x0, y0), (x1, y1), (255, 0, 0), 1)
    plt.imshow(canvas)
    plt.show()

def main():
    sensor = GlimpseSensor(64, 2)
    path = './test_data/cat2.jpg'
    img = torch.unsqueeze(transforms.functional.to_tensor(Image.open(path).resize((256, 256))), 0)
    loc = torch.zeros((1, 2))
    batch_crops = sensor.forward(img, loc)
    print('Shape of batch_crops', batch_crops.shape)
    for crops in batch_crops:
        for crop in crops:
            print('Crop shape', crop.shape)
            plt.imshow(transforms.functional.to_pil_image(crop))
            plt.show()
    # visualize_patch(img, (14, 100), sensor)

if __name__ == "__main__":
    main()


import tensorflow as tf
import numpy as np
import os
import matplotlib.pyplot as plt
from sklearn import preprocessing
import pathlib
import csv
from preprocessing.preprocessClassifier import load_image, Augment
from tqdm import tqdm
from sklearn.svm import SVC

BASE_PATH = "./data/croppedEars"
TRAIN_DATA_FOLDER = pathlib.Path(BASE_PATH + "/train")
TEST_DATA_FOLDER = pathlib.Path(BASE_PATH + "/test")
ANOTATIONS_PATH = "./data/annotations/recognition/ids.csv"

IMAGE_HEIGHT = 224
IMAGE_WIDTH = 224

MODEL_NAME = "EfficientNetB0-50E-myEars"

def filenamesAndLabels(path, train=True):
    filenames = ["train/"+x for x in os.listdir(path)] if train else ["test/"+x for x in os.listdir(path)]
    annotationsDict = {}

    with open(ANOTATIONS_PATH) as csv_file:
        csv_reader = csv.reader(csv_file, delimiter=',')
        for row in csv_reader:
            annotationsDict[row[0]] = int(row[1]) - 1

    labels = [annotationsDict[x] for x in filenames]
    # filenames = [os.path.join(BASE_PATH, x) for x in filenames]
    filenames = [os.path.join(BASE_PATH, "myT"+x[1:]) for x in filenames] # to use myEars

    return filenames, labels

def evalSVM(X_train, X_test, y_train, kernel="rbf",c=1):
    svm = SVC(C=c, kernel=kernel, random_state=42, probability=True)
    model = svm.fit(X_train, y_train)
    y_pred = model.predict_proba(X_test)
    return y_pred

def calcRankN(labelsProbs, trueLabel, n=1):
    if n >= len(labelsProbs):
        return None
    sortedProbs = np.argsort(labelsProbs)[::-1]
    if trueLabel in sortedProbs[:n]:
        return True
    else:
        return False

def displayCMC(ranks, save=False, saveName="CMC"):
    plt.plot(np.arange(1,len(ranks)+1), ranks)
    plt.title('Cumulative Match Curve')
    plt.xlabel('Rank')
    plt.ylabel('Probability of identification')
    plt.savefig(f"./resultsClassifier/{MODEL_NAME}-{saveName}.jpg") if save else plt.show()
    if save:
        np.savetxt(f"./resultsClassifier/{MODEL_NAME}-{saveName}-ranks.txt", ranks)

if __name__ == "__main__":
    filenames_train, labels_train = filenamesAndLabels(TRAIN_DATA_FOLDER)
    filenames_test, labels_test = filenamesAndLabels(TEST_DATA_FOLDER, train=False)

    dataset_train = tf.data.Dataset.from_tensor_slices((filenames_train, labels_train))
    train_images = dataset_train.map(lambda x, y: load_image(x, y, IMAGE_HEIGHT, IMAGE_WIDTH), num_parallel_calls=tf.data.AUTOTUNE)

    dataset_test = tf.data.Dataset.from_tensor_slices((filenames_test, labels_test))
    test_images = dataset_test.map(lambda x, y: load_image(x, y, IMAGE_HEIGHT, IMAGE_WIDTH), num_parallel_calls=tf.data.AUTOTUNE)

    # model = tf.keras.models.load_model(f"./feature_extractors/checkpoints/{MODEL_NAME}/weights0050.h5")
    # For transfer learning
    model = tf.keras.models.load_model(f"./feature_extractors/checkpoints/{MODEL_NAME}/weightsLast.h5")

    layer_name = "flatten"
    intermidiate_layer_model = tf.keras.models.Model(inputs=model.input, outputs=model.get_layer(layer_name).output)

    X_train = []
    y_train = np.array(labels_train)
    for element in tqdm(train_images.as_numpy_iterator()):
        image, label = element
        imageToPredict = image[None, :,:,:]
        labelsProbs = intermidiate_layer_model.predict(imageToPredict)[0]
        X_train.append(labelsProbs)
    X_train = np.array(X_train)

    X_test = []
    y_test = np.array(labels_test)
    for element in tqdm(test_images.as_numpy_iterator()):
        image, label = element
        imageToPredict = image[None, :,:,:]
        labelsProbs = intermidiate_layer_model.predict(imageToPredict)[0]
        X_test.append(labelsProbs)
    X_test = np.array(X_test)

    scaler = preprocessing.StandardScaler().fit(X_train)
    X_train_scaled = scaler.transform(X_train)
    X_test_scaled = scaler.transform(X_test)

    y_pred = evalSVM(X_train, X_test, y_train)

    ranks = np.zeros(99)
    for i in tqdm(range(y_pred.shape[0])):
        label = y_test[i]
        labelsProbs = y_pred[i,:]
        for i in range(1, 100):
            ranks[i-1] += calcRankN(labelsProbs, label, n=i)

    ranks /= len(filenames_test)
    displayCMC(ranks, save=True, saveName="FT+SVM")


from time import strftime, localtime
from datetime import datetime
import json

import logging as logme
from googletransx import Translator

# ref.
# - https://github.com/x0rzkov/py-googletrans#basic-usage
translator = Translator()

class tweet:
    """Define Tweet class
    """
    type = "tweet"

    def __init__(self):
        pass

def getMentions(tw):
    """Extract ment from tweet
    """
    logme.debug(__name__+':getMentions')
    try:
        mentions = tw["data-mentions"].split(" ")
    except:
        mentions = []

    return mentions

def getQuoteURL(tw):
    """Extract quote from tweet
    """
    logme.debug(__name__+':getQuoteURL')
    base_twitter = "https://twitter.com"
    quote_url = ""
    try:
        quote = tw.find("div","QuoteTweet-innerContainer")
        quote_url = base_twitter + quote.get("href")
    except:
        quote_url = ""

    return quote_url

def getText(tw):
    """Replace some text
    """
    logme.debug(__name__+':getText')
    text = tw.find("p", "tweet-text").text
    text = text.replace("http", " http")
    text = text.replace("pic.twitter", " pic.twitter")

    return text

def getStat(tw, _type):
    """Get stats about Tweet
    """
    logme.debug(__name__+':getStat')
    st = f"ProfileTweet-action--{_type} u-hiddenVisually"
    return tw.find("span", st).find("span")["data-tweet-stat-count"]

def getRetweet(tw, _config):
    """Get Retweet
    """
    logme.debug(__name__+':getRetweet')
    if _config.Profile:
        if int(tw["data-user-id"]) != _config.User_id:
            return _config.User_id, _config.Username
    else:
        _rt_object = tw.find('span', 'js-retweet-text')
        if _rt_object:
            _rt_id = _rt_object.find('a')['data-user-id']
            _rt_username = _rt_object.find('a')['href'][1:]
            return  _rt_id, _rt_username
    return '', ''

def Tweet(tw, config):
    """Create Tweet object
    """
    logme.debug(__name__+':Tweet')
    t = tweet()
    t.id = int(tw["data-item-id"])
    t.id_str = tw["data-item-id"]
    t.conversation_id = tw["data-conversation-id"]
    t.datetime = int(tw.find("span", "_timestamp")["data-time-ms"])//1000
    t.datestamp = strftime("%Y-%m-%d", localtime(t.datetime))
    t.timestamp = strftime("%H:%M:%S", localtime(t.datetime))
    t.user_id = int(tw["data-user-id"])
    t.user_id_str = tw["data-user-id"]
    t.username = tw["data-screen-name"]
    t.name = tw["data-name"]
    t.place = tw.find("a","js-geo-pivot-link").text.strip() if tw.find("a","js-geo-pivot-link") else ""
    t.timezone = strftime("%Z", localtime())
    for img in tw.findAll("img", "Emoji Emoji--forText"):
        img.replaceWith(img["alt"])
    t.mentions = getMentions(tw)
    t.urls = [link.attrs["data-expanded-url"] for link in tw.find_all('a',{'class':'twitter-timeline-link'}) if link.has_attr("data-expanded-url")]
    t.photos = [photo_node.attrs['data-image-url'] for photo_node in tw.find_all("div", "AdaptiveMedia-photoContainer")]
    t.video = 1 if tw.find_all("div", "AdaptiveMedia-video") != [] else 0
    t.tweet = getText(tw)
    t.hashtags = [hashtag.text for hashtag in tw.find_all("a","twitter-hashtag")]
    t.cashtags = [cashtag.text for cashtag in tw.find_all("a", "twitter-cashtag")]
    t.replies_count = getStat(tw, "reply")
    t.retweets_count = getStat(tw, "retweet")
    t.likes_count = getStat(tw, "favorite")
    t.link = f"https://twitter.com/{t.username}/status/{t.id}"
    t.user_rt_id, t.user_rt = getRetweet(tw, config)
    t.retweet = True if t.user_rt else False
    t.retweet_id = ''
    t.retweet_date = ''
    if not config.Profile:
        t.retweet_id = tw['data-retweet-id'] if t.user_rt else ''
        t.retweet_date = datetime.fromtimestamp(((int(t.retweet_id) >> 22) + 1288834974657)/1000.0).strftime("%Y-%m-%d %H:%M:%S") if t.user_rt else ''
    t.quote_url = getQuoteURL(tw)
    t.near = config.Near if config.Near else ""
    t.geo = config.Geo if config.Geo else ""
    t.source = config.Source if config.Source else ""
    t.reply_to = [{'user_id': t['id_str'], 'username': t['screen_name']} for t in json.loads(tw["data-reply-to-users-json"])]
    t.translate = ''
    t.trans_src = ''
    t.trans_dest = ''
    if config.Translate == True:
        try:
            ts = translator.translate(text=t.tweet, dest=config.TranslateDest)
            t.translate = ts.text
            t.trans_src = ts.src
            t.trans_dest = ts.dest
        # ref. https://github.com/SuniTheFish/ChainTranslator/blob/master/ChainTranslator/__main__.py#L31
        except ValueError as e:
            raise Exception("Invalid destination language: {} / Tweet: {}".format(config.TranslateDest, t.tweet))
            logme.debug(__name__+':Tweet:translator.translate:'+str(e))
    return t


import glob
import csv
import sys
import psycopg2
import pandas as pd
from os import listdir
import os
from os.path import isfile, join
from config import config
import time

class soramameSensorData:
    def __init__(self):
        self.timeStamps = None
        self.stationIDs = None
        self.pm25Data = None
        self.param = 'pm25'
        self.time = []

    def getSensorData(self, tempPath,tableName = 'data', outputParam=13):
        """

        Connects to database to get distinct timestamps and stationIDs.
        For each stationID execute sql query to get timeStamp and pm2.5 values and store it in a CSV file

        """
        self.time.append(time.time())
        #setting attribute

        if outputParam != 13:
            if outputParam == 1:
                self.param = 'sname'
            elif outputParam == 2 :
                self.param = 'time'
            elif outputParam == 3 :
                self.param = 'so2'
            elif outputParam == 4 :
                self.param = 'no'
            elif outputParam == 5 :
                self.param = 'no2'
            elif outputParam == 6 :
                self.param = 'nox'
            elif outputParam == 7 :
                self.param = 'co'
            elif outputParam == 8 :
                self.param = 'ox'
            elif outputParam == 9 :
                self.param = 'nmhc'
            elif outputParam == 10 :
                self.param = 'ch4'
            elif outputParam == 11:
                self.param = 'thc'
            elif outputParam == 12:
                self.param = 'spm'
            elif outputParam == 14:
                self.param = 'sp'
            elif outputParam == 15:
                self.param = 'wd'
            elif outputParam == 16:
                self.param = 'ws'
            elif outputParam == 17:
                self.param = 'temp'
            elif outputParam == 18:
                self.param = 'hum'
            else:
                print('Error : parameter index'+str(outputParam)+' out of range')

        # Connecting to PostgreSQL server
        conn = None
        try:
            # read connection parameters
            params = config()

            print('Connecting to the PostgreSQL database...')
            conn = psycopg2.connect(**params)

            # create a cursor
            cur = conn.cursor()

            #Execute sql query to find distinct timestamps in database
            query = 'SELECT DISTINCT time FROM ' + tableName+ ' order by time asc'
            cur.execute(query)
            self.timeStamps = cur.fetchall()

            #Execute sql query to find  distinct sensors in database
            query = 'SELECT DISTINCT(sname) FROM ' + tableName + ' order by sname asc'
            cur.execute(query)
            self.stationIDs = cur.fetchall();
            self.time.append(time.time())
            print("stationIDs Length: ",len(self.stationIDs), 'TimeStamps Length: ',len(self.timeStamps))

            # query to get pm2.5 values for each unique station ID
            # NOTE : Change attribute names(time,pm25) according to your table attributes
            for station in self.stationIDs:
                query = 'select time,' +self.param+' from '+tableName +' where sname=\''+str(station[0]) + '\' ORDER BY time asc'
                # print(query)
                cur.execute(query)
                self.pm25Data = cur.fetchall()

                self.temp = {}
                for sensorValue in self.pm25Data:
                    if sensorValue[1] in [-1000,9999]:
                        self.temp[str(sensorValue[0])] = 'NaN'
                    else :
                        self.temp[str(sensorValue[0])] = str(sensorValue[1])
                self.sensorDf = pd.DataFrame({'TimeStamp': self.temp.keys(), station[0] : self.temp.values()})

                self.sensorDf.to_csv(tempPath+str(station[0])+'.csv',index=None)
            print("Created stationID Files")

        except (Exception, psycopg2.DatabaseError) as error:
            print(error)
        finally:
            if conn is not None:
                conn.close()
                print('Database connection closed.')

    def mergeStationFiles(self,tempPath,outputFile):
        '''
            Merges each station File into single Dataframe and s
        '''
        self.time.append(time.time())
        self.dataframe = pd.DataFrame(self.timeStamps, columns= ['TimeStamp'])
        for filename in os.listdir(tempPath):
            if filename.endswith('.csv'):
                filePath = os.path.join(tempPath,filename)
                stationDf = pd.read_csv(filePath,parse_dates= [0])

                self.dataframe = pd.merge(self.dataframe, stationDf, on = 'TimeStamp', how= 'left')
                os.remove(filePath)
        self.dataframe.fillna('NaN', inplace = True)
        self.dataframe.to_csv(outputFile)
        print("Created soramame data file")
        self.time.append(time.time())
        print('-------time taken for execution-------')
        print('query execution :',self.time[1] - self.time[0],' sec')
        print('dataframe merge :',self.time[3] - self.time[2],' sec')
        print('Total time :',self.time[3] - self.time[0],' sec')
        # print('query execution :',self.time[1] - self.time[2],' sec')

if __name__ == "__main__":
    if len(sys.argv) == 3:
        print(len(sys.argv),"input args")
        dataExtraction = soramameSensorData()
        dataExtraction.getSensorData(tempPath=sys.argv[1], tableName = str('data'),  outputParam = int(13))
        dataExtraction.mergeStationFiles(tempPath = sys.argv[1],outputFile= sys.argv[2])
    elif len(sys.argv) == 4:
        print(len(sys.argv),"input args")
        dataExtraction = soramameSensorData()
        dataExtraction.getSensorData(tempPath=sys.argv[1], tableName = sys.argv[3],  outputParam = int(13))
        dataExtraction.mergeStationFiles(tempPath = sys.argv[1],outputFile= sys.argv[2])
    elif len(sys.argv) == 5:
        print(len(sys.argv),"input args")
        dataExtraction = soramameSensorData()
        dataExtraction.getSensorData(tempPath=sys.argv[1], tableName = sys.argv[3],  outputParam = int(sys.argv[4]))
        dataExtraction.mergeStationFiles(tempPath = sys.argv[1],outputFile= sys.argv[2])
    else :
        print("Error : Incorrect number of input parameters given : "+ str(len(sys.argv) - 1))
        print("Input Parameters-> temporary file path , outputFileName, tablename (default = data), outputParam (default = pm25)")


from fastapi import APIRouter, Depends, WebSocket

from app.dependencies.auth import AcaPyAuthVerified
from app.services.websocket import handle_websocket, websocket_auth
from shared.log_config import get_logger

logger = get_logger(__name__)
router = APIRouter()

@router.websocket("/ws/topic/{topic}")
async def websocket_endpoint_topic(
    websocket: WebSocket,
    topic: str,
    auth: AcaPyAuthVerified = Depends(websocket_auth),
):
    logger.info("Received websocket request on topic `{}`", topic)
    await handle_websocket(websocket, wallet_id="", topic=topic, auth=auth)

@router.websocket("/ws/{wallet_id}")
async def websocket_endpoint_wallet(
    websocket: WebSocket,
    wallet_id: str,
    auth: AcaPyAuthVerified = Depends(websocket_auth),
):
    logger.info("Received websocket request on wallet id `{}`", wallet_id)
    await handle_websocket(websocket, wallet_id=wallet_id, topic="", auth=auth)

@router.websocket("/ws/{wallet_id}/{topic}")
async def websocket_endpoint_wallet_topic(
    websocket: WebSocket,
    wallet_id: str,
    topic: str,
    auth: AcaPyAuthVerified = Depends(websocket_auth),
):
    logger.info(
        "Received websocket request on wallet id `{}` and topic `{}`", wallet_id, topic
    )
    await handle_websocket(websocket, wallet_id=wallet_id, topic=topic, auth=auth)


from math import atan2

import numpy as np
from itertools import starmap, accumulate
from operator import itemgetter

from utils import Angles, DH, Plotter
from hw2 import configuration, get_dh_parameters
from hw2.forward import forward_kinematics

def inverse_kinematics(end_effector_frame: np.ndarray) -> list[Angles]:
    # get end effector position and orientation as a rotation matrix and a vector
    end_effector_orientation: np.ndarray = end_effector_frame[:3, :3].copy()
    end_effector_position: np.ndarray = end_effector_frame[:3, 3].copy()

    # unpack configuration
    a1, a2, a3, a4, a5 = configuration

    # get wrist position relatively to the link 1
    wrist_position = end_effector_position - a5 * end_effector_orientation[:3, 2]
    wrist_position[2] -= a1

    # get two solutions for theta_1
    theta_1_1 = atan2(wrist_position[1], wrist_position[0])
    theta_1_2 = atan2(-wrist_position[1], -wrist_position[0])

    # calculate cosine of theta_3
    cos_3_numerator = np.sum(wrist_position ** 2) - a2 ** 2 - (a3 + a4) ** 2
    cos_3_denominator = 2 * a2 * (a3 + a4)
    cos_3 = cos_3_numerator / cos_3_denominator

    # calculate two solutions for sine of theta_3
    sin_3_1 = np.sqrt(1 - cos_3 ** 2)
    sin_3_2 = -np.sqrt(1 - cos_3 ** 2)

    # get two solutions for theta_3
    theta_3_1 = atan2(sin_3_1, cos_3)
    theta_3_2 = atan2(sin_3_2, cos_3)

    # calculate four solutions for theta_2
    xy_norm = np.linalg.norm(wrist_position[:2])
    theta_2_1 = atan2((a2 + (a3 + a4) * cos_3) * wrist_position[2] - (a3 + a4) * sin_3_1 * xy_norm,
                      (a2 + (a3 + a4) * cos_3) * xy_norm + (a3 + a4) * sin_3_1 * wrist_position[2])
    theta_2_2 = atan2((a2 + (a3 + a4) * cos_3) * wrist_position[2] + (a3 + a4) * sin_3_1 * xy_norm,
                      -(a2 + (a3 + a4) * cos_3) * xy_norm + (a3 + a4) * sin_3_1 * wrist_position[2])
    theta_2_3 = atan2((a2 + (a3 + a4) * cos_3) * wrist_position[2] - (a3 + a4) * sin_3_2 * xy_norm,
                      (a2 + (a3 + a4) * cos_3) * xy_norm + (a3 + a4) * sin_3_2 * wrist_position[2])
    theta_2_4 = atan2((a2 + (a3 + a4) * cos_3) * wrist_position[2] + (a3 + a4) * sin_3_2 * xy_norm,
                      -(a2 + (a3 + a4) * cos_3) * xy_norm + (a3 + a4) * sin_3_2 * wrist_position[2])

    # get rotation matrices from frame 0 to frame 3
    partial_angles = [Angles(theta_1_1, theta_2_1, theta_3_1, 0, 0, 0),
                      Angles(theta_1_1, theta_2_3, theta_3_2, 0, 0, 0),
                      Angles(theta_1_2, theta_2_2, theta_3_1, 0, 0, 0),
                      Angles(theta_1_2, theta_2_4, theta_3_2, 0, 0, 0)]

    partial_dh = [get_dh_parameters(_angles)[:3] for _angles in partial_angles]
    partial_transforms = [starmap(DH.transform, dh) for dh in partial_dh]
    partial_frames = [
        list(accumulate(transforms, func=np.dot, initial=np.identity(4)))[-1] for transforms in partial_transforms
    ]

    rotations_0_3 = list(map(itemgetter((slice(None, 3), slice(None, 3))), partial_frames))

    # get rotation matrices from frame 3 to frame 6
    rotations_3_6 = [_rotation.T.dot(end_effector_orientation) for _rotation in rotations_0_3]

    angles: list[Angles] = []

    # calculate angles for the spherical wrist
    for _rotation, _angles in zip(rotations_3_6, partial_angles):
        theta_4 = atan2(-_rotation[0, 2], _rotation[1, 2])
        theta_5 = atan2(np.sqrt(_rotation[0, 2] ** 2 + _rotation[1, 2] ** 2), _rotation[2, 2])
        theta_6 = atan2(-_rotation[2, 1], _rotation[2, 0])

        angles.append(Angles(*_angles[:3], theta_4, theta_5, theta_6))

        theta_4 = atan2(_rotation[0, 2], -_rotation[1, 2])
        theta_5 = atan2(-np.sqrt(_rotation[0, 2] ** 2 + _rotation[1, 2] ** 2), _rotation[2, 2])
        theta_6 = atan2(_rotation[2, 1], -_rotation[2, 0])

        angles.append(Angles(*_angles[:3], theta_4, theta_5, theta_6))

    return angles

def main():
    desired_frame = np.array([[0.82361604, 0.07306063, 0.56242222, 5.68374257],
                              [0.07306063, 0.9697373, -0.23296291, -2.35428326],
                              [-0.56242222, 0.23296291, 0.79335334, 1.79335334],
                              [0., 0., 0., 1.]])

    inverse_results = inverse_kinematics(desired_frame)
    limit = sum(configuration) / 2

    for i, coordinates in enumerate(inverse_results):
        forward_results = forward_kinematics(coordinates)
        print(i)
        print(coordinates)
        print(*forward_results, sep='\n', end='\n\n')
        plotter = Plotter(figure_size=(15, 15),
                          x_limit=(-limit, limit + 2),
                          y_limit=(-limit, limit),
                          z_limit=(-limit, limit))
        plotter.plot_frames(forward_results)
        plotter.plot_points(forward_results)
        plotter.show()
        plotter.save(f'hw2/images/inverse_{i}.png')


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

################################################################################################
# Plataforma para la Iniciativa Regional de Monitoreo Hidrológico de Ecosistemas Andinos (iMHEA)
# basada en los desarrollos realizados por:
#     1) FONDO PARA LA PROTECCIÓN DEL AGUA (FONAG), Ecuador.
#         Contacto: info@fonag.org.ec
#     2) EMPRESA PÚBLICA METROPOLITANA DE AGUA POTABLE Y SANEAMIENTO DE QUITO (EPMAPS), Ecuador.
#         Contacto: paramh2o@aguaquito.gob.ec
#
#  IMPORTANTE: Mantener o incluir esta cabecera con la mención de las instituciones creadoras,
#              ya sea en uso total o parcial del código.

from math import isnan

from django.db import connection

from anuarios.anuario import Anuarios
from anuarios.models import Var6Anuarios as HumedadSuelo
from anuarios.models import Var8Anuarios as PresionAtmosferica
from anuarios.models import Var9Anuarios as TemperaturaAgua
from anuarios.models import Var10Anuarios as Caudal
from anuarios.models import Var11Anuarios as NivelAgua
from home.functions import dictfetchall

class FormatoI(Anuarios):
    maximo = 0
    minimo = 0
    promedio = 0

    def consulta(self, estacion, variable, periodo, tipo):
        cursor = connection.cursor()
        tabla = "validacion_var" + str(variable) + "validado"
        sql = "SELECT "
        if tipo == "maixmo":
            sql += "max(valor) as valor, "
        elif tipo == "minimo":
            sql += "min(valor) as valor, "
        else:
            sql += "avg(valor) as valor, "

        sql += "date_part('month',fecha) as mes "
        sql += "FROM " + tabla + " "
        sql += (
            "WHERE estacion_id=" + str(estacion.est_id) + " and valor!='NaN'::numeric "
        )
        sql += "and date_part('year',fecha)=" + str(periodo)
        # if variable == 8:
        # sql += " and maximo!=0 and minimo!=0 and valor!=0 "
        if variable == 6 or variable == 8:
            sql += " and (maximo!=0 or minimo!=0 or valor!=0) "
        sql += "GROUP BY mes ORDER BY mes"
        cursor.close()

    def matriz(self, estacion, variable, periodo):
        pass

def matrizI(estacion, variable, periodo, tipo):
    datos = []
    cursor = connection.cursor()
    # tabla = variable.var_codigo + '.m' + periodo
    if tipo == "validado":
        tabla = "validacion_var" + str(variable) + "validado"
    else:
        tabla = "medicion_var" + str(variable) + "medicion"
    # máximos absolutos y máximos promedio
    sql = "SELECT max(max_abs) as maximo,  max(valor) as valor, "
    # sql = "SELECT max(maximo) as maximo,  max(valor) as valor, "
    sql += "date_part('month',fecha) as mes "
    # sql += "FROM " + tabla + " "
    sql += "FROM mensual_var" + str(variable) + "mensual "
    sql += "WHERE estacion_id=" + str(estacion.est_id) + " and valor!='NaN'::numeric "
    sql += "and date_part('year',fecha)=" + str(periodo) + " "
    sql += (
        "and vacios < (SELECT v.vacios FROM variable_variable v WHERE v.var_id = "
        + str(variable)
        + ") "
    )
    # if variable == 8:
    # sql += " and maximo!=0 and minimo!=0 and valor!=0 "
    if variable == 6 or variable == 8:
        # sql += " and (maximo!=0 or minimo!=0 or valor!=0) "
        sql += " and (max_abs!=0 or min_abs!=0 or valor!=0) "
    sql += "GROUP BY mes ORDER BY mes"
    cursor.execute(sql)
    med_max = dictfetchall(cursor)
    # mínimos absolutos y mínimos promedio
    # sql = "SELECT min(minimo) as minimo,  min(valor) as valor, "
    sql = "SELECT min(min_abs) as minimo,  min(valor) as valor, "
    sql += "date_part('month',fecha) as mes "
    # sql += "FROM " + tabla + " "
    sql += "FROM mensual_var" + str(variable) + "mensual "
    sql += "WHERE estacion_id=" + str(estacion.est_id) + " and valor!='NaN'::numeric "
    sql += "and date_part('year',fecha)=" + str(periodo) + " "
    sql += (
        "and vacios < (SELECT v.vacios FROM variable_variable v WHERE v.var_id = "
        + str(variable)
        + ") "
    )
    # if variable == 8:
    # sql += " and maximo!=0 and minimo!=0 and valor!=0 "
    if variable == 6 or variable == 8:
        # sql += " and (maximo!=0 or minimo!=0 or valor!=0) "
        sql += " and (max_abs!=0 or min_abs!=0 or valor!=0) "
    sql += "GROUP BY mes ORDER BY mes"
    cursor.execute(sql)
    med_min = dictfetchall(cursor)
    # valores promedio mensuales
    sql = "SELECT avg(valor) as valor, date_part('month',fecha) as mes "
    # sql += "FROM " + tabla + " "
    sql += "FROM mensual_var" + str(variable) + "mensual "
    sql += "WHERE estacion_id=" + str(estacion.est_id) + " and valor!='NaN'::numeric "
    sql += "and date_part('year',fecha)=" + str(periodo) + " "
    sql += (
        "and vacios < (SELECT v.vacios FROM variable_variable v WHERE v.var_id = "
        + str(variable)
        + ") "
    )
    # if variable == 8:
    # sql += " and maximo!=0 and minimo!=0 and valor!=0 "
    if variable == 6 and variable == 8:
        # sql += " and (maximo!=0 or minimo!=0 or valor!=0) "
        sql += " and (max_abs!=0 or min_abs!=0 or valor!=0) "
    sql += "GROUP BY mes ORDER BY mes"
    cursor.execute(sql)
    med_avg = dictfetchall(cursor)

    for item_max, item_min, item_avg in zip(med_max, med_min, med_avg):
        mes = int(item_avg.get("mes"))
        if variable == 6:
            obj_hsu = HumedadSuelo()
            obj_hsu.est_id = estacion
            obj_hsu.hsu_periodo = periodo
            obj_hsu.hsu_mes = mes
            obj_hsu.hsu_maximo = get_maximo(item_max)
            obj_hsu.hsu_minimo = get_minimo(item_min)
            obj_hsu.hsu_promedio = get_promedio(item_avg)
            datos.append(obj_hsu)
        elif variable == 8:
            obj_pat = PresionAtmosferica()
            obj_pat.est_id = estacion
            obj_pat.pat_periodo = periodo
            obj_pat.pat_mes = mes
            obj_pat.pat_maximo = get_maximo(item_max)
            obj_pat.pat_minimo = get_minimo(item_min)
            obj_pat.pat_promedio = get_promedio(item_avg)
            datos.append(obj_pat)
        elif variable == 9:
            obj_tag = TemperaturaAgua()
            obj_tag.est_id = estacion
            obj_tag.tag_periodo = periodo
            obj_tag.tag_mes = mes
            obj_tag.tag_maximo = get_maximo(item_max)
            obj_tag.tag_minimo = get_minimo(item_min)
            obj_tag.tag_promedio = get_promedio(item_avg)
            datos.append(obj_tag)
        elif variable == 10:
            obj_cau = Caudal()
            obj_cau.est_id = estacion
            obj_cau.cau_periodo = periodo
            obj_cau.cau_mes = mes
            obj_cau.cau_maximo = get_maximo(item_max)
            obj_cau.cau_minimo = get_minimo(item_min)
            obj_cau.cau_promedio = get_promedio(item_avg)
            datos.append(obj_cau)
        elif variable == 11:
            obj_nag = NivelAgua()
            obj_nag.est_id = estacion
            obj_nag.nag_periodo = periodo
            obj_nag.nag_mes = mes
            obj_nag.nag_maximo = get_maximo(item_max)
            obj_nag.nag_minimo = get_minimo(item_min)
            obj_nag.nag_promedio = get_promedio(item_avg)
            datos.append(obj_nag)
    cursor.close()
    return datos

def get_maximo(item_max):
    try:
        if isnan(item_max.get("maximo")):
            if isnan(item_max.get("valor")):
                return 0
            else:
                return round(item_max.get("valor"), 2)
        return round(item_max.get("maximo"), 2)
    except TypeError:
        if item_max.get("maximo") is None:
            if item_max.get("valor") is None:
                return 0
            else:
                return round(item_max.get("valor"), 2)
        return round(item_max.get("maximo"), 2)

def get_minimo(item_min):
    try:
        if isnan(item_min.get("minimo")):
            if isnan(item_min.get("valor")):
                return 0
            else:
                return round(item_min.get("valor"), 2)
        return round(item_min.get("minimo"), 2)
    except TypeError:
        if item_min.get("minimo") is None:
            if item_min.get("valor") is None:
                return 0
            else:
                return round(item_min.get("valor"), 2)
        return round(item_min.get("minimo"), 2)

def get_promedio(item_avg):
    if item_avg.get("valor") is None:
        return 0
    return round(item_avg.get("valor"), 2)


from __future__ import annotations

from contextlib import ExitStack
from dataclasses import dataclass, field
from itertools import chain
from types import TracebackType
from typing import Any

_DEFAULT_ROWS = 7
_DEFAULT_COLS = 100

@dataclass(frozen=True)
class _Show:
    """Context manager which adjusts the display of NDFrames."""

    dp: int | None = None
    rows: int | None = _DEFAULT_ROWS
    columns: int | None = _DEFAULT_COLS
    stack: ExitStack = field(default_factory=ExitStack)

    def __call__(
        self,
        *,
        dp: int | None = None,
        rows: int | None = _DEFAULT_ROWS,
        columns: int | None = _DEFAULT_COLS,
    ) -> _Show:
        return _Show(dp=dp, rows=rows, columns=columns)

    def __enter__(self) -> None:
        self._enter_pandas()
        self._enter_polars()
        _ = self.stack.__enter__()

    def _enter_pandas(self) -> None:
        try:
            from pandas import option_context
        except ModuleNotFoundError:  # pragma: no cover
            pass
        else:
            kwargs: dict[str, Any] = {}
            if self.dp is not None:
                kwargs["display.precision"] = self.dp
            if self.rows is not None:
                kwargs["display.min_rows"] = kwargs["display.max_rows"] = self.rows
            if self.columns is not None:
                kwargs["display.max_columns"] = self.columns
            if len(kwargs) >= 1:
                context = option_context(*chain(*kwargs.items()))
                self.stack.enter_context(context)

    def _enter_polars(self) -> None:
        try:
            from polars import Config
        except ModuleNotFoundError:  # pragma: no cover
            pass
        else:
            kwargs: dict[str, Any] = {}
            if self.dp is not None:
                kwargs["float_precision"] = self.dp
            if self.rows is not None:
                kwargs["tbl_rows"] = self.rows
            if self.columns is not None:
                kwargs["tbl_cols"] = self.columns
            config = Config(**kwargs)
            _ = self.stack.enter_context(config)

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc_val: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        _ = self.stack.__exit__(exc_type, exc_val, exc_tb)

show = _Show()

__all__ = ["show"]

{'option_context': 'pandas.option_context', 'Config': 'polars.Config'}


import os
import json
import requests
import random
import redis

from functools import lru_cache
from datetime import timedelta
from dotenv import load_dotenv

load_dotenv()

playlist_ids = [
    "37i9dQZF1DXbYM3nMM0oPk",
    "37i9dQZF1DX4WYpdgoIcn6",
    "37i9dQZF1DWXRqgorJj26U",
    "37i9dQZF1DX62Nfha2yFhL",
    "37i9dQZF1DX4VvfRBFClxm",
    "37i9dQZF1DX9qNs32fujYe",
    "37i9dQZF1DWYnwbYQ5HnZU",
    "37i9dQZF1DWXbttAJcbphz",
    "37i9dQZF1DX9tPFwDMOaN1",
    "37i9dQZEVXbNG2KDcFcKOF",
    "37i9dQZF1DWXtlo6ENS92N",
    "37i9dQZF1DXaKIA8E7WcJj",
    "37i9dQZF1DX2pSTOxoPbx9",
    "37i9dQZF1DWWjGdmeTyeJ6",
    "37i9dQZF1DX4pAtJteyweQ",
]

def get_spotify_token() -> str:
    """
    Retrieve a Spotify access token
    """

    redis_client = redis.Redis()
    access_token = redis_client.get("spotify_access_token")

    if not access_token:
        SPOTIFY_CLIENT_ID = os.environ["SPOTIFY_CLIENT_ID"]
        SPOTIFY_CLIENT_SECRET = os.environ["SPOTIFY_CLIENT_SECRET"]

        payload = {
            "Content-Type": "application/x-www-form-urlencoded",
            "grant_type": "client_credentials",
        }

        END_POINT = "https://accounts.spotify.com/api/token"

        response = requests.post(
            END_POINT, auth=(SPOTIFY_CLIENT_ID, SPOTIFY_CLIENT_SECRET), data=payload
        )

        access_token = response.json()["access_token"]

        redis_client.set("spotify_access_token", access_token)
        redis_client.expire("spotify_access_token", timedelta(minutes=30))
    else:
        access_token = access_token.decode("utf-8")

    return access_token

def get_random_song() -> dict:
    ACCESS_TOKEN = get_spotify_token()
    random.shuffle(playlist_ids)
    random_playlist_id = random.choice(playlist_ids)
    redis_client = redis.Redis()

    random_song_list = redis_client.get(f"playlist:{random_playlist_id}")
    if not random_song_list:
        print("cache miss")
        PLAYIST_URL = (
            f"https://api.spotify.com/v1/playlists/{random_playlist_id}/tracks"
        )

        headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
            "Authorization": f"Bearer {ACCESS_TOKEN}",
        }
        try:
            response = requests.get(PLAYIST_URL, headers=headers)

            if response.status_code == 200:
                random_song_list = response.json()["items"]

                redis_key = f"playlist:{random_playlist_id}"
                redis_client.set(redis_key, json.dumps(random_song_list))
                redis_client.expire(redis_key, timedelta(days=1))
            else:
                raise Exception("Error retrieving results!")
        except:
            raise Exception("Spotify API unreachable!")
    else:
        print("cache hit")
        random_song_list = json.loads(random_song_list)

    random.shuffle(random_song_list)
    random_song = random.choice(random_song_list)["track"]
    # print(random_song)
    song_details = {
        "track_id": random_song["id"],
        "title": random_song["name"],
        "artist": [artist["name"] for artist in random_song["artists"]],
        "cover_img": random_song["album"]["images"][0]["url"],
        "preview_url": random_song["preview_url"] or "",
        "spotify_url": random_song["external_urls"]["spotify"],
    }

    return song_details


import random
from datetime import datetime
from bs4 import BeautifulSoup
from urllib.request import urlopen
import vk_api
from vk_api.longpoll import VkLongPoll, VkEventType
import re

def write_msg(user_id, message):
    vk.method('messages.send', {'user_id': user_id, 'message': message, "random_id":random.randint(0,90000)})

# API-ключ созданный ранее
token = "token"

# Авторизуемся как сообщество
vk = vk_api.VkApi(token=token)

# Работа с сообщениями
longpoll = VkLongPoll(vk)

allCurse4 = ['ДО12СС21', 'ДОУИА13СС21', 'ИС15СС21', 'М19КР21', 'МД17КР21', 'МР18КР21', 'ПК18КР21', 'С14КР21', 'СД15СС21', 'ССА17СС21', 'СТПП21', 'ТО11СС21', 'ТПИ14СС21', 'ГД15КР20', 'ДО12СС20', 'ИС15СС20', 'МР18КР20', 'ООП12СС20', 'ОЭВМ11ПП20', 'ПК18КР20', 'С14КР20', 'СД15СС20', 'ТО11СС20', 'ТЭ13СС20', 'ЭБ16СС20', 'ГД15КР19', 'ДО11СС19', 'ДО12СС19', 'М19КР19', 'МР18КР19', 'ПК18КР19', 'СД15СС19', 'ССА17СС19', 'ТО11СС19', 'ПК18КР18', 'ССА17СС18', 'ТПИ14СС18']
allCurse = [['ДО12СС21'], ['ДОУИА13СС21'], ['ИС15СС21'], ['М19КР21'], ['МД17КР21'], ['МР18КР21'], ['ПК18КР21'], ['С14КР21'], ['СД15СС21'], ['ССА17СС21'], ['СТПП21'], ['ТО11СС21'], ['ТПИ14СС21'], ['ГД15КР20'], ['ДО12СС20'], ['ИС15СС20'], ['МР18КР20'], ['ООП12СС20'], ['ОЭВМ11ПП20'], ['ПК18КР20'], ['С14КР20'], ['СД15СС20'], ['ТО11СС20'], ['ТЭ13СС20'], ['ЭБ16СС20'], ['ГД15КР19'], ['ДО11СС19'], ['ДО12СС19'], ['М19КР19'], ['МР18КР19'], ['ПК18КР19'], ['СД15СС19'], ['ССА17СС19'], ['ТО11СС19'], ['ПК18КР18'], ['ССА17СС18'], ['ТПИ14СС18']]
days = ["Понедельник","Вторник","Среда","Четверг","Пятница","Суббота"]
karDays = ["ПН","ВТ","СР","ЧТ","ПТ"]
dayNedel = []

def newRas():

    all2 = []
    url = "http://www.sovprocollege.ru/club-5.php"
    page = urlopen(url)
    html = page.read().decode("utf-8")
    soup = BeautifulSoup(html, "html.parser")
    all2.append(soup.get_text())
    day2 = 1
    raspisanie = allCurse

    for da in range(5):
        n = all2[0].replace("\n", "|")
        n = n.replace("\xa0", "")
        n.find("1    курс")
        n = n[n.find(days[da]):len(n)]
        if da == 4:
            poned = n[0:n.find(days[day2])]
        else:
            poned = n[0:n.find(days[day2]) - 345]
        poned = poned.split("|")
        dayNedel.append(poned[0])
        print(dayNedel)
        del poned[0]
        refds = []
        if da == 3:
            for w in range(len(poned)):
                if w == 55:
                    refds.append("Обществознание ЭКЗАМЕН  (Волкова Л.В.) О.Кошев 3 ")
                refds.append(poned[w])
            poned = refds
        ras2 =[]
        newd = 1
        oldd = 40
        day = poned[newd:oldd]
        t = 0
        y = 1
        gf = 7
        nudjen = 1
        tred = False
        fgdg =False
        if da == 1:
            gf = 6
        for dey in range(gf):
            if dey == 6:
                day = poned[242:280]
            for r in range(len(allCurse)):
                for x in range(len(raspisanie)):
                    if raspisanie[x] == allCurse[r]:
                            ras2.append(raspisanie[x])
                            ras2[x].append(day[t])
                            t = t + 1

            y = y + 1
            t = 0
            newd = newd + 40
            oldd = oldd + 40
            if y == 7:
                nudjen = 2
            day = poned[nudjen+40*(y-1):40+40*(y-1)]
            raspisanie = ras2
            ras2 = []
        day2 = day2 + 1

def goden(num,prDay):
    g = num
    f = prDay
    mrs = []
    iu = 0
    mrs.append( allCurse[g][0]+"\n")
    if prDay == 0:
        kolvoDay = 5
    else:
        kolvoDay = 1
        yt = 0
        ferd = 0
        for l in karDays:
            if prDay == "ВТ":
                iu = 1
                f = 7
                break
            if prDay == l:
                f = yt
                iu = ferd
                break
            if ferd == 1:
                yt = yt + 6
            else:
                yt = yt + 7
            ferd = ferd + 1

    for t in range(kolvoDay):
        mrs.append("\n")
        mrs.append(dayNedel[iu])
        mrs.append("\n")
        if t == 1:
            ranss = 6
        elif prDay == "ВТ":
            ranss = 6
        else:
            ranss = 7
        for e in range(ranss):
            f = f + 1
            if allCurse[g][f] != "":
                mrs.append(f"{e+1} пара: "+allCurse[g][f]+"\n")
            elif allCurse[g][f] == "":
                mrs.append(f"Нет {e+1} пары" + "\n")

        iu = iu + 1

    return "".join([str(_) for _ in mrs])

def timme():
    current_datetime = datetime.now()
    hour = current_datetime.hour
    return hour

def famil(reqvest):
    prepodovatel = [[], [], [], [], [], ["Суббота Смотрите на сайте"]]
    for z in range(5):
        prepodovatel[z].append(dayNedel[z]+"\n")
    for tyd in range(37):
        o = 0
        for newd in range(5):
            htfg = 7
            if newd == 1:
                htfg = 6
            for eln in range(7):
                retd = re.split('\W+',allCurse[tyd][o])
                for gg in retd:
                    if gg == reqvest:
                        prepodovatel[newd].append(f"{eln} пара {allCurse[tyd][o]} группа {allCurse[tyd][0]}"+"\n" )
                        print()
                o = o + 1
    for r in range(len(prepodovatel)):
        prepodovatel[r].sort()
        prepodovatel[r].insert(0, prepodovatel[r].pop())
    return prepodovatel

newRas()
print("Готова")
print(allCurse4)
prover = [[], [], [], [], [], ["Суббота Смотрите на сайте"]]
for z in range(5):
    prover[z].append(dayNedel[z]+"\n")
chasi = timme()
# Основной цикл
while True:

        for event in longpoll.listen():
            # Если пришло новое сообщение
            if event.type == VkEventType.MESSAGE_NEW:
                # Если оно имеет метку для меня( то есть бота)
                if event.to_me:

                    # Сообщение от пользователя
                    request = event.text
                    # Каменная логика ответа
                    newChasi = timme()
                    if newChasi != chasi:
                        newRas()
                        chasi = timme()
                    print("Готова2")
                    if request:
                        print(request)
                        findFamil = request
                        request = request.upper()
                        messs = request
                        messs = messs.split(" ")

                        u = 0
                        for e in allCurse4:
                            if request == e:
                                iy = goden(u,0)
                                write_msg(event.user_id, f"{iy}")
                                stat = True
                                break
                            elif messs[0] == e:
                                stat = True
                                for l in karDays:
                                    if messs[1] == l:
                                        iy = goden(u, messs[1])
                                        write_msg(event.user_id, f"{iy}")
                                        statDay = True
                                        break
                                    else:
                                        statDay = False
                                if statDay == False:
                                    write_msg(event.user_id, "Неверно указан день недели,пример: ООП12СС20 пн, сса17сс21 вт")
                                break
                            else:
                                stat = False
                            u = u + 1
                        if stat == False:
                            if famil(findFamil) != prover:
                                tt = ""
                                dse = famil(findFamil)
                                for kd in range(5):
                                    tt = tt + "\n"
                                    for ytfas in range(len(dse[kd])):
                                        tt = tt + "".join([str(_) for _ in dse[kd][ytfas]])

                                write_msg(event.user_id, tt)
                                stat = True
                            elif len(request) > 20:
                                write_msg(event.user_id, f"Не существует группы")
                            else:
                                write_msg(event.user_id, f"Не существует группы {request}")


import boto3
import time
import subprocess
import requests
import logging
import json
import os
import sys
import re
from botocore.exceptions import BotoCoreError, ClientError

####################################
# Configuration
####################################
MY_REGION = 'us-west-1'
STEP_SIZE = 6
CONFIDENCE = '0.0'  # valid words seen for confidence of 0.043
MAXLINE_LEN = 90

# Configure logging settings
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

# Specify the S3 bucket name
# bucket_name = 'transcribeBucket'

# Specify the local video file path
audiodir = "./audio/"
dir = "./mediadir/"
outputdir = "./output/"
# telugu_video = 'CongressAchievement.mp4'
telugu_video = '2_20.mp4'
input_video_path = dir + telugu_video
video_only = dir + "video_only.mp4"
base_audio_input = "telugu_audio.mp3"
audio_only = dir + base_audio_input
audio_file = 'english_audio.mp3'
audio_path = outputdir + audio_file
english_video = 'english_video.mp4'
output_video_path = outputdir + english_video
t_text = 'telugu_text.txt'
telugu_text = outputdir + t_text
e_trans = 'english_text.txt'
english_text = outputdir + e_trans
myapp = "myapplication"

####################################
# Utility functions
####################################

def retrieve_audio_bucket():
    bucket = None
    s3 = boto3.client('s3')

    # Get a list of all bucket names
    buckets = s3.list_buckets()
    bucket_names = [bucket['Name'] for bucket in buckets['Buckets']]

    # Define a regular expression pattern for your bucket name
    pattern = re.compile(r'^telugutoenglishtranscrip-transcribebucket')

    # For each bucket name, check if it matches the regular expression
    for bucket_name in bucket_names:
        if pattern.match(bucket_name):
            print(f'The bucket you are looking for is: {bucket_name}')
            break
    return bucket_name

def convert_time_to_hh_mm_ss(seconds):
    hours = seconds // 3600
    minutes = (seconds % 3600) // 60
    seconds = seconds % 60

    return f'{hours:02d}:{minutes:02d}:{seconds:02d}'

def align_sentences(items, step_size):
    aligned_sentences = []
    current_sentence = []
    curline_len = 0
    current_time = 0.0
    start_time = 0.00
    for item in items:
        if 'start_time' in item:
            item_start_time = float(item['start_time'])
            # if start_time is None:
            #    start_time = item_start_time  # start time of the current_sentence
            if item_start_time - start_time > step_size or curline_len > MAXLINE_LEN:
                # aligned_sentences.append((start_time, " ".join(current_sentence)))
                aligned_sentences.append({"time": start_time, "line": " ".join(current_sentence)})
                current_sentence = []
                curline_len = 0
                start_time = item_start_time
            current_time = item_start_time
        if item['type'] == 'pronunciation':
            if 'alternatives' in item and item['alternatives'][0]['confidence'] > CONFIDENCE:
                current_sentence.append(item['alternatives'][0]['content'])
                curline_len += len(item['alternatives'][0]['content']) + 1
        elif item['type'] == 'punctuation':
            if current_sentence and item['alternatives'][0]['confidence'] > CONFIDENCE:  # check if current_sentence is not empty
                current_sentence[-1] += item['alternatives'][0]['content']
                curline_len += len(item['alternatives'][0]['content']) + 1
    if current_sentence:
        # aligned_sentences.append((start_time, " ".join(current_sentence)))
        aligned_sentences.append({"time": start_time, "line": " ".join(current_sentence)})
    return aligned_sentences

def get_transcription_job(job_name):
    # Create a transcribe client
    transcribe = boto3.client("transcribe")

    if job_name is None:
        print("No transcription job to get status for")
        return None, None

    try:
        response = transcribe.get_transcription_job(TranscriptionJobName=job_name)
        transcript_uri = response['TranscriptionJob']['Transcript']['TranscriptFileUri']
        return response['TranscriptionJob']['TranscriptionJobStatus'], transcript_uri
    except BotoCoreError as error:
        print(f"Error getting transcription job status: {error}")
        return None, None

def transcribe_job(job_name, src_text):
    # Wait for transcription to complete (Transcription already started by a lambda)
    while True:
        jobstatus, transcript_uri = get_transcription_job(job_name)
        if jobstatus in ['COMPLETED', 'FAILED']:
            break
        time.sleep(10)

    # 3b. Processing the transcription job result
    if jobstatus == 'COMPLETED':
        logging.info(f"Transcription completed, extracting {src_text}...")
        response = requests.get(transcript_uri)
        result = json.loads(response.text)
        items = result['results']['items']
        lines = align_sentences(items, STEP_SIZE)
        with open(src_text, 'w') as f:
            for item_line in lines:
                time_in_hh_mm_ss = convert_time_to_hh_mm_ss(int(item_line["time"]))
                f.write(f'{time_in_hh_mm_ss}: {item_line["line"]}\n')
        logging.info(f'Transcript has been saved to {src_text}')
    elif jobstatus == 'FAILED':
        logging.info("Transcription failed")
    else:
        logging.info("Transcription still in progress")

    return src_text

def remove_timecode(input_file):
    output_file = re.sub(r'\..*', '_no_tc.txt', input_file)
    with open(input_file, 'r') as file:
        lines = file.readlines()

    with open(output_file, 'w') as file:
        for line in lines:
            line = line.split(': ', 1)[-1]  # Remove the timecode at the beginning
            file.write(line)

    logging.info(f"Timecode removed. Result saved in '{output_file}'")
    return output_file

# ffmpeg -i telugu_video -c:v copy -map 0:v -c:a copy -map 0:a output_video.mp4 -c:a copy -map 0:a telugu_audio.mp3
# ffmpeg -i audio_video.mp4 -c:v copy -an video_only.mp4 -vn -c:a libmp3lame -q:a 2 audio_only.mp3
def split_video_audio(input_path, video_path, audio_path):
    # Run FFmpeg command to split video and audio
    # ffmpeg -i audio_video.mp4 -c:v copy -an video_only.mp4 -vn -c:a libmp3lame -q:a 2 audio_only.mp3
    ffmpeg_cmd = [
        'ffmpeg',
        '-i', input_path,
        '-c:v', 'copy',
        '-an', video_path,
        '-vn',
        '-c:a', 'libmp3lame',
        '-map', '0:a',
        '-q:a', "2",
        audio_path
    ]

    subprocess.run(ffmpeg_cmd, check=True)

# Combinee
def combine_video_audio(video_path, audio_path, output_path, offset = None):
    # Run FFmpeg command to combine video and audio
    ffmpeg_cmd = [
        'ffmpeg',
        '-i', video_path,
        '-i', audio_path,
        '-c:v', 'copy',
        '-c:a', 'aac',
        '-map', '0:v:0',
        '-map', '1:a:0',
        # '-itsoffset', offset,
        '-shortest',
        output_path
    ]

    subprocess.run(ffmpeg_cmd, check=True)

def get_lambda_function_name(app_name, function_id):
    ssm_client = boto3.client('ssm')

    param_name = f"/{app_name}/{function_id}FunctionName"

    try:
        response = ssm_client.get_parameter(
            Name=param_name,
            WithDecryption=True
        )
    except (BotoCoreError, ClientError) as error:
        print(error)
        raise error
    else:
        return response['Parameter']['Value']

####################################
# End of Utility functions
####################################

s3_client = boto3.client('s3')
lambda_client = boto3.client('lambda')

def process_audio_bucket(bucket_name):
    # Split multimedia into video and audio only
    split_video_audio(input_video_path, video_only, audio_only)

    # get the S3 bucket

    # Upload the video file to the S3 bucket
    s3_client.upload_file(audio_only, bucket_name, audio_only)

    logging.info(f"Audio upload of {input_video_path} completed.")

    event = {
        "bucket": bucket_name,
        "media": audio_only,
        "transcript_file": telugu_text,
        "dir": dir,
        "src_lang": "te-IN",
        "dst_lang": "en-US",
    }

    # Invoke the transcribe_audio Lambda function for telugu text availability
    function_name = get_lambda_function_name(myapp, "TranscriptionLambda")
    response = lambda_client.invoke(
        FunctionName=function_name,
        InvocationType='RequestResponse',
        Payload=json.dumps(event)
    )

    # Check if the response indicates completion
    payload = response['Payload'].read().decode('utf-8')
    resp = json.loads(payload)  # Convert the JSON string to a Python object
    if response['StatusCode'] == 200:

        # Get the transcript file for the target language
        job_name = resp
        transcript_file =  transcribe_job(job_name, telugu_text)
    else:
        logging.error(f"Received error: {response['StatusCode']}: {resp}")
        sys.exit(1)

    # Next do the translation (telugu) to (english)
    telugu_file = transcript_file

    event = {
        "bucket": bucket_name,
        "src_text": telugu_text,
        "dst_text": english_text,
        "dir": dir,
        "src_lang": "te-IN",
        "dst_lang": "en-US",
    }

    # Invoke the translate_text Lambda function for telugu to English text
    function_name = get_lambda_function_name(myapp, "TranslateLambda")
    response = lambda_client.invoke(
        FunctionName=function_name,
        InvocationType='RequestResponse',
        Payload=json.dumps(event)
    )

    # Check if the response indicates completion
    payload = response['Payload'].read().decode('utf-8')
    resp = json.loads(payload)  # Convert the JSON string to a Python object
    pprint(response)
    if response['StatusCode'] != 200:
        logging.error(f"Received error: {response['StatusCode']}: {resp}")
        sys.exit(1)

    # Now trigger the synthesis
    synth_file = remove_timecode(english_text)
    event = {
        "bucket": bucket_name,
        "synth_file": synth_file,
    }

    # Upload file to be synthesized
    s3_client.upload_file(synth_file, bucket_name, os.basename(synth_file))

    # Poll the synthesize_video Lambda function for audio stream availability
    function_name = get_lambda_function_name(myapp, "SynthesizeLambda")
    response = lambda_client.invoke(
        FunctionName=function_name,
        InvocationType='RequestResponse',
        Payload=json.dumps(event)
    )

    # Check if the response indicates completion
    if response['StatusCode'] == 200:
        payload = response['Payload'].read().decode('utf-8')
        result = json.loads(payload)
        if result[0] is None:
            logging.error(f"Error occurred: {result[1]}")
            sys.exit(2)

    # jobId = response['SynthesisTask']['TaskId']
    outputUri = response['SynthesisTask']['OutputUri']
    # if 'status' in response_payload and response_payload['status'] == 'completed':
    # English text is available, download it
    s3_client.download_file(bucket_name, os.path.basename(outputUri), audio_path)
    logging.info("Audio synthesis completed.")

    combine_video_audio(video_only, audio_path, output_video_path, offset = english_text)

    logging.info("English video creation completed.")

bucket_name = retrieve_audio_bucket()
process_audio_bucket(bucket_name)


import sys
sys.path.insert(0, "../../")
from helpers import utils
# --------------------------------------------------
from typing import List
import copy

def read(filename: str) -> List[str]:
    data = []
    with open(filename, 'r') as f:
        for _, row in enumerate(f):
            data.append([int(i) for i in list(row.split()[0])])
    return data

def part_1_sol(data, verbose=False):
    valid_row = []
    num_rows = len(data)
    num_cols = len(data[0])
    for i in range(num_rows):
        for j in range(num_cols):
            if j == 0:
                if data[i][j+1] > data[i][j]:
                    valid_row.append((i,j))
            elif j == num_cols-1:
                if data[i][j-1] > data[i][j]:
                    valid_row.append((i,j))
            else:
                if data[i][j-1] > data[i][j] and data[i][j+1] > data[i][j]:
                    valid_row.append((i,j))
    valid_col = []
    for j in range(num_cols):
        for i in range(num_rows):
            if i == 0:
                if data[i+1][j] > data[i][j]:
                    valid_col.append((i,j))
            elif i == num_rows-1:
                if data[i-1][j] > data[i][j]:
                    valid_col.append((i,j))
            else:
                if data[i-1][j] > data[i][j] and data[i+1][j] > data[i][j]:
                    valid_col.append((i,j))
    return list(set(valid_row) & set(valid_col))

def part_1(data, verbose=False):
    utils.start_timer()
    valid = part_1_sol(data, verbose=False)
    sum_ = 0
    for i,j in valid:
        sum_ += data[i][j]+1
    utils.pprint(1, sum_)
    return valid

def visited_before(i,j,visited):
    for visited_ in visited:
        if visited_[0] == i and visited_[1] == j:
            return True
    return False

def part_2(data,verbose=False):
    utils.start_timer()
    valid = part_1_sol(data, verbose=False)
    num_rows = len(data)
    num_cols = len(data[0])
    basin_sizes = []
    for i,j in valid:
        queue = []
        visited = []
        queue.append((i,j))
        visited.append((i,j))
        basic_size = 0
        while len(queue) > 0:
            i,j = queue.pop(0)
            basic_size += 1
            # right
            j_ = j+1
            while j_ < num_cols:
                if data[i][j_] != 9 and data[i][j_] > data[i][j_-1] and not visited_before(i,j_,visited):
                    queue.append((i,j_))
                    visited.append((i,j_))
                else:
                    break
                j_ += 1
            # left
            j_ = j-1
            while j_ > -1:
                if data[i][j_] != 9 and data[i][j_] > data[i][j_+1] and not visited_before(i,j_,visited):
                    queue.append((i,j_))
                    visited.append((i,j_))
                else:
                    break
                j_ -= 1
            # top
            i_ = i-1
            while i_ > -1:
                if data[i_][j] != 9 and data[i_][j] > data[i_+1][j] and not visited_before(i_,j,visited):
                    queue.append((i_,j))
                    visited.append((i_,j))
                else:
                    break
                i_ -= 1
            # bottom
            i_ = i+1
            while i_ < num_rows:
                if data[i_][j] != 9 and data[i_][j] > data[i_-1][j] and not visited_before(i_,j,visited):
                    queue.append((i_,j))
                    visited.append((i_,j))
                else:
                    break
                i_ += 1
        basin_sizes.append(basic_size)
    top_3 = sorted(basin_sizes,reverse=True)[:3]
    result = top_3[0]*top_3[1]*top_3[2]
    utils.pprint(2, result)

if __name__ == "__main__":
    print("Advent of Code 2021 - Day 8")
    print("-----------------------------")
    print("Example:")
    data = read('ex.txt')
    part_1(copy.deepcopy(data))
    part_2(copy.deepcopy(data))
    print("-----------------------------")
    print("Test:")
    data = read('input.txt')
    part_1(copy.deepcopy(data))
    part_2(copy.deepcopy(data))


import re
import time
import base64
import random
from io import BytesIO
from PIL import Image, ImageDraw
from typing import Optional, List, Tuple, Dict
from playwright.async_api import Page
from playwright.async_api import Cookie

def get_user_agent() -> str:
    user_agent = [
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.5112.79 Safari/537.36",
        "Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.53 Safari/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36"
    ]

    return random.choice(user_agent)

async def get_login_qrcode(page: Page, selector: str) -> str:
    """
    从目标选择其中找到登录二维码
    :param page:
    :param selector:
    :return:
    """
    try:
        elements = await page.wait_for_selector(
            selector=selector
        )
        login_qrcode_img = await elements.get_property("src")
        return str(login_qrcode_img)
    except Exception as e:
        print(e)
        return ""

def convert_cookies(cookies: Optional[List[Cookie]]) -> Tuple[str, Dict]:
    if not cookies:
        return "", {}
    cookies_str = ";".join([f"{cookie.get('name')}={cookie.get('value')}" for cookie in cookies])
    cookie_dict = dict()
    for cookie in cookies:
        cookie_dict[cookie.get('name')] = cookie.get('value')
    return cookies_str, cookie_dict

def show_qrcode(qr_code: str):
    """
    解析base64编码qrcode图像并显示它
    :param qr_code:
    :return:
    """
    qr_code = qr_code.split(",")[1]
    qr_code = base64.b64decode(qr_code)
    image = Image.open(BytesIO(qr_code))

    # 在二维码周围添加方形边框，显示在边框内，提高扫描精度.
    width, height = image.size
    new_image = Image.new('RGB', (width + 20, height + 20), color=(255, 255, 255))
    new_image.paste(image, (10, 10))
    draw = ImageDraw.Draw(new_image)
    draw.rectangle((0, 0, width + 19, height + 19), outline=(0, 0, 0), width=1)
    # new_image.show()
    # time.sleep(3)
    # image.close()
    return new_image

def get_current_timestamp():
    return int(time.time() * 1000)

def match_interact_info_count(count_str: str) -> int:
    if not count_str:
        return 0

    match = re.search(r'\d+', count_str)
    if match:
        number = match.group()
        return int(number)
    else:
        return 0


import logging
import os
import vtk, qt, ctk, slicer
import warnings
from slicer.ScriptedLoadableModule import *

from SubjectHierarchyPlugins import AbstractScriptedSubjectHierarchyPlugin

class FormatMarkups(ScriptedLoadableModule):
    def __init__(self, parent):
        ScriptedLoadableModule.__init__(self, parent)
        parent.title = "SlicerMorph Format Markups Plugin"
        parent.categories = [""]
        parent.contributors = [" Sara Rolfe (UW), Murat Maga (UW)"]
        parent.helpText = ""
        parent.hidden = not slicer.util.settingsValue('Developer/DeveloperMode', False, converter=slicer.util.toBool)

        #
        # register subject hierarchy plugin once app is initialized
        #
        def onStartupCompleted():
            import SubjectHierarchyPlugins
            from FormatMarkups import FormatMarkupsSubjectHierarchyPlugin
            scriptedPlugin = slicer.qSlicerSubjectHierarchyScriptedPlugin(None)
            scriptedPlugin.name = "FormatMarkups"
            scriptedPlugin.setPythonSource(FormatMarkupsSubjectHierarchyPlugin.filePath)
        slicer.app.connect("startupCompleted()", onStartupCompleted)

class FormatMarkupsSubjectHierarchyPlugin(AbstractScriptedSubjectHierarchyPlugin):
  """ Scripted subject hierarchy plugin for the formatMarkups extension.
  """

  # Necessary static member to be able to set python source to scripted subject hierarchy plugin
  filePath = __file__

  def __init__(self, scriptedPlugin):
    AbstractScriptedSubjectHierarchyPlugin.__init__(self, scriptedPlugin)

    pluginHandlerSingleton = slicer.qSlicerSubjectHierarchyPluginHandler.instance()
    self.subjectHierarchyNode = pluginHandlerSingleton.subjectHierarchyNode()
    self.formatMarkupsAction = qt.QAction(f"Apply formatting to siblings", scriptedPlugin)
    self.menu = qt.QMenu("Plugin Menu")
    self.formatMarkupsAction.setMenu(self.menu)

  #
  # item context menus are what happens when you right click on a selected line
  #
  def itemContextMenuActions(self):
    """the actions that could be shown for any of this plugins items"""
    return [self.formatMarkupsAction]

  def showContextMenuActionsForItem(self, itemID):
    """Set actions visible that are valid for this itemID"""
    # reset all menus
    self.formatMarkupsAction.visible = False
    self.formatMarkupsAction.enabled = False
    self.menu.clear()
    # check if this is parent or leaf
    siblingIDList = vtk.vtkIdList()
    itemDataNode = self.subjectHierarchyNode.GetItemDataNode(itemID)
    parentID = self.subjectHierarchyNode.GetItemParent(itemID)
    parentIsScene = bool(self.subjectHierarchyNode.GetItemName(parentID) == "Scene")
    markupNodeTypes=["vtkMRMLMarkupsFiducialNode", "vtkMRMLMarkupsNode"]
    if itemDataNode is not None and itemDataNode.IsA("vtkMRMLMarkupsFiducialNode") and not parentIsScene:
      self.subjectHierarchyNode.GetItemChildren(parentID, siblingIDList)
      if siblingIDList.GetNumberOfIds()>0:
        copyDisplayAction = self.menu.addAction(f"Copy display properties to siblings")
        copyVisibilityAction = self.menu.addAction(f"Copy point visibility to siblings")
        uniqueColorAction = self.menu.addAction(f"Apply unique node color to siblings")
        copyDisplayAction.triggered.connect(lambda: self.copyDisplayProperties(itemDataNode, itemID, siblingIDList))
        copyVisibilityAction.triggered.connect(lambda: self.copyPointVisibility(itemDataNode, itemID, siblingIDList))
        uniqueColorAction.triggered.connect(lambda: self.applyUniqueColors(siblingIDList))
        # enable all menu options
        self.formatMarkupsAction.visible = True
        self.formatMarkupsAction.enabled = True
        copyDisplayAction.visible = True
        copyDisplayAction.enabled = True
        copyVisibilityAction.visible = True
        copyVisibilityAction.enabled = True

  def copyDisplayProperties(self, itemDataNode, itemID, siblingIDList):
    markupsLogic = slicer.modules.markups.logic()
    nodeLocked = itemDataNode.GetLocked()
    controlPointNumberLocked = itemDataNode.GetFixedNumberOfControlPoints()
    for i in range(siblingIDList.GetNumberOfIds()):
      siblingID = siblingIDList.GetId(i)
      if(itemID != siblingID):
        siblingNode = self.subjectHierarchyNode.GetItemDataNode(siblingID)
        markupsLogic.CopyBasicDisplayProperties(itemDataNode.GetDisplayNode(), siblingNode.GetDisplayNode())
        siblingNode.SetFixedNumberOfControlPoints(controlPointNumberLocked)
        siblingNode.SetLocked(nodeLocked)
        if(itemDataNode.GetNumberOfControlPoints() != siblingNode.GetNumberOfControlPoints()):
          logging.debug("Cannot apply control point lock flags to sibling with unequal point number: %s" % siblingNode.GetName())
        else:
          for j in range(itemDataNode.GetNumberOfControlPoints()):
            siblingNode.SetNthControlPointLocked(j, itemDataNode.GetNthControlPointLocked(j))

  def copyPointVisibility(self, itemDataNode, itemID, siblingIDList):
    for i in range(siblingIDList.GetNumberOfIds()):
      siblingID = siblingIDList.GetId(i)
      if(itemID == siblingID):
        next
      siblingNode = self.subjectHierarchyNode.GetItemDataNode(siblingID)
      if(itemDataNode.GetNumberOfControlPoints() != siblingNode.GetNumberOfControlPoints()):
        logging.debug("Cannot apply control point visibility to sibling with unequal point number: %s" % siblingNode.GetName())
      else:
        for j in range(itemDataNode.GetNumberOfControlPoints()):
          siblingNode.SetNthControlPointVisibility(j, itemDataNode.GetNthControlPointVisibility(j))

  def applyUniqueColors(self, siblingIDList):
    colorTableList = slicer.mrmlScene.GetNodesByClassByName('vtkMRMLColorTableNode', 'GenericColors')
    colorTable = colorTableList.GetItemAsObject(0)
    color = [0,0,0,0]
    colorIndex = 1
    for i in range(siblingIDList.GetNumberOfIds()):
      siblingID = siblingIDList.GetId(i)
      siblingNode = self.subjectHierarchyNode.GetItemDataNode(siblingID)
      colorTable.GetColor(colorIndex,color)
      siblingNode.GetDisplayNode().SetSelectedColor(color[0:3])
      if colorIndex < 307:
        colorIndex+=1
      else:
        colorIndex=1


# -*- coding: utf-8 -*-
"""
Created on Wed Nov  4 14:14:59 2020

This script is used to download Point Cloud Library Files.
"""

import requests
from bs4 import BeautifulSoup
import wget #for downloading files
# convert url to normal string
from urllib.parse import unquote
import os

url_base = "https://sourceforge.net/"
url_start = "https://sourceforge.net/projects/pointclouds/files/PCD%20datasets/"

def download_folder(url):
    if url[-1] == '/':
        url = url[:-1]

    folder = unquote(url.split('/')[-1])
    if not os.path.exists(folder):
        os.mkdir(folder)
    os.chdir(folder)
    print("go to", folder)
    response = requests.get(url)
    soup = BeautifulSoup(response.text, features="lxml")
    items = soup.findAll("span", attrs={"class" : "name"})
    items = [item.parent.get("href").replace("/download", "") for item in items]
    print(len(items))

    files = []
    nexturls = []
    for item in items:
        if not item.endswith("/"):
            files.append(item)
        else:
            nexturls.append(url_base + item)

    for file in files:
        print("download ", file)
        # wget.download(file)
        """
        need to install the latest windows wget from
        https://eternallybored.org/misc/wget/
        """
        os.system("wget --continue " + file)

    for nexturl in nexturls:
        download_folder(nexturl)

    os.chdir("..")

if __name__ == "__main__":
    download_folder(url_start)