#!/usr/bin/env python3
import json
from itertools import groupby

from brushfire.client import brushfire
from churchsuite.client import churchsuite

def cs_to_brushfire_form_value(bf_field, cs_attendee_data):
    """
    Return the correct Value for Brushfire from Churchsuite
    1. I need to match the key in cs_attendee_data with something in the bf_field
    2. This depends on the type of the field.
    3. Based on the Type I need to know 2 things - how to match, how to set the value
    """
    field_type_match = {
        "Email": lambda a, bf: a["email"],
        "Name": lambda a, bf: a["last_name"]
        if "Last" in bf["Label"]
        else a["first_name"],
        "Phone": lambda a, bf: a["mobile"],
        "Dropdown": lambda a, bf: [
            o for o in bf["Options"] if o["Label"] == a["church"]
        ],
        "Text": lambda a, bf: a["other"],
    }
    return field_type_match[bf_field["Type"]](cs_attendee_data, bf_field)

def checkout_on_brushfire(event_id, attendees):
    cart_id = brushfire.cart_id()
    cart_data = brushfire.add_attendee_to_cart(
        cart_id, event_id, attendees["cart_input"]
    )
    # print(brushfire.get_event_signups(event_id))
    for idx, attendee in enumerate(cart_data["EventCart"]["Items"]):
        form = brushfire.get_attendee_form(cart_id, event_id, attendee["AttendeeId"])
        fields = form.get("Fields")
        form_values = []
        cs_attendee = attendees["attendee_input"][idx]
        for field in fields:
            form_values.append(
                {
                    "Id": field["Id"],
                    "Value": cs_to_brushfire_form_value(field, cs_attendee),
                }
            )
        brushfire.add_attendee_form(
            cart_id, event_id, attendee["AttendeeId"], form_values
        )

    data = brushfire.create_order_from_cart(
        {
            "CartId": cart_id,
            "BillingFirstName": "The C3",
            "BillingLastName": "Church",
            "BillingStreet1": "C3 Centre, Coldhams Lane",
            "BillingCity": "Cambridge",
            "BillingRegion": "Cambridgeshire",
            "BillingCountry": "GB",
            "BillingPostalCode": "CB1 3HR",
            "ContactPhone": "01223 844415",
            "ShippingBillingSame": True,
            "PaidInFull": True,
            "SendEmail": False,
            "PaymentMethodId": "00000000-0000-0000-0000-000000000004",
        }
    )
    # return data

def get_orders_from_churchsuite(event_id):
    return churchsuite.get_event_signups(event_id)

def translate_order(orders, event_id, type_id):

    for _order_id, order in groupby(orders, lambda o: o["batch_id"]):
        order_list = list(order)
        yield {
            "cart_input": {
                "EventId": event_id,
                "TypeId": type_id,
                "Amount": 0 * len(order_list),
                "SelectedAmount": 0 * len(order_list),
                "Quantity": len(order_list),
                "IsPreRegistered": True,
            },
            "attendee_input": [
                {
                    "email": attendee["person"]["email"],
                    "first_name": attendee["person"]["first_name"],
                    "last_name": attendee["person"]["last_name"],
                    "mobile": attendee["person"]["mobile"],
                    "church": attendee["question_responses"]["104"]["value"],
                    "other": attendee["question_responses"]["102"]["value"],
                }
                for attendee in order_list
            ],
        }

def main():
    churchsuite_event_id = "384"
    brushfire_event_id = "466168"
    brushfire_attentee_type_id = "5d220886-e25a-4b36-8f96-8bee2f9f7671"
    orders = get_orders_from_churchsuite(churchsuite_event_id)

    for order in translate_order(
        orders, brushfire_event_id, brushfire_attentee_type_id
    ):
        checkout_on_brushfire(brushfire_event_id, order)
    print(brushfire.get_event_signups(brushfire_event_id))

if __name__ == "__main__":
    main()


import logging
from pydblite.pydblite import Base

logger = logging.getLogger()

class ZipcodeDB(object):
    PYDBLITE_DB_FILE = './zipcode.db'

    def __init__(self,pydblite_db_file=None):
        if pydblite_db_file is None:
            pydblite_db_file = ZipcodeDB.PYDBLITE_DB_FILE
        try:
            self._db = Base(pydblite_db_file)
            self._db.open()
        except Exception:
            self._db = None

    def query_by_zip_code(self, zip_cd):
        logger.debug("**************** entering ZipcodeDB.query_zipcode_db_by_zip_code")
        record = {} # Empty dict if not found
        if self._db is not None:
            records = self._db(zip_cd=zip_cd)
            if len(records)==1:
                record = records[0]
        return record

    def get_timezone_for_zip_code(self, zip_code):
        logger.debug("**************** entering ZipcodeDB.get_timezone")

        ret_val = 'NoTZ/'+zip_code
        data = self.query_by_zip_code(zip_code)
        if data:
            # timezone is really just an offset
            timezone = data['data']['timezone']
            dst = data['data']['dst']
            tz_dic = {'-5+1':'US/Eastern', '-5+0':'US/East-Indiana', '-6+1':'US/Central', '-7+1':'US/Mountain',
                      '-7+0':'US/Arizona', '-8+1':'US/Pacific', '-9+1':'US/Alaska', '-10+0':'US/Hawaii',
                      '-10+1':'US/Aleutian'}
            key=timezone+'+'+dst
            if key in tz_dic:
                ret_val = tz_dic[key]

        return ret_val


from flask import Flask, Blueprint, jsonify, redirect
from flask_restx import Api as RestX_Api
from os import getenv
from ecom.commands import ecom_cli
import pymysql
from ecom import extensions
from ecom.ping.v1 import ping_api_v1
from ecom.category.v1 import category_api_v1
from ecom.product.v1 import product_api_v1
from ecom.design.models import Category, Product, CategoryToProductMap

api_blueprint = Blueprint("api", __name__, url_prefix="/api")

authorizations = {
    "JWT Token Authentication": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "key": "Bearer xxxx",
    }
}

rest_api = RestX_Api(
    api_blueprint,
    title="ECOM",
    version="1.0.0",
    description="Swagger UI / API specs for ECOM application",
    authorizations=authorizations,
)

rest_api.add_namespace(ping_api_v1, path="/v1")
rest_api.add_namespace(category_api_v1, path="/v1")
rest_api.add_namespace(product_api_v1, path="/v1")

# mysql driver required for sqlalchemy
pymysql.install_as_MySQLdb()

def create_app():
    """Create the flask app and intialize all the extensions"""

    # flask app
    app = Flask(__name__)

    print("App Created..")

    # register blueprint with application
    app.register_blueprint(api_blueprint)

    # load env specific configs
    app.config.from_object(get_config_object_path())
    print(app.config["SQLALCHEMY_DATABASE_URI"])

    # initialize all extensions
    extensions.init_extensions(app)

    app.cli.add_command(ecom_cli)

    # 404 route handler
    @app.errorhandler(404)
    def route_not_present(err):
        return (
            jsonify(
                {
                    "success": False,
                    "error": {"errorCode": err.code, "message": err.description},
                }
            ),
            404,
        )

    # redirect the root to /api for easy access of swagger docs for devs
    @app.route("/")
    def redirect_to_swagger_spec():
        return redirect("/api")

    @app.shell_context_processor
    def shell_context():  # pylint: disable=unused-variable # pragma: no cover
        """The app.shell_context_processor decorator registers the function as a shell context function.
        When the `flask shell` command runs, it will invoke this function
        and register the items returned by it in the shell session.
        """
        return {"db": extensions.db}

    return app

def get_config_object_path() -> str:
    """reads `FLASK_ENV` from OS and returns string that can be used with app.config.from_object.
    If `FLASK_ENV` not set in OS, returns `ipdo.config.DevelopmentConfig`

    Returns:
        str: one of 'ipdo.config.DevelopmentConfig', 'ipdo.config.TestingConfig', 'ipdo.config.ProductionConfig'
    """
    try:
        dev_config = "ecom.config.DevelopmentConfig"
        test_config = "ecom.config.TestingConfig"
        prod_config = "ecom.config.ProductionConfig"

        env = getenv("FLASK_ENV")
        environment_configuration = dev_config

        if env in ["development", "extdev", "dev", "demo"]:
            environment_configuration = dev_config
        elif env == "testing":
            environment_configuration = test_config
        elif env in ["production", "prod", "stage"]:
            environment_configuration = prod_config
    except Exception:
        # logging.error(
        #     "Failed to load FLASK_ENV in application factory. Loading Dev environment configs."
        # )
        environment_configuration = dev_config

    return environment_configuration


import numpy as np
import pygame

