from PyQt5.QtXml import QDomDocument
from qgis.core import QgsProject, QgsVectorLayer, QgsRasterLayer, QgsPrintLayout, \
    QgsReadWriteContext, QgsLayoutExporter, QgsDataSourceUri

def set_project(project_path, crs):
    # Create a new project
    project = QgsProject.instance()
    project.clear()
    project.read(project_path)
    manager = project.layoutManager()
    # set project crs
    QgsProject.instance().setCrs(crs)
    return project, manager

def load_vector_layer(project, layer_name, geo_file, crs):
    # Load the map layer from the geo file
    layer = QgsVectorLayer(geo_file, layer_name, 'ogr')
    if layer.isValid():
        project.addMapLayer(layer)
        layer.setCrs(crs)
        return layer
    else:
        return print("Layer not valid!")

def load_raster_layer(project, layer_name, geo_file, crs):
    # Load the map layer from the geo file
    layer = QgsRasterLayer(geo_file, layer_name, 'ogr')
    if layer.isValid():
        project.addMapLayer(layer)
        layer.setCrs(crs)
        return layer
    else:
        return print("Layer not valid!")

def load_wms_layer(project, layer_name, layer_url, crs):
    # Load the map layer from the geo file
    layer = QgsRasterLayer(layer_url, layer_name, 'wms')
    if layer.isValid():
        project.addMapLayer(layer)
        layer.setCrs(crs)
        return layer
    else:
        return print("Layer not valid!")

def load_wfs_layer(project, layer_name, layer_url, crs):
    # Load the map layer from the geo file
    layer = QgsVectorLayer(layer_url, layer_name, 'wfs')
    if layer.isValid():
        project.addMapLayer(layer)
        layer.setCrs(crs)
        return layer
    else:
        return print("Layer not valid!")

def load_wfs_layer_from_uri(project, layer_url, feature_name, crs):
    # Load the map layer from the geo file
    uri = QgsDataSourceUri()
    uri.setParam('url', layer_url)
    uri.setParam('type', 'WFS')
    uri.setParam('version', '1.0.0')
    uri.setParam('typename', feature_name)
    layer = QgsVectorLayer(uri.uri(), feature_name, 'WFS')
    if layer.isValid():
        project.addMapLayer(layer)
        layer.setCrs(crs)
        return layer
    else:
        return print("Layer not valid!")

def open_template(project, manager, layer, dof_layer, layout_template, map_name):
    # Load the layout template
    layout = QgsPrintLayout(project)
    template_file = open(layout_template, encoding="utf-8")
    template_content = template_file.read()
    template_file.close()
    document = QDomDocument()
    document.setContent(template_content)
    layout.loadFromTemplate(document, QgsReadWriteContext())
    layout.setName(map_name)
    manager.addLayout(layout)

    # accessing layout items
    my_table = layout.items()

    # set map
    map = my_table[3]
    map.setLayers([layer, dof_layer])
    rect = layer.extent()
    RECTSCALE = 1.2
    rect.scale(RECTSCALE)
    map.zoomToExtent(rect)
    map.refresh()

    legend = my_table[0]
    root = legend.model().rootGroup()
    legendLyr = QgsProject.instance().mapLayersByName('EM')[0]
    root.addLayer(legendLyr)
    legend.refresh()

    scale = my_table[2]
    # set segment size to "fit the segment size"
    scale.setSegmentSizeMode(1)
    return layout

def export_image(layout, map_name, output_img_path):
    # Export the layout as image or jpg
    exporter = QgsLayoutExporter(layout)
    exporter.exportToImage(output_img_path + map_name + '.jpg', exporter.ImageExportSettings())
    return print(f"{map_name} exported!")


"""
all the same fun of regular TimeSeries, but values can be dictionaries

need to figure out the right behavior for slicing
general idea is you have a timeseries where the vals are dictionaries
TimeSeries: [(0, {'v': 1,
                  'by': 'me'}),
             (1, {'v': 2,
                  'k': 2,
                  'by': 'you'}),
             (2, {'v': 3})]
some of the keys in the dictionary are data keys that you care about ('v', 'k')
and some of them are just sort of bonus contextual info ('by')
slicing should keep track of the data keys the same way a timeseries of lists
so t[2] = (2, {'v': 3,
               'k': 2})
k was unchanged since its last entry, and so is still 2, we dont care about by so it didnt pad out..

"""
from timeseries import TimeSeries
from utils import combine_dicts, float_div, index_of, pad_lists
import numpy as np
from operator import add, mul, sub
from collections import defaultdict

class TimeSeriesDict(TimeSeries):
    """
    work with timeseries data where the values are dictionaries

    Parameters
    ----------
    interpolate: bool, optional
        timeseries will interpolate value if a slice is requested at a time that is not in timeseries
        default False

    use_fv: bool, optional
        timeseries will return first value if a slice is requested before the first time
        default False

    first_val: any basic python type, optional
        timeseries slice will return first_val instead of the first value in timeseries if use_fv is False
        default 0

    val_keys: list, optional
        list of keys that are data keys in timeseries, these keys are the ones used in add/sub/etc operations
        default ['v']
    """
    def __init__(self, *args, **kwargs):
        if len(args) > 1:
            ts_iter = sorted(zip(args[0], args[1]))
        elif isinstance(args[0], list):
            ts_iter = sorted(args[0])
        elif isinstance(args[0], dict):
            ts_iter = sorted(args[0].items())
        else:
            raise Exception

        self.interpolate = kwargs.get('interpolate', False)
        self.val_keys = kwargs.get('val_keys', ['v'])
        self.first_val = kwargs.get('first_val', {key: 0 for key in self.val_keys})
        self._times, self._values = [None] * len(ts_iter), [None] * len(ts_iter)

        # edits underlying data. also a bit gross.
        # but more performant
        # and no need to change slicing method
        # still wonky with first_val false. will return nothing instead of first_val
        prev_val = {k: v for k, v in self.first_val.iteritems()} if self.first_val is not False else {}
        for i, (ts, val) in enumerate(ts_iter):
            self._times[i] = ts
            self.values[i] = {}
            for k, v in prev_val.items() + val.items():
                self._values[i][k] = v
                if k in self.val_keys:
                    prev_val[k] = v

    def __getitem__(self, key):
        times, values = self._new_slice(self._times, self._values, key)
        if not isinstance(times, list):
            return times, values
        return TimeSeriesDict(times, values,
                              interpolate=self.interpolate,
                              first_val=self.first_val,
                              val_keys=self.val_keys)

    def __add__(self, other_timeseries):
        return TimeSeriesDict(self._times_values_op(other_timeseries, add),
                              interpolate=self.interpolate,
                              first_val=self.first_val,
                              val_keys=self.val_keys)

    def __sub__(self, other_timeseries):
        return TimeSeriesDict(self._times_values_op(other_timeseries, sub),
                              interpolate=self.interpolate,
                              first_val=self.first_val,
                              val_keys=self.val_keys)

    def __mul__(self, other_timeseries):
        return TimeSeriesDict(self._times_values_op(other_timeseries, mul),
                              interpolate=self.interpolate,
                              first_val=self.first_val,
                              val_keys=self.val_keys)

    def __div__(self, other_timeseries):
        return TimeSeriesDict(self._times_values_op(other_timeseries, float_div),
                              interpolate=self.interpolate,
                              first_val=self.first_val,
                              val_keys=self.val_keys)

    def __or__(self, other_timeseries):
        tv = dict(zip(self._times, self._values))
        tv.update(dict(zip(other_timeseries.times, other_timeseries.values)))
        return TimeSeriesDict(tv,
                              interpolate=self.interpolate,
                              first_val=self.first_val,
                              val_keys=self.val_keys)

    def _times_values_op(self, other_timeseries, op):
        """
        helper function to perform operations on timeseries
        """
        tv = {}
        val_keys = set(self.val_keys) | set(other_timeseries.val_keys)
        op_list = [op] * len(val_keys)
        for time in sorted(self._times + other_timeseries.times):
            s_values, o_values = self[time][1], other_timeseries[time][1]
            tv[time] = combine_dicts(s_values, o_values, val_keys, op_list)
        return tv

    def _interpolate(self, interp_times, times, values):
        # TODO: there has to be a better way to do this
        # TODO: also not properly handling the non val_keys values
        if isinstance(interp_times, list):
            interped_values = [None] * len(interp_times)
            for val in self.val_keys:
                for i, x in enumerate(np.interp(interp_times, times, [x.get(val) for x in values]).tolist()):
                    try:
                        interped_values[i][val] = x
                    except TypeError:
                        interped_values[i] = {}
                        interped_values[i][val] = x
            return interped_values
        interped_val = {}
        for val in self.val_keys:
            interped_val[val] = np.interp(interp_times, times, [x.get(val) for x in values])
        return interped_val


