"""Can we download user information?"""

from datetime import date, datetime, timedelta

import pytest
import requests

import mal_scraper

class TestDiscovery(object):
    """Test discovery of usernames."""

    DISCOVERY_LINK = 'http://myanimelist.net/users.php'

    # TODO: Test Cache
    # TODO: Test fall-back

    def test_web_discovery(self, mock_requests):
        """Can we discover usernames?"""
        mock_requests.always_mock(self.DISCOVERY_LINK, 'users_discovery')

        users = mal_scraper.discover_users(use_cache=False, use_web=True)
        assert len(users) == 20
        assert users == {
            'WitcherEnvy', 'TrollTama', 'Kumatetsu_Rei82', 'Dragh', 'Tom_West',
            'MrNoname7890', 'Aaaavis002', 'nguyenthugiang', 'mrtnlm', 'Kaxkk',
            '9broken_angeL', 'AnimeFreak2205', 'TMZ', 'Oxenia', 'justunknown',
            '0ldboy', 'alkodrak', 'Derin', 'Insufance', 'fatalbert357',
        }

    def test_web_discovery_on_garbled_page(self, mock_requests):
        """Do we return a failure on bad pages?"""
        mock_requests.always_mock(self.DISCOVERY_LINK, 'garbled_user_discovery_page')
        users = mal_scraper.discover_users(use_cache=False, use_web=True)
        assert users == set()

    def test_web_discovery_on_failed_page(self, mock_requests):
        """Do we return a failure on failed pages?"""
        mock_requests.always_mock(self.DISCOVERY_LINK, 'garbled_user_discovery_page', status=500)
        with pytest.raises(requests.exceptions.HTTPError):
            mal_scraper.discover_users(use_cache=False, use_web=True)

class TestUserStats:
    """Test retrieving basic stats information."""

    PROFILE_URL = 'http://myanimelist.net/profile/'

    TEST_USER = 'SparkleBunnies'  # Sorry SparkleBunniesm, 'twas a random selection...
    TEST_USER_PAGE = PROFILE_URL + TEST_USER

    TEST_LAST_ONLINE_NOW_USER = 'Ichigo_Shiba'
    TEST_LAST_ONLINE_NOW_PAGE = PROFILE_URL + TEST_LAST_ONLINE_NOW_USER

    TEST_LAST_ONLINE_MINS_USER = 'El_Anciano'
    TEST_LAST_ONLINE_MINS_PAGE = PROFILE_URL + TEST_LAST_ONLINE_MINS_USER

    TEST_LAST_ONLINE_HOURS_USER = 'snip-snip'
    TEST_LAST_ONLINE_HOURS_PAGE = PROFILE_URL + TEST_LAST_ONLINE_HOURS_USER

    TEST_LAST_ONLINE_YESTERDAY_USER = 'Nyanloveanimes'
    TEST_LAST_ONLINE_YESTERDAY_PAGE = PROFILE_URL + TEST_LAST_ONLINE_YESTERDAY_USER

    TEST_LAST_ONLINE_DATE_USER = 'Elizi'
    TEST_LAST_ONLINE_DATE_PAGE = PROFILE_URL + TEST_LAST_ONLINE_DATE_USER

    def test_detect_bad_download(self, mock_requests):
        mock_requests.always_mock(self.TEST_USER_PAGE, 'garbled_user_page')
        with pytest.raises(mal_scraper.ParseError):
            mal_scraper.get_user_stats(self.TEST_USER)

    def test_user_does_not_exist(self, mock_requests):
        mock_requests.always_mock(
            'http://myanimelist.net/profile/asdghiuhunircg',
            'user_does_not_exist',
            status=404,
        )

        with pytest.raises(mal_scraper.RequestError) as err:
            mal_scraper.get_user_stats('asdghiuhunircg')
        assert err.value.code == mal_scraper.RequestError.Code.does_not_exist

    def test_user_stats(self, mock_requests):
        """Do we retrieve the right stats about a user?"""
        # Always mock this because the user will change it himself
        mock_requests.always_mock(self.TEST_USER_PAGE, 'user_test_page')

        meta, data = mal_scraper.get_user_stats(self.TEST_USER)

        # Fuzzy match datetime
        assert datetime.utcnow() - meta['when'] < timedelta(seconds=30)

        # Assert meta contained by ...
        assert meta.items() >= {
            'user_id': self.TEST_USER,
        }.items()

        # Fuzzy match datetime - last online was "10 hours ago"
        last_online = data['last_online']
        assert datetime.utcnow() - last_online < timedelta(hours=11)

        assert data == {
            'name': self.TEST_USER,
            'joined': date(year=2014, month=1, day=6),
            'last_online': last_online,  # Already checked
            'num_anime_watching': 14,
            'num_anime_completed': 129,
            'num_anime_on_hold': 9,
            'num_anime_dropped': 4,
            'num_anime_plan_to_watch': 16,
        }

    def test_user_last_online_now(self, mock_requests):
        mock_requests.always_mock(self.TEST_LAST_ONLINE_NOW_PAGE, 'user_last_online_now')

        data = mal_scraper.get_user_stats(self.TEST_LAST_ONLINE_NOW_USER).data
        last_online = data['last_online']
        assert datetime.utcnow() - last_online < timedelta(seconds=10)

    def test_user_last_online_minutes(self, mock_requests):
        # 23 minutes ago
        mock_requests.always_mock(self.TEST_LAST_ONLINE_MINS_PAGE, 'user_last_online_mins')

        data = mal_scraper.get_user_stats(self.TEST_LAST_ONLINE_MINS_USER).data
        last_online = data['last_online']
        assert (datetime.utcnow() - timedelta(minutes=23)) - last_online < timedelta(minutes=1)

    def test_user_last_online_hours(self, mock_requests):
        mock_requests.always_mock(self.TEST_LAST_ONLINE_HOURS_PAGE, 'user_last_online_hours')

        data = mal_scraper.get_user_stats(self.TEST_LAST_ONLINE_HOURS_USER).data
        last_online = data['last_online']  # '6 hours ago'
        assert (datetime.utcnow() - timedelta(hours=6)) - last_online < timedelta(seconds=10)

    def test_user_last_online_yesterday(self, mock_requests):
        # Yesterday, 9:01 AM
        mock_requests.always_mock(
            self.TEST_LAST_ONLINE_YESTERDAY_PAGE,
            'user_last_online_yesterday',
        )

        data = mal_scraper.get_user_stats(self.TEST_LAST_ONLINE_YESTERDAY_USER).data
        yesterday = datetime.utcnow() - timedelta(days=1)
        expected_date = yesterday.replace(hour=9, minute=1, second=0, microsecond=0)
        assert data['last_online'] == expected_date

    def test_user_last_online_date(self, mock_requests):
        # May 4, 8:09 AM
        mock_requests.always_mock(self.TEST_LAST_ONLINE_DATE_PAGE, 'user_last_online_date')

        _, info = mal_scraper.get_user_stats(self.TEST_LAST_ONLINE_DATE_USER)
        this_year = datetime.utcnow().year
        assert datetime(year=this_year, month=5, day=4, hour=8, minute=9) == info['last_online']

    def test_user_discovery_on_user_profile_page(self, mock_requests):
        mock_requests.always_mock('http://myanimelist.net/profile/SparkleBunnies', 'user_test_page')
        meta = mal_scraper.get_user_stats('SparkleBunnies').meta
        html = meta['response'].text

        usernames = list(mal_scraper.user_discovery.discover_users_from_html(html))
        assert usernames == [
            'SparkleBunnies', 'SparkleBunnies', 'SparkleBunnies', 'SparkleBunnies', 'AkitoKazuki',
            'Exmortus420', 'ChannelOrange', 'Brandon', 'Zeally', 'Daedalus',
            'HaXXspetten', 'Kagami', 'no_good_name', 'BlackFIFA19', 'Ichigo_Shiba',
            'Ichigo_Shiba', 'Sacchie', 'Sacchie', 'Woodenspoon', 'Woodenspoon',
            'Teddy_Bear56', 'Teddy_Bear56', 'Speeku', 'Speeku', 'stonemask',
            'stonemask', 'IIDarkII', 'IIDarkII', 'ThisNameSucks', 'ThisNameSucks',
            'Z6890', 'Z6890', 'BKZekken', 'BKZekken', 'Woodenspoon',
            'Woodenspoon', 'ChannelOrange', 'ChannelOrange', 'Padgit', 'Padgit',
        ]

class TestUserAnimeList(object):
    """Test retrieving basic stats information."""

    LIST_URL = 'http://myanimelist.net/animelist/{username}/load.json?offset={offset:d}&status=7'

    TEST_FORBIDDEN_USERNAME = 'SparkleBunnies'
    TEST_FORBIDDEN_PAGE = LIST_URL.format(username=TEST_FORBIDDEN_USERNAME, offset=0)

    TEST_USER_SMALL_NAME = 'Littoface'  # ~100 anime
    TEST_USER_SMALL_PAGE = LIST_URL.format(username=TEST_USER_SMALL_NAME, offset=0)
    TEST_USER_SMALL_END_PAGE = LIST_URL.format(username=TEST_USER_SMALL_NAME, offset=158)

    TEST_USER_LOTS_NAME = 'Vindstot'  # 5k anime...
    TEST_USER_LOTS_LIST_PAGE = LIST_URL.format(username=TEST_USER_LOTS_NAME, offset=0)

    TEST_USER_TAGS_NAME = 'reltats'
    TEST_USER_TAGS_PAGE = LIST_URL.format(username=TEST_USER_TAGS_NAME, offset=0)
    TEST_USER_TAGS_END_PAGE = LIST_URL.format(username=TEST_USER_TAGS_NAME, offset=263)

    def test_non_ok_download(self, mock_requests):
        mock_requests.always_mock(self.TEST_FORBIDDEN_PAGE, 'user_anime_list_forbidden', status=401)

        with pytest.raises(mal_scraper.RequestError) as err:
            mal_scraper.get_user_anime_list(self.TEST_FORBIDDEN_USERNAME)
        assert err.value.code == mal_scraper.RequestError.Code.forbidden

    def test_forbidden_access(self, mock_requests):
        mock_requests.always_mock(self.TEST_FORBIDDEN_PAGE, 'user_anime_list_forbidden', status=400)

        with pytest.raises(mal_scraper.RequestError) as err:
            mal_scraper.get_user_anime_list(self.TEST_FORBIDDEN_USERNAME)
        assert err.value.code == mal_scraper.RequestError.Code.forbidden

    def test_download_one_page_anime(self, mock_requests):
        mock_requests.always_mock(self.TEST_USER_SMALL_PAGE, 'user_anime_list_small')
        mock_requests.always_mock(self.TEST_USER_SMALL_END_PAGE, 'user_anime_list_end')

        anime = mal_scraper.get_user_anime_list(self.TEST_USER_SMALL_NAME)
        assert len(anime) == 158
        assert anime[0] == {
            'name': 'Danshi Koukousei no Nichijou',
            'id_ref': 11843,
            'consumption_status': mal_scraper.ConsumptionStatus.consuming,
            'is_rewatch': False,
            'score': 0,
            # 'start_date': None,
            'progress': 9,
            # 'finish_date': None,
            'tags': set(),
        }

    def test_user_tags(self, mock_requests):
        mock_requests.always_mock(self.TEST_USER_TAGS_PAGE, 'user_anime_list_tags')
        mock_requests.always_mock(self.TEST_USER_TAGS_END_PAGE, 'user_anime_list_tags_end')

        anime_list = mal_scraper.get_user_anime_list(self.TEST_USER_TAGS_NAME)
        assert len(anime_list) == 263
        assert anime_list[99]['tags'] == {
            'A masterpiece of failures. Yami wo Kirisaku',
            'LOAD THIS DRYER!',
        }


import sys

import cv2
import matplotlib.pyplot as plt
print(cv2.__version__)
path = '../main/big_data/lecture/week9/data/cat.bmp'
img = cv2.imread(path)
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.show()

if img is None:
    print('image load failed')
    sys.exit()

cv2.namedWindow('image')
cv2.imshow('image',img)
cv2.waitKey()
cv2.destroyAllWindows()
# cv2.destroyWindow('image')
# img write
cv2.imwrite('../main/big_data/lecture/week9/data/cat.png',img)

#
path = '../main/big_data/lecture/week9/data/cat.bmp'
img = cv2.imread(path,cv2.IMREAD_GRAYSCALE)
plt.imshow(img)
plt.imshow(img,cmap='gray')
plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
plt.show()
img.shape

if img is None:
    print('image load failed')
    sys.exit()

# 확장자 변경
# img : bmp file
cv2.imwrite('../main/big_data/lecture/week9/data/cat_gray.png',img)