class Board:
    def __init__(self):
        # colors
        self.white = (255, 255, 255)
        self.black = (0, 0, 0)
        self.gray = (50, 50, 50)
        self.red = (255, 0, 0)
        self.yellow = (255, 255, 0)

        # self
        self.rows = 6
        self.columns = 7

        # create empty self
        self.board = self.create_board()

        # square size of the screen
        self.square_size = 100
        # piece radius of the screen
        self.radius = int(self.square_size / 2 - 5)

        # height and width of the screen
        self.width = self.columns * self.square_size
        self.height = (self.rows + 1) * self.square_size

        # size of the screen
        self.size = (self.width, self.height)

        # set screen for the game
        self.screen = pygame.display.set_mode(self.size)

    # create board with zeros
    def create_board(self):
        board = np.zeros((self.rows, self.columns))
        return board

    # place the piece on board
    def place_piece(self, row, column, piece):
        self.board[row][column] = piece

    # check if the place for piece empty
    def is_empty(self, column):
        if self.board[self.rows - 1][column] == 0:
            return True
        else:
            return False

    def get_next_row(self, column):
        for row in range(self.rows):
            if self.board[row][column] == 0:
                return row

    def print_board(self):
        print('--- STATE ---')
        printer = np.flip(self.board.copy(), 0)
        for i in range(self.rows):
            for j in range(self.columns):
                if int(printer[i][j]) == 1:
                    print('R ', end='')
                elif int(printer[i][j]) == 2:
                    print('Y ', end='')
                else:
                    print('0 ', end='')
            print()
        print()

    def get_valid_locations(self):
        valid_locations = []
        for column in range(self.columns):
            if self.board[self.rows - 1][column] == 0:
                valid_locations.append(column)
        return valid_locations

    def check_win(self, piece):
        # Check horizontal locations for win
        for column in range(self.columns - 3):
            for row in range(self.rows):
                if (self.board[row][column] == piece
                        and self.board[row][column + 1] == piece
                        and self.board[row][column + 2] == piece
                        and self.board[row][column + 3] == piece):
                    return True

        # Check vertical locations for win
        for column in range(self.columns):
            for row in range(self.rows - 3):
                if (self.board[row][column] == piece
                        and self.board[row + 1][column] == piece
                        and self.board[row + 2][column] == piece
                        and self.board[row + 3][column] == piece):
                    return True

        # Check positively sloped diagonals
        for column in range(self.columns - 3):
            for row in range(self.rows - 3):
                if (self.board[row][column] == piece
                        and self.board[row + 1][column + 1] == piece
                        and self.board[row + 2][column + 2] == piece
                        and self.board[row + 3][column + 3] == piece):
                    return True

        # Check negatively sloped diagonals
        for column in range(self.columns - 3):
            for row in range(3, self.rows):
                if (self.board[row][column] == piece
                        and self.board[row - 1][column + 1] == piece
                        and self.board[row - 2][column + 2] == piece
                        and self.board[row - 3][column + 3] == piece):
                    return True
        #return False

    def draw_board(self, player_1, player_2):
        for column in range(self.columns):
            for row in range(self.rows):
                pygame.draw.rect(self.screen, self.white, (
                    column * self.square_size, row * self.square_size + self.square_size, self.square_size,
                    self.square_size))
                pygame.draw.circle(self.screen, self.gray, (
                    int(column * self.square_size + self.square_size / 2),
                    int(row * self.square_size + self.square_size + self.square_size / 2)), self.radius)

        for column in range(self.columns):
            for row in range(self.rows):
                if self.board[row][column] == player_1:
                    pygame.draw.circle(
                        self.screen,
                        self.red,
                        (
                            int(column * self.square_size + self.square_size / 2),
                            self.height - int(row * self.square_size + self.square_size / 2)
                        ),
                        self.radius
                    )
                elif self.board[row][column] == player_2:
                    pygame.draw.circle(
                        self.screen,
                        self.yellow,
                        (
                            int(column * self.square_size + self.square_size / 2),
                            self.height - int(row * self.square_size + self.square_size / 2)
                        ),
                        self.radius
                    )
        pygame.display.update()


import HtmlTestRunner  # for Html Test runner reports generation
import unittest                                                # for unit test package
from selenium import webdriver                                 # For selnium webdriver package
from selenium.webdriver.common.keys import Keys                # for button element finding when its overlapping with results list

class MyTestCase(unittest.TestCase):

    @classmethod # To Annotate
    def setUpClass(cls):    # Use to setup once for all the test [not opening the browser for every test start seperately]
        cls.driver= webdriver.Chrome("../drivers/chromedriver.exe")
        cls.driver.implicitly_wait(10)
        cls.driver.maximize_window()

    # def setUp(self):  # To Initial Setup before every test
    #
    #   self.driver = webdriver.Chrome("../drivers/chromedriver.exe")
    #   # self.driver=webdriver.Chrome(ChromeDriverManager.install(self))
    #   self.driver.implicitly_wait(10)
    #   self.driver.maximize_window()

    def test_search_1(self):
        self.driver.get("https://www.google.com")
        self.driver.implicitly_wait(2)
        self.driver.find_element_by_name("q").send_keys("Muhammad Anas Ansari")
        self.driver.implicitly_wait(2)
        self.driver.find_element_by_name("btnK").click()
        self.driver.implicitly_wait(2)
        self.driver.find_element_by_xpath("//a[@class='q qs'][contains(text(),'Images')]").click()
        # self.driver.implicitly_wait(200)
        x = self.driver.title
        print(x)

        self.assertEqual(x,"Muhammad Anas Ansari - Google Search")

    def test_search_2(self):
        self.driver.get("https://www.google.com")
        self.driver.implicitly_wait(2)
        self.driver.find_element_by_name("q").send_keys("World's Best Automation Engineer")
        self.driver.implicitly_wait(20)
        # self.driver.find_element_by_name("btnK").send_keys(Keys.ENTER)
        self.driver.find_element_by_class_name("gNO89b").send_keys(
            Keys.ENTER)  # if the button is overlapping with results list
        self.driver.implicitly_wait(2)
        self.driver.find_element_by_xpath("//a[@class='q qs'][contains(text(),'Images')]").click()
        # self.driver.implicitly_wait(200)
        x = self.driver.title
        print(x)

        self.assertEqual(x,"World's Best Automation Engineer - Google Search")  # To match the actual results with given expected result

    @unittest.skip("This is a skipped test.")  # To the test failed
    def test_skip(self):
        """ This test should be skipped. """
        pass

    # def tearDown(self):  # To do something after every test
    #     self.driver.close()
    #     self.driver.quit()

    @classmethod # to Annotate
    def tearDownClass(cls):      #Use to close browser once after all test runs [not for every test run]
        cls.driver.close()
        cls.driver.quit()

if __name__ == '__main__':     # Use to run the test from command line
    unittest.main(testRunner=HtmlTestRunner.HTMLTestRunner(output='C:/Users/Flo-DataScience/PycharmProjects/Selenium/Reports'), verbosity=2) # change the "\" to "/" or "\\"
    # unittest.main(verbosity=2) # no need now with above line

# When Run from Command line then directory>python -v file_name.py [for additional informations in command line insimple for Logging]


# coding: utf-8

# https://gist.github.com/lukaskollmer/962625208f872c242706

# Load Twitter profile pictures and set them as image to your iOS contacts

# https://forum.omz-software.com/topic/2736/share-code-update-ios-contact-images-with-twitter-profile-pictures

import twitter
import json
import urllib
import contacts
import console
from objc_util import *
import ui
import os
from PIL import Image
import webbrowser

console.clear()

CNContactStore = ObjCClass('CNContactStore')
CNContact = ObjCClass('CNContact')
CNSaveRequest = ObjCClass('CNSaveRequest')
UIActivityIndicatorView = ObjCClass('UIActivityIndicatorView')

activity_indicator = None
twitter_contacts = []
current_contact_index = -1
did_present = False
store = CNContactStore.alloc().init()

view = ui.load_view()
all_view_element_names = [
	'contact_name_label',
	'old_image_title_label',
	'new_image_title_label',
	'left_image_view',
	'right_image_view',
	'keep_image_button',
	'use_new_image_button',
	'progress_view_container'
]

def setup_view():
	view['left_image_view'].content_mode = ui.CONTENT_SCALE_ASPECT_FIT
	view['right_image_view'].content_mode = ui.CONTENT_SCALE_ASPECT_FIT

	view['keep_image_button'].action = keep_current_image
	view['use_new_image_button'].action = use_twitter_image
class NoTwitterAccountSetUpError(Exception):
	pass

class LoadTwitterProfilePictureError(Exception):
	def __init__(self, status_code):
		self.status_code = status_code

		def __str__():
			return 'Could not retrieve profile image (status: %i)' % (self.status_code,)

class Contact(object):
	def __init__(self, name, twitter_username, has_image, current_image, twitter_image):
		self.name = name
		self.twitter_username = twitter_username
		self.has_image = has_image
		self.current_image = current_image
		self.twitter_image = twitter_image

	def __str__(self):
		return 'Contact {} (@{})'.format(self.name, self.twitter_username)

def show_message_in_image_view(message, image_view):
	if len(image_view.subviews) > 0: # already a message subview
		for subview in image_view.subviews:
			if type(subview) is ui.Label:
				subview.text = message
	else:
		label = ui.Label()
		label.frame = image_view.frame
		label.text = message
		label.text_color = 0.4
		label.center = ((image_view.width / 2), (image_view.height / 2))
		label.alignment = ui.ALIGN_CENTER
		image_view.add_subview(label)