"""project URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
import allauth.urls
from django.contrib import admin
from django.urls import path, include
from rest_framework.urlpatterns import format_suffix_patterns
from my_silant import views

from rest_framework import routers
from rest_framework.schemas import get_schema_view

router = routers.DefaultRouter()
router.register(r'machines', views.MachineListApiView)
router.register(r'maintenances', views.MachineListApiView)
router.register(r'reclamations', views.ReclamationListApiView)

urlpatterns = [
    path('machine_api/', views.MachineListApiView.as_view()),
    path('maintenance_api/', views.MaintenanceListApiView.as_view()),
    path('reclamation_api/', views.ReclamationListApiView.as_view()),
    path('admin/', admin.site.urls),
    path('', include('my_silant.urls')),
    path('accounts/', include('allauth.urls')),

    path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    path('openapi/', get_schema_view(
        title="my_silant",
        description="API for all things …"
    ), name='openapi-schema'),
]

urlpatterns = format_suffix_patterns(urlpatterns)


import os
import argparse

from deep_ed_PyTorch.entities.ent_name2id_freq import EntNameID

class MergeCrosswikisWiki(object):
    def __init__(self, args):
        self.args = args
        assert os.path.isdir(args.root_data_dir)

        self.wiki = 'generated/wikipedia_p_e_m.txt'
        self.wiki_file = os.path.join(args.root_data_dir, self.wiki)
        self.cross_wiki = 'basic_data/p_e_m_data/crosswikis_p_e_m.txt'
        self.cross_wiki_file = os.path.join(args.root_data_dir, self.cross_wiki)
        self.out = 'generated/crosswikis_wikipedia_p_e_m.txt'
        self.out_file = os.path.join(args.root_data_dir, self.out)

        self.ent_name_id = EntNameID(self.args)

        self.merged_e_m_counts = dict()
        self.load_from_wiki()
        self.merged_with_cross_wiki()
        self.write_p_e_m()

    @property
    def dict(self):
        return self.merged_e_m_counts

    def load_from_wiki(self):

        print('\nMerging Wikipedia and Crosswikis p_e_m')
        print('Process Wikipedia')

        self.load_from_txt(self.wiki_file)

    def merged_with_cross_wiki(self):
        print('Process Crosswikis')

        self.load_from_txt(self.cross_wiki_file)

    def load_from_txt(self, txt_file):
        with open(txt_file) as reader:
            for line in reader:
                line = line.rstrip('\t\n')
                parts = line.split('\t')
                mention = parts[0]

                if 'Wikipedia' not in mention and 'wikipedia' not in mention:
                    if mention not in self.dict:
                        self.dict[mention] = dict()

                    assert int(parts[1]) >= 1, "wiki total frequency error: {}, {}".format(repr(line), parts[1])
                    total_freq = int(parts[1])

                    num_ents = len(parts) - 2
                    for i in range(2, num_ents + 2):
                        ent_str = parts[i].split(',')

                        ent_wikiid = int(ent_str[0])
                        try:
                            ent_wikiid >= 1, "wiki id error: {}, {}".format(repr(line), ent_wikiid)

                        except:
                            print("wiki id error: {} :**: {}".format(repr(line), ent_wikiid))
                            raise 1

                        assert int(ent_str[1]) >= 1, "wiki frequency error: {}, {}".format(repr(line), ent_str[1])
                        freq = int(ent_str[1])

                        if ent_wikiid not in self.dict[mention]:
                            self.dict[mention][ent_wikiid] = 0

                        self.dict[mention][ent_wikiid] += freq

    def write_p_e_m(self):
        print('====> Now sorting and writing p_m_e_from_wiki..')
        with open(self.out_file, 'w') as writer:
            for mention in self.dict:
                el_list = self.dict[mention]
                sorted_dict_items = sorted(el_list.items(), key=lambda x: x[1], reverse=True)
                s = ''
                total_freq = 0
                num_ents = 0
                for wikiid, freq in sorted_dict_items:
                    if self.ent_name_id.is_valid_ent(wikiid):
                        s += str(wikiid) + ',' + str(freq) + ','
                        s += self.ent_name_id.get_ent_name_from_wikiid(wikiid).replace(' ', '_') + '\t'
                        total_freq += freq
                        num_ents += 1

                        if num_ents >= 100:
                            break

                s = mention + '\t' + str(total_freq) + '\t' + s + '\n'
                writer.write(s)

        print('    Done sorting and writing.')

def test(args):
    merge_crosswikis_wiki = MergeCrosswikisWiki(args)
    return merge_crosswikis_wiki

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='generate the prior dictionary p(e|m) from wikipedia'
    )

    parser.add_argument(
        '--root_data_dir',
        type=str,
        # default='/scratch365/yding4/deep_ed_PyTorch/data/',
        required=True,
        help='Root path of the data, $DATA_PATH.',
    )

    args = parser.parse_args()
    merge_crosswikis_wiki = test(args)


import numpy as np
import os
import cv2
from sklearn import preprocessing
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.model_selection import cross_val_score
from sklearn import metrics

def reading_data():
    data = []
    labels = []
    dataset_path = "animals"
    class_folders = os.listdir(dataset_path)

    for class_name in class_folders:
        image_list = os.listdir(dataset_path +  '/' + class_name)
        for image in image_list:
            image = cv2.imread(dataset_path +  '/' + class_name + '/' + image)
            image = cv2.resize(image, (32, 32),interpolation=cv2.INTER_CUBIC)
            data.append(image)
            labels.append(class_name)
    return (data, labels)

def loading_data(data, labels):
    x = np.array(data)
    y = np.array(labels)
    return (x,y)

def encoding_data(x,y):
    label_encoder = preprocessing.LabelEncoder()
    x = x.reshape((x.shape[0],3072))
    y = label_encoder.fit_transform(y)
    return (x,y)

def train_validation_test(x,y, train_ratio, val_ratio, test_ratio):
    second_ratio = (val_ratio)/(train_ratio + val_ratio)
    train_x, test_x, train_y, test_y = train_test_split(x,y, test_size = test_ratio, random_state = 27)
    train_x, val_x, train_y, val_y= train_test_split(train_x,train_y, test_size = second_ratio, random_state = 27)
    return train_x, test_x, train_y, test_y, val_x, val_y

def metrics_score(y_test, y_pred):
    print("Accuracy:",metrics.accuracy_score(y_test, y_pred))
    print("Precision:",metrics.precision_score(y_test, y_pred, average= 'weighted'))
    print("Recall:",metrics.recall_score(y_test, y_pred, average= 'weighted'))
    print("F Score:",metrics.f1_score(y_test,y_pred, average = 'weighted'))

def knn(train_x, test_x, train_y, test_y, neighbors, metrics):
    for neighbor in neighbors:
        for metric in metrics:
            model = KNeighborsClassifier(n_neighbors=neighbor, p=metric)
            model.fit(train_x, train_y)
            y_predict = model.predict(test_x)
            print("Neighborhood = {}, L{}".format(neighbor, metric))
            metrics_score(test_y, y_predict)
            print()

x,y = reading_data()
x,y = loading_data(x,y)
x,y = encoding_data(x,y)
train_x, test_x, train_y, test_y, val_x, val_y = train_validation_test(x,y, 0.7, 0.1, 0.2)

print("KNN Classifier.")
#Cross validation for K = [3,5,7] and L = [L1,L2]
print('Evaluating perfomance over the predictions in the validation set:')
knn(train_x, val_x, train_y, val_y, [3,5,7], [1,2])
#Testing the KNN classifier over the actual testing set
print('Evaluating perfomance over the predictions in the testing set:')
knn(train_x, test_x, train_y, test_y, [7], [1])


"""