path = '../main/big_data/lecture/week9/data/cat_gray.png'
img = cv2.imread(path,cv2.IMREAD_GRAYSCALE)
cv2.imshow('../main/big_data/lecture/week9/data/cat_gray.png',img)
cv2.waitKey()
cv2.destroyAllWindows()

#
path = '../main/big_data/lecture/week9/data/cat.bmp'
img = cv2.imread(path,cv2.IMREAD_GRAYSCALE)
cv2.imshow('image',img)
cv2.waitKey()

# 키 할당
while True:
    if cv2.waitKey() == ord('q'):
        cv2.destroyAllWindows()
        print(1)
        break

imgBGR = cv2.imread(path)
imgRGB = cv2.cvtColor(imgBGR,cv2.COLOR_BGR2RGB)
plt.axis('off')
plt.imshow(imgRGB)
plt.imshow(imgBGR)

img_gray = cv2.imread(path,cv2.IMREAD_GRAYSCALE)
plt.axis('off')
plt.imshow(img_gray,cmap='gray')

plt.subplot(121)
plt.axis('off')
plt.imshow(imgRGB)
plt.subplot(122)
plt.axis('off')
plt.imshow(img_gray,cmap='gray')


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Feb 26 11:46:50 2023
@author: BD

Evaluate and plot the effect of package color on the win rate
"""
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def draw_it(i):
    ax.errorbar(x[i], MC_mean[i], yerr=uncertainty[i], fmt='o', elinewidth=2,\
                ms=5, color='black', zorder=0)
    ax.plot(x[i]-0.1, MC_mean[i], marker='>', ms=5, color='black', zorder=0)
    ax.plot(x[i]+0.1, MC_mean[i], marker='<', ms=5, color='black', zorder=0)

    ax.errorbar(x[i], MC_mean[i], yerr=uncertainty[i]*0.99, fmt='o', elinewidth=1.5,\
                ms=1, color=color_codes[i], zorder=1)
    ax.plot(x[i], MC_mean[i], marker='o', ms=5, color='black', zorder=2)
    ax.plot(x[i], MC_mean[i], marker='o', ms=4, color=color_codes[i], zorder=3)
    ax.plot(x[i]-0.1, MC_mean[i], marker='>', ms=4, color=color_codes[i], zorder=2)
    ax.plot(x[i]+0.1, MC_mean[i], marker='<', ms=4, color=color_codes[i], zorder=2)

if __name__ == '__main__':
    path = ''
    # Read in the table
    tbl = pd.read_csv(path+'candy_data_extended.csv')

    # The list of all colors and their color codes (thank you chatGPT)
    # cyan is used when there are many colors or no colors (placebo)
    colors = np.unique(tbl['main color'])
    color_codes = ['#00FFFF', 'k', 'b', '#8B4513', 'y', 'gray', 'g', '#00FFFF',\
                  '#FFA500', '#FFC0CB', 'purple', 'r', 'w', '#FFD700']

    # Calculate the mean
    # Calculate the uncertainty of the mean as the rms/sqrt(N)
    MC_mean = []
    MC_std = []
    N = []
    for color in colors:
        MC_mean.append(np.mean(tbl['winpercent'][tbl['main color']==color]))
        MC_std.append(np.std(tbl['winpercent'][tbl['main color']==color]))
        N.append(len(tbl[tbl['main color']==color]))
    uncertainty = MC_std/np.sqrt(N)

    # Plot the results
    x = range(len(colors))

    plt.figure(figsize=(7,4))
    ax = plt.subplot(111)
    for i in range(len(N)):
        draw_it(i)
        ax.text(x[i]-0.1, 32, N[i])

    ax.text(-0.85, 32, 'N =')
    ax.set_xticks(range(len(colors)), colors, rotation=90)
    ax.set_ylabel('win [%]')
    ax.set_xlim([-1,len(colors)])

    plt.tight_layout()
    plt.savefig(path+'package_colors.pdf')


from django.contrib.gis.geos import Point, Polygon, GEOSGeometry
import requests
import pytest
import json

API_URL = "http://127.0.0.1:8000/api/buildings/"

@pytest.fixture
def building_data():
    return  {
        "geom": {
            "type": "Polygon",
            "coordinates": [
                [
                    [
                        5.0,
                        5.0
                    ],
                    [
                        5.0,
                        10.0
                    ],
                    [
                        10.0,
                        10.0
                    ],
                    [
                        10.0,
                        5.0
                    ],
                    [
                        5.0,
                        5.0
                    ]
                ]
            ]
        },
        "address": "CreatedFromTest3"
    }

def test_list_buildings():
    res = requests.get(API_URL + '')
    assert res.status_code == 200
    data = res.json()
    return data

@pytest.fixture
def get_big_distance_and_point():
    return (100000000, Point(0, 0))

@pytest.fixture
def get_big_distance_filtered_list(get_big_distance_and_point):
    res = requests.get(API_URL + '?dist={0}&&point={1},{2}'.
                       format(get_big_distance_and_point[0],
                              get_big_distance_and_point[1].x, get_big_distance_and_point[1].y))
    assert res.status_code == 200
    data = res.json()
    return data

@pytest.fixture
def get_full_building_list():
    return test_list_buildings()

#Testing that for 100000 km (100000000m) it will return all values
def test_distance_filter_big(get_big_distance_filtered_list, get_full_building_list):
    assert get_big_distance_filtered_list == get_full_building_list

@pytest.fixture
def get_zero_distance_and_point():
    return (0, Point(-5, -5, srid=4326))

@pytest.fixture
def get_zero_distance_filtered_list(get_zero_distance_and_point):
    res = requests.get(API_URL + '?dist={0}&&point={1},{2}'.
                       format(get_zero_distance_and_point[0],
                              get_zero_distance_and_point[1].x, get_zero_distance_and_point[1].y))
    assert res.status_code == 200
    data = res.json()
    return (data, get_zero_distance_and_point[1])

def test_distance_filter_zero(get_zero_distance_filtered_list, get_full_building_list):
    filtered = get_zero_distance_filtered_list[0]["features"]
    full = get_full_building_list["features"]
    point = get_zero_distance_filtered_list[1]
    for feature in filtered:
        geom = GEOSGeometry(json.dumps(feature['geometry']))
        #For zero distance each filtered geometry should cover this point
        assert geom.covers(point)

    #We count here from full list how many objects covers point
    cnt_test = 0
    for feature in full:
        geom = GEOSGeometry(json.dumps(feature['geometry']))
        #For zero distance each geometry should contain this point
        if geom.covers(point):
            cnt_test += 1
    cnt_api = len(filtered)
    #Checking that cnts of filtered object in api and in test are equal
    assert cnt_api == cnt_test

@pytest.mark.skip(reason="no need to check now")
def test_distance_filter(get_full_building_list):
    for i in range(10):
        dist = 10
        x = 0.0
        y = 0.0
        for j in range(10):
            res = requests.get(API_URL + '?dist={0}&&point={1},{2}'.
                           format(dist, x, y))
            assert res.status_code == 200
            data = res.json()
            x += 2
            y += 2
        dist *= 10

def test_distance_filter(get_full_building_list):
    res = requests.get(API_URL + '?dist={0}&&point={1},{2}'.
                       format(5, 5, 5))

    assert res.status_code == 200
    # if filter args are incorrect, should return all
    res = requests.get(API_URL + '?&point={0},{1}'.
                       format(5, 5))
    assert res.status_code == 200
    data = res.json()
    data == get_full_building_list

    #if filter args are incorrect, should return all
    res = requests.get(API_URL + '??dist=&point={0},{1}'.
                       format(5, 5))
    assert res.status_code == 200
    data = res.json()
    data == get_full_building_list

@pytest.fixture
def post_new_polygon():
    x = 0
    y = 1
    d = {"geom": {"type": '', "coordinates": ''}}
    # area is 1/2 square degree or ~ 6160500000 square meters
    p = Polygon(((x, x), (y, y), (x, y), (x, x)), srid=4326)
    d["geom"]["type"] = p.geom_type
    d["geom"]["coordinates"] = p.coords
    res = requests.post(API_URL + '', json=d)
    assert res.status_code == 201
    newBuildingData = res.json()
    return newBuildingData

def test_distance_filtel_value(post_new_polygon):

    id = post_new_polygon["id"]

    #point is 1 degree distance or ~ 111000
    res = requests.get(API_URL + '?dist=100000&point=0,2.0')
    filteredData = res.json()
    assert res.status_code == 200
    assert post_new_polygon not in filteredData["features"]

    res = requests.get(API_URL + '?dist=200000&point=0,2.0')
    filteredData = res.json()
    assert res.status_code == 200
    assert post_new_polygon in filteredData["features"]

    res = requests.delete(API_URL + str(id))
    assert res.status_code == 204
    return

#print(Distance(Point(0,0,srid=27700),(Point(1,0,srid=27700))).


# "Eugene Morozov"<Eugene ~at~ HiEugene.com>

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from util import get_color
import time

def plotX(X, mu, M, N, K, r, ax):
  for i in range(M):
    if plotX.px[i]: plotX.px[i].remove()
    if N == 2:
      plotX.px[i] = ax.scatter(X[i,0], X[i,1], color=get_color(r[i]), marker="o")
    else:
      plotX.px[i] = ax.scatter(X[i,0], X[i,1], X[i,2], color=get_color(r[i]), marker="o")
  for i in range(K):
    if plotX.pmu[i]: plotX.pmu[i].remove()
    if N == 2:
      plotX.pmu[i] = ax.scatter(mu[i,0], mu[i,1], color=get_color(i+K), marker="*", linewidths=5)
    else:
      plotX.pmu[i] = ax.scatter(mu[i,0], mu[i,1], mu[i,2], color=get_color(i+K), marker="*", linewidths=5)

def calc_dist(x, y):
  d = ((x - y)**2).sum() # sqrt omitted
  return d

def K_means(X, K, v, title_str):
  M, N = X.shape # M samples of dimension N
  r = np.zeros(M, dtype="int") # cluster id for each point

  mu_new = np.empty([K,N])
  for i in range(K):
    mu_new[i,:] = X[i*M//K,:]
  mu = np.zeros([K,N])

  if v and (N==2 or N==3):
    plotX.px = [None]*M
    plotX.pmu = [None]*K
    fig = plt.figure()
    if N == 2:
      ax = fig.gca()
    else:
      ax = fig.gca(projection='3d')
    plt.ion()
    plt.grid(True)
    plt.show()

  # while np.abs(mu_new - mu).sum() > 1e-10: # or track when the assignments no longer change
  while calc_dist(mu_new, mu) > 1e-10: # or track when the assignments no longer change
    if v and (N==2 or N==3):
      plotX(X, mu_new, M, N, K, r, ax)
      # ax.set_title(f"diff = {np.abs(mu_new - mu).sum()}")
      ax.set_title(f"diff = {calc_dist(mu_new, mu)}")
      fig.canvas.flush_events(); time.sleep(0.5)
    mu = mu_new.copy()
    for i in range(M):
      min_dist = 1e10
      r[i] = 0
      for k in range(K):
        dist = calc_dist(X[i,:], mu[k,:])
        if dist < min_dist:
          min_dist = dist
          r[i] = k

    for k in range(K):
      mu_new[k] = X[r==k,:].sum(axis=0) / X[r==k,:].shape[0]
  if v and (N==2 or N==3):
    ax.set_title(title_str)
    fig.canvas.flush_events(); time.sleep(0.5)
  return r


import rospy
import open3d as o3d
import sensor_msgs.point_cloud2 as pc2
from sensor_msgs.msg import PointCloud2
import numpy as np

class RealSensePointCloud:
    def __init__(self):
        # Initialize ROS node
        rospy.init_node('realsense_pointcloud_visualizer')

        # Create a subscriber to the RealSense point cloud ROS topic
        # Make sure to use the correct topic where your RealSense camera publishes the point cloud data
        self.pc_subscriber = rospy.Subscriber("/camera/depth/color/points", PointCloud2, self.pointcloud_callback, queue_size=1)

        # Initialize Open3D visualizer
        self.vis = o3d.visualization.Visualizer()
        self.vis.create_window(window_name='RealSense Point Cloud')

        # Initialize point cloud variable
        self.pcd = o3d.geometry.PointCloud()

        self.voxel_size = 0.01  # Adjust this value as needed

        # Variable to control the main loop
        self.is_running = True

    def pointcloud_callback(self, msg):
        try:
            # Convert ROS PointCloud2 message to array of xyz points
            pc_array = pc2.read_points(msg, field_names=("x", "y", "z"), skip_nans=True)
            points = np.array(list(pc_array))

            if points.size == 0:
                rospy.logwarn("Received an empty point cloud.")
                return

            # Update point cloud
            self.pcd.clear()
            self.pcd.points = o3d.utility.Vector3dVector(points)

            # Voxel downsample the point cloud
            self.pcd = self.pcd.voxel_down_sample(voxel_size=self.voxel_size)

            # Update the visualizer
            self.update_visualizer()
        except Exception as e:
            rospy.logerr("An error occurred in pointcloud_callback: %s", str(e))

    def update_visualizer(self):
        self.vis.clear_geometries()  # Clear old geometries
        self.vis.add_geometry(self.pcd)  # Add the current point cloud
        self.vis.poll_events()  # Update the visualizer events
        self.vis.update_renderer()  # Render the point cloud

    def run(self):
        # Main loop
        while not rospy.is_shutdown() and self.is_running:
            rospy.spin()

        # If ROS is shut down or the script is stopped, close the visualizer window
        self.vis.destroy_window()

# Main function
if __name__ == "__main__":
    try:
        pointcloud_visualizer = RealSensePointCloud()
        pointcloud_visualizer.run()
    except rospy.ROSInterruptException:
        pass


# -*- coding: utf-8 -*-
"""
Created on Fri Aug  2 11:57:41 2019
Updated 20220904 22:42WER