def profile_picture_for_account(username):
	all_accounts = twitter.get_all_accounts()
	if len(all_accounts) >= 1:
		account = all_accounts[0]
		parameters = {'screen_name': username}
		status, data = twitter.request(account, 'https://api.twitter.com/1.1/users/show.json', 'GET', parameters)
		if status == 200:
			user_info = json.loads(data)
			avatar_url = user_info['profile_image_url']
			# Get the URL for the original size:
			avatar_url = avatar_url.replace('_normal', '')
			filename, headers = urllib.urlretrieve(avatar_url)
			img = Image.open(filename)
			format = img.format.lower()
			path = os.path.expanduser('~/Documents/{}.{}'.format(username, format))
			img.save(path, format=format)
			image = ui.Image.named(path)
			os.remove(path)
			return image
		else:
			raise LoadTwitterProfilePictureError(status)
	else:
		raise NoTwitterAccountSetUpError('You don\'t have any Twitter accounts (or haven\'t given permission to access them).')

def addressBook_account_with_name(name):
	predicate = CNContact.predicateForContactsMatchingName_(name)
	keys_to_fetch = ['identifier', 'givenName', 'familyName', 'imageDataAvailable', 'imageData']

	result = store.unifiedContactsMatchingPredicate_keysToFetch_error_(predicate, keys_to_fetch, None)[0]

	return result

def load_contacts_with_twitter():
	contacts_with_twitter = []
	all_people = contacts.get_all_people()
	for person in all_people:
		view['currently_loading_info_label'].text = 'Loading image for {}'.format(person.full_name)
		social = person.social_profile
		if social:
			for service in social:
				if service[1]['service'] == 'twitter':
					twitter_username = service[1]['username']
					should_dismiss_view_because_no_account_error = False
					try:
						twitter_image = profile_picture_for_account(twitter_username)
					except LoadTwitterProfilePictureError as error:
						twitter_image = None
					except NoTwitterAccountSetUpError as error:
						twitter_image = None
						should_dismiss_view_because_no_account_error = True
						if console.alert(error.message, button1='open settings') == 1:
							webbrowser.open('app-settings://')
						view.close()
					finally:
						if should_dismiss_view_because_no_account_error == True:
							view.close()
					image = None
					result = addressBook_account_with_name(person.full_name)
					has_image = result.imageDataAvailable()
					if has_image == True:
						data = NSData.dataWithData_(result.imageData())
						image = UIImage.imageWithData_(data)
					contacts_with_twitter.append(Contact(person.full_name, twitter_username, has_image, image, twitter_image))
	return contacts_with_twitter

def update_contact_with_name_set_new_image(name, image):
	contact = addressBook_account_with_name(name).mutableCopy()
	image_data = bytearray(image.to_png())
	contact.imageData = image_data
	save_resuest = CNSaveRequest.new()
	save_resuest.updateContact_(contact)
	store.executeSaveRequest_error_(save_resuest, None)

def current_contact():
	if current_contact_index <= len(twitter_contacts):
		return twitter_contacts[current_contact_index]
	else:
		return None

def show_next_contact():
	global current_contact_index
	current_contact_index = current_contact_index + 1
	if current_contact_index >= len(twitter_contacts):
		view.close()
		return
	contact = current_contact()
	view['contact_name_label'].text = contact.name
	if contact.current_image is None:
		left_message = 'No image set'
	else:
		left_message = ''
	show_message_in_image_view(left_message, view['left_image_view'])
	ObjCInstance(view['left_image_view']).image = contact.current_image
	if contact.twitter_image is None:
		right_message = 'Unable to load image'
	else:
		right_message = ''
	show_message_in_image_view(right_message, view['right_image_view'])
	view['right_image_view'].image = contact.twitter_image
	view['use_new_image_button'].enabled = not contact.twitter_image == None

def keep_current_image(sender):
	show_next_contact()

def use_twitter_image(sender):
	contact = current_contact()
	name = contact.name
	new_image = view['right_image_view'].image
	update_contact_with_name_set_new_image(name, new_image)
	show_next_contact()

def set_all_views_hidden(hidden):
	for element in all_view_element_names:
		view[element].hidden = hidden

@on_main_thread
def show_ui(loading):
	global did_present
	global activity_indicator
	if did_present == False:
		try:
			view.present('sheet')
			did_present = True
		except:
			pass
	set_all_views_hidden(loading)
	if loading:
		activity_indicator = UIActivityIndicatorView.alloc().initWithActivityIndicatorStyle_(2) #2= grey style
		activity_indicator.startAnimating()
		activity_indicator_container = ObjCInstance(view['progress_view_container'])
		activity_indicator.center = activity_indicator_container.convertPoint_fromView_(activity_indicator_container.center(), activity_indicator_container.superview())
		activity_indicator_container.addSubview_(activity_indicator)
	else:
		activity_indicator.stopAnimating()
		show_next_contact()
	view['progress_view_container'].hidden = not loading
	view['currently_loading_info_label'].hidden = not loading

if __name__ == '__main__':
	setup_view()
	show_ui(loading=True)
	twitter_contacts = load_contacts_with_twitter()
	show_ui(loading=False)

contacts/Add Twitter Profile Picture to iOS Contacts.py


from matplotlib import style
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import time
import influxdb
from influxdb import InfluxDBClient
from flask import Flask,request,render_template,jsonify
app = Flask(__name__)

@app.route('/process',methods= ['GET'])
def process():
    global x, y, z
    x = float(request.args.get('key1'))
    y = float(request.args.get('key2'))
    z = float(request.args.get('key3'))
    print(x)
    print(y)
    print(z)

    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    p = [x, y, z]

    origin = [0, 0, 0]
    X, Y, Z = zip(origin)
    U, V, W = zip(p)

    ax.set_xlabel('North(%d)' % x, labelpad=15)
    ax.set_ylabel('East(%d)' % y, labelpad=15)
    ax.set_zlabel('Depth(%d)' % z, labelpad=15)

    ax.set_xlim([-100000, 100000])
    ax.set_ylim([-100000, 100000])
    ax.set_zlim([-100000, 0])

    plt.title('Depth and direction of disruption', fontsize=20)

    print("refreshed")
    ax.quiver(X, Y, Z, U, V, W, arrow_length_ratio=0.3, color="red", label="ESP Vector", linewidth=8)

    # Turn off tick labels
    plt.locator_params(axis='x', nbins=5)
    plt.locator_params(axis='y', nbins=5)
    plt.locator_params(axis='z', nbins=5)

    plt.tight_layout(0.1)
    fig.savefig('3D_Vector_Arrow.png', bbox_inches='tight')
    plt.show()
    plt.close(fig)

    return "vector picture done"
app.run(host="localhost", port=7000, debug=True)


import tornado.ioloop
import tornado.web  #web应用api
import tornado.options
from tornado.options import define, options

from handlers.main import IndexHandler, ExploreHandler, PostHandler, UpdateHandler
from handlers.users import RegisterHandler, LoginHandler
from handlers.photo import PhotographyHandler, TravelHandler, FashionHandler, AboutHandler, ContactHandler
from handlers.chat import RoomHandler, EchoWebSocket

define("port", default="8888", help="Listening port", type=int)

class Application(tornado.web.Application): #tornado配置，比如静态文件
    def __init__(self):
        handlers = [
            (r"/", IndexHandler),
            (r"/expore", ExploreHandler),
            (r"/post/(?P<post_id>[0-9]+)", PostHandler),
            (r"/register", RegisterHandler),    # 注册
            (r"/login", LoginHandler),      # 登录
            (r"/update", UpdateHandler),    # 图片上传
            (r"/photography", PhotographyHandler),  # 摄影
            (r"/travel", TravelHandler),  # 旅行
            (r"/fashion", FashionHandler),  # 时尚
            (r"/about", AboutHandler),  # 关于我
            (r"/contact", ContactHandler),  # 联系我
            (r"/room", RoomHandler),  # 在线咨询
            (r"/echoweb", EchoWebSocket),  #
        ]
        settings = dict(
            debug=True,
            template_path="templates",  # 配置模板路径
            static_path="statics",
            xsrf_cookies=True,
            cookie_secret="fdasjofdsa-gfdsgdsafadsdfsd",
            pycket={
                'engine': 'redis',
                'storage': {
                    'host': '127.0.0.1',  # ip
                    'port': 6379,
                    'db_sessions': 7,
                    'max_connections': 2 ** 31,
                },
                'cookies': {
                    # 设置过期时间
                    'expires_days': 2,
                    # 'expires':None, #秒
                },
            },
            login_url="/login",

        )

        super().__init__(handlers, **settings)

if __name__ == "__main__":  #只有在当前文件运行的时候才会执行
    tornado.options.parse_command_line()    # 命令行
    application = Application()  # 实例化
    application.listen(options.port)
    tornado.ioloop.IOLoop.current().start() #开启tornado服务


from agent_dir.agent import Agent
from agent_dir.model import Net, CNN2

import scipy.misc
import numpy as np
import matplotlib.pyplot as plt

import torch
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
from torch.distributions import Bernoulli, Categorical

import time
import random
from itertools import count

torch.manual_seed(22)
USE_CUDA = torch.cuda.is_available()
# USE_CUDA = False

def prepro(o,image_size=[80,80]):
    """
    Call this function to preprocess RGB image to grayscale image if necessary
    This preprocessing code is from
        https://github.com/hiwonjoon/tf-a3c-gpu/blob/master/async_agent.py

    Input:
    RGB image: np.array
        RGB screen of game, shape: (210, 160, 3)
    Default return: np.array
        Grayscale image, shape: (80, 80, 1)

    """
    #############
    # toast add #
    #############
    o = o[35:190, :, :]     # remove the score board
    y = 0.2126 * o[:, :, 0] + 0.7152 * o[:, :, 1] + 0.0722 * o[:, :, 2]

    #############
    # toast end #
    #############
    y = y.astype(np.uint8)
    resized = scipy.misc.imresize(y, image_size)
    return np.expand_dims(resized.astype(np.float32),axis=2)/255.