If your script has more than a set number of lines,
your code lines are leaking :)

Linescreen counts the lines of code, and 'yells' if the
limit is exceeded. The 'yelling' behaviour can be silenced
The default limit of 500 can be overridden.
Finally, limits can be applied to physical or logical lines.

USAGE: linescreen.py [-h] [-l LIMIT] [-s] [-p] filename

positional arguments:
  filename              The name of the file to screen.

optional arguments:
  -h, --help            show this help message and exit
  -l, --limit		Sets the line limit.
  -s, --stopyell        Overrides limit enforcement, and
			returns just the number of lines
			in the script.
  -p, --physical        Enforces limit on physical lines
			instead of logical lines. It is
			only useful if limit is enforced

(c) Copyright 2019.

"""

__version__ = '0.9.7'

import re
import sys
import logging
import argparse
from tokenize import generate_tokens, open
from token import NEWLINE, STRING, OP, NAME, NL, COMMENT, ENDMARKER

ENFORCE_LIMIT = 'enforce'
IGNORE_LIMIT = 'ignore'
LOGICAL_MODE = 'logical'
PHYSICAL_MODE = 'physical'
LIMIT_REACHED = 'reached'
LIMIT_NOT_REACHED = 'not reached'

#WARNINGS & MESSAGES
LLY404 = ("\n\t| {}-LINE {} LIMIT EXCEEDED!\n"
	  "\t| {} has {} {} lines.\n"
	  "\t| Limit was exceeded at line [{}].\n")

LLY405 = "\n\t| {} has no live code.\n"

LLI200 = "\n\t{} has: \n\t{} {} lines \n\t{} {} lines.\n"

LLI201 = "NUMBER OF LINES WITHIN LIMIT."

class screen:

	def __init__(self, filename=None,
		     mode=LOGICAL_MODE, limit=500,
		     limit_behaviour=ENFORCE_LIMIT,
		     count_lines=False):

		self.filename = filename
		self.mode = mode.lower()
		self.limit = limit
		self.limit_behaviour = limit_behaviour
		self.limit_status = LIMIT_NOT_REACHED
		self.leak_line = None
		self.logical_line_count = None
		self.physical_line_count = None
		self.tokens = []

		try:
			# overriding default IO open with tokenize.open()
			# for automatic encoding detection
			with open(filename) as f:
				for token in generate_tokens(f.readline):
					self.tokens.append(token)
		except FileNotFoundError:
			#...enter code here
			pass

		# COUNT THE LINES
		self._counter()

		# CHECK FOR LIMIT, AND YELL IF LIMIT PASSED
		line_count = self.logical_line_count if \
			     self.mode==LOGICAL_MODE else \
			     self.physical_line_count

		if limit_behaviour==ENFORCE_LIMIT:
			self._yell()
		else:
			self._show_count()

	def _counter(self):
		"""
		Counts the logical lines and physical lines
		of the script, and documents the first line
		that breaks the limit for easy reference.

		"""

		# getting the last line of code.
		# the last line number should be the first item
		# of the third item(a tuple, of course) in the last token
		last_line_no = self.tokens[-1][2][0]

		prev_token_type = None
		prev_line_no = None
		log_line_count = 0

		physical_line_deductions = 0

		for token_type, token_value, a, b, c in self.tokens:

			start_line_num = a[0]
			end_line_num = b[0]
			phy_line_count = start_line_num - physical_line_deductions

			# making necessary adjustments for physical and logical line counts
			# when blank lines, comments, and docstrings are encountered.
			# NOTE: docstrings not considered in count.
			if token_type==NEWLINE:
				log_line_count += 1

			elif ((token_type==NL and start_line_num!=prev_line_no)
				or token_type==COMMENT
				or token_type==ENDMARKER):
				physical_line_deductions += 1

			elif (token_type==STRING
				and token_value.startswith(('"""', "'''"))
				and prev_token_type != OP):
					log_line_count -= 1
					if end_line_num==start_line_num:
						physical_line_deductions += 1
					else:
						physical_line_deductions += end_line_num - start_line_num + 1

			# checking for leak line while limit is not reached.
			# check stops if limit has been reached
			if self.limit_status==LIMIT_NOT_REACHED:
				if  ((log_line_count > self.limit and self.mode==LOGICAL_MODE)
					or (phy_line_count > self.limit and self.mode==PHYSICAL_MODE)
					and token_type not in (NL, COMMENT, STRING)):
						self.limit_status=LIMIT_REACHED
						self.leak_line = start_line_num

			prev_token_type = token_type
			prev_line_no = start_line_num

		self.physical_line_count = last_line_no - physical_line_deductions
		self.logical_line_count = log_line_count

	def _show_count(self):
		sys.stdout.write(LLI200.format(
					self.filename, self.physical_line_count,
					PHYSICAL_MODE, self.logical_line_count,
					LOGICAL_MODE
			)
		)

	def _yell(self):
		line_count = self.logical_line_count if \
				self.mode==LOGICAL_MODE else \
				self.physical_line_count

		if self.limit_status==LIMIT_REACHED:
			logging.warning(
				LLY404.format(
					self.limit, self.mode.upper(), self.filename,
					line_count, self.mode, self.leak_line
				)
			)
		else:
			if line_count:
				sys.stdout.write(LLI201)
				self._show_count()

			else:
				logging.warning(LLY405.format(self.filename,))

def _main():

	parser = argparse.ArgumentParser()
	parser.add_argument("filename",  help="The name of the file to screen.")
	parser.add_argument("-l", "--limit", type=int, help="Sets the line limit.")

	parser.add_argument(
		"-s", "--silence", action="store_true",
		help="Overrides limit enforcement, "
		     "and returns just the number of lines "
		     "in the script."
	)

	parser.add_argument(
		"-p", "--physical", action="store_true",
		help="Enforces limit on physical "
		     "lines instead of logical lines. "
		     "It is only useful if limit is enforced"
	)

	args = parser.parse_args()

	#setting up screen
	filename = args.filename
	limit = args.limit if args.limit else 500
	limit_behaviour = IGNORE_LIMIT if args.silence else ENFORCE_LIMIT
	mode = PHYSICAL_MODE if args.physical else LOGICAL_MODE

	screen(filename, mode, limit, limit_behaviour)

if __name__ == '__main__':
    _main()


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

import os
import requests
import json
import time
from cqhttp import CQHttp
# 引入时间调度器 apscheduler 的 BlockingScheduler
from apscheduler.schedulers.blocking import BlockingScheduler

# bot = CQHttp(api_root='http://127.0.0.1:5701/')

# 创建调度器实例
sched = BlockingScheduler()
interval_delay = 120

header = {
	"Host": "api.amemv.com",
	"Connection": "keep-alive",
	"Accept-Encoding": "gzip",
	"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1"
}

def load_user_config():
	with open('config.json','rb') as f:
		config = json.load(f)
		return config

def get_latest_tiktok_id(config):
	nickname = config["name"]
	for i in init_data:
		if i["nickname"] == nickname:
			return i["latest_tiktok_id"]

def update_latest_tiktok_id(config, latest_tiktok_id):
	nickname = config["name"]
	for i in init_data:
		if i["nickname"] == nickname:
			i["latest_tiktok_id"] = latest_tiktok_id

def get_tiktok_list(sec_uid):
	video_url = 'https://www.iesdouyin.com/web/api/v2/aweme/post/?{0}'
	video_form = {
		"sec_uid": sec_uid,
		"count":"21",
		"max_cursor":"0",
		"aid":"1128",
		"_signature": '4hIiBAAAg525HWk-tq3rV-ISIh', # signature
		"dytk": ''# dytk
	}
	url = video_url.format(
        '&'.join([key + '=' + video_form[key] for key in video_form]))
	response = requests.get(url, headers=header).json()
	tiktok_list = response["aweme_list"]
	return tiktok_list