@authors: wrosing, mfitz
"""

import os
import pathlib
import sys
import socket

import glob

# This routine here removes all mention of previous configs from the path...
# for safety and local computer got clogged with all manner of configs in the path

path_removals = []
for q in range(len(sys.path)):
    if "ptr-observatory" in sys.path[q] and "configs" in sys.path[q]:
        print("Removing old config path: " + str(sys.path[q]))
        path_removals.append(sys.path[q])

for remover in path_removals:
    sys.path.remove(remover)

pathdone = 0

# First try to get the hostname from a file in the directory above (..) ptr-observatory
cwd = str(pathlib.Path().resolve())
hwd = cwd.replace("ptr-observatory", "")
hostname_file = glob.glob(hwd + "hostname*")

try:
    site_name = hostname_file[0].replace('.txt','').split("hostname")[1]
    sys.path.append(os.path.join(pathlib.Path().resolve(), "configs", site_name))
    pathdone = 1
except OSError:
    print(
        "Could not find a hostname* file in the directory above ptr-observatory \
        (e.g. hostnamesro).\n Trying another method..."
    )

if pathdone == 0:
    print("Attempting hostname approach to config file...")
    host_site = socket.gethostname()[:3].lower()
    sys.path.append(os.path.join(pathlib.Path().resolve(), "configs", host_site))

try:
    from obs_config import *

except ImportError:
    print(
        "Failed the hostname approach to config file.\n"
        + str(host_site)
        + " isn't a real place, or there isn't a config file \
                        that I can find!"
    )

    try:
        site_name = input("What site am I running at?\n")
        sys.path.append(os.path.join(pathlib.Path().resolve(), "configs", site_name))
        from site_config import *

    except ImportError:
        print(
            str(site_name)
            + " isn't a real place, or there isn't a config file \
                        that I can find! Make sure you supplied \
                        a correct site name. Exiting."
        )
        sys.exit()


from typing import MutableSequence
def fsort(a: MutableSequence, max:int)->None:
    # 원소의값은 0이상 max이하
    n = len(a)
    f = [0]*(max+1)
    b = [0]*n
    # 도수 분포표  : 해당 값을 인덱스로 가지는 배열을 만들어서 count하면 해당 값이 몇번? 나왔는지 알수있음
    for i in range(n): f[a[i]] += 1 # 1 step
    # 누적 도수 분포표 : 0 ~ n까지 몇개의 데이터가 있는지 누적된 값을 나타냄
    for i in range(1,max+1) : f[i] += f[i-1]
    # 작업용 배열 : 원래배열의 원소값과 누적도수 분포표를 대조해서 정렬을 완료한 배열
    for i in range(n-1, -1, -1) : f[a[i]] -=1 ; b[f[a[i]]] = a[i]
    for i in range(n) : a[i] = b[i]

def countingSort(a:MutableSequence)->None:
    fsort(a, max(a))

def main()->None:
    from random import sample
    data = sample(range(1000),10)
    print(f"original : {data}")
    countingSort(data)
    print(f"sorted : {data}")

if __name__=="__main__":
    main()


from __future__ import print_function
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
import pandas as pd
from IPython.display import HTML

import logging
import boto3
import boto3.session
from botocore.exceptions import ClientError

#ELO Weights
WIN = 1
TIE = 0.5
LOSS = 0

class CatanStats():

    def __init__(self):
        # If modifying these scopes, delete the file token.json.
        self.SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly']

        # The ID and range of a sample spreadsheet.
        self.SAMPLE_SPREADSHEET_ID = '1MG7jhiarGpRunEILVbkEfD3mLRUBpIMI4au7cMID_EM'
        self.SAMPLE_RANGE_NAME = 'Games'

        # Dataframe containing Games and All Player Data from Google Sheets
        self.games, self.player_scores = self.get_games()

        # Initialize Elo Dict
        self.elo = self.elo_init()

        for index, row in self.games.iterrows():
            self.new_ratings(row)

    # Fetches data from Google Sheets
    def get_games(self):
        creds = None
        # The file token.json stores the user's access and refresh tokens, and is
        # created automatically when the authorization flow completes for the first
        # time.
        if os.path.exists('token.json'):
            creds = Credentials.from_authorized_user_file('token.json', self.SCOPES)
        # If there are no (valid) credentials available, let the user log in.
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    'credentials.json', self.SCOPES)
                creds = flow.run_local_server(port=0)
            # Save the credentials for the next run
            with open('token.json', 'w') as token:
                token.write(creds.to_json())

        service = build('sheets', 'v4', credentials=creds)

        sheet = service.spreadsheets()
        result = sheet.values().get(spreadsheetId=self.SAMPLE_SPREADSHEET_ID,
                                    range=self.SAMPLE_RANGE_NAME).execute()
        values = result.get('values', [])

        df = pd.DataFrame(values[1:], columns=values[0])

        p1 = df[['Player 1', 'Starting Production 1', 'Ending VP 1']].rename(columns={"Player 1": "Player", "Starting Production 1": "Starting Production",  "Ending VP 1": "Ending VP"})
        p2 = df[['Player 2', 'Starting Production 2', 'Ending VP 2']].rename(columns={"Player 2": "Player", "Starting Production 2": "Starting Production",  "Ending VP 2": "Ending VP"})
        p3 = df[['Player 3', 'Starting Production 3', 'Ending VP 3']].rename(columns={"Player 3": "Player", "Starting Production 3": "Starting Production",  "Ending VP 3": "Ending VP"})
        p4 = df[['Player 4', 'Starting Production 4', 'Ending VP 4']].rename(columns={"Player 4": "Player", "Starting Production 4": "Starting Production",  "Ending VP 4": "Ending VP"})

        players = pd.concat([p1, p2, p3, p4])

        nan_value = float("NaN")
        players.replace("", nan_value, inplace=True)
        players = players.dropna()

        players = players.reset_index()
        players_scores = players.apply(pd.to_numeric, errors='ignore')

        return df, players_scores

    # Initializes Elo Dict
    def elo_init(self):
        elo = {}
        for player in self.player_scores['Player'].unique():
            elo[player] = 1000
        return elo

    # Individual elo calculation based on outcome
    def elo_calc(self, eloA, eloB, actual_outcome):
        K_VAL = 15
        expected_outcome = 1 / (1 + 10**((eloB - eloA)/400) )
        elo_adjustment_a = K_VAL * (actual_outcome - expected_outcome)
        return elo_adjustment_a

    # Calculates new elo ratings for game
    def new_ratings(self, game):

        # Check if Variant Game
        if(game["Variant"] == "TRUE"):
            variant = True
        else:
            variant = False

        # Check if 3 or 4 player
        if(game["Player 4"] == ''):
            four_player_game = False
        else:
            four_player_game = True

        # Pairwise elo_calc
        player_elo_adj = []

        if(not variant):
            if(four_player_game):

                # Iterate over pairwise player matchups
                for i in range(1,5):

                    player_cummulative = 0

                    for j in range(1,5):

                        if(int(game["Ending VP {}".format(i)]) > int(game["Ending VP {}".format(j)])):
                            outcome = WIN
                        elif(int(game["Ending VP {}".format(i)]) == int(game["Ending VP {}".format(j)])):
                            outcome = TIE
                        else:
                            outcome = LOSS

                        match_up = self.elo_calc(self.elo[game['Player {}'.format(i)]], self.elo[game['Player {}'.format(j)]], outcome)

                        player_cummulative += match_up

                    player_elo_adj.append(player_cummulative)

                # Elo adjustments after calculations
                for i in range(0,4):
                    self.elo[game['Player {}'.format(i + 1)]] += round(player_elo_adj[i])

            else:
                # Iterate over pairwise player matchups
                for i in range(1,4):

                    player_cummulative = 0

                    for j in range(1,4):
                        try:
                            if(int(game["Ending VP {}".format(i)]) > int(game["Ending VP {}".format(j)])):
                                outcome = WIN
                            elif(int(game["Ending VP {}".format(i)]) == int(game["Ending VP {}".format(j)])):
                                outcome = TIE
                            else:
                                outcome = LOSS
                        except ValueError:
                            print(game["Game #"],i, " ", j, " ", game["Ending VP {}".format(i)], " " , game["Ending VP {}".format(j)] )

                        match_up = self.elo_calc(self.elo[game['Player {}'.format(i)]], self.elo[game['Player {}'.format(j)]], outcome)

                        player_cummulative += match_up

                    player_elo_adj.append(player_cummulative)

                # Elo adjustments after calculations
                for i in range(0,3):
                    self.elo[game['Player {}'.format(i + 1)]] += round(player_elo_adj[i])

        return None

    # Aggregate Stats on Players
    def player_info(self):

        # Add Elo scores to Player_Scores
        self.player_scores['Elo'] = self.player_scores.apply(lambda row: int(self.elo[row['Player']]), axis = 1)

        # Add aggregate data to player_stats df
        player_stats = self.player_scores.groupby('Player').agg({'Starting Production': ['mean', 'std'], 'Ending VP': ['mean', 'std'], 'Elo': ['mean']})
        player_stats["Games Played"] = self.player_scores.groupby('Player').size()
        player_stats["Wins"] = self.player_scores.loc[self.player_scores['Ending VP'] >= 10].groupby('Player').size()
        player_stats["Win Percentage"] = player_stats["Wins"] / player_stats["Games Played"]

        # Rounds data to 2 decimal spots
        player_stats[('Starting Production','mean')] = player_stats[('Starting Production','mean')].round(2)
        player_stats[('Starting Production','std')] = player_stats[('Starting Production','std')].round(2)
        player_stats[('Ending VP','mean')] = player_stats[('Ending VP','mean')].round(2)
        player_stats[('Ending VP','std')] = player_stats[('Ending VP','std')].round(2)
        player_stats['Win Percentage'] = (player_stats['Win Percentage'] * 100).round(2)

        # Filter players who have played more than 1 game
        player_stats = player_stats[player_stats['Games Played'] > 1]

        # return player_stats.sort_values([('Ending VP', 'mean'),('Elo', 'mean'),"Win Percentage"], ascending=False)
        # return player_stats.sort_values(["Games Played", ('Ending VP', 'mean'),('Elo', 'mean'),"Win Percentage"], ascending=False)
        # return player_stats.sort_values([('Starting Production', 'mean'),"Win Percentage",('Ending VP', 'mean')], ascending=False)
        # return player_stats.sort_values(["Win Percentage",('Elo', 'mean'),('Ending VP', 'mean')], ascending=False)
        return player_stats.sort_values([('Elo', 'mean'),"Win Percentage",('Ending VP', 'mean')], ascending=False)

    # Aggregate Stats on Dice
    def dice_info(self):
        df = get_data()

    # Creates HTML File from player_info
    def create_player_table(self):

        html = self.player_info().to_html(classes=["table", "table-striped"])

        # write html to file
        text_file = open("index.html", "w")
        text_file.write('<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">\n')
        text_file.write(html)
        text_file.close()

def upload_s3(file):

    session = boto3.session.Session(profile_name='default')
    s3_client = session.client('s3')

    try:
        s3_client.upload_file(Filename=file, Bucket="adamlenning.com", Key=file,  ExtraArgs={'ContentType': "text/html"})
    except ClientError as e:
        logging.error(e)
        return False
    return True

if __name__ == '__main__':
    c1 = CatanStats()
    c1.create_player_table()
    upload_s3('index.html')


import curses

##########################################################################
## Este código 'movimenta' o X no terminal ao pressionar as teclas W,S,A,D
##########################################################################

def main(stdscr):
    # Configurações iniciais do terminal
    stdscr.clear()
    curses.curs_set(0)  # Desativa o cursor

    # Tamanho da tela
    height, width = stdscr.getmaxyx()

    # Posição inicial do jogador
    player_y, player_x = height // 2, width // 2

    # Loop principal do jogo
    while True:
        stdscr.nodelay(True)

        # Obtém o input do usuário (não bloqueante)
        key = stdscr.getch()

        stdscr.addch(player_y, player_x, ' ')

        # Atualiza a posição do jogador com base na tecla pressionada
        if key == ord('w'):
            player_y -= 1
        elif key == ord('s'):
            player_y += 1
        elif key == ord('a'):
            player_x -= 1
        elif key == ord('d'):
            player_x += 1

        # Limita a posição do jogador dentro dos limites da tela
        player_y = max(0, min(player_y, height - 1))
        player_x = max(0, min(player_x, width - 1))

        # Desenha o jogador na tela
        stdscr.addch(player_y, player_x, 'X')

        # Atualiza a tela
        stdscr.refresh()

if __name__ == "__main__":
    curses.wrapper(main)


from collections import OrderedDict as OD
from numpy import exp, log as ln
from styles import mark_styles

def KWW(t, tau, beta):
    "Kohlrausch-Williams-Watts compressed (beta>1) exponential function."
    return 1 - exp(-(t/tau)**beta)
KWW.title = 'Non-normalized Kohlrausch-Williams-Watts compressed (beta>1) exponential function'
KWW.title_lowercase = 'non-normalized Kohlrausch-Williams-Watts compressed (beta>1) exponential function'
KWW.equation = '1 - exp(-(t/tau)^beta)'

def KWW_inverse(KWW_output, tau, beta):
    return tau * (-ln(1 - KWW_output))**(1/beta)
def KWW_prime(t, tau, beta):
    return (beta/tau) * (t/tau)**(beta-1) * exp(-(t/tau)**beta)
def KWW_doubleprime(t, tau, beta):
    return -(beta/tau**2) * (t/tau)**(beta-2) * exp(-(t/tau)**beta) * (1 + beta*((t/tau)**beta - 1))
def KWW_inflection(tau, beta):
    "Returns inflection time."
    return tau * ((beta-1)/beta)**(1/beta)
def KWW_lagtime(tau, beta, t_inflection, leadtime = False):
    _KWW = KWW(t_inflection, tau, beta)
    _KWW_prime = KWW_prime(t_inflection, tau, beta)
    return ((leadtime - _KWW)/_KWW_prime) + t_inflection

def KWW_scaled(t, tau, slowbeta, scale, t0):
    fastbeta = slowbeta**4
    _KWW = lambda t: KWW(t, tau, fastbeta)
    return ( scale * _KWW((t-t0)/scale) )
KWW_scaled.title = 'Scaled KWW'
KWW_scaled.title_lowercase = 'scaled KWW'
KWW_scaled.equation = '( scale * KWW((t-t0)/scale, tau, slowbeta^4) )'
KWW_scaled.marks = {'t_5%', 't_10%', 't_90%', 't_95%', 'lagtime'}
KWW_scaled.styles = mark_styles

def KWW_descaler(tau, slowbeta, scale = None, t0 = None):
    "Converts KWW_scaled parameters to KWW parameters."
    fastbeta = slowbeta**4
    return tau, fastbeta
def KWW_timeinfo(tau, beta, t_inflection):
    t_percent = lambda percent: KWW_inverse(percent, tau, beta)
    return OD({
        't_5%': t_percent(0.05),
        't_10%': t_percent(0.1),
        't_90%': t_percent(0.9),
        't_95%': t_percent(0.95),
        'lagtime': KWW_lagtime(tau, beta, t_inflection) })
def KWW_timescaler(t, scale, t0):
    "Takes a time on an unscaled KWW curve and returns the corresponding time on the scaled curve."
    return scale * t + t0

if __name__ == '__main__':
    scaled_args = { 'tau': 1.2, 'slowbeta': 1.2, 'scale': 2.9, 't0': 0.4 }
    descaled_args = KWW_descaler(**scaled_args)

    t_inflection = KWW_inflection(*descaled_args)
    rate_max = KWW_prime(t_inflection, *descaled_args)

    print(*(f"{value['title']}: {value['value']}" for key, value in KWW_timeinfo(*descaled_args, t_inflection).items()), sep = '\n')


from lib.cuckoo.common.abstracts import Signature

class DiskInformation(Signature):
    name = "antivm_generic_disk"
    description = "Queries information on disks, possibly for anti-virtualization"
    severity = 3
    categories = ["anti-vm"]
    authors = ["nex"]
    minimum = "2.0"

    filter_apinames = [
        "NtCreateFile",
        "DeviceIoControl",
        "NtDeviceIoControlFile",
    ]

    indicators = [
        "scsi0",
        "physicaldrive0",
    ]

    ioctls = {
        2954240: "IOCTL_STORAGE_QUERY_PROPERTY",
        458752: "IOCTL_DISK_GET_DRIVE_GEOMETRY",
        315400: "IOCTL_SCSI_MINIPORT",
    }

    def init(self):
        self.drive_opened = False

    def on_call(self, call, process):
        if call["api"] == "NtCreateFile":
            filepath = call["arguments"]["filepath"].lower()
            if "scsi0" in filepath or "physicaldrive0" in filepath:
                self.drive_opened = True
                self.mark_call()

        if call["api"] in ["DeviceIoControl", "NtDeviceIoControlFile"]:
            if self.drive_opened and call["arguments"]["control_code"] in self.ioctls:
                self.mark_call()
                return True


import streamlit as st
import pandas as pd
import numpy as np
import folium
import os
from folium.plugins import HeatMap
from streamlit_folium import st_folium, folium_static
# from gbq_functions.big_query_download import *
from gbq_functions.params import *
import matplotlib.pyplot as plt
import matplotlib as mpl
from google.cloud import bigquery
from google.oauth2 import service_account
import streamlit.components.v1 as components
from functions_for_website.load_outputs import *

st.set_page_config(page_title="LocA", layout="wide", initial_sidebar_state="auto", menu_items=None)

for key in st.session_state.keys():
        del st.session_state[key]

st.markdown("
LocA
", unsafe_allow_html=True)

st.markdown("
Alternative Mapping Technique
", unsafe_allow_html=True)

credentials = service_account.Credentials.from_service_account_info(st.secrets["gcp_service_account"])

@st.cache_data(persist=True)
def get_master_district_df():
    '''function that returns the full master district df.
    Dataframe contains district name (primary key), lat_lons for the center,
    lat_lons for the edges of rectangle around area, and the area of the
    rectangle in Hectares'''

    query = f"""
            SELECT {",".join(MASTER_COLUMN_NAMES_RAW)}
            FROM {GCP_PROJECT}.{BQ_DATASET}.{BQ_DISTRICT_TABLE}
            ORDER BY HECTARES DESC
        """

    client = bigquery.Client(project=GCP_PROJECT, credentials=credentials)
    query_job = client.query(query)
    result = query_job.result()
    master_districts_df = result.to_dataframe()
    return master_districts_df
master_df = get_master_district_df()
master_df['start'] = master_df['District_ID'].astype(str).str[0] #gets the letter at start of dist.
master_df = master_df[master_df['start'] == "E"]
master_df = master_df[~master_df['District'].str.contains("London",regex=False)]
master_df = master_df.sort_values(by="District", ascending=True) #sorts

# create drop down box
option = st.selectbox("Select District:",
                      list(master_df['District']))

# set up the website to show Dorset on initializing
if 'district' not in st.session_state:
    st.session_state['district'] = 'Adur District'

@st.cache_data
def create_map(district):
    @st.cache_data  # :point_left: Add the caching decorator
    def load_data(csv):
        df = pd.read_csv(csv)
        return df

    df_good = load_data(os.path.abspath("outputs/display_gd.csv"))
    df_bad = load_data(os.path.abspath("outputs/display_bad.csv"))
    df = pd.concat([df_good, df_bad],ignore_index=True)
    golden_df = df[df['district_name'] == district]
    golden_df = golden_df.drop_duplicates(['lat', 'lng'])
    golden_df['id'] = golden_df.index

    mapObj = folium.Map(location=[golden_df['lat'].mean(),golden_df['lng'].mean()], zoom_start=10, prefer_canvas=True)

    lats = np.array( golden_df['lat'] )
    longs = np.array( golden_df['lng'] )

    # set up the grid
    lat_step = max(n2 - n1 for n1, n2 in zip(sorted(set(lats)), sorted(set(lats))[1:]))
    long_step = max(n2 - n1 for n1, n2 in zip(sorted(set(longs)), sorted(set(longs))[1:]))

    my_geo_json = {
      "type": "FeatureCollection",
      "features": []}

    for i in range(len(lats)):
        my_geo_json['features'].append(
            {
            "type": "Feature",
            "properties": {},
            "geometry": {
                "type": "Polygon",
                "coordinates": [[
                    [longs[i] - long_step/2, lats[i] - lat_step/2],
                    [longs[i] - long_step/2, lats[i] + lat_step/2],
                    [longs[i] + long_step/2, lats[i] + lat_step/2],
                    [longs[i] + long_step/2, lats[i] - lat_step/2],
                    [longs[i] - long_step/2, lats[i] - lat_step/2],
                ]]},
            "id": int(golden_df['id'].values[i])
            }
        )

    folium.Choropleth(
        geo_data=my_geo_json,
        data=golden_df,
        columns = ['id','metric'],
        fill_color='RdYlGn',
        fill_opacity=0.6,
        line_opacity=0,
        key_on='feature.id',
        bins=5
    ).add_to(mapObj)

    folium_static(mapObj, width = 725)

if st.button('Submit!'):
    st.session_state['district'] = option
create_map(st.session_state['district'])


#!/usr/bin/python

import numpy as np
import copy
import time
import itertools

from sim import *
from scenario import *
from AStarAlgo import aStar, clearcache

def parse_env(env):
    fires = []
    lakes = []
    #print(env)
    for value in env:
        x, y, z = value
        x = np.float(x)
        y = np.float(y)
        z = np.float(z)
        #print(x, y, z)
        if env[value] == "orange":
            fires.append(np.array([x, y + 2, z]))
        if env[value] == "blue":
            lakes.append(np.array([x, y + 1, z]))
        if env[value] == "drone":
            fires.insert(0, np.array([x, y, z]))

    graph = []
    for i, fire1 in enumerate(fires):
        vertex = []
        for j, fire2 in enumerate(fires):
            edge = {}
            edge['start'] = fire1
            edge['dist'] = np.inf
            edge['end'] = fire1
            edge['path'] = []
            edge['path'].append(fire2)
            if i == j:
                edge['lake'] = None
                vertex.append(edge)
                continue

            for k, lake in enumerate(lakes):
                #print(i, j, k)
                #dist1 = len(aStar(tuple(fire1), tuple(lake), env))
                #dist2 = len(aStar(tuple(lake), tuple(fire2), env))
                dist1 = np.linalg.norm(fire1 - lake)
                dist2 = 2 * np.linalg.norm(lake - fire2)
                #num_edges += 1
                if (dist1 + dist2) < edge['dist']:
                    edge['dist'] = dist1 + dist2
                    edge['lake'] = lake
                    edge['fire'] = fire2
            #print(vertex)
            vertex.append(edge)
        graph.append(vertex)
    #print(graph, fires, lakes)
    return (graph, fires, lakes)

def pick_neighbors(graph, start_vertex_id, max_dist, max_num_vertices):
    vertex = graph[start_vertex_id]
    short_list = []
    num_vertices = 1
    short_list.append(start_vertex_id)
    for i, edge in enumerate(vertex):
        if edge['dist'] > max_dist:
            continue
        short_list.append(i)
        num_vertices += 1
        if num_vertices >= max_num_vertices:
            break
    return short_list

def pick_farthest_neighbor(graph, cur_vertex_id):
    farthest_vertex_id = 0
    farthest_dist = 0
    vertex = graph[cur_vertex_id]
    for i, edge in enumerate(vertex):
        if edge['dist'] == np.inf:
            continue
        if edge['dist'] > farthest_dist:
            farthest_dist = edge['dist']
            farthest_vertex_id = i
    #print(farthest_vertex_id)
    return farthest_vertex_id

def mark_vertices_covered(graph, short_list):
    for vertex_id in short_list:
        #vertex = graph[vertex_id]
        #for edge in vertex:
        #    edge['dist'] = np.inf
        for vertex in graph:
            vertex[vertex_id]['dist'] = np.inf
    #print(graph)
    if len(short_list) == 1:
        return graph
    for vertex_id in short_list[1:]:
        graph[vertex_id][vertex_id]['path'] = None
    return graph

def find_shortest_path(graph, short_list):
    shortest_path = []
    shortest_dist = np.inf
    if (short_list[0] == 0):
        start_vertex_id = short_list[0]
        for path in itertools.permutations(short_list[1:]):
            prev_vertex_id = start_vertex_id
            cur_dist = 0
            for vertex_id in path:
                cur_dist += graph[prev_vertex_id][vertex_id]['dist']
                prev_vertex_id = vertex_id
            if cur_dist < shortest_dist:
                shortest_path = copy.deepcopy(list(path))
                shortest_dist = cur_dist
        shortest_path.insert(0, start_vertex_id)
    else:
        for path in itertools.permutations(short_list):
            prev_vertex_id = path[0]
            cur_dist = 0
            for vertex_id in path[1:]:
                cur_dist += graph[prev_vertex_id][vertex_id]['dist']
                prev_vertex_id = vertex_id
            if cur_dist < shortest_dist:
                shortest_path = copy.deepcopy(list(path))
                shortest_dist = cur_dist
    return shortest_path

def populate_path_with_lakes(graph, path):
    path_with_lakes = []
    #final_path.append(fires[0])
    prev_vertex_id = path[0]
    path_with_lakes.extend(graph[prev_vertex_id][prev_vertex_id]['path'])
    for vertex_id in path[1:]:
        path_with_lakes.append(graph[prev_vertex_id][vertex_id]['lake'])
        path_with_lakes.extend(graph[prev_vertex_id][vertex_id]['path'])
        #print(path_with_lakes)
        prev_vertex_id = vertex_id
    #print(final_path)
    return path_with_lakes

def reconstruct_graph(graph, lakes):
    new_graph = []
    for i, v1 in enumerate(graph):
        if v1[i]['path'] == None:
            continue
        vertex = []
        for j, v2 in enumerate(graph):
            if v2[j]['path'] == None:
                continue
            edge = {}
            edge['start'] = v1[i]['start']
            edge['dist'] = np.inf
            edge['end'] = v1[i]['path'][-1]
            edge['path'] = copy.deepcopy(v2[j]['path'])
            if i == j:
                edge['lake'] = None
                vertex.append(edge)
                continue

            for k, lake in enumerate(lakes):
                #print(i, j, k)
                #dist1 = len(aStar(tuple(fire1), tuple(lake), env))
                #dist2 = len(aStar(tuple(lake), tuple(fire2), env))
                dist1 = np.linalg.norm(v1[i]['path'][-1] - lake)
                dist2 = 2 * np.linalg.norm(lake - v2[j]['path'][0])
                #num_edges += 1
                if (dist1 + dist2) < edge['dist']:
                    edge['dist'] = dist1 + dist2
                    edge['lake'] = lake
                    edge['fire'] = v2[j]['path'][0]
            #print(vertex)
            vertex.append(edge)
        new_graph.append(vertex)
    #print(graph, fires, lakes)
    return new_graph

def form_clusters(graph, max_dist, max_num_vertices, lakes):
    num_nodes_covered = 0
    num_clusters = 0
    start_vertex_id = 0
    while num_nodes_covered < len(graph):
        short_list = pick_neighbors(graph, start_vertex_id, max_dist, max_num_vertices)
        if (len(short_list) > 1):
            shortest_path = find_shortest_path(graph, short_list)
            shortest_path_with_lakes = populate_path_with_lakes(graph, shortest_path)
            #if num_clusters % 2 == 0:
            graph[shortest_path[0]][shortest_path[0]]['path'] = copy.deepcopy(shortest_path_with_lakes)
            #print("Clustered Nodes", num_clusters)
            #print(shortest_path)
            #print(graph[shortest_path[0]][shortest_path[0]]['path'])
            graph = mark_vertices_covered(graph, shortest_path)
            start_vertex_id = pick_farthest_neighbor(graph, shortest_path[-1])
            #else:
            #    graph[shortest_path[-1]][shortest_path[-1]]['path'] = copy.deepcopy(list(reversed(shortest_path_with_lakes)))
            #    graph = mark_vertices_covered(graph, short_list)
            #    start_vertex_id = pick_farthest_neighbor(graph, shortest_path[0])
        else:
            graph = mark_vertices_covered(graph, short_list)
            start_vertex_id = pick_farthest_neighbor(graph, short_list[0])
            #print("Clustered Nodes", num_clusters)
            #print(short_list)
            #print(graph[short_list[0]][short_list[0]]['path'])
        num_nodes_covered += len(short_list)
        num_clusters += 1
        #print(num_nodes_covered, num_clusters)
        #print(start_vertex_id)
    #print("Number of Clusters", num_clusters)
    graph = reconstruct_graph(graph, lakes)
    return (graph, num_clusters)

def shortest(env):
    orig_graph, orig_fires, orig_lakes = parse_env(env)
    #print("Orig_Graph", orig_graph)
    #print("Orig_Lakes", orig_lakes)
    #print("Orig_Fires", orig_fires)
    graph = copy.deepcopy(orig_graph)
    max_dist = 64
    max_num_vertices = 10
    while True:
        #print("Max Dist", max_dist)
        (graph, num_clusters) = form_clusters(graph, max_dist, max_num_vertices, orig_lakes)
        if (num_clusters == 1):
            break
        max_dist = max_dist * 2
    #print(graph[0][0]['path'])
    #prev_coord = graph[0][0]['path'][0]
    #total_dist = 0
    #for i, coord in enumerate(graph[0][0]['path'][1:]):
    #    if i % 2 == 0:
    #        total_dist += np.linalg.norm(prev_coord - coord)
    #    else:
    #        total_dist += 2 * np.linalg.norm(prev_coord - coord)
    #    prev_coord = coord
    #print("Total Distance: ", total_dist)
    convertedPath=[]
    for path in graph[0][0]['path'][1:]:
        x, y, z = path
        convertedPath.append(tuple((x, y, z)))
    #print(convertedPath)
    return convertedPath
    #print(orig_graph)

if __name__ == "__main__":
    scene = scenario_cityfire()
    scene.generate_scenario()
    clearcache()
    env = scene.simul.state()
    start = time.time()
    p = shortest(env)
    elapsed = time.time() - start
    print("Elapsed Time")
    print(elapsed)


import array
import fcntl
import os
import re
import subprocess
import time
from multiprocessing import cpu_count
from tempfile import mkstemp
from termios import FIONREAD

from catkin_tools.common import log
from catkin_tools.common import version_tuple
from catkin_tools.terminal_color import ColorMapper

mapper = ColorMapper()
clr = mapper.clr

def memory_usage():
    """
    Get used and total memory usage.

    :returns: Used and total memory in bytes
    :rtype: tuple
    """

    # Handle optional psutil support
    try:
        import psutil

        psutil_version = version_tuple(psutil.__version__)
        if psutil_version < (0, 6, 0):
            usage = psutil.phymem_usage()
            used = usage.used
        else:
            usage = psutil.virtual_memory()
            used = usage.total - usage.available

        return used, usage.total

    except ImportError:
        pass

    return None, None

JOBSERVER_SUPPORT_MAKEFILE_OLD = b'''
all:
\techo $(MAKEFLAGS) | grep -- '--jobserver-fds'
'''

JOBSERVER_SUPPORT_MAKEFILE = b'''
all:
\techo $(MAKEFLAGS) | grep -- '--jobserver-auth'
'''

def test_gnu_make_support_common(makefile_content):
    """
    Test if "make -f MAKEFILE -j2" runs successfully when MAKEFILE
    contains makefile_content.
    """

    fd, makefile = mkstemp()
    os.write(fd, makefile_content)
    os.close(fd)

    ret = subprocess.call(['make', '-f', makefile, '-j2'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    os.unlink(makefile)
    return ret == 0

def test_gnu_make_support_old():
    """
    Test if the system 'make' supports the job server implementation.

    This simply checks if the `--jobserver-fds` option is supported by the
    `make` command. It does not tests if the jobserver is actually working
    properly.
    """

    return test_gnu_make_support_common(JOBSERVER_SUPPORT_MAKEFILE_OLD)

def test_gnu_make_support():
    """
    Test if the system 'make' supports the job server implementation.

    This simply checks if the `--jobserver-auth` option is supported by the
    `make` command. It does not tests if the jobserver is actually working
    properly.
    """

    return test_gnu_make_support_common(JOBSERVER_SUPPORT_MAKEFILE)

class GnuMake(object):
    def __init__(self):
        if test_gnu_make_support():
            self.make_args = lambda job_pipe: ["--jobserver-auth=%d,%d" % JobServer._job_pipe]
        elif test_gnu_make_support_old():
            self.make_args = lambda job_pipe: ["--jobserver-fds=%d,%d" % JobServer._job_pipe, "-j"]
        else:
            self.make_args = None

    def is_supported(self):
        return not (self.make_args is None)

class JobServer(object):
    # Whether the job server has been initialized
    _initialized = False

    # Flag designating whether the `make` program supports the GNU Make
    # jobserver interface
    _gnu_make = None

    # Initialize variables
    _load_ok = True
    _mem_ok = True
    _internal_jobs = []
    _max_load = 0
    _max_jobs = 0
    _job_pipe = os.pipe()

    # Setting fd inheritance is required in Python > 3.4
    # This is set by default in Python 2.7
    # For more info see: https://docs.python.org/3.4/library/os.html#fd-inheritance
    if hasattr(os, 'set_inheritable'):
        for fd in _job_pipe:
            os.set_inheritable(fd, True)
            if not os.get_inheritable(fd):
                log(clr('@{yf}@!Warning: jobserver file descriptors are not inheritable.@|'))

    @classmethod
    def _set_max_jobs(cls, max_jobs):
        """Set the maximum number of jobs to be used with the jobserver.

        This will wait for all active jobs to be completed, then re-initialize the job pipe.
        """

        # Read all possible tokens from the pipe
        try:
            os.read(cls._job_pipe[0], cls._max_jobs)
        except (BlockingIOError, InterruptedError):
            pass

        # Update max jobs
        cls._max_jobs = max_jobs

        # Initialize the pipe with max_jobs tokens
        for i in range(cls._max_jobs):
            os.write(cls._job_pipe[1], b'+')

    @classmethod
    def _set_max_mem(cls, max_mem):
        """
        Set the maximum memory to keep instantiating jobs.

        :param max_mem: String describing the maximum memory that can be used
        on the system. It can either describe memory percentage or absolute
        amount.  Use 'P%' for percentage or 'N' for absolute value in bytes,
        'Nk' for kilobytes, 'Nm' for megabytes, and 'Ng' for gigabytes.
        :type max_mem: str
        """

        if max_mem is None:
            cls._max_mem = None
            return
        elif type(max_mem) is float or type(max_mem) is int:
            mem_percent = max_mem
        elif type(max_mem) is str:
            m_percent = re.search(r'([0-9]+)\%', max_mem)
            m_abs = re.search(r'([0-9]+)([kKmMgG]{0,1})', max_mem)

            if m_percent is None and m_abs is None:
                cls._max_mem = None
                return

            if m_percent:
                mem_percent = m_abs.group(1)
            elif m_abs:
                val = float(m_abs.group(1))
                mag_symbol = m_abs.group(2)

                _, total_mem = memory_usage()

                if mag_symbol == '':
                    mag = 1.0
                elif mag_symbol.lower() == 'k':
                    mag = 1024.0
                elif mag_symbol.lower() == 'm':
                    mag = pow(1024.0, 2)
                elif mag_symbol.lower() == 'g':
                    mag = pow(1024.0, 3)

                mem_percent = 100.0 * val * mag / total_mem

        cls._max_mem = max(0.0, min(100.0, float(mem_percent)))

    @classmethod
    def _check_load(cls):
        if cls._max_load is not None:
            try:
                load = os.getloadavg()
                if load[0] < cls._max_load:
                    cls._load_ok = True
                else:
                    cls._load_ok = False
            except NotImplementedError:
                cls._load_ok = True

        return cls._load_ok

    @classmethod
    def _check_mem(cls):
        if cls._max_mem is not None:
            mem_used, mem_total = memory_usage()
            mem_percent_used = 100.0 * float(mem_used) / float(mem_total)
            if mem_percent_used > cls._max_mem:
                cls._mem_ok = False
            else:
                cls._mem_ok = True

        return cls._mem_ok

    @classmethod
    def _check_conditions(cls):
        return (cls._check_load() and cls._check_mem()) or cls._running_jobs() == 0

    @classmethod
    def _acquire(cls):
        """
        Obtain a job server token. Be sure to call _release() to avoid
        deadlocks.
        """
        try:
            # read a token from the job pipe
            token = os.read(cls._job_pipe[0], 1)
            return token
        except (BlockingIOError, InterruptedError):
            pass

        return None

    @classmethod
    def _release(cls):
        """
        Write a token to the job pipe.
        """
        os.write(cls._job_pipe[1], b'+')

    @classmethod
    def _running_jobs(cls):

        try:
            buf = array.array('i', [0])
            if fcntl.ioctl(cls._job_pipe[0], FIONREAD, buf) == 0:
                return cls._max_jobs - buf[0]
        except (NotImplementedError, OSError):
            pass

        return cls._max_jobs

def initialized():
    """Return True if the job server has been initialized."""
    return JobServer._initialized

def initialize(max_jobs=None, max_load=None, max_mem=None, gnu_make_enabled=False):
    """
    Initialize the global GNU Make jobserver.

    :param max_jobs: the maximum number of jobs available
    :param max_load: do not dispatch additional jobs if this system load
    value is exceeded
    :param max_mem: do not dispatch additional jobs if system physical
    memory usage exceeds this value (see _set_max_mem for additional
    documentation)
    :param gnu_make_enabled: Set gnu make compatibility enabled
    """

    # Check initialization
    if JobServer._initialized is True:
        return

    # Check if the jobserver is supported
    if JobServer._gnu_make is None:
        JobServer._gnu_make = GnuMake()

    if not JobServer._gnu_make.is_supported():
        log(clr('@!@{yf}WARNING:@| Make job server not supported. The number of Make '
                'jobs may exceed the number of CPU cores.@|'))

    # Set gnu make compatibility enabled
    JobServer._gnu_make_enabled = gnu_make_enabled

    # Set the maximum number of jobs
    if max_jobs is None:
        try:
            max_jobs = cpu_count()
        except NotImplementedError:
            log('@{yf}WARNING: Failed to determine the cpu_count, falling back to 1 jobs as the default.@|')
            max_jobs = 1
    else:
        max_jobs = int(max_jobs)

    JobServer._set_max_jobs(max_jobs)
    JobServer._max_load = max_load
    JobServer._set_max_mem(max_mem)

    JobServer._initialized = True

def load_ok():
    return JobServer._load_ok

def mem_ok():
    return JobServer._mem_ok

def set_max_mem(max_mem):
    """
    Set the maximum memory to keep instantiating jobs.

    :param max_mem: String describing the maximum memory that can be used on
    the system. It can either describe memory percentage or absolute amount.
    Use 'P%' for percentage or 'N' for absolute value in bytes, 'Nk' for
    kilobytes, 'Nm' for megabytes, and 'Ng' for gigabytes.
    :type max_mem: str
    """

    JobServer._set_max_mem(max_mem)

def wait_acquire():
    """
    Block until a job server token is acquired, then return it.
    """

    token = None

    while token is None:
        # make sure we're observing load and memory maximums
        if not JobServer._check_conditions():
            time.sleep(0.01)
            continue

        # try to get a job token
        token = JobServer._acquire()

    return token

def acquire():
    """
    Block until a job server token is acquired, then return it.
    """

    token = None

    # make sure we're observing load and memory maximums
    if JobServer._check_conditions():
        # try to get a job token
        token = JobServer._acquire()

    return token

def add_label(label):
    JobServer._internal_jobs.append(label)

def del_label(label):
    JobServer._internal_jobs.remove(label)

def try_acquire_gen():
    """
    Yield None until a job server token is acquired, then yield it.
    """
    while True:
        # make sure we're observing load and memory maximums
        if JobServer._check_conditions() and running_jobs() < max_jobs():
            # try to get a job token
            token = JobServer._acquire()
            yield token
        else:
            yield None

def try_acquire():
    """
    Try to acquire a job token, return None if not available.
    """
    # make sure we're observing load and memory maximums
    if JobServer._check_conditions() and running_jobs() < max_jobs():
        # try to get a job token
        token = JobServer._acquire()
        return token

    return None

def release(label=None):
    """
    Release a job server token.
    """
    JobServer._release()
    if label is not None:
        del_label(label)

def gnu_make_enabled():
    return JobServer._gnu_make.is_supported() and JobServer._gnu_make_enabled

def gnu_make_args():
    """
    Get required arguments for spawning child gnu Make processes.
    """

    if JobServer._gnu_make_enabled:
        return JobServer._gnu_make.make_args(JobServer._job_pipe)
    else:
        return []

def max_jobs():
    """
    Get the maximum number of jobs.
    """

    return JobServer._max_jobs

def running_jobs():
    """
    Try to estimate the number of currently running jobs.
    """

    if not gnu_make_enabled():
        return 0

    return JobServer._running_jobs()

def internal_jobs():
    return JobServer._internal_jobs

class JobGuard:

    """
    Context manager representing a jobserver job.
    """

    def __enter__(self):
        wait_acquire()
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        release()
        return False


"""
Random test stuff
"""

from pysynth.seq import Sequencer
from pysynth.utils import *
from pysynth.osc import *
from pysynth.synth import *
from pysynth.filters import *
from pysynth.output.base import OutputHandler
from pysynth.output.modules import *
from pysynth.wrappers import querty, mml
from pysynth.wrappers.midi import midi
from pysynth.envelope.amp import *

import time
import pyaudio
import threading
import struct
import wave
import math
import os
import copy

from tkinter import Tk, Frame

BITRATE = 16000
#MAX_AMPLITUDE = 32767.0
#MAX_AMPLITUDE = 127.0

paudio = pyaudio.PyAudio()
stream = paudio.open(format=pyaudio.paFloat32,
                     channels=1,
                     output=True,
                     rate=44100)

def type_test():

    # Tests the audio type test

    audio = AudioValue(0.5, 0, 1000)
    #audio.add_event(ExponentialRamp, 800.0, get_time()+10000000000)
    #audio.add_event(LinearRamp, 500.0, get_time()+20000000000)

    #audio.add_event(ExponentialRamp, 800.0, get_time()+10)
    #audio.add_event(LinearRamp, 500.0, get_time()+20)

    #audio.exponential_ramp(0, get_time() + 2000000000)

    audio.linear_ramp(1, get_time() + 1000000000)

    audio.cancel_all_events()

    audio.linear_ramp(0, get_time() + 2000000000)

    while True:

        thing = audio.value

        print(thing)

        if thing == 0:

            break

        time.sleep(0.01)

def start_stream(osc):

    """
    Starts the audio thread and starts streaming data
    """

    thread = threading.Thread(target=stream_data, args=[osc])
    thread.daemon = True
    thread.start()

    return thread

def stream_data(data, time=None):

    """
    Streams data to PyAudio.
    Data is a oscillator we iterate over

    :param data: Data to stream
    """

    for num, i in enumerate(data):

        #print(i)

        stream.write(struct.pack('f', i))

        if time and num > time:

            # We are done

            return

def write_wave(data, seconds):

    """
    Writes a specified amount of wave data:
    :param data: Data to write
    :param seconds: Seconds to write
    """

    # Open the wave file:

    wav = wave.open('blorck.wav', 'wb')

    wav.setnchannels(1)
    wav.setsampwidth(4)
    wav.setframerate(44100.0)

    iter(data)

    for i in range(44100*seconds):

        value = next(data)

        wav.writeframesraw(struct.pack('<f', value))

    wav.close()

class KeyboardHandler:

    """
    Handles keyboard input, removing and adding
    oscillators to an AudioCollection as necessary.
    """

    def __init__(self, aud, keys):

        self.aud = aud  # Audio collection to maintain
        self.keys = keys  # Mapping keys to oscillators.

        # Mapping keys to oscillators to generate

        self.osc_keys = {'z': SineOscillator, 'x': SquareOscillator, 'c': SawToothOscillator, 'v': TriangleOscillator}

    def remove_key(self, key):

        """
        Removes an oscillator from the AudioCollection.

        :param key: Oscillator to remove
        """

        print("Removed key: {}".format(key.keysym))

        if key.keysym in self.osc_keys:

            # DO nothing,

            return

        if self.keys[key.keysym] not in self.aud._objs:

            # Nothing, return

            return

        self.aud.remove_module(self.keys[key.keysym])

    def add_key(self, key):

        """
        Adds an oscillator from the AudioCollection.

        :param key: Oscillator to add
        """

        print("Added Key: {}".format(key.char))

        if key.char in self.osc_keys:

            # Generate a new oscillator mp

            self.keys = gen_oscs(self.osc_keys[key.char])

            return

        if self.keys[key.char] in self.aud._objs:

            # Nothing, return

            return

        self.aud.add_module(self.keys[key.char])

def gen_oscs(osc):

    """
    Generates a list of oscillators
    :param osc: Oscillator to use to generate our list
    :return: List of keys mapped to oscillators
    """

    oscs = {}
    keys = ['q', 'a', 'w', 's', 'e', 'd', 'r', 'f', 't', 'g', 'y', 'h', 'u', 'j', 'i', 'k', 'o', 'l', 'p']

    # Generating oscillators and mapping them to keys:

    for note in range(-17, 2):

        oscs[keys[note+17]] = osc()
        oscs[keys[note + 17]].freq = 440 * (2 ** (1 / 12)) ** note
        iter(oscs[keys[note + 17]])

    return oscs

def old_keyboard_input():

    """
    Maps certain keys to a keyboard,
    and adding the notes as we press/release keys.
    """

    oscs = gen_oscs(SineOscillator)

    print(oscs)

    # Create AudioCollection for output

    collec = AudioCollection()
    collec.add_module(ZeroOscillator())

    # Create KeyBoardHandler:

    hand = KeyboardHandler(collec, oscs)

    # Start streaming the AudioCollection:

    start_stream(collec)

    # Disabling continuous keypresses:

    os.system("xset r off")

    # Creating TKinter data:

    root = Tk()
    f = Frame(root, width=100, height=100)
    f.bind("<KeyPress>", hand.add_key)
    f.bind("<KeyRelease>", hand.remove_key)
    f.pack()
    f.focus_set()
    root.mainloop()

    os.system("xset r on")

def pitch_comp(osc):

    """
    Tests the pitch of the incoming oscillator.
    :param osc:
    """

    sine = SineOscillator(freq=osc.freq.value)

    print("Pitch One:")

    stream_data(sine, time=220500)

    print("Pitch Two:")

    stream_data(osc, time=220500)

def chord_test():

    """
    Tests if the additive synthesis is producing valid chords
    """

    osc1 = TriangleOscillator(freq=440.0)
    osc2 = TriangleOscillator(freq=880.0)
    osc3 = TriangleOscillator(freq=1320.0)
    osc4 = TriangleOscillator(freq=1760.0)

    thing = AudioCollection()

    thing.add_module(osc1, start=True)

    start_stream(thing)

    time.sleep(1)
    thing.add_module(osc2, start=True)
    time.sleep(1.5)
    thing.add_module(osc3, start=True)
    time.sleep(1.76)
    thing.add_module(osc4, start=True)

def avgfilt_test():

    """
    Tests Moving Average Filter
    """

    # Set up a sine wave:

    sine = SineOscillator()

    # Set up a filter:

    filter = MovingAverage(101)

    filter.bind(sine)

    sine.freq = 1234.0

    print(sine.freq.value)
    print(filter.freq.value)

    # Start the stream:

    start_stream(filter)

def fm_test():

    """
    Tests FM Synthesis
    """

    osc1 = SineOscillator(freq=440)
    osc2 = SineOscillator(freq=6160)

    oscmain = SineOscillator(freq=440)

    collec = AudioCollection()

    twomain = FMSynth(oscmain, osc2, 1)

    oneandtwo = FMSynth(twomain, osc1, 5)

    collec.add_node(oneandtwo)

    start_stream(twomain)

def keyboard_input():

    # Tests the QUERTY sequencer, and the QUERTYKeyboard input module.

    # Create the QWERTY wrapper

    sequencer = querty.QWERTYWrapper()

    # Create the output handler:

    out = OutputHandler()
    pyaudo = PyAudioModule()
    pyaudo.special = True
    out.add_output(pyaudo)

    #out.add_output(WaveModule("test_seq.wav"))

    # Configure for keyboard:

    sequencer.load_keyboard()

    attack = 1000000000
    decay = 1000000000
    sustain = 0.2
    release = 1000000000

    env1 = ADSREnvelope(attack, decay, sustain, release)
    env2 = ADSREnvelope(attack, decay, sustain, release)
    env3 = ADSREnvelope(attack, decay, sustain, release)
    env4 = ADSREnvelope(attack, decay, sustain, release)

    env1.bind(SineOscillator(440.0))
    env2.bind(SquareOscillator(440.0))
    env3.bind(SawToothOscillator(440.0))
    env4.bind(TriangleOscillator(440.0))

    # Get controller for sine wave oscillator:

    sine = out.bind_synth(env1)
    square = out.bind_synth(env2)
    saw = out.bind_synth(env3)
    tri = out.bind_synth(env4)

    # Add sine oscillator for default instrument:

    sequencer.add_synth(sine, name=0)
    sequencer.add_synth(square, name=1)
    sequencer.add_synth(saw, name=2)
    sequencer.add_synth(tri, name=3)

    # Start the output handler:

    out.start()

    # Start the sequencer:

    sequencer.start()
    sequencer.join()
    sequencer.stop()
    out.stop()

def freq_conv(num, middle_pitch=440.0):

    # Calculate and return the frequency of the note:

    return middle_pitch * pow(2, (((num) / 12)))

def mixing_test():

    # Tests mixing operations:

    pass

def mml_test():

    # Tests the MML wrapper

    #song = '$ t120 o4 l4 e f+ b > c+ d < f+ e > c+  d c+ <e f+ b > c+ d < f+ e > c+  d c+'
    #song = '$o4 c r e r g r b r;$o4 r d r f r a r <c'
    #song = 'o4 c d e f g a b <c d'

    #song = "o4 l1 ca"

    #song = "t60 l4 o4 /: [ceg] [fac]1 :/4"

    #song = "t92 l8 o4 [>cg<cea]2. [>cg<ceg]4 [>>a<a<c+fa+]2. [>>a<a<c+ea]4 " \
    #       "[>>f<fg+<cg]2. [>>f<fg+<cf]4 [>>g<gg+b<g+]2." \
    #       "[>>g<g<g]4 o3 l32 v6 cdef ga b<c de fg"

    #song = "t92 l4 o4 [>>a<a<c+fa+]"
    #song = 'o3 l32 v6 cdefgab<cdefg'
    #song = 't30 a'
    #song = "t92 [>>f<fg+<cg]2. [>>f<fg+<cf]4 [>>g<gg+b<g+]2. [>>g<g<g]4 o3 t92 l32 v6 cdef ga b<c de fg"
    #song = 't60 [>>g<g<g]4'

    #song = 't60 o3 l4 cdefgab<c> l8 cdefgab<c> l16 cdefgab l32 cdefgab'

    song1 = "t92 l8 o4 $ [>cg<cea]2. [>cg<ceg]4 [>>a<a<c+fa+]2. [>>a<a<c+ea]4 " \
           "[>>f<fg+<cg]2. [>>f<fg+<cf]4 [>>g<gg+b<g+]2. r4; " \
           "t92 $ l1 o3 v12 r r r r2 r8 l32 v6 cdef ga b<c de fg;"

    song = "t60 l4 o4 a+ r a+;" \
           "t60 l4 o4 r  r >a+"

    song = "t120$l8 o3 >g+2.. g+ a+4. a+ <c2 >a+ g+2.. a+4 a+4 <c4. >d+ a+ g+2. g+ a+4. a+ <c2 >a+ g+2.. a+4 a+4 <c2."

    song = "t120$l8 o3 >g+2.. g+ a+4. a+ <c2 >a+ g+2.. a+4 a+4 <c4. >d+ a+ g+2. g+ a+4. a+ <c2 >a+ g+2.. a+4 a+4 <c2."

    song = 't60 l1 a r8 a. r8 a.. r8 a...'

    song = "t105 l8 o5 q75 v100 " \
        "ab-> c4c4c4 c4.faf fedccc+dccdfdd c4. c4c4c4 c4.faf fedccc+dcccc< f>ccc< g>ccc< [e>c]2 [f>c]4 [b->d]2.^2 [b-]4 [ca]2[cb-]4 [fa]4 <f4>" \
        "r4 c4>c4r4< c4>c4r4< [cdf]4[cdf]4[cdf]4 [ce]4r4" \
        "r4 f>ccc< f>ccc< g>ccc< [e>c]2 [f>c]4 [b->d]2.^2 [b-]4 [ca]2[cb-]4 [fa]4 <f4>;" \

    song2 = "t120$l8 o4 v9rr g  g4  g+ a+4  d4  d4  d+2 d  c   g   g4 g+   a+4 d4 d4 d+2 rr g g4 g+ a+4 d4 d4 d+2 d c g g4 g+ a+4 d4 d4 d+2.;" \
           "t120$l8 o4 v9 rr d+ d+2 r  >a+4 a+4 <c2 >a+ g+ <d+ d+2 r  >a+4 a+4 a+2 rr d+ d+2 r >a+4 a+4 <c2 >a+ g+ <d+ d+2 r >a+4 a+4 a+2.;" \
           "t120$l8 o4 v9 rr c  c2  r  >f4  f4  g2  a+  g+ <c  c2 >f  f4   r   f g2< rr c c2 r >f4 f4 g2 a+ g+ <c c2 >f f4 r f g2.<;" \
           "t120$l8 o3 v8 >g+2.. g+ a+4. a+ <c2 >a+ g+2.. a+4 a+4 <c4. >d+ a+ g+2. g+ a+4. a+ <c2 >a+ g+2.. a+4 a+4 <r2." \

    #song = 't60 o3 l4 cdefgab<c> l8 cdefgab<c> l16 cdefgab l32 cdefgab'

    #song = '$ t120 o4 l4 e f+ b > c+ d < f+ e > c+  d c+ <e f+ b > c+ d < f+ e > c+  d c+'

    sec = mml.MMLWrapper()

    sec.load_string(song2)

    out = OutputHandler()

    pyaud = PyAudioModule()
    pyaud.special = True
    out.add_output(pyaud)

    # --== Instrument Selection: ==--
    # Uncomment the instrument you want to use!

    #osc = SineOscillator(freq=440.0)
    #osc = SquareOscillator(freq=440.0)
    osc = SawToothOscillator(freq=440.0)
    #osc = TriangleOscillator(freq=440.0)

    final = osc
    # --== End Instrument Selection! ==--

    # --== Other Output Options: ==--
    # Uncomment to write to a wav file:

    #wave = WaveModule('saw_test.wav')
    #out.add_output(wave)

    # --== End Other Output Options! ==--

    # --== ADSR Options: ==--
    # Configure the parameters for the ADSR envelope:

    attack = 10000000
    decay = 100000
    sustain = 0.2
    release = 5000000

    # Uncomment to enable the envelope:

    #env = ADSREnvelope(attack, decay, sustain, release)
    #env.bind(osc)
    #final = env

    # --== End ADSR Options! ==--

    amp = AmpScale()
    amp.bind(final)
    final = amp

    cont = out.bind_synth(final)

    sec.add_synth(cont)

    # Start output handler:

    out.start()

    # Start the sequencer:

    sec.start()
    sec.join()

    print("Done joining")

    print("Stopping sequencer...")

    sec.stop()

    print("Stopping output...")

    out.stop()

def delay():

    # Tests the delay function of OutputControl

    osc = TriangleOscillator(freq=440.0)

    out = OutputHandler()

    pyaud = PyAudioModule()
    pyaud.special = True
    out.add_output(pyaud)

    cont = out.bind_synth(osc)

    out.start()

    cont.start(time=3000000000 + get_time())

    cont.stop(time=5000000000 + get_time())

    time.sleep(8)

    out.stop()

def deepcopy():

    # Tests the deep copy of synths

    # OutputHandler:

    out = OutputHandler()

    # PyAudio module:

    pyaud = PyAudioModule()
    pyaud.special = True

    out.add_output(pyaud)

    osc = SineOscillator(freq=440.0)

    final = out.bind_synth(osc)

    # Make a deep copy:

    thing = copy.deepcopy(final)

def audio_param():

    # Tests the audio parameter as it alters the frequency of the pitch

    # Create a simple sine oscillator:

    osc = SineOscillator(freq=440)

    out = OutputHandler()

    pyaud = PyAudioModule()
    pyaud.special = True
    out.add_output(pyaud)

    sine = out.bind_synth(osc)

    out.start()

    # Start the oscillator:

    sine.start()

    # Have the frequency go up to 550 in 5 seconds:

    sine.info.freq.exponential_ramp(550.0, get_time()+5000000000)

    time.sleep(5)

    # Have the frequency go back down to 440 in 10 seconds:

    sine.info.freq.linear_ramp(440.0, get_time()+60000000000)

    sine.join()

    out.stop()

def test_output():

    # Creates and registers a oscillator. Used for testing output.

    osc1 = TriangleOscillator(freq=440.0)
    osc2 = TriangleOscillator(freq=880.0)
    osc3 = TriangleOscillator(freq=1320.0)
    osc4 = TriangleOscillator(freq=1760.0)

    out = OutputHandler()

    out.add_output(WaveModule("test.wav"))

    # Add the PyAudio module:

    pyaud = PyAudioModule()
    pyaud.special = True

    out.add_output(pyaud)

    # Add the WaveModule:

    # Bind the synth:

    final = out.bind_synth(osc1)
    final2 = out.bind_synth(osc2)
    final3 = out.bind_synth(osc3)
    final4 = out.bind_synth(osc4)

    # Start the OutputHandler:

    #out.start()

    # Start the synth:

    final.start()

    out.start()

    time.sleep(1)
    final2.start()

    time.sleep(1)
    final3.start()

    time.sleep(1)
    final4.start()

    time.sleep(1)

    final4.stop()
    time.sleep(1)
    final3.stop()
    time.sleep(1)
    final2.stop()
    time.sleep(3)
    final.stop()

    out.stop()

def module_connection():

    # Tests the connectivity features of modules

    mod1 = SineOscillator(freq=640.0)
    mod2 = DummyModule()
    mod3 = DummyModule()
    mod4 = DummyModule()

    mod2.bind(mod1)
    mod3.bind(mod2)
    mod4.bind(mod3)

    print("Connected modules: {}".format(mod4.info.connected))
    print("Should be 4!")

    print("Chain frequency: {}".format(mod4.info.freq))
    print("Should be 640.0!")

class DummyWait(BaseModule):

    """
    Passes values from the inputs attached,
    but continues to play for 5 seconds after we are released.
    """

    def __init__(self):

        super().__init__()

        self.finishing = False
        self.wait = 5000000000
        self.start_time = 0

    def start(self):

        self.finishing = False
        self.wait = 5000000000
        self.start_time = 0

    def finish(self):

        print("Dummy module finishing...")

        self.finishing = True

        self.start_time = get_time()

        print("Starting time: {}".format(self.start_time))

    def get_next(self):

        # Check if we are finishing

        if self.finishing:

            if get_time() > self.start_time + self.wait:

                # We are done, lets say we are finished:

                print("Dummy module done!")
                print("Current time: {}".format(get_time()))
                print("Target time: {}".format(self.start_time + self.wait))

                self.done()

                return None

        # Otherwise, lets just return the input:

        return self.get_input()

def fade_test():

    # Tests the ability for synths to continue to play after they have been stopped

    out = OutputHandler()

    pyaud = PyAudioModule()
    pyaud.special = True
    out.add_output(pyaud)

    osc = TriangleOscillator(freq=440.0)

    dummy = DummyWait()

    dummy.bind(osc)

    cont = out.bind_synth(dummy)

    sec = Sequencer()
    sec.add_synth(cont)

    print(cont.info.connected)
    print(cont.info.done)

    out.start()

    print("Starting controller...")

    cont.start()

    print("Waiting...")

    time.sleep(5)

    print("Stopping controller...")

    cont.stop()

    time.sleep(5)

    print(("Waiting..."))

    time.sleep(5)

    print("Synth should be stopped!")

    print("Starting synth...")

    cont.start()

    print("Waiting...")

    time.sleep(5)

    print("Stopping cont...")

    cont.stop()

    print("Waiting three seconds to interrupt...")

    time.sleep(3)

    print("Interrupting!")

    cont.start()

    cont.stop()

    print("Finished!")

    #sec.start()

def adsr_test():

    # Tests the ADSR envelope

    attack = 1000000000
    decay = 1000000000
    sustain = 0.5
    release = 1000000000

    # Create the OutputHandler:

    out = OutputHandler()

    pyaud = PyAudioModule()
    pyaud.special = True
    out.add_output(pyaud)

    osc = SawToothOscillator(freq=440.0)

    env = ADSREnvelope(attack, decay, sustain, release)

    env.bind(osc)

    cont = out.bind_synth(env)

    out.start()

    print("Starting:")

    cont.start()

    time.sleep(5)

    print("Stopping:")

    cont.stop()

    time.sleep(5)

def MIDI_test():

    # Tests if we can get MIDI info from ALSA

    out = OutputHandler()

    pyaud = PyAudioModule()
    pyaud.special = True
    out.add_output(pyaud)

    osc = SineOscillator(freq=440.0)
    #osc = SquareOscillator(freq=440.0)
    #osc = SawToothOscillator(freq=440.0)
    #osc = TriangleOscillator(freq=440.0)

    attack = 1000000000
    decay = 1000000000
    sustain = 0.2
    release = 1000000000

    env = ADSREnvelope(attack, decay, sustain, release)
    env.bind(osc)

    cont = out.bind_synth(env)

    seq = midi.MIDIWrapper()
    seq.alsa_live()

    seq.add_synth(cont)

    out.start()
    seq.start()

    seq.join()

    seq.stop()
    out.stop()


""" 05_Collisions_v2 by Sun Woo Yi
This version will be carried on from 05_Collisions_v1_testing_2
This version will show a collision being detected between two objects
When the objects collide the game will quit automatically
26/05/2023
"""

import pygame

# Initialize Pygame
pygame.init()

# Set the dimensions of the window
window_width = 261
window_height = 377

# Create the window
screen = pygame.display.set_mode((window_width, window_height))

class GreenBox(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((50, 50))
        self.image.fill((0, 255, 0))
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

class BlueBox(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((50, 50))
        self.image.fill((0, 0, 255))
        self.rect = self.image.get_rect()
        self.rect.x = x
        self.rect.y = y

# Set up the green box
green_box_x = window_width/2 - 50/2
green_box_y = 377 - 50
green_box = GreenBox(green_box_x, green_box_y)
all_sprites = pygame.sprite.Group(green_box)

# Set up the blue box
blue_box_x = window_width/2 - 50/2
blue_box_y = 0
blue_box = BlueBox(blue_box_x, blue_box_y)
all_sprites.add(blue_box)

# Game loop
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # Move the boxes
    green_box.rect.y -= 1
    blue_box.rect.y += 1

    # Check for collisions
    if pygame.sprite.collide_rect(green_box, blue_box):
        quit()

    # Draw the boxes
    screen.fill((255, 255, 255))
    all_sprites.draw(screen)
    pygame.display.update()

pygame.quit()


from functools import lru_cache
from typing import List

class Solution:
    def minScoreTriangulation(self, values: List[int]) -> int:
        @lru_cache(None)
        def dp(i, j):
            if i + 2 > j:
                return 0
            if i + 2 == j:
                return values[i] * values[i + 1] * values[j]
            return min((values[i] * values[k] * values[j] + dp(i, k) + dp(k, j)) for k in range(i + 1, j))

        return dp(0, len(values) - 1)

if __name__  == '__main__':
    values = [1,2,3]
    values = [3,7,4,5]
    values = [1,3,1,4,1,5]
    rtn = Solution().minScoreTriangulation(values)
    print(rtn)

1039. 多边形三角剖分的最低得分.py


import os

from fontTools.designspaceLib import DesignSpaceDocument, AxisDescriptor, SourceDescriptor, InstanceDescriptor, RuleDescriptor

root = os.getcwd()
doc = DesignSpaceDocument()

familyName = "MutatorSansTest"

#------
# axes
#------

a1 = AxisDescriptor()
a1.maximum = 1000
a1.minimum = 0
a1.default = 0
a1.name = "width"
a1.tag = "wdth"
doc.addAxis(a1)

a2 = AxisDescriptor()
a2.maximum = 1000
a2.minimum = 0
a2.default = 0
a2.name = "weight"
a2.tag = "wght"
doc.addAxis(a2)

#---------
# masters
#---------

s0 = SourceDescriptor()
s0.path = "MutatorSansLightCondensed.ufo"
s0.name = "master.MutatorSansTest.LightCondensed.0"
s0.familyName = familyName
s0.styleName = "LightCondensed"
s0.location = dict(weight=0, width=0)
s0.copyLib = True
s0.copyInfo = True
s0.copyGroups = True
s0.copyFeatures = True
doc.addSource(s0)

s1 = SourceDescriptor()
s1.path = "MutatorSansBoldCondensed.ufo"
s1.name = "master.MutatorSansTest.BoldCondensed.1"
s1.familyName = familyName
s1.styleName = "BoldCondensed"
s1.location = dict(weight=1000, width=0)
doc.addSource(s1)

s2 = SourceDescriptor()
s2.path = "MutatorSansLightWide.ufo"
s2.name = "master.MutatorSansTest.LightWide.2"
s2.familyName = familyName
s2.styleName = "LightWide"
s2.location = dict(weight=0, width=1000)
doc.addSource(s2)

s3 = SourceDescriptor()
s3.path = "MutatorSansBoldWide.ufo"
s3.name = "master.MutatorSansTest.BoldWide.3"
s3.familyName = familyName
s3.styleName = "BoldWide"
s3.location = dict(weight=1000, width=1000)
doc.addSource(s3)

#-----------
# instances
#-----------

i0 = InstanceDescriptor()
i0.name = 'instance_LightCondensed'
i0.familyName = familyName
i0.styleName = "Medium"
i0.path = os.path.join(root, "instances", "MutatorSansTest-Medium.ufo")
i0.location = dict(weight=500, width=327)
i0.kerning = True
i0.info = True
doc.addInstance(i0)

#-------
# rules
#-------

rd = RuleDescriptor()
rd.name = 'fold_I_serifs'
rd.conditionSets = [[{'minimum': 0.0, 'maximum': 328.0, 'name': 'width'}]]
rd.subs = [('I', 'I.narrow')]
doc.addRule(rd)

rd = RuleDescriptor()
rd.name = 'fold_S_terminals'
rd.conditionSets = [[{'minimum': 0.0, 'maximum': 1000.0, 'name': 'width'}, {'minimum': 0.0, 'maximum': 500.0, 'name': 'weight'}]]
rd.subs = [('S', 'S.closed')]
doc.addRule(rd)

#--------
# saving
#--------

path = os.path.join(root, "MutatorSans__Test__.designspace")
doc.write(path)


'''
Write a Python Script that captures images from your webcam video stream
Extract all faces from the image frame(using haarcascade)
Store the face information into numpy arrays

1. Read and show video stream, capture images
2. Detect faces and show bounding box
3. Flatten the largest face image(gray scale image) and save it in numpy arrays
4. Repeat the above for multiple people to generate training data
'''

import cv2
from cv2 import sort
import numpy as np

cap = cv2.VideoCapture(0)

# Face detection
face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")
skip=0
face_data = []
dataset_path = './data/'
file_name = input("Enter your name - ")
while True:
    ret, frame = cap.read()
    if ret ==False:
        continue

    gray_frame = cv2.cvtColor(frame,cv2.COLOR_BGR2GRAY)

    faces = face_cascade.detectMultiScale(frame, 1.3,5)
    # print(faces)
    faces = sorted(faces,key = lambda f:f[2]*f[3], reverse=True)
    face_section=frame
    for face in faces:
        x,y,w,h = face
        cv2.rectangle(frame, (x,y),(x+w, y+h),(255,255,0),3)
        #Extract region of interest means crop out the face
        offset = 10
        face_section = frame[y-offset:y+h+offset, x-offset:x+w+offset]
        face_section = cv2.resize(face_section,(100,100))

        if skip%10==0:
            face_data.append(face_section)
            print(skip/10)

    cv2.imshow("Frame", frame)

    #cv2.imshow("Face frame", face_section)

    skip+=1
    key_pressed = cv2.waitKey(1) & 0xFF
    if key_pressed==ord('q'):
        break
face_data = np.asarray(face_data)
face_data = face_data.reshape((face_data.shape[0],-1))
print(face_data.shape)

np.save(dataset_path+file_name+'.npy', face_data)
print("data saved at - "+dataset_path+file_name+'.npy')
cap.release()
cv2.destroyAllWindows()


# 섬의 개수
# pypy3: 200ms

from collections import deque

def bfs(start):
    queue = deque()
    queue.append(start)
    mapp[start[0]][start[1]] = 0

    while queue:
        now = queue.popleft()
        for m in move:
            new_r, new_c = now[0] + m[0], now[1] + m[1]
            if new_r in range(h) and new_c in range(w) and mapp[new_r][new_c]:
                mapp[new_r][new_c] = 0
                queue.append((new_r, new_c))

move = [(0, 1), (1, 0), (0, -1), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)]
w, h = map(int, input().split())
while w and h:
    mapp = []
    for _ in range(h):
        mapp.append(list(map(int, input().split())))

    cnt = 0
    for i in range(h):
        for j in range(w):
            if mapp[i][j]:
                bfs((i,j))
                cnt += 1

    print(cnt)

    w,h = map(int, input().split())


"""Backward compatibility: in the past, APITaxi was deployed on a dedicated
server and nginx was configured to redirect / to the console, and /doc to
swagger.