class Agent_PG(Agent):
    def __init__(self, env, args):
        """
        Initialize every things you need here.
        For example: building your model
        """

        super(Agent_PG,self).__init__(env)

        ##################
        # YOUR CODE HERE #
        ##################

        # environment
        self.env = env.env

        # args
        self.gamma = args.gamma
        self.episode = args.episode
        self.batch_size = args.batch_size
        self.render = args.render
        self.save_state = None
        # initialize model
        self.model = CNN2()
        if USE_CUDA:
            self.model = self.model.cuda()

        # optimizer
        self.optimizer = optim.RMSprop(params=self.model.parameters(), lr=args.learning_rate)

        if args.test_pg or args.pretrain:
            #you can load your model here
            print('loading trained model -- cnn2')
            self.model.load_state_dict(torch.load('model/pg_model.pth'))
            # self.model.eval()

    def init_game_setting(self):
        """

        Testing function will call this function at the begining of new game
        Put anything you want to initialize if necessary

        """
        ##################
        # YOUR CODE HERE #
        ##################
        torch.manual_seed(1)

    def train(self):
        """
        Implement your training algorithm here
        """
        ##################
        # YOUR CODE HERE #
        ##################

        self.model.train()

        # memory
        state_pool = []
        action_pool = []
        reward_pool = []
        prob_pool = []
        log_prob_pool = []
        # for observation
        # frame_pool = []

        for episode in range(self.episode):
            # print('episode: ', episode)
            # setup
            state = self.env.reset()
            state = prepro(state)
            state = torch.FloatTensor(state)
            state = Variable(state).cuda() if USE_CUDA else Variable(state)

            new_state = state
            save_state = state

            # start playing
            while True:
                if self.render:
                    self.env.render()

                dif_state = new_state - save_state
                # plt.imshow(dif_state.cpu().data.numpy()[:, :, 0], cmap='gray')
                # plt.colorbar()
                # plt.show()
                save_state = new_state
                prob = self.model(dif_state)
                # if len(frame_pool) <= 150:
                #     frame_pool.append(dif_state.cpu().data.numpy())
                prob_pool.append(prob.cpu().data.numpy())
                m = Categorical(prob)

                # memorize!
                state_pool.append(dif_state)

                # take action
                # 1-> do nothing, 2->up, 3->down
                # action = int(prob.topk(1)[1]) + 1

                # print('episode: ', episode)
                # print(prob)
                action = m.sample() + 1

                ######
                # test
                ######
                log_prob_pool.append(m.log_prob(action-1))
                ######
                # test
                ######
                action_pool.append(action)

                if USE_CUDA:
                    action = action.cpu().data.numpy().astype(int)[0]
                else:
                    action = action.data.numpy().astype(int)[0]

                # action = random.sample([1, 2, 3], 1)

                state, reward, done, info = self.env.step(action)
                state = prepro(state)
                state = torch.FloatTensor(state)
                state = Variable(state).cuda() if USE_CUDA else Variable(state)
                new_state = state
                # memorize!
                reward_pool.append(reward)

                # if terminated
                if done:
                    break

            if episode > 0 and episode % self.batch_size == 0:
                # print('update policy..')
                # update network (policy)
                # Discount reward
                running_add = 0.
                # print(reward_pool[-5:])

                avg_reward = np.sum(reward_pool)/self.batch_size
                print("episode: %d | average reward: %.2f" % (episode, avg_reward))
                with open('reward_log_cnn2.csv', 'a+') as log:
                    log.write('%d,%f\n' % (episode, avg_reward))

                for i in reversed(range(len(state_pool))):
                    if reward_pool[i] == 1 or reward_pool[i] == -1:
                        running_add = reward_pool[i]
                    else:
                        running_add = running_add * self.gamma + reward_pool[i]
                        reward_pool[i] = running_add
                        # print(running_add)

                # Normalize reward
                # reward_mean = np.mean(reward_pool)
                # reward_std = np.std(reward_pool)

                # for i in range(len(state_pool)):
                #     reward_pool[i] = (reward_pool[i] - reward_mean) / reward_std

                # print(reward_pool[-5:])
                # plt.plot(np.arange(len(reward_pool)), reward_pool)
                # plt.xlabel('frame')
                # plt.ylabel('reward')
                # plt.title('moving average reward')
                # plt.show()
                # assert False
                # optimize!
                self.optimizer.zero_grad()
                # loss = 0
                for i in range(len(state_pool)):
                    state = state_pool[i]
                    # action = action_pool[i]
                    # action = int(action)

                    # action = Variable(torch.FloatTensor([action_pool[i]]))
                    # action = action.cuda() if USE_CUDA else action

                    reward = reward_pool[i]

                    # prob = self.model(state)
                    # m = Bernoulli(prob[action_pool[i] - 1])
                    # m = Categorical(prob)
                    # loss += -m.log_prob(action) * reward
                    # loss += -torch.log(prob[action-1]) * (reward - reward_mean) # substract baseline (arbitrary)
                    # loss = -torch.log(prob[action-1]) * reward # substract baseline (arbitrary)

                    loss = -log_prob_pool[i] * reward
                    loss.backward()

                # loss /= len(state_pool)
                # loss.backward()
                self.optimizer.step()

                ###############
                # observe the std of probability of each action
                # (does the network really react to different frame?)
                #
                # print("the std of each action over an episode:")
                # print(np.asarray(prob_pool).std(0))
                # plt.plot(np.arange(len(prob_pool)), np.asarray(prob_pool).T[0], label='none')
                # plt.plot(np.arange(len(prob_pool)), np.asarray(prob_pool).T[1], label='up')
                # plt.plot(np.arange(len(prob_pool)), np.asarray(prob_pool).T[2], label='down')
                plt.plot(np.arange(150), np.asarray(prob_pool[:150]).T[0], label='none')
                plt.plot(np.arange(150), np.asarray(prob_pool[:150]).T[1], label='up')
                plt.plot(np.arange(150), np.asarray(prob_pool[:150]).T[2], label='down')
                plt.xlabel('t')
                plt.ylabel('probability')
                plt.title('std of none: %.4f, up: %.4f, down: %.4f' % (np.asarray(prob_pool).std(0)[0], np.asarray(prob_pool).std(0)[1], np.asarray(prob_pool).std(0)[2]))
                plt.legend()
                plt.savefig('plot/std_cnn2.png')
                plt.close()

                ###########
                # observe
                ###########
                # for idx, frame in enumerate(frame_pool):
                #     plt.imshow(frame[:, :, 0])
                #     plt.savefig('plot/frames/frame%d.png' % idx)
                #     plt.close()

                # clear memory!
                state_pool = []
                action_pool = []
                reward_pool = []
                prob_pool = []
                log_prob_pool = []
                # frame_pool = []
                # assert False

                torch.save(self.model.state_dict(), 'model/pg_model_cnn2.pth')

    def make_action(self, observation, test=True):
        """
        Return predicted action of your agent

        Input:
            observation: np.array
                current RGB screen of game, shape: (210, 160, 3)

        Return:
            action: int
                the predicted action from trained model
        """
        ##################
        # YOUR CODE HERE #
        ##################
        observation = prepro(observation)
        observation = torch.FloatTensor(observation)
        observation = Variable(observation).cuda() if USE_CUDA else Variable(observation)

        if self.save_state is None:
            self.save_state = observation
        dif_state = observation - self.save_state
        self.save_state = observation

        prob = self.model(dif_state)
        # action = int(action.topk(1)[1]) + 1
        m = Categorical(prob)

        action =  m.sample() + 1
        if USE_CUDA:
            action = action.cpu().data.numpy().astype(int)[0]
        else:
            action = action.data.numpy().astype(int)[0]

        return action
        # return self.env.get_random_action()


import pytest
import numpy as np
import astropy.units as u
from ..ionization_states import IonizationState
from plasmapy.utils import AtomicError, RunTestError, InvalidIsotopeError, run_test
from plasmapy.atomic import (
    atomic_number,
    mass_number,
    atomic_symbol,
    isotope_symbol,
    Particle,
)
import collections
import warnings

test_cases = {
    'Li': {
        'particle': 'Li',
        'ionic_fractions': np.array([0.4, 0.3, 0.2, 0.1]),
        'tol': 1e-15,
    },

    'Li ground state': {
        'particle': 'Li',
        'ionic_fractions': np.array([1, 0, 0, 0], dtype=np.int64),
        'tol': 1e-15,
    },

    'H': {
        'particle': 'H',
        'ionic_fractions': [0.6, 0.4],
        'tol': 1e-8,
    },

    'H acceptable error': {
        'particle': 'H',
        'ionic_fractions': [0.6, 0.400_000_001],
        'tol': 1e-8,
    },

    'D': {
        'particle': 'deuterium',
        'ionic_fractions': [0.7, 0.3],
        'tol': 1e-15,
        'n_e': 3e14 * u.m ** -3,
    },

    'He': {
        'particle': 'He',
        'ionic_fractions': [0.5, 0.3, 0.2],
        'n_elem': 1e20 * u.m ** -3,
    },

    'number densities': {
        'particle': 'T',
        'ionic_fractions': np.array([1e4, 1]) * u.cm ** -3,
    }

}