def monitor_worker(config):
	new_tiktok_list = []
	time_record = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
	print("开始查询：" + time_record)
	msg_queue = []
	# 获取用户数据
	tiktok_list = get_tiktok_list(config['sec_uid'])

	# 最后一次获取的抖音的id
	if len(tiktok_list) == 0:
		print(str(config["name"]) + "未获取到数据，请检查用户是否发过抖音")
	else:
		msg = str(config["name"]) + "：共有"+str(len(tiktok_list))+"条抖音"
		print(msg)
	msg = ""
	latest_tiktok_id = get_latest_tiktok_id(config)
	for i in tiktok_list:
		# aweme_id = 当前抖音的id
		aweme_id = i["aweme_id"]
		if int(aweme_id) > int(latest_tiktok_id):
			new_tiktok_list.append(i)
			update_latest_tiktok_id(config, aweme_id)

	if tiktok_list:
		print("发现" + str(len(new_tiktok_list)) + "条新抖音\n")
		for i in new_tiktok_list:
			share_desc = i["desc"]
			cover_img = "https://p9-dy.byteimg.com/img/" + i["video"]["cover"]["uri"] + "~c5_300x400.jpg"
			play_addr = "https://aweme.snssdk.com/aweme/v1/play/?video_id=" + i["video"]["play_addr"]["uri"] + "&line=0"
			msg = [{'type': 'at', 'data': {'qq': str(config['at'])}},
					{'type': 'text', 'data': {'text': '\n%s更新抖音啦！\n标题：%s\n封面：\n' % (config["name"],share_desc)}},
					{'type': 'image','data': {'file': cover_img}},
					{'type': 'text', 'data': {'text': '\n链接：%s\n' % play_addr}}]
			msg_queue.append(msg)

	else:
		print("未发现更新\n")
	return msg_queue

def tiktok_monitor():
	try:
		time_record = time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(time.time()))
		print(time_record, 'Monitoring Tiktok ...')
		for config in configs:
			msg_queue = monitor_worker(config)
			if len(msg_queue) > 0:
				for msg in msg_queue:
					bot.send_group_msg_async(group_id=config['qq_group'], message=msg, auto_escape=False)
					time.sleep(0.1)
	except Exception as e:
		print("ERROR When monitoring %s" % e)
	finally:
		pass

try:
	sched.add_job(
	    tiktok_monitor, 'interval', seconds=interval_delay,
	    misfire_grace_time=interval_delay, coalesce=True, max_instances=15)
except Exception as e:
	print(e)
# 开始调度任务

def init_task():
	for config in configs:
		sec_uid = config["sec_uid"]
		latest_tiktok_id = get_tiktok_list(sec_uid)[0]["aweme_id"]
		d = {}
		d["nickname"] = config["name"]
		d["sec_uid"] = sec_uid
		d["latest_tiktok_id"] = latest_tiktok_id
		init_data.append(d)

if __name__ == '__main__':
	init_data = []
	configs = load_user_config()
	init_task()
	sched.start()
	print(init_data)


from src.body import Body
import pygame

class Projectile(Body):

    def __init__(self, speed, pos, damage=0, color=(0, 255, 0), parent=None, effects=None, *args, **kwargs):

        super().__init__(*args, **kwargs)

        self.speed = speed
        self.pos = pygame.Vector2(pos)
        self.damage = damage
        self.color = color
        self.parent = parent
        if effects is None:
            self.effects = []
        else:
            self.effects = effects
        self.mask = pygame.mask.from_surface(self.image)

        # Temporary
        pygame.draw.rect(self.image, self.color, pygame.Rect(0, 0, self.width, self.height))
        self.original_image = self.image.copy()
        self.rect = self.image.get_rect(center=self.pos)

    def update(self, delta_time, display=None, *args, **kwargs):
        super().update(delta_time, *args, **kwargs)
        self.image = pygame.transform.rotate(self.original_image, self.angle)
        self.rect = self.image.get_rect(center=self.rect.center)
        self.move(delta_time)
        # print(self.effects)
        if not display.get_surface().get_rect().contains(self.rect):
            self.kill()
            del self

    def move(self, delta_time):
        self.pos += (self.direction * self.speed * delta_time) / 1000
        self.rect.center = self.pos