Now, the infrastructure is deployed on CleverCloud. We need to perform these
redirections here, since it is impossible to perform redirections on
CleverCloud's loadbalancers.
"""

from flask import Blueprint, current_app, redirect

blueprint = Blueprint('index', __name__)

@blueprint.route('/', methods=['GET'])
def index():
    url = current_app.config.get('CONSOLE_URL')
    return redirect(url, code=301)

@blueprint.route('/doc', methods=['GET'])
def doc():
    url = current_app.config.get('SWAGGER_URL')
    return redirect(url, code=301)


import time
import requests as rs
from bs4 import BeautifulSoup as bs
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
import smtplib

url = "http://www.gumtree.com.au/s-cats-kittens/launceston/c18435l3000393"
domain = "http://www.gumtree.com.au"

def load_exempt(path="./exempt.txt"):
    exempt_list = []
    with open(path) as f:
        for line in f.readlines():
            exempt_list.append(line.strip())
    return exempt_list

def abstract_entry(entry):
    result = {}
    result["name"] = entry("a", itemprop="url", class_="ad-listing__title-link")[0].text.strip()
    if result["name"].upper().startswith("WANTED"):
        result["name"]= ""
    result["price"]= entry("span", class_="j-original-price")[0].text.strip()
    result["add_datetime"]= entry("div", class_="ad-listing__date")[0].text.strip()
    result["entry_url"]= entry("a", itemprop="url")[0]["href"]
    img = entry("img")
    if len(img)==0:
        img = None
    else:
        img = img[0]["src"]
    result["img"] = img
    area = entry("span", class_="ad-listing__location-area")[0].text.strip()
    suburb = entry("span", class_="ad-listing__location-suburb")
    if len(suburb) == 0:
        suburb = ""
    else:
        suburb = suburb[0].text.strip()
    result["loc"] = area + suburb
    result["description"] = entry("p", class_="ad-listing__description")[0].text.strip()
    return result

def notify(result):
    # Send an HTML email with an embedded image and a plain text message for
    # email clients that don't want to display the HTML.

    # Define these once; use them twice!
    strFrom = 'myme5261314@sina.com'
    # strTo = 'jingna93@163.com'
    strTo = 'myme5261314@126.com'

    # Create the root message and fill in the from, to, and subject headers
    msgRoot = MIMEMultipart('related')
    msgRoot['Subject'] = result["name"]
    msgRoot['From'] = strFrom
    msgRoot['To'] = strTo
    msgRoot.preamble = 'This is a multi-part message in MIME format.'

    # Encapsulate the plain and HTML versions of the message body in an
    # 'alternative' part, so message agents can decide which they want to display.
    msgAlternative = MIMEMultipart('alternative')
    msgRoot.attach(msgAlternative)

    text_message_template = "Title: %s\nPrice: %s\nLocation: %s\nAdd Time: %s\nDescription: %s\n"
    text_message = text_message_template % (result["name"], result["price"], result["loc"] , result["add_datetime"], result["description"])
    # msgText = MIMEText('This is the alternative plain text message.')
    msgText = MIMEText(text_message)
    msgAlternative.attach(msgText)

    # We reference the image in the IMG SRC attribute by the ID we give it below
    html_msg = text_message.replace("\n", "
")
    if result["img"] is not None:
        html_msg += '' % result["img"]
    html_msg += 'link' % (domain + result["entry_url"])
    # msgText = MIMEText('Some HTML text and an image.

Nifty!', 'html')
    msgText = MIMEText(html_msg, 'html')
    msgAlternative.attach(msgText)

    # # This example assumes the image is in the current directory
    # fp = open('test.jpg', 'rb')
    # msgImage = MIMEImage(fp.read())
    # fp.close()

    # # Define the image's ID as referenced above
    # msgImage.add_header('Content-ID', '<image1>')
    # msgRoot.attach(msgImage)

    # Send the email (this example assumes SMTP authentication is required)
    try:
        smtp = smtplib.SMTP()
        smtp.connect('smtp.sina.com')
        smtp.login('myme5261314', 'Lp5261314!')
        smtp.sendmail(strFrom, strTo, msgRoot.as_string())
        smtp.quit()
    except Exception as e:
        print(e)
        return False
    return True

def main():
    exempt_list = load_exempt()
    while True:
        list_page = rs.get(url)
        list_page = bs(list_page.text)
        area = list_page("ul", id="srchrslt-adtable")
        entry_list = area[0]("li")
        result_list = [abstract_entry(entry) for entry in entry_list]
        result_list = [result for result in result_list if result["name"] != "" and result["entry_url"].split("/")[-1] not in exempt_list]
        for result in result_list:
            print(result)
            if notify(result):
                exempt_list.append(result["entry_url"].split("/")[-1])
            time.sleep(30)
        with open("./exempt.txt", "a") as f:
            for result in result_list:
                idx = result["entry_url"].split("/")[-1]
                f.write("%s\n" % idx)
        time.sleep(120)

if __name__ == '__main__':
    main()