test_names = test_cases.keys()

class Test_IonizationState:
    """Test instances of IonizationState."""

    @classmethod
    def setup_class(cls):
        cls.instances = {}

    @pytest.mark.parametrize('test_name', test_names)
    def test_instantiation(self, test_name):
        try:
            self.instances[test_name] = IonizationState(**test_cases[test_name])
        except Exception as exc:
            raise RunTestError(
                f"Unable to create IonizationState instance for "
                f"test case {test_name}.")

    @pytest.mark.parametrize(
        'test_name',
        [name for name in test_names if 'ionic_fractions' in test_cases[name].keys()],
    )
    def test_ionic_fractions(self, test_name):
        instance = self.instances[test_name]

        inputted_fractions = test_cases[test_name]['ionic_fractions']
        if isinstance(inputted_fractions, u.Quantity):
            inputted_fractions = inputted_fractions.to(u.m ** -3)
            inputted_fractions = (inputted_fractions / inputted_fractions.sum()).value

        try:
            assert np.allclose(instance.ionic_fractions, inputted_fractions)
        except:
            raise RunTestError(f"Mismatch in ionic fractions for test {test_name}")

    def test_equality1(self):
        assert self.instances['Li'] == self.instances['Li'], \
            "Identical IonizationState instances are not equal."

    def test_equality2(self):
        assert self.instances['H'] == self.instances['H acceptable error'], \
            ("Two IonizationState instances that are approximately the "
             "same to within the tolerance are not testing as equal.")

    def test_inequality(self):
        """
        Test that instances with different ionic fractions are not equal
        to each other.
        """
        assert self.instances['Li ground state'] != self.instances['Li'], \
            "Different IonizationState instances are equal."

    def test_equality_error(self):
        """
        Test that comparisons of IonizationState instances for
        different elements fail.
        """
        with pytest.raises(AtomicError):
            self.instances['Li'] == self.instances['H']

    @pytest.mark.parametrize('test_name', test_names)
    def test_iteration(self, test_name: str):
        """Test that IonizationState instances iterate impeccably."""
        try:
            states = [state for state in self.instances[test_name]]
        except Exception:
            raise AtomicError(
                f"Unable to perform iteration for {test_name}.")

        try:
            integer_charges = [state.integer_charge for state in states]
            ionic_fractions = np.array([state.ionic_fraction for state in states])
            ionic_symbols = [state.ionic_symbol for state in states]
        except Exception:
            raise AtomicError("An attribute may be misnamed or missing.")

        try:
            base_symbol = isotope_symbol(ionic_symbols[0])
        except InvalidIsotopeError:
            base_symbol = atomic_symbol(ionic_symbols[0])
        finally:
            atomic_numb = atomic_number(ionic_symbols[1])

        errors = []

        expected_charges = np.arange(atomic_numb + 1)
        if not np.all(integer_charges == expected_charges):
            errors.append(
                f"The resulting integer charges are {integer_charges}, "
                f"which are not equal to the expected integer charges, "
                f"which are {expected_charges}.")

        expected_fracs = test_cases[test_name]['ionic_fractions']
        if isinstance(expected_fracs, u.Quantity):
            expected_fracs = (expected_fracs / expected_fracs.sum()).value

        if not np.allclose(ionic_fractions, expected_fracs):
            errors.append(
                f"The resulting ionic fractions are {ionic_fractions}, "
                f"which are not equal to the expected ionic fractions "
                f"of {expected_fracs}.")

        expected_particles = [Particle(base_symbol, Z=charge) for charge in integer_charges]
        expected_symbols = [particle.ionic_symbol for particle in expected_particles]
        if not ionic_symbols == expected_symbols:
            errors.append(
                f"The resulting ionic symbols are {ionic_symbols}, "
                f"which are not equal to the expected ionic symbols of "
                f"{expected_symbols}.")

        if errors:
            errors.insert(0, (
                f"The test of IonizationState named '{test_name}' has "
                f"resulted in the following errors when attempting to "
                f"iterate."))
            errmsg = " ".join(errors)
            raise AtomicError(errmsg)

    def test_slicing_error(self):
        with pytest.raises(TypeError):
            self.instances['Li'][1:3]

    @pytest.mark.parametrize('index', [-1, 4, 'Li'])
    def test_indexing_error(self, index):
        with pytest.raises(AtomicError):
            self.instances['Li'][index]

    def test_normalization(self):
        H = self.instances['H acceptable error']
        assert not H.is_normalized(tol=1e-15)
        H.normalize()
        assert H.is_normalized(tol=1e-15)

    @pytest.mark.parametrize('test_name', test_names)
    def test_identifications(self, test_name):
        """
        Test that the identification attributes for test IonizationState
        instances match the expected values from the Particle instance.
        """

        Identifications = collections.namedtuple(
            "Identifications",
            ["element", "isotope", "base_particle", "atomic_number"],
        )

        expected_identifications = Identifications(
            self.instances[test_name].element,
            self.instances[test_name].isotope,
            self.instances[test_name].base_particle,
            self.instances[test_name].atomic_number,
        )

        expected_element = self.instances[test_name]._particle.element
        expected_isotope = self.instances[test_name]._particle.isotope
        expected_atomic_number = self.instances[test_name]._particle.atomic_number

        resulting_identifications = Identifications(
            expected_element,
            expected_isotope,
            expected_isotope if expected_isotope else expected_element,
            expected_atomic_number,
        )

        assert resulting_identifications == expected_identifications, (
            f"For IonizationState test {test_name}, the resulting "
            f"identifications of {resulting_identifications} differ "
            f"from the expected identifications of "
            f"{expected_identifications}."
        )

    @pytest.mark.parametrize('tol', [-1e-16, 1.0000001])
    def test_invalid_tolerances(self, tol):
        """Test that invalid tolerances raise appropriate errors."""
        test_name = "Li"
        instance = self.instances[test_name]
        with pytest.raises(ValueError):
            instance.tol = tol

    @pytest.mark.parametrize('test_name', test_cases.keys())
    def test_particles(self, test_name):
        """
        Test that IonizationState returns the correct Particle
        instances.
        """
        instance = self.instances[test_name]
        base_particle = instance.base_particle
        nstates = instance.atomic_number + 1
        expected_particles = [Particle(base_particle, Z=Z) for Z in range(nstates)]
        assert expected_particles == instance.particles, (
            f"The expected Particle instances of {expected_particles} "
            f"are not all equal to the IonizationState particles of "
            f"{instance.particles} for test {test_name}."
        )

    def test_electron_density_from_n_elem_ionic_fractions(self):
        test_name = 'He'
        instance = self.instances[test_name]
        n_elem = test_cases[test_name]['n_elem']
        ionic_fractions = test_cases[test_name]['ionic_fractions']
        assert instance.n_elem == n_elem, \
            f"n_elem is not being stored correctly for test {test_name}"
        assert np.isclose(
            instance.n_e,
            np.sum(n_elem * ionic_fractions * np.array([0, 1, 2])),
            rtol=1e-12, atol=0 * u.m ** -3), \
            "n_e is not the expected value."

IE = collections.namedtuple("IE", ["inputs", "expected_exception"])

tests_for_exceptions = {
    'too few nstates': IE({'particle': 'H', 'ionic_fractions': [1.0]}, AtomicError),
    'too many nstates': IE({'particle': 'H', 'ionic_fractions': [1, 0, 0, 0]}, AtomicError),

    'redundant ndens 1': IE({
        'particle': 'H', 'ionic_fractions': np.array([3, 4]) * u.m ** -3, 'n_e': 4 * u.m ** -3,
    }, AtomicError),

    'redundant ndens 2': IE({
        'particle': 'H', 'ionic_fractions': np.array([3, 4]) * u.m ** -3, 'n_elem': 7 * u.m ** -3,
    }, AtomicError),
}

@pytest.mark.parametrize('test', tests_for_exceptions.keys())
def test_execeptions(test):
    """
    Test that appropriate exceptions are raised for inappropriate inputs
    to IonizationStates.
    """
    run_test(
        IonizationState,
        kwargs=tests_for_exceptions[test].inputs,
        expected_outcome=tests_for_exceptions[test].expected_exception,
    )


# Standards:
import os, sys

# Custom:
from uuid import uuid4
import utils
from HaGraph import HaGraph
from HaGraph import HaGraphItem
from hafarm import GraphvizRender
from hafarm import SlurmRender

# Host specific:
import ix