class ProjectileEffect(Body):

    def __init__(self, direct_damage=0, damage_over_time=0, duration=0, parent=None, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.direct_damage = direct_damage  # this is dealt on the initial hit with the target
        self.damage_over_time = damage_over_time  # this is dealt on every call of update()
        self.duration = duration * 1000
        self.current_duration = 0
        self.parent = parent
        self.targets_affected = []  # check which target(s) are affected by the effect
        self.targets_hit = []
        self.expired = False

    def spawn(self, pos, parent=None):
        self.pos = pos
        self.rect.center = pos
        self.parent = parent
        self.current_duration = 0
        self.expired = False
        self.targets_hit = []
        self.targets_affected = []

    def update(self, delta_time, *args, **kwargs):
        self.current_duration += delta_time
        if self.current_duration >= self.duration:
            self.expired = True
        for target in self.targets_hit:
            if target.is_dead:
                self.targets_hit.remove(target)
                if target in self.targets_affected:
                    self.targets_affected.remove(target)
            if target not in self.targets_affected:
                target.receive_damage_over_time_effect(self.damage_over_time, self.duration)
                self.targets_affected.append(target)

class Explosion(ProjectileEffect):

    def __init__(self, radius=0, explosion_lifetime=0.0, *args, **kwargs):
        self.radius = radius
        size = radius * 2
        super().__init__(size=(size, ), *args, **kwargs)
        self.explosion_lifetime = explosion_lifetime * 1000

    def update(self, delta_time, *args, **kwargs):
        super().update(delta_time, *args, **kwargs)
        self.move(delta_time)
        if self.current_duration >= self.explosion_lifetime:
            self.image.fill((0, 0, 0))
            # self.mask = self.image.get_masks()

    def spawn(self, *args, **kwargs):
        super().spawn(*args, **kwargs)
        self.direction = self.parent.direction
        self.speed = self.parent.speed / 4
        pygame.draw.circle(self.image, self.color, self.image.get_rect().center, self.radius)
        self.mask = self.image.get_masks()
        return Explosion(self.radius, self.explosion_lifetime, self.direct_damage, self.damage_over_time,
                         self.duration, self.color)

projectile_effect_dict = {'small_explosion': Explosion(radius=50, explosion_lifetime=0.25, direct_damage=40,
                                                       damage_over_time=40, duration=4, color=(170, 200, 20))}


'''
Given a list of numbers L, implement a method sum(i, j) which returns the sum from the sublist L[i:j] (including i, excluding j).

For example, given L = [1, 2, 3, 4, 5], sum(1, 3) should return sum([2, 3]), which is 5.

You can assume that you can do some pre-processing. sum() should be optimized over the pre-processing step.
'''

from typing import List

class ListSumOptimizer:
    def __init__(self, num_arr: List[int]):
        self._num_arr = num_arr
        self._sum_arr = []
        self._calculate_sum()

    # Preprocess method. Creates a list of sums of all previous numbers prior to its index.
    def _calculate_sum(self):
        self._sum_arr = [0]
        for i, num in enumerate(self._num_arr):
            self._sum_arr.append(num + self._sum_arr[i])
        return

    # Set the num_arr to a property so that when it's modified, we can automatically recalculate _sum_arr.
    @property
    def num_arr(self):
        return self._num_arr

    @num_arr.setter
    def num_arr(self, num_arr: List[int]):
        self._num_arr = num_arr
        self._calculate_sum()
        return

    # Sum method. Due to preprocessing, we can do a single subtraction operation to get the sum from i to j instead of a loop.
    def sum(self, i: int, j: int):
        if i > j:
            raise ValueError("Start index must be less than or equal to the end index.")
        if i < 0 or j > len(self._num_arr):
            raise IndexError("Start index and/or end index out of range.")

        return self._sum_arr[j] - self._sum_arr[i]

x = ListSumOptimizer([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
print(x.sum(3, 7)) # 22
print(x.sum(0, 10)) # 55
x.num_arr = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
print(x.sum(3, 7)) # 62


'''
    Contains functions that project labels from panoramas to 3D and vice-versa
    Some functions are borrowed/adapted from: https://github.com/mikedh/trimesh/blob/master/trimesh/transformations.py

    author : Iro Armeni
    version: 1.0
'''

import os
import numpy as np
import math
from scipy.misc import imsave
from PIL import Image

## <-- From: https://github.com/mikedh/trimesh/blob/master/trimesh/transformations.py ##

_EPS = np.finfo(float).eps * 4.0  # epsilon for testing whether a number is close to zero
_NEXT_AXIS = [1, 2, 0, 1]  # axis sequences for Euler angles
# map axes strings to/from tuples of inner axis, parity, repetition, frame
_AXES2TUPLE = {
    'sxyz': (0, 0, 0, 0), 'sxyx': (0, 0, 1, 0), 'sxzy': (0, 1, 0, 0),
    'sxzx': (0, 1, 1, 0), 'syzx': (1, 0, 0, 0), 'syzy': (1, 0, 1, 0),
    'syxz': (1, 1, 0, 0), 'syxy': (1, 1, 1, 0), 'szxy': (2, 0, 0, 0),
    'szxz': (2, 0, 1, 0), 'szyx': (2, 1, 0, 0), 'szyz': (2, 1, 1, 0),
    'rzyx': (0, 0, 0, 1), 'rxyx': (0, 0, 1, 1), 'ryzx': (0, 1, 0, 1),
    'rxzx': (0, 1, 1, 1), 'rxzy': (1, 0, 0, 1), 'ryzy': (1, 0, 1, 1),
    'rzxy': (1, 1, 0, 1), 'ryxy': (1, 1, 1, 1), 'ryxz': (2, 0, 0, 1),
    'rzxz': (2, 0, 1, 1), 'rxyz': (2, 1, 0, 1), 'rzyz': (2, 1, 1, 1)}
_TUPLE2AXES = dict((v, k) for k, v in _AXES2TUPLE.items())

def unit_vector(data, axis=None, out=None):
    '''
        Return ndarray normalized by length, i.e. Euclidean norm, along axis.
    '''
    if out is None:
        data = np.array(data, dtype=np.float64, copy=True)
        if data.ndim == 1:
            data /= math.sqrt(np.dot(data, data))
            return data
    else:
        if out is not data:
            out[:] = np.array(data, copy=False)
        data = out
    length = np.atleast_1d(np.sum(data * data, axis))
    np.sqrt(length, length)
    if axis is not None:
        length = np.expand_dims(length, axis)
    data /= length
    if out is None:
        return data

def rotation_matrix(angle, direction, point=None):
    '''
        Return matrix to rotate about axis defined by point and
        direction.
        Parameters
        -------------
        angle     : float, or sympy.Symbol
                    Angle, in radians or symbolic angle
        direction : (3,) float
                    Unit vector along rotation axis
        point     : (3, ) float, or None
                    Origin point of rotation axis
        Returns
        -------------
        matrix : (4, 4) float, or (4, 4) sympy.Matrix
                 Homogenous transformation matrix
    '''
    # special case sympy symbolic angles
    if type(angle).__name__ == 'Symbol':
        import sympy as sp
        sina = sp.sin(angle)
        cosa = sp.cos(angle)
    else:
        sina = math.sin(angle)
        cosa = math.cos(angle)

    direction = unit_vector(direction[:3])
    # rotation matrix around unit vector
    M = np.diag([cosa, cosa, cosa, 1.0])
    M[:3, :3] += np.outer(direction, direction) * (1.0 - cosa)

    direction = direction * sina
    M[:3, :3] += np.array([[0.0, -direction[2], direction[1]],
                           [direction[2], 0.0, -direction[0]],
                           [-direction[1], direction[0], 0.0]])

    # if point is specified, rotation is not around origin
    if point is not None:
        point = np.array(point[:3], dtype=np.float64, copy=False)
        M[:3, 3] = point - np.dot(M[:3, :3], point)

    # return symbolic angles as sympy Matrix objects
    if type(angle).__name__ == 'Symbol':
        return sp.Matrix(M)

    return M

def quaternion_matrix(quaternion):
    '''
        Return rotation matrix from quaternion ([w, x, y, z])
    '''
    # epsilon for testing whether a number is close to zero
    q = np.array(quaternion, dtype=np.float64, copy=True)
    n = np.dot(q, q)
    if n < _EPS:
        return np.identity(4)
    q *= math.sqrt(2.0 / n)
    q = np.outer(q, q)
    return np.array([
        [1.0-q[2, 2]-q[3, 3],     q[1, 2]-q[3, 0],     q[1, 3]+q[2, 0], 0.0],
        [    q[1, 2]+q[3, 0], 1.0-q[1, 1]-q[3, 3],     q[2, 3]-q[1, 0], 0.0],
        [    q[1, 3]-q[2, 0],     q[2, 3]+q[1, 0], 1.0-q[1, 1]-q[2, 2], 0.0],
        [                0.0,                 0.0,                 0.0, 1.0]])

def euler_from_quaternion(quaternion, axes='sxyz'):
    '''
        Return Euler angles from quaternion for specified axis sequence.
    '''
    return euler_from_matrix(quaternion_matrix(quaternion), axes)

def euler_from_matrix(matrix, axes='sxyz'):
    '''
        Return Euler angles from rotation matrix for specified axis sequence.
        axes : One of 24 axis sequences as string or encoded tuple
        Note that many Euler angle triplets can describe one matrix.
    '''
    try:
        firstaxis, parity, repetition, frame = _AXES2TUPLE[axes.lower()]
    except (AttributeError, KeyError):
        _TUPLE2AXES[axes]  # noqa: validation
        firstaxis, parity, repetition, frame = axes

    i = firstaxis
    j = _NEXT_AXIS[i+parity]
    k = _NEXT_AXIS[i-parity+1]

    M = np.array(matrix, dtype=np.float64, copy=False)[:3, :3]
    if repetition:
        sy = math.sqrt(M[i, j]*M[i, j] + M[i, k]*M[i, k])
        if sy > _EPS:
            ax = math.atan2( M[i, j],  M[i, k])
            ay = math.atan2( sy,       M[i, i])
            az = math.atan2( M[j, i], -M[k, i])
        else:
            ax = math.atan2(-M[j, k],  M[j, j])
            ay = math.atan2( sy,       M[i, i])
            az = 0.0
    else:
        cy = math.sqrt(M[i, i]*M[i, i] + M[j, i]*M[j, i])
        if cy > _EPS:
            ax = math.atan2( M[k, j],  M[k, k])
            ay = math.atan2(-M[k, i],  cy)
            az = math.atan2( M[j, i],  M[i, i])
        else:
            ax = math.atan2(-M[j, k],  M[j, j])
            ay = math.atan2(-M[k, i],  cy)
            az = 0.0

    if parity:
        ax, ay, az = -ax, -ay, -az
    if frame:
        ax, az = az, ax
    return ax, ay, az

## --> ##

def get_coord_start(pose):
    '''
        Convert the raw pose csv information to translation vectors and rotation matrices
        Args:
            pose : the camera pose array [0-2: xyz, 3-6: quaternion in xyzw]
        Return: (tuple)
            trans   : translation vector
            quat    : wxyz quaternion
            rot_mat : rotation matrix
    '''
    trans = np.zeros((3))
    trans[0:3] = pose[0:3]
    quat = np.zeros((4))
    # from xyzw format to wxyz
    quat[0:4] = [pose[6], pose[3], pose[4], pose[5]]
    rot_mat = quaternion_matrix(quat)
    return (trans, quat, rot_mat)

def quaternion_to_euler_angle(w, x, y, z):
    '''
       Convert a quaternion to euler angles
       Args:
            w, x, y, z : quaternion parameters
        Return:
            X, Y, Z : euler angles around respective axes
    '''
    t0 = +2.0 * (w * x + y * z)
    t1 = +1.0 - 2.0 * (x * x + y * y)
    X = math.degrees(math.atan2(t0, t1))
    t2 = +2.0 * (w * y - z * x)
    t2 = +1.0 if t2 > +1.0 else t2
    t2 = -1.0 if t2 < -1.0 else t2
    Y = math.degrees(math.asin(t2))
    t3 = +2.0 * (w * z + x * y)
    t4 = +1.0 - 2.0 * (y * y + z * z)
    Z = math.degrees(math.atan2(t3, t4))
    return X, Y, Z

def getpntfrompix(row, col, origin, size, radius):
    '''
        Find the 3D point that corresponds to that pixel in the panorama
        Args:
            row : the row pixel coord
            col : the column pixel coord
            size : the size of the panorama
            radius : the distance of the point from the camera center
        Return:
            point : 3D coordinates of point
    '''
    origin_euler = euler_from_matrix(origin[2])
    ## From: http://panocontext.cs.princeton.edu/panorama.pdf
    theta_y = (math.pi * row) / size[0]  # find angle between radius from sphere center and floor (vertical slice of 3D sphere)
    theta_x = (2 * math.pi * col) / size[1]  # find angle between radius from sphere center and point (horizontal slice of 3D sphere)
    r = radius * abs(math.cos(theta_y))  # find adjacent edge (trigonometry) of distance from sphere origin to 3D point (found from mist value)
    # calculate the 3D point -- Y is the axis up
    point = np.zeros((3), dtype=float)
    point[0] = r * math.cos(-1.0*theta_x + math.pi/2)
    point[1] = r * math.sin(-1.0*theta_x + math.pi/2)
    sign = -1.0
    point[2] = sign * (r * math.tan(theta_y))
    point = np.dot(origin[2][0:3, 0:3], point)
    point += origin[0]
    return point

def export_txt(path, cloud):
    '''
        Export 3D point cloud to a txt file
        Args:
            path  : file path to export file
            cloud : 3D point cloud
    '''
    file = open(path, 'w')
    for pnt in cloud:
        for i in range(len(pnt)):
            if i < len(pnt) - 1:
                file.write(str(pnt[i]) + " ")
            else:
                file.write(str(pnt[i]) + "\n")
    file.close()
    return

def dist_from_mist(value):
    '''
        Find the distance of each 3D point on the obj/pixel to the camera center
    '''
    max_m = 65536
    max_d = 128.0
    d = value * (max_d / max_m)
    return d

def get_pano_label_from3Dcoords(points, semt, mist, pose, thresh, pano, length=None):
    '''
        Find labels for the 3D point cloud given the pano semantic segmentation
        Args:
            points : the point cloud (sampled points on mesh faces)
            semt   : the semantics of the panorama
            mist   : the mist pano
            pose   : the camera pose
            thresh : radius in 3D of assigning pixel label to a 3D point
            length : radius of points from camera location to assign a label
        Return:
            labels  : semantics to assign to each 3D point (that exists in the to_keep)
            to_keep : indices of 3D points to keep based on defined length
            pixels  : pixel coordinates for each 3D point (N x 2)
    '''
    origin = get_coord_start(pose)  # camera location
    interpol = 4  # interpolation factor used in the panorama aggregation code
    semt_large = Image.fromarray(semt.astype('int32'))
    semt_large = semt_large.resize((semt_large.size[0]*interpol, semt_large.size[1]*interpol), Image.NEAREST)
    semt_large = np.array(semt_large)
    mist = Image.fromarray(mist)
    mist = mist.resize((mist.size[0]*interpol, mist.size[1]*interpol), Image.BILINEAR)
    mist = np.array(mist)
    pixels, to_keep = get_pixel_from_3Dcoord(points, origin, mist.shape, mist, thresh, length)
    if len(semt_large.shape) == 2:
        labels = semt_large[pixels[to_keep, 0], pixels[to_keep, 1]]
    else:
        labels = semt_large[pixels[to_keep, 0], pixels[to_keep, 1], :]
    return labels, to_keep, ((pixels[to_keep, 0]/interpol).astype(int), (pixels[to_keep, 1]/interpol).astype(int))

def get_pixel_from_3Dcoord(points, origin, size, mist, thresh, length=None):
    '''
        Assign pixels to 3D points
        Args:
            points : the point cloud (sampled points on mesh faces)
            origin : the camera pose tuple with: translation vector, quaternion, and rotation matrix
            size   : pano size
            mist   : the raw mist pano
            thresh : radius in 3D of assigning pixel label to a 3D point
            length : radius of points from camera location to assign a label
        Return:
            pixel  : pixel coordinate for each 3D point
            export : indices of 3D points to keep based on defined length
    '''
    ## find 3D point coordinates w.r.t. 0,0,0
    temp = points - origin[0]
    temp = np.dot(temp, origin[2][0:3, 0:3])
    point_o = np.zeros((points.shape))
    point_o[:,0] = temp[:,0]
    point_o[:,1] = temp[:,2]
    point_o[:,2] = temp[:,1]
    ## map 3d point onto sphere
    point_ = np.zeros((points.shape))
    for i in range(3):
        point_[:,i] = point_o[:,i]/np.sqrt(np.square(point_o[:,0])+np.square(point_o[:,1])+np.square(point_o[:,2]))
    ## convert to spherical coordinates (phi, theta)
    y_theta = None
    y_theta = -1.0 * np.arcsin(point_[:,1])
    x_theta = None
    x_theta = np.arccos(point_[:,2] / np.cos(y_theta))
    below_zero = np.where((np.sin(x_theta)*np.cos(y_theta))*point_[:,0] < 0)[0]
    if len(below_zero) > 0:
        x_theta[below_zero] = -1.0 * x_theta[below_zero]
    ## convert to spherical image coordinates
    pixel = np.zeros((points.shape[0], 2), dtype=int)
    pixel[:,1] = x_theta * size[1]/(2*math.pi)
    pixel[:,0] = y_theta * size[0]/math.pi
    keep_Y = None
    keep_X = None
    for i in range(2):
        pixel[:, i] =  pixel[:, i] + size[i]/2
        temp = np.where(pixel[:,i] >= 0)[0]
        if len(temp > 0):
            temp2 = np.where(pixel[temp, i] < size[i])[0]
            if len(temp2 > 0):
                if i == 0:
                    keep_Y = temp[temp2]
                else:
                    keep_X = temp[temp2]
    rows = None
    if keep_X is not None and keep_Y is not None:
        rows = np.intersect1d(keep_Y, keep_X)
    if rows is not None and (rows.shape[0])>0:
        dist = np.linalg.norm(point_o[rows], axis=1)
        diff = dist - dist_from_mist(mist[pixel[rows, 0], pixel[rows, 1]])
        to_keep = np.where(abs(diff) <= thresh)[0]
        depth = dist_from_mist(mist[pixel[rows[to_keep], 0], pixel[rows[to_keep], 1]])
        if length is not None:
            to_keep2 = np.where(depth <= length)[0]
            export = rows[to_keep[to_keep2]]
        else:
            export = rows[to_keep]
    return pixel, export

def project_labels_3Dpnts_2_pano(semt_img, pixels_tokeep, colors, class2col=None, output_path=None):
    '''
        Exports panorama segmentation
        Args:
            semt_img      : the semantic segmentation
            pixels_tokeep : indices of pixels if not wanting to export all
            colors        : the color palette
            class2col     : maps object classes to color IDs in the color palette
            output_path   : file path to export segmentation
    '''
    temp  = np.zeros((semt_img.shape[0], semt_img.shape[1], 3))
    image = np.ones((semt_img.shape), dtype=int) * -1
    image[pixels_tokeep[0], pixels_tokeep[1]] = semt_img[pixels_tokeep[0], pixels_tokeep[1]]
    un_labs = np.unique(image)
    for un_ in un_labs:
        if un_ == -1:
            continue
        locs = np.where(image == un_)
        if class2col is not None:
            temp[locs[0], locs[1], :] = colors[class2col.index(un_)]
        else:
            temp[locs[0], locs[1], :] = colors[un_]
    imsave(output_path, temp)

def project_labels_3Dpnts_2_pano_blendedorig(semt_img, rgb_img, colors, class2col=None, output_path=None):
    '''
        Exports panorama segmentation superimposed on the raw RGB panorama
        Args:
            semt_img    : the semantic segmentation
            rgb_img     : the raw RGB panorama
            colors      : the color palette
            class2col   : maps object classes to color IDs in the color palette
            output_path : file path to export segmentation
    '''
    original = Image.fromarray(rgb_img.copy())
    original = original.convert("RGBA")
    temp = np.zeros((rgb_img.shape))
    uniq_class = np.unique(semt_img)
    for un_ in uniq_class:
        if un_ == 0:
            continue
        locs = np.where(semt_img==un_)
        if class2col is not None:
            temp[locs[0], locs[1], :] = colors[class2col.index(un_)]
        else:
            temp[locs[0], locs[1], :] = colors[un_]
    temp2 = Image.fromarray(temp.astype('uint8'))
    temp2 = temp2.convert("RGBA")
    blended = Image.blend(original, temp2, alpha=.5)
    blended.save(output_path)


from enum import StrEnum, auto
from math import sin, cos, pi

from shapely import Point, LineString, Polygon
from shapely.affinity import translate, rotate

class SectionType(StrEnum):
    Triangle = auto()
    Rectangle = auto()
    Trapezoid = auto()
    TShape = auto()
    IShape = auto()
    LShape = auto()
    Box = auto()
    Pipe = auto()
    RegularPolygon = auto()
    Circle = auto()
    Ellipse = auto()
    Custom = auto()

def move_centroid_to_origin(section: Polygon) -> Polygon:
    return translate(section, -section.centroid.x, -section.centroid.y)

def TriangleSct(b: float, h: float) -> Polygon:
    sct = Polygon([(0, h), (-b/2, 0), (b/2, 0)])
    return move_centroid_to_origin(sct)

def RectangleSct(b: float, h: float) -> Polygon:
    return Polygon([(b/2, h/2), (-b/2, h/2), (-b/2, -h/2), (b/2, -h/2)])

def TrapzoidSct(b1: float, b2: float, h: float) -> Polygon:
    sct = Polygon([(b1/2, h/2), (-b1/2, h/2), (-b2/2, -h/2), (b2/2, -h/2)])
    return move_centroid_to_origin(sct)

def TShapeSct(b: float, h: float, th1: float, tb1: float,
              th2: float|None=None, tb2: float|None=None) -> Polygon:
    tb2 = tb1 if tb2 == None else tb2
    th2 = th1 if th2 == None else th2
    sct = Polygon([(b/2, h), (-b/2, h), (-b/2, h-th1), (-tb2/2, h-th2),
                  (-tb1/2, 0), (tb1/2, 0), (tb2/2, h-th2), (b/2, h-th1), (b/2, h)])
    return move_centroid_to_origin(sct)

def IShapeSct(b_top: float, t_top: float, c_top: float, h:float, tw: float,
              b_bot: float|None=None, t_bot: float|None=None, c_bot: float|None=None) -> Polygon:
    b_bot = b_top if b_bot==None else b_bot
    t_bot = t_top if t_bot==None else t_bot
    c_bot = c_top if c_bot==None else c_bot
    sct = Polygon([(b_top/2, h/2), (-b_top/2, h/2), (-b_top/2, h/2-t_top), (-tw/2, h/2-t_top-c_top),
                   (-tw/2, -h/2+t_bot+c_bot), (-b_bot/2, -h/2+t_bot), (-b_bot/2, -h/2), (b_bot/2, -h/2),
                   (b_bot/2, -h/2+t_bot), (tw/2, -h/2+t_bot+c_bot), (tw/2, h/2-t_top-c_top), (b_top/2, h/2-t_top),
                   (b_top/2, h/2)])
    return move_centroid_to_origin(sct)

def LShapeSct(b: float, h: float, th1: float, tb1: float,
              th2: float|None=None, tb2: float|None=None) -> Polygon:
    tb2 = tb1 if tb2 == None else tb2
    th2 = th1 if th2 == None else th2
    sct = Polygon([(0, 0), (0, h), (b, h), (b, h-th1),
                  (tb2, h-th2), (tb1, 0), (0, 0)])
    return move_centroid_to_origin(sct)

def BoxSct(b: float, h: float, th: float) -> Polygon:
    outerRect = Polygon(RectangleSct(b, h))
    innerRect = Polygon(RectangleSct(b-2*th, h-2*th))
    return outerRect.difference(innerRect)

def CircleSct(d: float) -> Polygon:
    return Point(0, 0).buffer(d/2)

def PipeSct(d: float, th: float) -> Polygon:
    outerCircle = Polygon(CircleSct(d))
    innerCircle = Polygon(CircleSct(d-(2*th)))
    return outerCircle.difference(innerCircle)

def CreateEllipseSct(a: float, b: float) -> Polygon:
    n = 100
    theta = [2 * pi * i / n for i in range(n)]
    return Polygon([(a*cos(t), b*sin(t)) for t in theta])

def DistanceFrom(section: Polygon, dist: float, position: str="top") -> LineString:
    minx, miny, maxx, maxy = section.bounds
    if (position == "top" or position=="bottom") and (dist > maxy-miny):  # type: ignore
        raise ValueError("distance is greater than height of shape.")
    if (position == "right" or position=="left") and (dist > maxx-minx):  # type: ignore
        raise ValueError("distance is greater than width of shape.")
    line = LineString([(maxx+10, maxy-dist), (minx-10, maxy-dist)]) if position=="top" else \
               LineString([(maxx+10, miny+dist), (minx-10, miny+dist)]) if position=="bottom" else \
               LineString([(maxx-dist, maxy+10), (maxx-dist, miny-10)]) if position=="right" else \
               LineString([(minx+dist, maxy+10), (minx+dist, miny-10)]) if position=="left" else None
    return section.intersection(line)

def Edge(section: Polygon, position: str = "top") -> LineString:
    coords = list(section.exterior.coords)
    del coords[-1]
    minx, miny, maxx, maxy = section.bounds
    Points = [point for point in coords if point[1]==maxy] if position=="top" else\
                [point for point in coords if point[1]==miny] if position=="bottom" else\
                [point for point in coords if point[0]==maxx] if position=="right" else\
                [point for point in coords if point[0]==minx] if position=="left" else None
    spoint1 = (0, 0)
    spoint2 = (0, 0)
    IsThreePoint: bool = False
    if Points==None: raise ValueError("position is wrong")
    if len(Points) > 1:
        output = LineString(Points)
    else:
        firstPoint = Points[0]
        secondPoint = (0, 0)
        if coords.index(firstPoint)==0:
            secondPoint = coords[coords.index(firstPoint)+1]
        elif coords.index(firstPoint)==len(coords)-1:
            secondPoint = coords[coords.index(firstPoint)-1]
        else:
            spoint1 = coords[coords.index(firstPoint)-1]
            spoint2 = coords[coords.index(firstPoint)+1]
            if position == "top":
                IsThreePoint = firstPoint[1]-spoint1[1] == firstPoint[1]-spoint2[1]
                secondPoint = spoint1 if firstPoint[1]-spoint1[1] < firstPoint[1]-spoint2[1] else spoint2
            elif position == "bottom":
                IsThreePoint = spoint1[1]-firstPoint[1] == spoint2[1]-firstPoint[1]
                secondPoint = spoint1 if spoint1[1]-firstPoint[1] < spoint2[1]-firstPoint[1] else spoint2
            elif position == "right":
                IsThreePoint = firstPoint[0]-spoint1[0] == firstPoint[0]-spoint2[0]
                secondPoint = spoint1 if firstPoint[0]-spoint1[0] < firstPoint[0]-spoint2[0] else spoint2
            elif position == "left":
                IsThreePoint = spoint1[0]-firstPoint[0] == spoint2[0]-firstPoint[0]
                secondPoint = spoint1 if spoint1[0]-firstPoint[0] < spoint2[0]-firstPoint[0] else spoint2

        output = LineString([spoint1, firstPoint, spoint2]) if IsThreePoint else LineString([firstPoint, secondPoint])
    return output


from random import choice
from settings import Settings
import pygame

class Moves():
    def __init__(self):
        super().__init__()
        self.laban = {
            "Press":"Images/press.bmp",
            "Flick":"Images/flick.bmp",
            "Wring":"Images/wring.bmp",
            "Dab":"Images/dab.bmp",
            "Slash":"Images/slash.bmp",
            "Glide":"Images/glide.bmp",
            "Thrust":"Images/thrust.bmp",
            "Float":"Images/float.bmp",
        }

        self.transitions = {
            "Spin":"Images/spin.bmp",
            "Step":"Images/step.bmp",
        }

        self.s = Settings()
        self.X = self.s.X//2
        self.Y = self.s.Y//2
        self.pos =  "Centerstage center"

    def get_move(self):
        '''Return a random move'''
        self.rand = choice([0,1])
        if self.rand == 0:
            move = choice(list(self.laban.keys()))
        else:
            move = choice(list(self.transitions.keys()))

        return move

    def update(self,new_img):
        self.image = pygame.image.load(new_img)

    def show_move(self, move):
        if self.rand == 0:
            self.image = pygame.image.load(self.laban[move])
        else:
            self.image = pygame.image.load(self.transitions[move])
        self.image = pygame.transform.scale(self.image,(40,40))
        self.img_rect = self.image.get_rect()
        self.img_rect.center = (self.X,self.Y)
        self.s.screen.blit(self.image,self.img_rect)

    def walk(self):
        if self.rand != 0:
            Xnew = 0
            Ynew = 0
            self.pos = ""
            if self.Y == self.s.Y//6:
                Ynew = choice([0,self.s.Y//3])
                self.pos += "Upstage "
            elif self.Y == self.s.Y//3:
                Ynew = choice([-self.s.Y//3,0,self.s.Y//3])
                self.pos += "Centerstage "
            elif self.Y == self.s.Y//2:
                Ynew = choice([-self.s.Y//3,0])
                self.pos += "Downstage "
            if self.X == self.s.X//6:
                Xnew = choice([0,self.s.X//3])
                self.pos += "left"
            elif self.X == self.s.X//3:
                Xnew = choice([-self.s.X//3,0,self.s.X//3])
                self.pos += "center"
            elif self.X == self.s.X//2:
                Xnew = choice([-self.s.X//3,0])
                self.pos += "right"

            self.X += Xnew
            self.Y += Ynew
            self.s.screen.blit(self.image,self.img_rect)
        return self.pos


# Henrique Sander Lourenço
# 10802705

import gi

gi.require_version("Gtk", "3.0")

from gi.repository import Gtk
from os.path import abspath, dirname, join

# Create App class
class App:

    def __init__(self):

        # Connect to glade file
        self.builder = Gtk.Builder()
        self.builder.add_from_file('converter.glade')

        # Get window object and set title
        self.window = self.builder.get_object('window')
        self.window.set_title('Conversor de unidades')

        # Create quantities and units lists in Gtk
        self.quantities_list = Gtk.ListStore(int, str)
        self.volume_units_list = Gtk.ListStore(int, str)
        self.temperature_units_list = Gtk.ListStore(int, str)

        # Quantities
        quantities = [
            [1, 'Volume'],
            [2, 'Temperatura']]

        # Append quantities to Gtk list
        for qty in quantities:
            self.quantities_list.append(qty)

        # Volume units
        volume_units = [
            [1, 'milímetros cúbicos (mm³)'],
            [2, 'centímetros cúbicos (cm³)'],
            [3, 'litros (L)']]

        # Temperature units
        temperature_units = [
            [1, 'Celsius'],
            [2, 'Farenheint'],
            [3, 'Kelvin']
        ]

        # Append volume units to Gtk list
        for unit in volume_units:
            self.volume_units_list.append(unit)

        # Append temperature units to Gtk list
        for unit in temperature_units:
            self.temperature_units_list.append(unit)

        # Configure combo boxes
        self.combo_qty = self.builder.get_object('combo_qty') # Quantities combo box
        self.combo_from = self.builder.get_object('combo_from') # 'From units' combo box
        self.combo_to = self.builder.get_object('combo_to') # 'To units' combo box
        self.combo_qty.set_model(self.quantities_list) # Set quantities combo box model
        self.combo_from.set_model(self.volume_units_list) # Set 'from units' combo box model
        self.combo_to.set_model(self.volume_units_list) # Set 'to units' combo box model

        # Add combo boxes renderer
        renderer_text = Gtk.CellRendererText()
        self.combo_qty.pack_start(renderer_text, True)
        self.combo_from.pack_start(renderer_text, True)
        self.combo_to.pack_start(renderer_text, True)

        # Choose what to show on combo boxes
        self.combo_qty.add_attribute(renderer_text, "text", 1)
        self.combo_from.add_attribute(renderer_text, "text", 1)
        self.combo_to.add_attribute(renderer_text, "text", 1)

        # Default options
        self.combo_qty.set_active(0)
        self.combo_from.set_active(0)
        self.combo_to.set_active(0)
        self.unitFrom = 1 # Tell which unit the quantity is being converted from
        self.unitTo = 1 # Tell which unit the quantity is being converted to

        # Connect signals
        self.builder.connect_signals(self)

        # Show window
        self.window.show()

        self.quantity = 0 # Tell which quantity is being converted (0 is volume and 1 is temperature)

        self.input = self.builder.get_object('input')
        self.out = self.builder.get_object('output')

    # Close window button
    def on_window_destroy(self, widget):
        Gtk.main_quit()

    # Quantity being converted changed
    def on_qty_changed(self, widget):
        model = widget.get_model()
        active = widget.get_active()
        option = model[active][1] # Get which quantity is selected
        self.input.set_text('')
        # print('Opção selecionada: {}'.format(option)) # Debug only
        if active == 0: # Volume selected
            self.combo_from.set_model(self.volume_units_list)
            self.combo_to.set_model(self.volume_units_list)
            self.combo_from.set_active(0) # Clear 'from units' combo box
            self.combo_to.set_active(0) # Clear 'to units' combo box
            self.quantity = 0
        if active == 1: # Temperature selected
            self.combo_from.set_model(self.temperature_units_list)
            self.combo_to.set_model(self.temperature_units_list)
            self.combo_from.set_active(0) # Clear 'from units' combo box
            self.combo_to.set_active(0) # Clear 'to units' combo box
            self.quantity = 1

    # From unit changed
    def on_from_changed(self, widget):
        model = widget.get_model()
        active = widget.get_active()
        option = model[active][0]
        self.unitFrom = option
        input_text = self.input.get_text()
        self.input.set_text('') # So that on_input_changed is called
        self.input.set_text(input_text)

    # To unit changed
    def on_to_changed(self, widget):
        model = widget.get_model()
        active = widget.get_active()
        option = model[active][0]
        self.unitTo = option
        input_text = self.input.get_text()
        self.input.set_text('') # So that on_input_changed is called
        self.input.set_text(input_text)

    # Input changed
    def on_input_changed(self, widget):
        input = widget.get_text()
        if (input == ''): # Input is empty
            self.out.set_text('')
        if (input.isdigit()): # Input is a number
            # Volume conversion
            if (self.quantity == 0):
                if (self.unitFrom == self.unitTo): # Same units
                    self.out.set_text(input)
                if (self.unitFrom == 1 and self.unitTo == 2): # mm3 to cm3
                    self.out.set_text(str(int(input) / 1000))
                if (self.unitFrom == 1 and self.unitTo == 3): # mm3 to L
                    self.out.set_text(str(int(input) / 1000000))
                if (self.unitFrom == 2 and self.unitTo == 1): # cm3 to mm3
                    self.out.set_text(str(int(input) * 1000))
                if (self.unitFrom == 2 and self.unitTo == 3): # cm3 to L
                    self.out.set_text(str(int(input) / 1000))
                if (self.unitFrom == 3 and self.unitTo == 1): # L to mm3
                    self.out.set_text(str(int(input) * 1000000))
                if (self.unitFrom == 3 and self.unitTo == 2): # L to cm3
                    self.out.set_text(str(int(input) * 1000))
            # Temperature conversion
            if (self.quantity == 1):
                if (self.unitFrom == self.unitTo): # Same units
                    self.out.set_text(input)
                if (self.unitFrom == 1 and self.unitTo == 2): # Celsius to Farenheint
                    self.out.set_text(str(int(input) * 9 / 5 + 32))
                if (self.unitFrom == 1 and self.unitTo == 3): # Celsius to Kelvin
                    self.out.set_text(str(int(input) + 273.15))
                if (self.unitFrom == 2 and self.unitTo == 1): # Farenheint to Celsius
                    self.out.set_text(str((int(input) - 32) * 5 / 9))
                if (self.unitFrom == 2 and self.unitTo == 3): # Farenheint to Kelvin
                    self.out.set_text(str((int(input) - 32) * 5 / 9 + 273.15))
                if (self.unitFrom == 3 and self.unitTo == 1): # Kelvin to Celsius
                    self.out.set_text(str(int(input) - 273.15))
                if (self.unitFrom == 3 and self.unitTo == 2): # Kelvin to Farenheint
                    self.out.set_text(str((int(input) - 273.15) * 9 / 5 + 32))

if __name__ == '__main__':
    try:
        gui = App()
        Gtk.main()
    except KeyboardInterrupt:
        pass