class ClarisseWrapper(HaGraphItem):
    def __init__(self, clarisse_image, render_archive, *args, **kwargs):
        index, name = str(uuid4()), 'clarisse'
        tags, path = '/clarisse/farm', ''
        dependencies = []
        super(ClarisseWrapper, self).__init__(index, dependencies, name, path, tags, *args, **kwargs)
        # Note: Force non-default version of backend support class.
        self.parms['command'] << { 'command': '$CLARISSE_HOME/crender' }
        # NOTE: SCENE_FILE has to be first in a row
        self.parms['req_license'] = 'clarisse_lic=1'
        self.parms['req_resources'] = ''
        self.parms['job_on_hold'] = False
        self.parms['queue'] = '3d'
        self.parms['group'] = ''
        #NOTE We render single frame per task, so double 'start_frame' isn't a bug.
        self.parms['frame_range_arg'] = ["-start_frame %s -end_frame %s", 'start_frame', 'start_frame']
        self.parms['pre_render_script'] = 'clarisse_temp_dir=`mktemp -d --tmpdir=/tmp`; \
        export CLARISSE_TEMP_DIR=$clarisse_temp_dir;\n echo Making Clarisse own tmp place in $clarisse_temp_dir '
        self.parms['post_render_script'] = 'echo Deleting Clarisse tmp: $clarisse_temp_dir; rm -rf $CLARISSE_TEMP_DIR;'
        command = self.parms['command_arg']
        self.parms['target_list'] = [clarisse_image.get_full_name()]
        self.parms['priority'] = 0
        self.parms['job_on_hold'] = False
        # Scene file.
        # command += ['@SCENE_FILE/>']
        self.parms['scene_file'] = render_archive
        # Add camera option to commanline:
        camera = self.parms['target_list']

        self.parms['command_arg'] = ['@SCENE_FILE/>'
                    , '-startup_script'
                    , '$HAFARM_HOME/scripts/clarisse/123.py'
                    , ' -image %s ' % camera[0] ]
        # target image to render:
        picture_path = clarisse_image.m_object.get_attribute("save_as").get_string()
        format_idx   = int(clarisse_image.m_object.get_attribute("format").get_double())
        self.parms['output_picture'] = picture_path + "00001" +  CLARISSE_FORMATS[format_idx]

        # name, frame range per Clarisse image:
        _, job_name  = os.path.split(render_archive)
        self.parms['job_name']    = self.generate_unique_job_name(job_name) + "_" + clarisse_image.get_name()
        self.parms['start_frame'] = int(clarisse_image.m_object.get_attribute("first_frame").get_string())
        self.parms['end_frame']   = int(clarisse_image.m_object.get_attribute("last_frame").get_string())
        # Get rid of frames = 0
        if self.parms['start_frame'] < 1:
            self.parms['start_frame'] = 1

"""
==================  THIS IS TEMPORARY HAFARM GUI for NON-PyQt environment. ==========================
"""

CLARISSE_FORMATS = ('.exr', '.exr', ".jpg", ".bmp", ".tga", ".png", ".png", ".tiff", ".tiff", ".tiff")

class RenderButton(ix.api.GuiPushButton):
    def __init__(self, parent, x, y, w, h, label):
        ix.api.GuiPushButton.__init__(self, parent, x, y, w, h, label)
        self.connect(self, 'EVT_ID_PUSH_BUTTON_CLICK', self.on_click)
        self.parent = parent

    def on_click(self, sender, evtid):
        '''Runs per selected iamge in Clarisse GUI.
        '''
        render_archive  = self.parent.parms['render_archive']
        selected_images = self.parent.parms['selected_images']

        graph = HaGraph()
        graph.set_render(SlurmRender.SlurmRender)

        for image in selected_images:
            graph.add_node( ClarisseWrapper( image, str(render_archive) )  )

        graph.render()

        self.set_label('Render sent!')

def hafarm_run():
    """To be called by shelf button in Clarisse.
    """
    # If archive is not there, quit:
    render_archive =  ix.api.GuiWidget_open_file(ix.application)
    if not render_archive or not os.path.isfile(render_archive):
        sys.exit()

    # Windows and its labeling.
    window = ix.api.GuiWindow(ix.application, 5, 0, 320, 280)
    label  = ix.api.GuiLabel(window, 5, 5, 320, 40, "Clarisse Hafarm manager.")
    label.set_justification(ix.api.GuiWidget.JUSTIFY_CENTER)
    # TODO: This isn't used atm
    label2  = ix.api.GuiLabel(window, 5, 10, 320, 70, "Host groups:")
    groups = ix.api.GuiListView(window, 5, 60, 100, 60)
    for group in ('old_intel', 'new_intel', 'grafika', 'render'):
        groups.add_item(str(group))

    # The list of selected images
    selected_images = []
    for item in ix.selection:
        if item.is_kindof("Image"):
            selected_images.append(item)

    # The list of Clarisse imagaes to be rendered one by one on a farm (these will be separeted jobs)
    label3  = ix.api.GuiLabel(window, 5, 110, 320, 60, "Clarisse images:")
    images  = ix.api.GuiLineEdit(window, 5,150, 320, 20, " ". join([x.get_name() for x in selected_images]))

    # Get frame range
    frame_range = ix.application.get_current_frame_range()
    start_frame = int(frame_range[0])
    end_frame   = int(frame_range[1])

    # Get rid of 0 frame...
    if start_frame < 1: start_frame = 1

    # Set frameranges
    startFrameLabel  = ix.api.GuiLabel(window,  200, 60, 80, 20, "Start Frame:")
    startFrameLineEdit = ix.api.GuiLineEdit(window, 280, 60, 40, 20)
    startFrameLineEdit.set_text(str(start_frame))
    endFrameLabel  = ix.api.GuiLabel(window,  200, 80, 80, 20, "End Frame:")
    endFrameLineEdit = ix.api.GuiLineEdit(window, 280, 80, 40, 20)
    endFrameLineEdit.set_text(str(end_frame))

    # Render me button
    renderButton = RenderButton(window, 5, 250, 128, 22, "Render")

    # FIXME: In proper class this wouldn't be here...
    window.parms = {}
    window.parms['selected_images'] = selected_images
    window.parms['render_archive']  = render_archive
    window.parms['start_frame'] = start_frame
    window.parms['end_frame']   = end_frame
    # show time...
    window.show()
    while window.is_shown(): ix.application.check_for_events()


# Script for extracting keywords from Darwin's letters
# Author: Nicole Tamer

# Import libraries:

import os
import csv
from bs4 import BeautifulSoup

# Directory names and tag names:
folder = '/Users/nt/Documents/Darwin_copy'
#tagnames = ['persName', 'date', 'keywords', 'abstract', 'text']

# List of files in input directory:
files = os.listdir(folder)
files = [f for f in files [:] if 'xml' in f]
#files = ["DCP-LETT-13840.xml"]

# All the strings for output:
output_strs = []

# Open output file:
outfile = open('/Users/nt/Documents/Darwin_project/output/all_fields.txt', 'w+')
output_writer = csv.writer(outfile, delimiter = "\t")

# Write header row:
output_writer.writerow(["Letter_id", "Date_sent", "Sender", "Receiver", "abstract", "keywords", "letter_text"]) # TO DO: add more fields

# Open each file:
for fname in files:
    print(fname)
    with open(os.path.join(folder, fname), "r") as infile:
        file_data = []

        # Read the file:
        content = infile.read()

        # Initialize the fields required (sender, receiver, date_sent, keywords, abstract, letter_text):
        sender = ""
        receiver = ""
        date_sent = "" # the date in which the letter was sent
        keywords = "" # list of all keywords in the letter
        scientific_terms = []
        abstract = "" # summary of the letter
        letter_text = "" # transcription of the letter

        # Parse the XML of the file:
        soup = BeautifulSoup(content,'xml')

        # Extract sender, receiver, and date sent of the letter:
        transcription = soup.find(type="transcription")

        if transcription is not None:
            letter_text = transcription.get_text(separator=" ", strip=True)
            print("letter_text:", letter_text)

        try:

            corr_action = soup.find_all("correspAction")
        #print("sender_receiver=",sender_receiver)
            for s in corr_action:

                if s.get('type') == "sent":
                    sender = s.persName.get_text()
                    print("sender:", sender)
                    for sender in s.persName:
                        tag_data = soup.find(sender)
                        date_sent = s.date["when"]
                        print("date sent:", date_sent)

                elif s.get('type') == "received":
                    receiver = s.persName.get_text()
                    print("receiver:", receiver)
                    if receiver is None:
                        print("MISSING receiver")
        except:
            print("missing corr_action") #missing corr_action

       # abstr = soup.find_all("abstract")
        #for s in abstr:
        #    print(s)
            #abstract = s.abstract.get_text()
            #print("abstract:", abstract)

        #transcription of letters

        #transcr = soup.find_all("body")
        #print("transcr:",transcr)
        #for s in transcr:

            #if s.get('type') == "transcription":
            #    letter_text = s.get_text()
             #   print("letter_text:", letter_text)

        output_writer.writerow([fname, date_sent, sender, receiver,letter_text])

print(len(output_strs))
# close output file:
outfile.close()


#!/usr/bin/env python
"""fns for clang-format, clang-tidy, oclint"""
###############################################################################
import argparse
import shutil
import subprocess as sp
import sys

class Command:
    """Super class that all commands inherit"""

    def __init__(self, command, look_behind, args):
        self.args = args
        self.command = command
        self.version_lookbehind = look_behind
        self.files = []

        self.check_installed()

        self.retcode = 0
        self.stdout = b""
        self.stderr = b""

    def raise_error(self, problem, details):
        """Raise a formatted error."""
        msg = "Problem with {}: {}\n{}".format(self.command, problem, details)
        print(msg)
        self.stderr = msg
        self.retcode = 1

    def get_version_str(self):
        """Get the version string like 8.0.0 for a given command."""
        sp_child = sp.run([self.command, "--version"], capture_output=True)
        self.check_sp_errors(sp_child)
        version_str = str(sp_child.stdout)
        # After version like `8.0.0` is expected to be '\n' or ' '
        if self.version_lookbehind not in version_str:
            self.raise_error(
                "getting version",
                "The version format for this command has changed."
                "Create an issue at github.com/pocc/pre-commit-hooks.",
            )
        version = (
            version_str.split(self.version_lookbehind)[1]
            .split("\\n")[0]
            .split(" ")[0]
        )
        return version

    def check_installed(self):
        """Check if command is installed and fail exit if not."""
        # If result is None, then executable was not found
        path = shutil.which(self.command)
        if path is None:
            website = "https://github.com/pocc/pre-commit-hooks#prerequisites"
            problem = self.command + " not found"
            details = (
                "Make sure "
                + self.command
                + " is installed and on your PATH.\n For more info: "
                + website
            )
            self.raise_error(problem, details)

    def run_command(self, filename):
        """Run the command and check for errors"""
        sp_child = sp.run(
            [self.command, filename] + self.args, capture_output=True
        )
        self.check_sp_errors(sp_child)

    def check_sp_errors(self, sp_child):
        """Check for errors for a given executed subprocess and command."""
        sp_descriptor = "running command with args, " + " ".join(self.args)
        self.stderr = sp_child.stderr.decode("utf-8")
        self.stdout = sp_child.stdout.decode("utf-8")
        if sp_child.returncode != 0:
            self.raise_error(
                "On "
                + sp_descriptor
                + "\n\nReturn code:"
                + str(sp_child.returncode),
                "\nSTDOUT:\n"
                + sp_child.stdout.decode("utf-8")
                + "\nSTDERR:\n"
                + sp_child.stderr.decode("utf-8"),
            )
            # It's already being fromatted into stderr, so drop it here
            self.stdout = ""

    def assert_version(self, actual_version, expected_version):
        """--version hook arg enforces specific versions of tools."""
        expected_len = len(expected_version)  # allows for fuzzy versions
        if expected_version not in actual_version[:expected_len]:
            problem = "Version of " + self.command + " is wrong"
            details = (
                "Expected version is "
                + expected_version
                + " but actual system version is "
                + actual_version
                + ".\nEdit your pre-commit config or use "
                + "a different version of "
                + self.command + "."
            )
            self.raise_error(problem, details)

    def parse_args(self, args):
        """pre-commit sends file as last arg, which causes problems with --
        This function converts args (1 3 5 7 -- 6 8 0) => (0 1 3 5 7 -- 6 8),
        Where 0 is the file pre-commit sends to the utility
        See https://github.com/pre-commit/pre-commit/issues/1000

        Skip this for clang-format, as it's unexpected
        """
        parser = argparse.ArgumentParser()
        parser.add_argument("filenames", nargs="*", help="Filenames to check")
        parser.add_argument("--version", nargs=1, help="Version check")
        known_args, other_args = parser.parse_known_args(
            args
        )  # Exclude this filename from args
        if "version" in known_args and known_args.version is not None:
            expected_version = known_args.version[0]
            actual_version = self.get_version_str()
            self.assert_version(actual_version, expected_version)

        # Rotate if -- exists AND last arg is file AND pre-commit called $0
        if "--" in other_args:
            dd_idx = args.index("--")
            other_args = (
                [other_args[-1]] + other_args[:dd_idx] + other_args[dd_idx:-1]
            )
        # Add a -- -DCAMKE_EXPORT_COMPILE_COMMANDS if -- is not specified
        # To avoid compilation database errors. Skipping clang-format
        # for arg rotation, as this may actually create errors
        elif self.command != "clang-format":
            other_args += ["--", "-DCMAKE_EXPORT_COMPILE_COMMANDS=ON"]
        self.files = known_args.filenames
        self.args = other_args

    def pipe_to_std_files(self):
        """Send stdout/stderr of this command to system stdout/stderr"""
        sys.stdout.write(self.stdout)
        sys.stderr.write(self.stderr)


##############################################################################
#
# Copyright (c) 2007 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id$
"""
__docformat__ = 'restructuredtext'

from zope.interface import implements

from z3c.resource import interfaces
from z3c.proxy.container import ContainerLocationProxy

class ResourceLocationProxy(ContainerLocationProxy):
    """Proxy a resource container.

    The specific resource container implementation of a traversable component
    can be proxied.

    Now we create our resource test implementation. We take a container that
    implements ResourceTraversable:

        >>> from z3c.resource.testing import ResourceTraversableContainer
        >>> resourcetraversable = ResourceTraversableContainer()

    Inside this resource traversabel we put our resource container:

        >>> from z3c.resource.testing import TestResourceContainer
        >>> resourcetraversable['pc'] = pc = TestResourceContainer()

    Now we proxy our prototyped resource container implementation. The
    resourcetraversable will be the parent of the proxied resource container and
    that should be named to '@@':

        >>> pcproxy = ResourceLocationProxy(pc, resourcetraversable)
        >>> pcproxy.__name__ == u'@@'
        True
        >>> pcproxy.__parent__ is resourcetraversable
        True

    Containment put into the resource container appears in the
    resource location proxy:

        >>> from z3c.resource.testing import TestResourceItem
        >>> pc['x'] = x = TestResourceItem()

        >>> x1 = pcproxy['x']
        >>> from zope.proxy import isProxy
        >>> isProxy(x1)
        True
        >>> x1 == x
        True

    Containment put into the resource location proxy appears in
    the resource container:

        >>> pcproxy['y'] = y = TestResourceItem()
        >>> pc['y'] is y
        True

        >>> y1 = pcproxy['y']
        >>> isProxy(y1)
        True
        >>> y1 == y
        True

    Finaly we check all other methods of the proxy:

        >>> 'x' in pcproxy
        True
        >>> pcproxy.has_key('x')
        1
        >>> keys = [key for key in pcproxy.keys()]; keys.sort(); keys
        [u'x', u'y']
        >>> items = [item for item in pcproxy.items()]; items.sort()
        >>> items == [(u'x', x), (u'y', y)]
        True
        >>> pcproxy.get('x') == x
        True
        >>> iterator = iter(pcproxy)
        >>> iterator.next() in pcproxy
        True
        >>> iterator.next() in pcproxy
        True
        >>> iterator.next()
        Traceback (most recent call last):
        ...
        StopIteration
        >>> values = pcproxy.values(); values.sort();
        >>> x in values, y in values
        (True, True)
        >>> len(pcproxy)
        2
        >>> del pcproxy['x']
        >>> 'x' in pcproxy
        False

    """

    implements(interfaces.IResourceLocationProxy)

    def __init__(self, context, parent):
        # preconditions
        if not interfaces.IResourceContainer.providedBy(context):
            raise ValueError(
                'Parameter context: IResourceContainer required.')

        if not interfaces.IResourceTraversable.providedBy(parent):
            raise ValueError(
                'Parameter parent: IResourceTraversable required.')

        # essentails
        super(ResourceLocationProxy, self).__init__(context)
        self.__name__ = u'@@'
        self.__parent__ = parent


# coding:utf-8
from feedparser import parse
from datetime import datetime as dt
import webbrowser
import pickle

def selectgenre(genretitles, genredata):
    while True:
        print(f"Genre:{genretitles}")
        try:
            genrenum = int(input("閲覧するFeed typeを番号で入力してください: "))
            ftype = genredata[genrenum]
            break
        except ValueError:
            print("Value Errorです")
        except TypeError:
            print("Type Errorです")
        except IndexError:
            print("Index Errorです")
    return ftype

def parseDate(dateData):
    """日付のパース用関数"""
    return dt(
        dateData.tm_year, dateData.tm_mon, dateData.tm_mday,
        dateData.tm_hour, dateData.tm_min, dateData.tm_sec)

# 取得&整形
def getentries(gurls, oldentry, checkedtitle, displaymode=True):
    getentry = oldentry

    # 除外する記事のキーワード
    exclusionword = ["セール情報", "閲覧注意"]

    for url in gurls:
        entrydic = parse(url).entries
        if displaymode:
            print(f"{url}の記事を{len(entrydic)}件取得")
        else:
            pass
        for entry in entrydic:
            kiji = {
                "title": entry["title"], "link": entry["link"],
                "date": parseDate(entry["updated_parsed"] or entry["published_parsed"]),
                "sourceurl": url
            }
            if True in [word in kiji["title"] for word in exclusionword]:
                continue
            elif True in [kiji["link"] == k["link"] for k in getentry]:
                continue
            elif True in [kiji["link"] == k["link"] for k in checkedtitle]:
                continue
            else:
                getentry.append(kiji)

    getentry.sort(key=lambda x: x['date'], reverse=True)
    return getentry

def readpickle(filename):
    pathname = './data_pickle'
    filename = f'{filename}.pickle'
    filepath = pathname+'/'+filename
    try:
        with open(filepath, 'rb') as f:
            variable = pickle.load(f)
    except FileNotFoundError:
        import os
        os.makedirs(pathname, exist_ok=True)
        variable = []
        with open(filepath, 'wb') as f:
            pickle.dump(variable, f)
    return variable

def writepickle(variable, filename):
    pathname = './data_pickle'
    filename = f'{filename}.pickle'
    filepath = pathname+'/'+filename
    with open(filepath, 'wb') as f:
        pickle.dump(variable, f)

def displayTitle(rssfeeds, maxcolumn, maxrow, searchword="", searchmode=False):
    pretitle = ""
    preday = ""
    if searchmode:
        import re
        # 検索パターンをコンパイル。大文字小文字を区別しない
        repatter = re.compile(searchword, re.IGNORECASE)
    for number, entry in enumerate(rssfeeds):
        # タイトルを表示
        thistitle = entry['title']
        sourceurl = entry["sourceurl"]
        lastday = "{0:%Y/%m/%d}".format(entry["date"])
        if searchmode:
            result = repatter.search(thistitle + sourceurl)
            if result:
                pass
            else:
                maxcolumn += 1
                continue
        elif pretitle == thistitle or thistitle == "":
            maxcolumn += 1
            continue
        if preday != lastday:
            print(lastday)
            preday = lastday
        pretitle = thistitle
        thistitle = thistitle.replace("'", "")
        thistitle = thistitle.replace("Coronavirus", "Covid19")
        print(f"{number:0=2}: {thistitle[:maxrow]}")
        if number > maxcolumn:
            return None

def savefeed(rssfeeds, checkedfeeds, ftype):
    rssfeeds.sort(key=lambda x: x["date"], reverse=True)
    checkedfeeds.sort(key=lambda x: x["date"], reverse=True)
    writepickle(rssfeeds[:300], ftype+'oldentry')
    writepickle(checkedfeeds[:300], ftype+'checkedfeeds')

def readfeed(ftype):
    """return (checkedfeeds, oldentry)"""
    checked = readpickle(ftype+"checkedfeeds")
    old = readpickle(ftype+"oldentry")
    return checked, old

def readchecked(feeds, maxrow=37):
    print("   "*200)
    while True:
        print("    "*25)
        displayTitle(feeds, 37, maxrow)
        print("閲覧済モード")
        print("閲覧済モードを終える場合はqを入力")
        n = input("みたい記事の番号を入力: ")
        if n.lower() == "q":
            break
        else:
            try:
                n = int(n)
                webbrowser.open_new(feeds[n]["link"])
            except ValueError:
                print("適切な数値を入力してください")
                continue
            except IndexError:
                print("IndexError")
                continue

def main():
    with open(file="feedurls.txt", mode="r", encoding="utf-8") as f:
        urlinfo = f.read()
    jogaimoji = (" ", "\n", "\t")

    for jmoji in jogaimoji:
        urlinfo = urlinfo.replace(jmoji, "")

    urlinfo = urlinfo.split("---")
    urls = {}

    for i in urlinfo:
        i = i.strip(",")
        a = i.split("@")
        feedurls = a[1].strip().split(",")
        urls[a[0]] = feedurls

    feed_genres = list(urls.keys())
    display_genres = "|"

    for n, s in enumerate(feed_genres):
        display_genres += f"{n}: {s}|"

    feedtype = selectgenre(display_genres, feed_genres)
    # 記事を取得する場合に各サイトの取得件数を表示する場合はTrue
    displaymode = True
    searchmode = False
    searchword = ""
    checkedtitle, oldentry = readfeed(feedtype)
    while True:
        rssentries = getentries(
            urls[feedtype], oldentry, checkedtitle, displaymode)
        # 2回目以降は取得件数を表示しない
        displaymode = False
        print("     "*25)
        print(f"Genre: {feedtype}")
        maxcol = 37
        if "english" in feedtype.lower():
            maxrow = 65
        else:
            maxrow = 37
        if searchmode:
            print("     "*25)
            print(f"検索ワード:{searchword}")
            displayTitle(rssentries, maxcol, maxrow, searchword, searchmode)
            print("""
            fq で通常モードに戻る
            """)
        else:
            displayTitle(rssentries, maxcol, maxrow, searchword, searchmode)
            print()
            print("g:ジャンルを変更する c:閲覧済記事を読む d:記事除外Mode f:検索モード q:終了")

        n = input("見たい記事の番号を入力: ")

        if n.lower() == "q":
            break
        elif searchmode:
            if n.lower() == "fq":
                searchmode = False
                continue
            else:
                pass

        elif n.lower() == "g":
            savefeed(rssentries, checkedtitle, feedtype)
            feedtype = selectgenre(display_genres, feed_genres)
            displaymode = True
            checkedtitle, oldentry = readfeed(feedtype)
            continue
        elif n.lower() == "c":
            readchecked(checkedtitle, maxrow)
            continue

        elif n.lower() == "f":
            searchmode = True
            searchword = input("検索ワード＝: ")
            continue

        try:
            n = int(n)
            webbrowser.open_new(rssentries[n]["link"])
            checked = rssentries.pop(n)
            checkedtitle.append(checked)
        except ValueError:
            print("ValueError")
            continue
        except IndexError:
            print("IndexError")
            continue

    savefeed(rssentries, checkedtitle, feedtype)

if __name__ == "__main__":
    main()


##############################################################################
#
# Copyright (c) 2006-2007 Zope Foundation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""Grok components"""

from zope import component, interface
from zope.viewlet.manager import ViewletManagerBase
from zope.viewlet.viewlet import ViewletBase

from grokcore.viewlet import interfaces, util

class ViewletManager(ViewletManagerBase):
    interface.implements(interfaces.IViewletManager)

    template = None

    def __init__(self, context, request, view):
        super(ViewletManager, self).__init__(context, request, view)
        self.context = context
        self.request = request
        self.view = view
        self.__name__ = self.__view_name__
        self.static = component.queryAdapter(
            self.request,
            interface.Interface,
            name=self.module_info.package_dotted_name,
            )

    def sort(self, viewlets):
        """Sort the viewlets.

        ``viewlets`` is a list of tuples of the form (name, viewlet).
        """
        # In Grok, the default order of the viewlets is determined by
        # util.sort_components. util.sort_components() however expects
        # a list of just components, but sort() is supposed to deal
        # with a list of (name, viewlet) tuples.
        # To handle this situation we first store the name part on the
        # viewlet, then use util.sort_components() and then "unpack"
        # the name from the viewlet and recreate the list of (name,
        # viewlet) tuples, now in the correct order.
        s_viewlets = []
        for name, viewlet in viewlets:
            # Stuff away viewlet name so we can later retrieve it.
            # XXX We loose name information in case the same viewlet
            # is in the viewlets list twice, but with a different
            # name. Most probably this situation doesn't occur.
            viewlet.__viewlet_name__ = name
            s_viewlets.append(viewlet)
        s_viewlets = util.sort_components(s_viewlets)
        return [(viewlet.__viewlet_name__, viewlet) for viewlet in s_viewlets]

    def default_namespace(self):
        namespace = {}
        namespace['context'] = self.context
        namespace['request'] = self.request
        namespace['static'] = self.static
        namespace['view'] = self.view
        namespace['viewletmanager'] = self
        return namespace

    def namespace(self):
        return {}

    def update(self):
        super(ViewletManager, self).update()
        # Filter out the unavailable viewlets *after* the viewlet's update()
        # has been called.
        self.viewlets = filter(lambda v: v.available(), self.viewlets)

    def render(self):
        """See zope.contentprovider.interfaces.IContentProvider"""
        # Now render the view
        if self.template:
            return self.template.render(self)
        else:
            return u'\n'.join([viewlet.render() for viewlet in self.viewlets])

class Viewlet(ViewletBase):
    """Batteries included viewlet.
    """

    def __init__(self, context, request, view, manager):
        super(Viewlet, self).__init__(context, request, view, manager)
        self.context = context
        self.request = request
        self.view = view
        self.viewletmanager = manager
        self.__name__ = self.__view_name__
        self.static = component.queryAdapter(
            self.request,
            interface.Interface,
            name=self.module_info.package_dotted_name,
            )

    def default_namespace(self):
        namespace = {}
        namespace['context'] = self.context
        namespace['request'] = self.request
        namespace['static'] = self.static
        namespace['view'] = self.view
        namespace['viewlet'] = self
        namespace['viewletmanager'] = self.manager
        return namespace

    def namespace(self):
        return {}

    def update(self):
        pass

    def available(self):
        """Return True if this viewlet is to be rendered. False otherwise.

        Note that the available() method is called *after* update() but
        *before* render() has been called.
        """
        return True

    def render(self):
        return self.template.render(self)


# https://leetcode.com/explore/featured/card/30-day-leetcoding-challenge/528/week-1/3285/

"""
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.

Example:

Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Follow up:

If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
"""

from typing import List

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        if not nums:
            return nums

        current_sum = max_sum = nums[0]
        for num in nums[1:]:
            current_sum += num
            # Reset current sum in case current number is
            # greater (will be the case if there were negative numbers
            # in between that brought the sum down. So better to start
            # from current num in this case)
            current_sum = max(num, current_sum)
            # Update "global" max
            max_sum = max(max_sum, current_sum)

        return max_sum

s = Solution()

example = [-2,1,-3,4,-1,2,1,-5,4]
print(s.maxSubArray(example))


#!/usr/local/bin/python3

from flask import Flask, render_template, request, redirect
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime

app = Flask(__name__)

@app.route('/')
@app.route('/index.html')
def index2_page():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)