import numpy as np

from reportlab.pdfgen import canvas
from reportlab.lib.units import mm
from reportlab.pdfbase.pdfmetrics import registerFont
from reportlab.pdfbase.ttfonts import TTFont
from datetime import date, timedelta, datetime, time
from .get_data import get_data, get_h_m

# フォントを登録
registerFont(TTFont('GenShinGothic',
                    './static/font_data/GenShinGothic-Monospace-Medium.ttf'))

def avarage_enter_time(query):
    total_enter_timestamp = 0
    total_date = 0

    for i in range(len(query)) :

        timestamp = timedelta(
            hours=query[i].hour,
            minutes=query[i].minute,
            seconds=query[i].second
        ).total_seconds()
        if(timestamp != 0):
            timestamp = int(timestamp/60)
            total_enter_timestamp = total_enter_timestamp + timestamp
            total_date = total_date + 1

    if (total_enter_timestamp != 0):
        avarage_enter_timestamp = total_enter_timestamp / total_date
    else :
        avarage_enter_timestamp = 0

    return avarage_enter_timestamp

def make_report(user_id, date, enter_time, stay_time, lastweek_enter_time, lastweek_stay_time):
    file_path = './static/report/' + str(user_id) + '.pdf'  # 出力ファイル名を設定
    graph_path = "./static/graph/" + str(user_id) + ".jpg"

    paper = canvas.Canvas(file_path)  # 白紙のキャンバスを用意
    paper.saveState()  # 初期化
    paper.setFont('GenShinGothic', 20)  # フォントを設定

    # 横wと縦hの用紙サイズを設定
    w = 210 * mm
    h = 220 * mm

    paper.setPageSize((w, h))  # 用紙のサイズをセット
    title1 = "大塚研究室　出席週間レポート"
    title2 = str(date[0]) + "～" + str(date[4])
    paper.drawString(w/2 - 130, h - 60, title1) # テキストの書き込み

    paper.setFont('GenShinGothic', 18)  # フォントを設定
    paper.drawString(w/2 - 100, h - 85, title2) # テキストの書き込み

    # 画像を埋め込み(画像ファイルのパス, 横位置, 縦位置, 画像横サイズ, 画像縦サイズ)
    paper.drawInlineImage(graph_path, 31*mm, h-150*mm, 148*mm, 111*mm)
    paper.setFont('GenShinGothic', 15)  # フォントを設定

    paper.setFont('GenShinGothic', 13)  # フォントを設定

    # 今週の平均入室時間と合計滞在時間
    thisweek_enter_time_hour, thisweek_enter_time_minute = get_h_m(avarage_enter_time(enter_time))
    thisweek_stay_time_hour, thisweek_stay_time_minute = get_h_m(sum(stay_time))

    # 先週の平均入室時間と合計滞在時間
    lastweek_enter_time_hour, lastweek_enter_time_minute = get_h_m(avarage_enter_time(lastweek_enter_time))
    lastweek_stay_time_hour, lastweek_stay_time_minute = get_h_m(sum(lastweek_stay_time))

    enter_time_info = "平均入室時間：　"
    enter_time_info = enter_time_info + "先週…" + str(lastweek_enter_time_hour) + "時" + str(lastweek_enter_time_minute) + "分　"
    enter_time_info = enter_time_info + "今週…" + str(thisweek_enter_time_hour) + "時" + str(thisweek_enter_time_minute) + "分"

    stay_time_info = "合計在室時間：　"
    stay_time_info = stay_time_info + "先週…" + str(lastweek_stay_time_hour) + "時間" + str(lastweek_stay_time_minute) + "分　"
    stay_time_info = stay_time_info + "今週…" + str(thisweek_stay_time_hour) + "時間" + str(thisweek_stay_time_minute) + "分　"

    paper.drawString(w/2-150, 61*mm, enter_time_info)

    enter_time_ratio_hour, enter_time_ratio_minute = get_h_m(avarage_enter_time(enter_time) - avarage_enter_time(lastweek_enter_time))
    paper.drawString(w/2-10, 52*mm, '平均入室時間先週比 ' + str(enter_time_ratio_hour) + '時間' + str(enter_time_ratio_minute) + '分')

    paper.drawString(w/2-150, 32*mm, stay_time_info)

    if(sum(lastweek_stay_time) != 0):
        stay_time_ratio = int(sum(stay_time)/sum(lastweek_stay_time)*100)
        paper.drawString(w/2+10, 24*mm, '滞在時間先週比 ' + str(stay_time_ratio) + '％')

    paper.save()  # PDFを保存

    context_info = []
    context_info.append(str(thisweek_enter_time_hour) + "時" + str(thisweek_enter_time_minute) + "分")
    context_info.append(str(lastweek_enter_time_hour) + "時" + str(lastweek_enter_time_minute) + "分")
    context_info.append(str(enter_time_ratio_hour) + '時間' + str(enter_time_ratio_minute) + '分')
    context_info.append(str(thisweek_stay_time_hour) + "時間" + str(thisweek_stay_time_minute) + "分")
    context_info.append(str(lastweek_stay_time_hour) + "時間" + str(lastweek_stay_time_minute) + "分")
    if(sum(lastweek_stay_time) != 0):
        stay_time_ratio = int(sum(stay_time)/sum(lastweek_stay_time)*100)
        context_info.append(str(stay_time_ratio) + '％')

    return context_info


from django.shortcuts import render,redirect
from users.forms import UserRegisterForm
from django.contrib.auth.decorators import login_required
from django.contrib import messages
# Create your views here.

def register(request):
    if request.method=="POST":
        form=UserRegisterForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request,'You have been successfully registered')
            return redirect('blog-home')

    else:
        form=UserRegisterForm()

    return render(request,'users/register.html',{'form':form})
 #Yet to be developed
'''@login_required
def profile(request):
    return render(request,'users/profile.html')'''


from django.http import HttpResponse
from django.template.loader import get_template
from django.conf import settings
import os
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.lib.units import inch
import qrcode

def generate_order_pdf(order):
    # Generate a unique filename for the PDF
    filename = f"order_{order.order_number}.pdf"
    filepath = os.path.join(settings.MEDIA_ROOT, "pdf", filename)
    app_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    relative_filepath = os.path.relpath(filepath, app_root)

    # Create a canvas and set the PDF properties
    c = canvas.Canvas(filepath, pagesize=letter)

    # Add content to the PDF
    c.setFont("Helvetica", 12)
    c.drawString(1 * inch, 10 * inch, f"Order Number: {order.order_number}")
    c.drawString(1 * inch, 9 * inch, f"Recipient Name: {order.recipient_name}")
    c.drawString(1 * inch, 8 * inch, f"Recipient Address: {order.recipient_address}")
    c.drawString(1 * inch, 7 * inch, f"Business: {order.sender.name}")
    c.drawString(1 * inch, 6 * inch, f"Region: {order.recipient_region.name}")

    # Generate the QR code with order details
    qr = qrcode.QRCode(
        version=1,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=10,
        border=4,
    )
    qr.add_data(order.order_number)

    qr.make(fit=True)
    qr_img = qr.make_image(fill_color="black", back_color="white")
    qr_img.save(
        os.path.join(settings.MEDIA_ROOT, "qr_codes", f"order_{order.order_number}.png")
    )

    # Draw the QR code on the PDF
    qr_img_path = os.path.join(
        settings.MEDIA_ROOT, "qr_codes", f"order_{order.order_number}.png"
    )
    c.drawImage(qr_img_path, 1 * inch, 4 * inch, width=2 * inch, height=2 * inch)

    # Save and close the canvas
    c.showPage()
    c.save()

    # Return the file path for further use
    return relative_filepath


import copy
import logging
from typing import Any, Dict, Iterable, Optional, Tuple

import attr
from synapse.api.constants import EventTypes, Membership
from synapse.api.room_versions import EventFormatVersions, RoomVersion
from synapse.events import EventBase
from synapse.module_api import ModuleApi
from synapse.types import StateMap
from synapse.util.stringutils import random_string

logger = logging.getLogger(__name__)

@attr.s(auto_attribs=True, frozen=True)
class ManageLastAdminConfig:
    promote_moderators: bool = False

class ManageLastAdmin:
    def __init__(self, config: ManageLastAdminConfig, api: ModuleApi):
        self._api = api
        self._config = config

        self._api.register_third_party_rules_callbacks(
            check_event_allowed=self.check_event_allowed
        )

    @staticmethod
    def parse_config(config: Dict[str, Any]) -> ManageLastAdminConfig:
        return ManageLastAdminConfig(
            config.get("promote_moderators", False),
        )

    async def check_event_allowed(
        self,
        event: EventBase,
        state_events: StateMap[EventBase],
    ) -> Tuple[bool, Optional[Dict[str, Any]]]:
        """Implements synapse.events.ThirdPartyEventRules.check_event_allowed.

        Checks the event's type and the current rule and calls the right function to
        determine whether the event can be allowed.

        Args:
            event: The event to check.
            state_events: A dict mapping (event type, state key) to state event.
                State events in the room the event originated from.

        Returns:
            True if the event should be allowed, False if it should be rejected. If the
            event should be allowed but with some of its data replaced (or its context
            needs to be recalculated, eg because the state of the room has changed), a
            dictionary might be returned in addition to the boolean.
        """

        # If the event is a leave membership update, check if the last admin is leaving
        # the room
        if (
            event.type == EventTypes.Member
            and event.membership == Membership.LEAVE
            and event.is_state()
        ):
            await self._on_room_leave(event, state_events)

        return True, None

    async def _on_room_leave(
        self,
        event: EventBase,
        state_events: StateMap[EventBase],
    ) -> None:
        """React to a m.room.member event with a "leave" membership.

        Checks if the user leaving the room is the last admin in the room. If so, checks
        if there are users with lower but non-default power levels that can be promoted
        to admins. If so, promotes them to admin if the configuration allows it,
        otherwise change admin rule of the room.

        Args:
            event: The event to check.
            state_events: The current state of the room.
        """
        # Check if the last admin is leaving the room.
        pl_content = _get_power_levels_content_from_state(state_events)
        if pl_content is None:
            return

        last_admin_leaving = _is_last_admin_leaving(event, pl_content, state_events)
        if not last_admin_leaving:
            return

        # If so, search for users to promote if the configuration allows it.
        if self._config.promote_moderators:
            # Look for users to promote.
            users_to_promote = _get_users_with_highest_nondefault_pl(
                pl_content["users"],
                pl_content.get("users_default", 0),
                state_events,
                ignore_user=event.state_key,
            )

            # If we found users to promote, update the power levels event in the room's
            # state.
            if users_to_promote:
                logger.info(
                    "Promoting users to admins in room %s: %s",
                    event.room_id,
                    users_to_promote,
                )
                await self._promote_to_admins(users_to_promote, pl_content, event)
                return

        # If not, we see the default power level as admin
        logger.info("Make admin as default level in room %s", event.room_id)

        current_power_levels = state_events.get((EventTypes.PowerLevels, ""))

        # Make a deep copy of the content so we don't edit the "users" dict from
        # the event that's currently in the room's state.
        power_levels_content = (
            {}
            if current_power_levels is None
            else copy.deepcopy(current_power_levels.content)
        )

        # Send a new power levels event with a similar content to the previous one
        # except users_default is 100 to allow any user to be admin of the room.
        power_levels_content["users_default"] = 100

        # Just to be safe, also delete all users that don't have a power level of
        # 100, in order to prevent anyone from being unable to be admin the room.
        # Julien : I am not why it's needed
        users = {}
        for user, level in power_levels_content["users"].items():
            if level == 100:
                users[user] = level
        power_levels_content["users"] = users

        await self._api.create_and_send_event_into_room(
            {
                "room_id": event.room_id,
                "sender": event.sender,
                "type": EventTypes.PowerLevels,
                "content": power_levels_content,
                "state_key": "",
                **_maybe_get_event_id_dict_for_room_version(
                    event.room_version, self._api.server_name
                ),
            }
        )

        return

    async def _promote_to_admins(
        self,
        users_to_promote: Iterable[str],
        pl_content: Dict[str, Any],
        event: EventBase,
    ) -> None:
        """Promotes a given list of users to admins.

        Args:
            users_to_promote: The users to promote.
            pl_content: The content of the m.room.power_levels event that's currently in
                the room state.
            event: The event we want to use the sender and room_id of to send the new
                power levels event.
        """
        # Make a deep copy of the content so we don't edit the "users" dict from
        # the event that's currently in the room's state.
        new_pl_content = copy.deepcopy(pl_content)
        for user in users_to_promote:
            new_pl_content["users"][user] = pl_content["users"][event.sender]

        await self._api.create_and_send_event_into_room(
            {
                "room_id": event.room_id,
                "sender": event.sender,
                "type": EventTypes.PowerLevels,
                "content": new_pl_content,
                "state_key": "",
                **_maybe_get_event_id_dict_for_room_version(
                    event.room_version, self._api.server_name
                ),
            }
        )

def _maybe_get_event_id_dict_for_room_version(
    room_version: RoomVersion, server_name: str
) -> Dict[str, str]:
    """If this room version needs it, generate an event id"""
    if room_version.event_format != EventFormatVersions.ROOM_V1_V2:
        return {}

    random_id = random_string(43)
    return {"event_id": "!%s:%s" % (random_id, server_name)}

def _is_last_admin_leaving(
    event: EventBase,
    power_level_content: Dict[str, Any],
    state_events: StateMap[EventBase],
) -> bool:
    """Checks if the provided leave event is the last admin in the room leaving it.

    Args:
        event: The leave event to check.
        power_level_content: The content of the power levels event that's currently in
            the room's state.
        state_events: The current state of the room, from which we can check the room's
            member list.

    Returns:
        Whether this event is the last admin leaving the room.
    """
    # Get every admin user defined in the room's state
    admin_users = {
        user
        for user, power_level in power_level_content["users"].items()
        if power_level >= 100
    }

    if event.sender not in admin_users:
        # This user is not an admin, ignore them
        return False

    if any(
        event_type == EventTypes.Member
        and event.membership in [Membership.JOIN, Membership.INVITE]
        and state_key in admin_users
        and state_key != event.sender
        for (event_type, state_key), event in state_events.items()
    ):
        # There's another admin user in, or invited to, the room
        return False

    return True

def _get_power_levels_content_from_state(
    state_events: StateMap[EventBase],
) -> Optional[Dict[str, Any]]:
    """Extracts the content of the power levels content from the provided set of state
    events. If the event has no "users" key, or there is no power levels event in the
    state of the room, None is returned instead.

    Args:
        state_events: The state events to extract power levels from.

    Returns:
        A dict representing the content of the power levels event, or None if no power
        levels event exist in the given state events or if one exists but its content is
        missing a "users" key.
    """
    power_level_state_event = state_events.get((EventTypes.PowerLevels, ""))
    if power_level_state_event is None:
        return None
    power_level_content = power_level_state_event.content

    # Do some validation checks on the power level state event
    if (
        not isinstance(power_level_content, dict)
        or "users" not in power_level_content
        or not isinstance(power_level_content["users"], dict)
    ):
        # We can't use this power level event to determine whether the room should be
        # frozen. Bail out.
        return None

    return power_level_content

def _get_users_with_highest_nondefault_pl(
    users_dict: Dict[str, Any],
    users_default_pl: int,
    state_events: StateMap[EventBase],
    ignore_user: str,
) -> Iterable[str]:
    """Looks at the provided bits of power levels event content to figure out what the
    maximum user-specific non-default power level is with users still in the room (or
    invited to it) and which users have it.

    Args:
        users_dict: The "users" dictionary from the power levels event content.
        users_default_pl: The default power level for users who don't appear in the users
            dictionary.
        state_events: The current state of the room, from which we can check the room's
            member list.
        ignore_user: A user to ignore, i.e. to consider they've left the room even if the
            room's state says otherwise.

    Returns:
        A tuple of users with the highest non-default power level, or an empty set if no
        such users exist in the room.
    """
    # Make a copy of the users dict so we don't modify the actual event content.
    users_dict_copy = users_dict.copy()

    if ignore_user in users_dict_copy:
        del users_dict_copy[ignore_user]

    while True:
        # If there's no more user to evaluate, return an empty tuple.
        if not users_dict_copy:
            return []

        # Get the max power level in the dict.
        max_pl = max(users_dict_copy.values())

        # Bail out if the max power level is the default one (or is lower).
        if max_pl <= users_default_pl:
            return []

        # Figure out which users have that maximum power level.
        users_with_max_pl = [
            user_id for user_id, pl in users_dict_copy.items() if pl == max_pl
        ]

        # Among those users, figure out which ones are still in the room (or have a
        # pending invite to it): those are the users we need to promote.
        users_to_promote = [
            user_id
            for user_id in users_with_max_pl
            if (
                _get_membership(user_id, state_events)
                in [Membership.JOIN, Membership.INVITE]
            )
        ]

        # If we've got users in the room to promote, break out and return.
        if users_to_promote:
            return users_to_promote

        # Otherwise, remove the users we've considered and start again.
        for user_id in users_with_max_pl:
            del users_dict_copy[user_id]

def _get_membership(
    user_id: str,
    state_events: StateMap[EventBase],
) -> Optional[str]:
    evt: Optional[EventBase] = state_events.get((EventTypes.Member, user_id))

    if evt is None:
        return None

    return evt.membership


from flask import *
import sqlite3
import os

app = Flask(__name__)

path = os.getcwd()
db = path + "\data\data.db"
print("数据库链接成功！")

def connect_db():
    return sqlite3.connect(db)

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

@app.route('/db')
def data():
    conn = connect_db()
    conn.row_factory = sqlite3.Row
    cur = conn.cursor()
    cur.execute("select lowPrice,highPrice,avgPrice,pubDate from vegetables where pubDate >= ('2022-09-25 00:00:00')")
    rows = cur.fetchall()
    return render_template("vegetables.html", rows=rows)

@app.route('/test')
def test():
    conn = connect_db()
    conn.row_factory = sqlite3.Row
    cur1 = conn.cursor()
    cur1.execute("select lowPrice,highPrice,avgPrice from vegetables where prodName=('大白菜')")
    rows1 = cur1.fetchall()
    return render_template("test.html", rows=rows1)

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


#!/usr/bin/env python3
"""Solve Day 6/Part 1 of the AdventOfCode

=============================
Day 6: Probably a Fire Hazard
=============================

Because your neighbors keep defeating you in the holiday house
decorating contest year after year, you've decided to deploy one
million lights in a 1000x1000 grid.

Furthermore, because you've been especially nice this year, Santa has
mailed you instructions on how to display the ideal lighting
configuration.

Lights in your grid are numbered from 0 to 999 in each direction; the
lights at each corner are at 0,0, 0,999, 999,999, and 999,0. The
instructions include whether to turn on, turn off, or toggle various
inclusive ranges given as coordinate pairs. Each coordinate pair
represents opposite corners of a rectangle, inclusive; a coordinate
pair like 0,0 through 2,2 therefore refers to 9 lights in a 3x3
square. The lights all start turned off.

To defeat your neighbors this year, all you have to do is set up your
lights by doing the instructions Santa sent you in order.

For example:

- "turn on 0,0 through 999,999" would turn on (or leave on) every
  light.

- "toggle 0,0 through 999,0" would toggle the first line of 1000
  lights, turning off the ones that were on, and turning on the ones
  that were off.

- "turn off 499,499 through 500,500" would turn off (or leave off) the
  middle four lights.

After following the instructions, how many lights are lit?

"""

import enum
import re

import numpy as np

class ChristmasLightState(enum.IntEnum):
    OFF = 0
    ON = 1

class ChristmasLights(object):
    """Wrapper for manipulating Christmas lights

    The wrapper is constructed with the size of the (square) grid,
    with all lights initially off. It then supports several operations
    on slices of the lights, where a slice is defined as a range from
    the top left to the bottom right (in a 4-tuple (top, left, bottom,
    right)). The bottom-right range is inclusive, meaning the range
    (0, 0, 2, 2) represents a 3x3 grid of 9 lights.

    The operations supported are:

    - func:`turn_on` set all of the lights in the slice to on

    - func:`turn_off` set all of the lights in the slice to off

    - func:`toggle` set all of the lights that were on to off, and all
      the ones that were off to on.

    """
    def __init__(self, size=1000):
        """Create the grid and set every light to off"""
        self.size = size
        self.lights = ChristmasLightState.OFF * np.ones((self.size, self.size))

    def __eq__(self, other):
        return np.all(self.lights == other.lights)

    def copy(self):
        """Create a copy of the current state of the lights"""
        lights = ChristmasLights(self.size)
        lights.lights[:] = self.lights

        return lights

    def count_lit(self):
        """Return the number of lights that are on

        >>> l = ChristmasLights(4)
        >>> l.turn_on(0, 0, 2, 2)
        >>> l.count_lit()
        9

        """
        return int(np.sum(self.lights))

    def toggle(self, top, left, bottom, right):
        """Toggle lights in the given range

        >>> l = ChristmasLights(4)
        >>> l.toggle(0, 1, 3, 2)
        >>> expected = np.array([[0, 1, 1, 0]] * 4)
        >>> np.all(l.lights == expected)
        True

        >>> l.toggle(1, 0, 2, 3)
        >>> expected = np.array([
        ... [ 0,  1,  1,  0],
        ... [ 1,  0,  0,  1],
        ... [ 1,  0,  0,  1],
        ... [ 0,  1,  1,  0],
        ... ])
        >>> np.all(l.lights == expected)
        True

        """
        self._multiply_add_and_clamp(top, left, bottom, right, -1, 1)

    def turn_on(self, top, left, bottom, right):
        """Turn on lights in the given range

        >>> l = ChristmasLights(4)
        >>> l.turn_on(0, 0, 1, 1)
        >>> l.turn_on(2, 2, 3, 3)
        >>> expected = np.array([
        ... [  1,  1,  0,  0 ],
        ... [  1,  1,  0,  0 ],
        ... [  0,  0,  1,  1 ],
        ... [  0,  0,  1,  1 ],
        ... ])
        >>> np.all(l.lights == expected)
        True

        """
        self._multiply_add_and_clamp(top, left, bottom, right, 0, 1)

    def turn_off(self, top, left, bottom, right):
        """Turn off lights in the given range

        >>> l = ChristmasLights(4)
        >>> l.turn_on(0, 0, 3, 3)
        >>> l.turn_off(0, 0, 1, 1)
        >>> l.turn_off(2, 2, 3, 3)
        >>> expected = np.array([
        ... [  0,  0,  1,  1 ],
        ... [  0,  0,  1,  1 ],
        ... [  1,  1,  0,  0 ],
        ... [  1,  1,  0,  0 ],
        ... ])
        >>> np.all(l.lights == expected)
        True

        """
        self._multiply_add_and_clamp(top, left, bottom, right, 0, 0)

    def _multiply_add_and_clamp(self, top, left, bottom, right, mult, addend):
        """Perform operations on the given range and clamp values"""
        self.lights[top:bottom+1, left:right+1] *= mult
        self.lights[top:bottom+1, left:right+1] += addend

        mask = self.lights < ChristmasLightState.OFF
        self.lights[mask] = ChristmasLightState.OFF

def parse_and_update(lights, string):
    """Parse the given string and update the lights accordingly

    >>> base = ChristmasLights(4)
    >>> l = base.copy()
    >>> parse_and_update(l, "turn on 0,1 through 2,3")
    >>> l.count_lit()
    9
    >>> expected = base.copy()
    >>> expected.turn_on(0, 1, 2, 3)
    >>> l == expected
    True

    >>> base = ChristmasLights(4)
    >>> base.turn_on(0, 0, 3, 3)
    >>> l = base.copy()
    >>> parse_and_update(l, "turn off 0,1 through 2,3")
    >>> l.count_lit()
    7
    >>> expected = base.copy()
    >>> expected.turn_off(0, 1, 2, 3)
    >>> l == expected
    True

    >>> base = ChristmasLights(4)
    >>> base.turn_on(1, 0, 2, 3)
    >>> l = base.copy()
    >>> parse_and_update(l, "toggle 0,1 through 2,3")
    >>> l.count_lit()
    5
    >>> expected = base.copy()
    >>> expected.toggle(0, 1, 2, 3)
    >>> l == expected
    True

    >>> l = ChristmasLights(4)
    >>> parse_and_update(l, 'hello world')
    Traceback (most recent call last):
     ...
    ValueError: Unrecognized command

    """
    positions = try_parse_turn_on(string)
    if positions:
        lights.turn_on(*positions)
        return

    positions = try_parse_turn_off(string)
    if positions:
        lights.turn_off(*positions)
        return

    positions = try_parse_toggle(string)
    if positions:
        lights.toggle(*positions)
        return

    raise ValueError("Unrecognized command")

def try_parse_positions(string):
    """Try to parse the range or return None

    >>> try_parse_positions("0,0 through 999,999")
    (0, 0, 999, 999)
    >>> try_parse_positions("0,10 through 50,999")
    (0, 10, 50, 999)
    >>> try_parse_positions("0,10 through 50,999 ")
    (0, 10, 50, 999)
    >>> try_parse_positions("0,10 through ,999") is None
    True
    >>> try_parse_positions(" 0,10 through 10,999") is None
    True
    >>> try_parse_positions("000000") is None
    True

    """
    match = re.match(r'(\d+),(\d+) through (\d+),(\d+)', string)
    if not match:
        return None

    (top, left, bottom, right) = map(int, match.groups())

    return (top, left, bottom, right)

def try_parse_turn_on(string):
    """Try to parse the turn on command or return None

    >>> try_parse_turn_on("turn on 0,0 through 999,999")
    (0, 0, 999, 999)
    >>> try_parse_turn_on("turn off 0,0 through 999,999") is None
    True
    >>> try_parse_turn_on("turn on 0,0 through 999,") is None
    True

    """
    turn_on = re.match(r'turn on ', string)
    if not turn_on:
        return None

    positions = try_parse_positions(string[turn_on.end():])

    return positions

def try_parse_turn_off(string):
    """Try to parse the turn off command or return None

    >>> try_parse_turn_off("turn off 0,0 through 999,999")
    (0, 0, 999, 999)
    >>> try_parse_turn_off("turn on 0,0 through 999,999") is None
    True
    >>> try_parse_turn_off("turn off 0,0 through 999,") is None
    True

    """
    turn_off = re.match(r'turn off ', string)
    if not turn_off:
        return None

    positions = try_parse_positions(string[turn_off.end():])

    return positions

def try_parse_toggle(string):
    """Try to parse the toggle command or return None

    >>> try_parse_toggle("toggle 0,0 through 999,999")
    (0, 0, 999, 999)
    >>> try_parse_toggle("turn off 0,0 through 999,999") is None
    True
    >>> try_parse_toggle("toggle 0,0 through 999,") is None
    True

    """
    toggle = re.match(r'toggle ', string)
    if not toggle:
        return None

    positions = try_parse_positions(string[toggle.end():])

    return positions

def main(filename):
    """Read instructions for lights and count lit ones"""
    lights = ChristmasLights(1000)
    with open(filename, 'r') as f:
        for line in f:
            parse_and_update(lights, line)

    count = lights.count_lit()
    print(count)

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('filename')
    args = parser.parse_args()

    main(**vars(args))


import numpy as np
from scipy.special import expit
from sklearn import preprocessing
from functools import reduce
from total_arrange import get_data

if __name__ == '__main__':
    # get training and test data
    A1, Y, Ate, Yte = get_data('dataset3.csv', 5, 'hot')
    A1 = preprocessing.scale(A1)
    Ate = preprocessing.scale(Ate)

    for k in range(9, 10):
        np.random.seed(7)
        eta = 0.01
        nh = [20] * 11
        W = [0] * 11
        G = [0] * 11
        Ao = [0] * 11
        d = [0] * 11

        # h1(hidden) x m(input)
        W[0] = 2 * np.random.random((nh[0], 136)) - 1
        G[0] = np.zeros((nh[0], 136))

        for i in range(1, 10):
            W[i] = 2 * np.random.random((nh[i], nh[i - 1])) - 1
            G[i] = np.zeros((nh[i], nh[i - 1]))

        W[10] = 2 * np.random.random((4, nh[9])) - 1
        G[10] = np.zeros((4, nh[9]))

        for i in range(1, 10000):
            # expit(x) = 1 / (1 + exp(-x))
            eta = 1 / i
            # A2
            Ao[0] = expit(np.dot(W[0], A1.T))

            for j in range(1, 11):
                Ao[j] = expit(np.dot(W[j], Ao[j - 1]))

            d[10] = Ao[10] - Y.T

            for j in range(9, -1, -1):
                d[j] = np.dot(W[j + 1].T, d[j + 1]) * (Ao[j] * (1 - Ao[j]))

            for j in range(10, 0, -1):
                # print(G[j].shape)
                # print(d[j].shape)
                # print(Ao[j-1].shape)
                G[j] = G[j] + np.dot(d[j], Ao[j - 1].T)
            G[0] = G[0] + np.dot(d[0], A1)

            for j in range(1, 11):
                W[j] = W[j] - (eta * G[j])

        """
        corr = reduce(tr, np.rint(A3.T) - Y, 0)
        ratio = corr / Y.shape[0]

        print("Number of hidden units: {0}".format(nh))
        print("Training accuracy: {0}%".format( ratio * 100))

        # running the test data on the neural network
        A2te = expit(np.dot(W1, Ate.T))
        A3te = expit(np.dot(W2, A2te))
        corr = reduce(tr, np.rint(A3te.T) - Yte, 0)
        ratio = corr / Yte.shape[0]

        print("Test accuracy: {0}%\n".format( ratio * 100))
        """
        diff = np.rint(Ao[10].T) - Y
        count_correct = 0
        print(diff)
        for i in diff:
            if np.array_equal(i, [0, 0, 0, 0]):
                count_correct += 1
        print(count_correct)
        """
        print('[')
        for i in range(W2.shape[0]):
            print('[',end=' ')
            for j in range(W2.shape[1]):
                print(W2[i, j], end=' ')
            print('],', end=' ')
        print(']')
        """


from aiogram import types
from aiogram.dispatcher import filters

from database.UsersDatabaseManager import UsersDatabaseManager
from loader import dp, bot
from utility import format_name, rank_degrees, karma_degrees

@dp.message_handler(commands='top_spamers')
@dp.message_handler(filters.Text(equals='!Спамеры', ignore_case=True))
async def spamers_repr(message: types.Message):
    with UsersDatabaseManager() as db_worker:
        data = db_worker.get_messages(message.chat.id)
    out = 'Топ пользователей по написанным сообщениям: \n'
    num = 1
    for user in data:
        count = int(user[2])
        rank = ""
        for c, r in rank_degrees.items():
            if count > c:
                rank = r
                break
        out += f'{num}. {rank} {format_name(user[0], user[1])} - {count} сообщений\n'
        num += 1
    await bot.send_message(message.chat.id, out)
    await bot.delete_message(message.chat.id, message.message_id)

@dp.message_handler(commands='top_karma')
@dp.message_handler(filters.Text(equals='!Карма', ignore_case=True))
async def karma_repr(message: types.Message):
    with UsersDatabaseManager() as db_worker:
        data = db_worker.karma_repr(message.chat.id)
    out = 'Топ кармы: \n'
    num = 1
    for user in data:
        karma = int(user[2])
        rank = ""
        for c, r in karma_degrees.items():
            if karma < c:
                rank = r
                break
        out += f'{num}. {rank} {format_name(user[0], user[1])} - {karma}\n'
        num += 1
    await bot.send_message(message.chat.id, out)
    await bot.delete_message(message.chat.id, message.message_id)


import json

from . import graph

def label(vertex):
    """Graph vertex label in dot format"""
    label = f"{vertex.name} {vertex.state or ''}\n{vertex.traceback or ''}"
    if not label.endswith("\n"):
        label += "\n"
    label = json.dumps(label).replace("\\n", r"\l")
    return f"[label={label}]"

def dumps(tasks):
    """
    Renders Task dependency graph in graphviz format.
    Returns a string.
    """

    prefix = "\n        "

    vertices, edges = graph.new(tasks)
    vertices = prefix.join(f"{id(vertex.task)} {label(vertex)}" for vertex in vertices)
    edges = prefix.join(f"{id(edge.src.task)} -> {id(edge.dst.task)}" for edge in edges)

    return f"""
    digraph {{
        node [shape="note", fontname="Courier New"];
        {vertices}
        {edges}
    }}
    """.strip()


import numpy as np
import torch

from saliency_metrics.datasets import build_pipeline

def test_build_pipeline():
    cfg = [
        dict(type="Resize", height=5, width=5),
        dict(type="Normalize", mean=(0.2, 0.3, 0.4), std=(0.2, 0.3, 0.4)),
        dict(type="ToTensorV2"),
    ]
    pipeline = build_pipeline(cfg)
    img = np.ones((8, 8, 3)) * np.array([120, 140, 160])
    img = img.astype(np.uint8)
    img_transformed = pipeline(image=img)["image"]
    expected_values = (torch.tensor([120, 140, 160]) - torch.tensor([0.2, 0.3, 0.4]) * 255) / (
        torch.tensor([0.2, 0.3, 0.4]) * 255
    )
    expected = torch.ones((3, 5, 5), dtype=torch.float32) * expected_values.reshape(3, 1, 1)
    torch.testing.assert_allclose(img_transformed, expected)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# pvpython script to convert CHeart data to vtk unstructured grid format (vtu)
#
# author: Andreas Hessenthaler
# modified by: Will Zhang (willwz@gmail.com)
# data: 3/31/2022
from __future__ import print_function
import os.path
import numpy as np
import numpy.typing as npt
import sys
import argparse
import glob
import re
import struct
import enum
import dataclasses
import typing as tp

from .xmlwriter.vtk_elements import *
from .xmlwriter.xmlclasses import *

################################################################################################
# Compressed binary VTU file format?
try:
  import meshio
  compress_method = 'meshio'
  use_Compression = True
except ModuleNotFoundError:
  use_Compression = False
  print(">>>WARNING: You need either python-paraview or python-vtk or meshio for compression. Compression option disabled.")
################################################################################################
# The argument parse

parser = argparse.ArgumentParser(description='converts cheart output Dfiles into vtu files for paraview',add_help=False)
parser.add_argument('--prefix',        '-p', dest='prefix',      action='store', default='paraview',
                    type=str, help='OPTIONAL: supply a prefix name to be used for the exported vtu files. If -p is not supplied, then "paraview" will be used. that is the outputs will be named paraview-#.D')
parser.add_argument('--folder',        '-f', dest='infolder',     action='store', default='',
                    type=str, help='OPTIONAL: supply the path to the folder where the .D files are stored. If -f is not supplied, then the path is assumed to be the current folder.')
parser.add_argument('--out-folder',    '-o', dest='outfolder',    action='store', default='',
                    type=str, help='OPTIONAL: supply the path to the folder where the vtu outputs should be saved to. If -f is not supplied, then the path is assumed to be the current folder.')

settinggroup = parser.add_argument_group(title='Settings')
settinggroup.add_argument('--no-progressbar',   action='store_false', dest='progress',
                          help='OPTIONAL: controls whether to show a progress bar. Default is True.')
settinggroup.add_argument('--verbose', '-v', action='store_true',
                          help='OPTIONAL: print more info')
settinggroup.add_argument('--binary', action='store_true',
                          help='OPTIONAL: assumes that the .D files being imported is binary')
settinggroup.add_argument('--no-compression', dest='compress',action='store_false',
                          help='OPTIONAL: disable compression.')
settinggroup.add_argument('--cores', '-c', dest='cores', action='store', default=None, type=int, metavar=('#'),
                          help='OPTIONAL: use multicores.')
extrasgroup = parser.add_argument_group(title='Extras')
extrasgroup.add_argument('--make-time-series', dest='time_series', default=None,
                         type=str, help='OPTIONAL: incorporate time data, supply a file for the time step.')
parser.add_argument('variablenames', nargs='*', action='store', default=list(),
                    type=str, metavar=('var'), help='Optional: specify the variables to add to the vtu files. Multiple variable can be listed consecutively.')

main_parser = argparse.ArgumentParser(parents=[parser])

indexgroup = main_parser.add_argument_group(title='Indexing')
indexgroup.add_argument('--index',         '-i', nargs=3, dest='irange',    action='store', default=[0,0,1],
                    type=int, metavar=('start', 'end', 'step'), help='MANDATORY: specify the start, end, and step for the range of data files. If -i is not used, only step 0 will be processed. For non trivial use, this is mandatory.')
indexgroup.add_argument('--use-subiter',   '-si', dest='subiter', nargs=3, action='store', default=None,
                    type=int, metavar=('start', 'end', 'step'), help='OPTIONAL: specify the start, end, and step for the range of data files. If -i is not used, only step 0 will be processed. For non trivial use, this is mandatory.')
indexgroup.add_argument('--use-subauto',   '-sa', dest='subauto', action='store_true',
                    help='OPTIONAL: specify the start, end, and step for the range of data files. If -i is not used, only step 0 will be processed. For non trivial use, this is mandatory.')
topgroup = main_parser.add_argument_group(title='Topology')
topgroup.add_argument('--space',         '-x', dest='xfile',        action='store', default="mesh_FE.X",
                    type=str, help='MANDATORY: supply a relative path and file name  from the current directory to the .X file (must end in .X), or a variable name relative to the input folder for the Space variable. default is "Space" in cuurent folder')
topgroup.add_argument('--t-file',        '-t', dest='tfile',        action='store', default="mesh_FE.T",
                    type=str, help='MANDATORY: supply a relative path and file name from the current directory to the topology file, the default is mesh_FE.T')
topgroup.add_argument('--b-file',        '-b', dest='bfile',        action='store', default=None,
                    type=str, help='OPTIONAL: supply a relative path and file name  from the current directory to the boundary file, the default is None')

subparsers = main_parser.add_subparsers(help='Collective of subprogram')
parser_find= subparsers.add_parser('find', help='determine settings automatically', parents=[parser])
parser_find.add_argument('--mesh', dest='mesh', action='store', default='mesh',
                         type=str, help='OPTIONAL: supply a directory which contains the mesh files')
parser_find.add_argument('--step', dest='mesh', action='store', default=1,
                         type=str, help='OPTIONAL: supply a directory which contains the mesh files')
parser_find.set_defaults(find=True)

################################################################################################
# Get a library to use for compression
# try:
#   from paraview.simple import XMLUnstructuredGridReader, XMLUnstructuredGridWriter
#   compress_method = 'pvpython'
# except ImportError:
#   try:
#     from vtk import VTK_MAJOR_VERSION, vtkXMLUnstructuredGridReader, vtkXMLUnstructuredGridWriter
#     compress_method = 'vtk'
#   except ImportError:
#     try:
#       import meshio
#       compress_method = 'meshio'
#     except ImportError:
#       use_Compression = False
#       print(">>>WARNING: You need either python-paraview or python-vtk or meshio for compression. Compression option disabled.")
################################################################################################
# Check if multiprocessing is available
try:
  from concurrent import futures
  futures_avail = True
except:
  futures_avail = False
################################################################################################

def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '*', printEnd = "\r"):
  """
  Call in a loop to create terminal progress bar
  @params:
      iteration   - Required  : current iteration (Int)
      total       - Required  : total iterations (Int)
      prefix      - Optional  : prefix string (Str)
      suffix      - Optional  : suffix string (Str)
      decimals    - Optional  : positive number of decimals in percent complete (Int)
      length      - Optional  : character length of bar (Int)
      fill        - Optional  : bar fill character (Str)
      printEnd    - Optional  : end character (e.g. "\r", "\r\n") (Str)
  """
  percent = ("{0:." + str(decimals) + "f}").format(100 if (total == 0) else 100 * (iteration / float(total)))
  filledLength = int(length if (total == 0) else length * iteration // total)
  bar = fill * filledLength + '-' * (length - filledLength)
  print('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = printEnd)
  # Print New Line on Complete
  if iteration == total:
      print()

class progress_bar:
  def __init__(self, message, max=100):
    self.n = max
    self.i = 0
    self.message = message
    printProgressBar(self.i, self.n, prefix = self.message, suffix = 'Complete', length = 50)
  def next(self):
    self.i = self.i + 1
    printProgressBar(self.i, self.n, prefix = self.message, suffix = 'Complete', length = 50)
  def finish(self):
    printProgressBar(self.n, self.n, prefix = self.message, suffix = 'Complete', length = 50)

################################################################################################
# Codes
################################################################################################
# set file suffixes for VTU and CHeart (and possible compression)
vtksuffix           = ".vtu"
cheartsuffix        = ".D"
gzsuffix            = ".gz"

def read_D_binary(file) -> np.ndarray:
  with open(file, mode='rb') as f:
    nnodes = struct.unpack("i", f.read(4))[0]
    dim    = struct.unpack("i", f.read(4))[0]
    arr    = np.zeros((nnodes, dim))
    for i in range(nnodes):
      for j in range(dim):
        bite     = f.read(8)
        if (not bite): raise BufferError('Binary buffer being read ran out before indicated range')
        arr[i,j] = struct.unpack("d", bite)[0]
  return arr

class CheartDataFormat(enum.Enum):
  mesh = 0
  var  = 1
  zip  = 2

@dataclasses.dataclass
class InputArgs:
  vars : tp.List[str]
  i0 : int = 0
  it : int = 1
  di : int = 1
  index : npt.NDArray[np.int32] = None
  prefix : str = 'paraview'
  outputfile : str = 'paraview'
  infolder : str = ''
  outfolder : str = ''
  topologyname : str = "Mesh_FE.T"
  boundaryname : tp.Optional[str] = None
  spacename : str = "Mesh_FE.X"
  displace : tp.Optional[str] = None
  progress : bool = False
  useCompression : bool = False
  binary : bool = False
  verbose : bool = False
  cores : int = 1
  spacetype : CheartDataFormat = CheartDataFormat.mesh
  spacetemp : tp.Callable[[int], str] = lambda x : str(x)
  space : npt.NDArray[np.float64] = dataclasses.field(default_factory=lambda:np.zeros(1, np.float64))
  n_space : int = 0
  disptype : CheartDataFormat = CheartDataFormat.var
  disptemp : tp.Callable[[int], str] = lambda x : str(x)
  varstype : tp.List[CheartDataFormat] = dataclasses.field(default_factory=list)
  varstemp : tp.List[tp.Callable[[int], str]] = dataclasses.field(default_factory=list)

def print_header() ->None:
  print("################################################################################################")
  print("")
  print("    pvpython script to convert CHeart data to vtk unstructured grid format (vtu)")
  print("")
  print("    author: Andreas Hessenthaler")
  print("    modified by: Will Zhang")
  print("    data: 3/31/2022")
  print("")
  print("################################################################################################")

def print_input_info(inp:InputArgs) -> None:
  print("The output will be saved to ", inp.outputfile)
  print("The file name of the output will start with", inp.prefix)
  print("The script will be ran through the following range", [inp.index[0], inp.index[-1]])
  print("The data are stored in the folder ", inp.infolder)
  print("The topology file to use is ", inp.topologyname)
  print("The boundary file to use is ", inp.boundaryname)
  print("The space file to use is ", inp.spacename)
  print("The varibles to add are: ")
  print(inp.vars)
  print("<<< Output folder:               {}".format(inp.outfolder))
  print("<<< Output file name prefix:     {}".format(inp.prefix))

def compress_vtu(name:str, method:str = 'meshio', verbose:bool=False) -> None:
  # if method=='pvpython':
  #   # read ASCII vtu file and write raw vtu file
  #   fin = XMLUnstructuredGridReader(FileName=name)
  #   # CompressorType: 0 - None, 1 - ZLib, 2 - LZ4, 3 - LZMA
  #   # DataMode: 0 - Ascii, 1 - Binary, 2 - Appended
  #   foutbin = XMLUnstructuredGridWriter(CompressorType=1, DataMode=2)
  #   foutbin.FileName = name
  #   foutbin.UpdatePipeline()
  # elif method=='vtk':
  #   vtuReader=vtkXMLUnstructuredGridReader()
  #   vtuReader.SetFileName(name)
  #   vtuReader.Update()
  #   vtuWriter=vtkXMLUnstructuredGridWriter()
  #   vtuWriter.SetDataModeToAppended()
  #   vtuWriter.SetCompressorTypeToZLib()
  #   vtuWriter.SetFileName(name)
  #   if VTK_MAJOR_VERSION <= 5:
  #       vtuWriter.SetInput(vtuReader.GetOutput())
  #   else:
  #       vtuWriter.SetInputData(vtuReader.GetOutput())
  #   vtuWriter.Write()
  # elif method=='meshio':
    if verbose:
      size = os.stat(name).st_size
      print("File size before: {:.2f} MB".format(size / 1024 ** 2))
    mesh = meshio._helpers.read(name, file_format='vtu')
    meshio.vtu.write(name, mesh, binary=True, compression="zlib")
    if verbose:
      size = os.stat(name).st_size
      print("File size after: {:.2f} MB".format(size / 1024 ** 2))
  # else:
  #   raise ImportError("How did you get here! Should have found that none of the module for compression was found!")

def get_inputs(args) -> InputArgs:

  inp = InputArgs(args.variablenames)
  if args.find:
    if not inp.vars:
      index = np.zeros(1,dtype=int)
      inp.index = index
    for var in inp.vars:
      files = glob.glob(os.path.join(args.infolder, f"{var}-*.D"))
      index = [int(re.search(rf'{var}-(\d+).D', os.path.basename(s)).group(1)) for s in files]
    if inp.index is None:
      index = np.array(sorted(index))
      inp.index = index
    else:
      if (inp.index != np.array(sorted(index))).all():
        raise ValueError(f"Not all variables have the same index, find method cannot be used")
    inp.i0 = index[0]
    inp.it = index[-1]
    inp.di = None
    inp.topologyname = args.mesh+"_FE.T"
    inp.boundaryname = args.mesh+"_FE.B"
    if not os.path.isfile(inp.boundaryname):
      print(">>>NOTICE: No boundary file found ")
      inp.boundaryname = None
    inp.spacename    = args.mesh+"_FE.X"
  else:
    index = np.arange(args.irange[0], args.irange[1] + 1, args.irange[2])
    inp.i0 = args.irange[0]
    inp.it = args.irange[1]
    inp.di = args.irange[2]
    inp.topologyname = args.tfile
    inp.boundaryname = args.bfile
    if args.bfile is None:
      print(">>>NOTICE: No boundary file is supplied ")
    space = args.xfile.split("+")
    if (len(space) == 2):
      inp.spacename     = space[0]
      inp.deformedSpace = space[1]

  inp.index = index

  inp.infolder  = args.infolder
  if args.outfolder != '':
    if not os.path.isdir(args.outfolder):
      print(">>>WARNING: output directory does not exist, it will be created!")
    os.makedirs(args.outfolder, exist_ok=True)
  inp.prefix    = args.prefix
  inp.outputfile = os.path.join(args.outfolder, args.prefix)

  ################################################################################################
  # get run path and add trailing slash if it's not already there
  # !!! NOTE(WILL): check how the spacename works
  inp.progress = False if args.verbose else args.progress
  inp.useCompression = args.compress if use_Compression else use_Compression
  inp.binary = args.binary
  inp.verbose = args.verbose
  if args.cores is not None:
    if futures_avail:
      inp.cores = args.cores
    else:
      raise ModuleNotFoundError("Concurrent.futures is not available for parallel processing please turn off")
  else:
    inp.cores = None
  return inp

def check_variables(inp : InputArgs) -> None:
  filecheckerr = False
  ################################################################################################
  # Check all requested items on whether they exists
  print("")
  print("<<< Time series:                 From {} to {} with increment of {}".format(inp.i0, inp.it - 1, inp.di))
  print("<<< Compressed VTU format:       {}".format(compress_method if inp.useCompression else "None"))
  print("<<< Import data as binary:       {}".format(inp.binary))
  if inp.boundaryname is not None:
    print("<<< Output file name (boundary): {}_boundary{}".format(inp.prefix, vtksuffix))
  # pre-flight to check whether all files exist
  if not(os.path.isfile(inp.topologyname)):
    print(">>>ERROR: File "+inp.topologyname+" does not exist.")
    filecheckerr = True
  if inp.boundaryname is not None and not(os.path.isfile(inp.boundaryname)):
    print(">>>WARNING: File "+inp.boundaryname+" does not exist.")
  ################################################################################################
  # check what the space name is
  if os.path.isfile(inp.spacename):
    inp.spacetype = CheartDataFormat.mesh
    inp.spacetemp = lambda i: inp.spacename
    inp.space = np.loadtxt(inp.spacetemp(0),  skiprows=1, dtype=float)
    inp.n_space = inp.space.shape[0]
  elif os.path.isfile(os.path.join(inp.infolder, f"{inp.spacename}-{inp.index[0]}{cheartsuffix}")):
    inp.spacetype = CheartDataFormat.var
    inp.spacetemp = lambda i: os.path.join(inp.infolder, f"{inp.spacename}-{i}{cheartsuffix}")
  elif os.path.isfile(os.path.join(inp.infolder, f"{inp.spacename}-{inp.index[0]}{cheartsuffix}.gz")):
    inp.spacetype = CheartDataFormat.zip
    inp.spacetemp = lambda i: os.path.join(inp.infolder, f"{inp.spacename}-{i}{cheartsuffix}.gz")
  else:
    print(f">>>ERROR: No file matching the template {inp.spacename} can be found.")
    filecheckerr = True

  if inp.displace is None:
    pass
  elif os.path.isfile(inp.displace):
    inp.disptype = CheartDataFormat.mesh
    inp.disptemp = lambda i: inp.displace
  elif os.path.isfile(os.path.join(inp.infolder, f"{inp.displace}-{inp.index[0]}{cheartsuffix}")):
    inp.disptype = CheartDataFormat.var
    inp.disptemp = lambda i: os.path.join(inp.infolder, f"{inp.displace}-{i}{cheartsuffix}")
  elif os.path.isfile(os.path.join(inp.infolder, f"{inp.displace}-{inp.index[0]}{cheartsuffix}.gz")):
    inp.disptype = CheartDataFormat.zip
    inp.disptemp = lambda i: os.path.join(inp.infolder, f"{inp.displace}-{i}{cheartsuffix}.gz")
  else:
    print(f">>>ERROR: No file matching the template {inp.displace} can be found.")
    filecheckerr = True
  ################################################################################################
  # check what the var name is
  for var in inp.vars:
    if os.path.isfile(os.path.join(inp.infolder, f"{var}-{inp.index[0]}{cheartsuffix}")):
      inp.varstype.append(CheartDataFormat.var)
      inp.varstemp.append(lambda i: os.path.join(inp.infolder, f"{var}-{i}{cheartsuffix}"))
    elif os.path.isfile(os.path.join(inp.infolder, f"{var}-{inp.index[0]}{cheartsuffix}.gz")):
      inp.varstype.append(CheartDataFormat.zip)
      inp.varstemp.append(lambda i: os.path.join(inp.infolder, f"{var}-{i}{cheartsuffix}.gz"))
    else:
      print(f">>>ERROR: No file matching the template for {var} at time step {inp.index[0]} can be found.")
      filecheckerr = True
  ################################################################################################
  # loop over all requested times
  warnvariable = [True for _ in inp.vars]
  for time in inp.index:
    for i, var in enumerate(inp.vars):
      if not(os.path.isfile(inp.varstemp[i](time))):
        print(f">>>WARNING: File {var} at step {time} not found. Using file from previous step.")
        warnvariable[i] = False
  if filecheckerr:
    raise FileNotFoundError(">>>FILE ERROR!!!")

def get_space_data(inp:InputArgs, time:int) -> tp.Tuple[int, int, npt.NDArray[np.float64]]:
  if inp.spacetype is CheartDataFormat.mesh:
    fx = inp.space
    fnd = 3
    fnn = inp.n_space
  else:
    fx  = np.loadtxt(inp.spacetemp(time),  skiprows=1, dtype=float)
    fnn = fx.shape[0]
    fnd = fx.shape[1]
  # if deformed space, then add displacement
  if inp.displace is not None:
    fd  = np.loadtxt(inp.disptemp(time), skiprows=1, dtype=float)
    fx  = fx + fd
  # VTU files are defined in 3D space, so we have to append a zero column for 2D data
  if (fnd == 1):
    print(">>>ERROR: Cannot convert data that lives on 1D domains.")
    return
  elif (fnd == 2):
    z   = np.zeros((fnn,1), dtype=float)
    fx  = np.append(fx, z, axis=1)
  return fnn, fx

class LoadTopology:
  def __init__(self, inp: InputArgs) -> None:
    ################################################################################################
    # read topology and get number of elements, number of nodes per elements
    self._ft  = np.loadtxt(inp.topologyname, skiprows=1, dtype=int)
    if self._ft.ndim==1:
      self._ft = np.array([self._ft])
    self.ne = self._ft.shape[0]
    self.nc = self._ft.shape[1]
    # guess the VTK element type
    # bilinear triangle
    if (self.nc == 3):
      self.vtkelementtype = VtkBilinearTriangle
      self.vtksurfacetype = VtkLinearLine
    # biquadratic triangle
    elif (self.nc == 6):
      self.vtkelementtype = VtkBiquadraticTriangle
      self.vtksurfacetype = VtkQuadraticLine
    # bilinear quadrilateral / trilinear tetrahedron
    elif (self.nc == 4):
      if inp.boundaryname is None:
        print(">>>ERROR: bilinear quadrilateral / trilinear tetrahedron detected but boundary file supplied is ", inp.boundaryname)
        print("          please supply the corresponding boundary file to proceed ", inp.boundaryname)
        sys.exit()
      else:
        with open(inp.boundaryname, "r") as f:
          _ = f.readline().strip()
          second_line = [int(i) for i in f.readline().strip().split()]
        # bilinear quadrilateral
        if (len(list(second_line)) == 4):
          self.vtkelementtype = VtkBilinearQuadrilateral
          self.vtksurfacetype = VtkLinearLine
        # trilinear tetrahedron
        elif (len(list(second_line)) == 5):
          self.vtkelementtype = VtkTrilinearTetrahedron
          self.vtksurfacetype = VtkBilinearTriangle
        else:
          print(f">>>ERROR: boundary file {inp.boundaryname} is not consistent with the topology file {inp.topologyname}")
          sys.exit()
    # biquadratic quadrilateral
    elif (self.nc == 9):
      self.vtkelementtype = VtkBiquadraticQuadrilateral
      self.vtksurfacetype = VtkQuadraticLine
    # triquadratic tetrahedron
    elif (self.nc == 10):
      self.vtkelementtype = VtkTriquadraticTetrahedron
      self.vtksurfacetype = VtkQuadraticLine
    # trilinear hexahedron
    elif (self.nc == 8):
      self.vtkelementtype = VtkTrilinearHexahedron
      self.vtksurfacetype = VtkBilinearQuadrilateral
    # triquadratic hexahedron
    elif (self.nc == 27):
      self.vtkelementtype = VtkTriquadraticHexahedron
      self.vtksurfacetype = VtkBiquadraticQuadrilateral
    else:
      sys.exit(">>>ERROR: Element type not implemented.")
  def __setitem__(self, index, data):
      self._ft[index] = data
  def __getitem__(self, index):
      return self._ft[index]

def export_boundary(tp : LoadTopology, inp : InputArgs) -> None:
  ################################################################################################
  # export patch IDs from B-file
  if inp.boundaryname is None:
    print(">>> NOTICE: No boundary file is supplied, export of boundary patch is skipped")
  else:
    print("<<< Working on exporting the boundary patch IDs from", inp.boundaryname)
    # for boundary coordinates, let's default to first time step
    time = inp.index[0]
    # read boundary and get number of elements, number of nodes per elements
    fb      = np.loadtxt(inp.boundaryname, skiprows=1, dtype=int)
    fb = fb[:,1:]
    fben    = fb.shape[0]
    fbcn    = fb.shape[1] - 1
    # write header
    foutfile = inp.outputfile+"_boundary"+vtksuffix
    fnn, fx = get_space_data(inp, 0)
    vtkfile = XMLElement('VTKFile', type="UnstructuredGrid")
    grid = vtkfile.add_elem(XMLElement("UnstructuredGrid"))
    piece = grid.add_elem(XMLElement('Piece', Name=f'{inp.prefix}', NumberOfPoints=f'{fnn}', NumberOfCells=f'{fben}'))
    dataarr = piece.add_elem(XMLElement('Points')).add_elem(XMLElement('DataArray', type="Float64", NumberOfComponents="3", Format="ascii"))
    dataarr.add_data(fx, XMLWriters.PointWriter)
    cell = piece.add_elem(XMLElement('CellData', Scalars="scalars"))
    dataarr = cell.add_elem(XMLElement('DataArray', type="Int8", Name="PatchIDs", Format="ascii"))
    dataarr.add_data(fb[:, - 1], XMLWriters.IntegerWriter)
    cell = piece.add_elem(XMLElement('Cells', Scalars="scalars"))
    dataarr = cell.add_elem(XMLElement('DataArray', type="Int64", Name="connectivity", Format="ascii"))
    dataarr.add_data(fb[:, :-1], tp.vtksurfacetype.write)
    dataarr = cell.add_elem(XMLElement('DataArray', type="Int64", Name="offsets", Format="ascii"))
    dataarr.add_data(np.arange(fbcn, (fbcn)*(fben+1), fbcn), XMLWriters.IntegerWriter)
    dataarr = cell.add_elem(XMLElement('DataArray', type="Int8", Name="types", Format="ascii"))
    dataarr.add_data(np.full((fben,), tp.vtkelementtype.vtksurfaceid), XMLWriters.IntegerWriter)
    # print(foutfile)
    with open(foutfile, "w") as fout:
      vtkfile.write(fout)
    if inp.useCompression:
      compress_vtu(foutfile, method=compress_method, verbose=inp.verbose)

def export_data_iter(tp : LoadTopology, inp : InputArgs, time, lastvariable, lastspace, lastdeform, bart:progress_bar=None) -> None:
  # print(f'bart is {bart}')
  ############################################################################################
  # convert space variable
  xfile = inp.spacetemp(time)
  if os.path.isfile(xfile):
    lastspace = xfile
  elif lastspace is None:
    sys.exit(">>>ERROR: Even though the space file was found during check, it is not found during import")
  else:
    xfile = lastspace
  fnn, fx = get_space_data(inp, time)
  # VTU files are defined in 3D space, so we have to append a zero column for 2D data
  foutfile = inp.outputfile+"-"+str(time)+vtksuffix

  vtkfile = XMLElement('VTKFile', type="UnstructuredGrid")
  grid    = vtkfile.add_elem(XMLElement("UnstructuredGrid"))
  piece   = grid.add_elem(XMLElement('Piece', Name=f'{inp.prefix}', NumberOfPoints=f'{fnn}', NumberOfCells=f'{tp.ne}'))
  points    = piece.add_elem(XMLElement('Points'))
  dataarr     = points.add_elem(XMLElement('DataArray', type="Float64", NumberOfComponents="3", Format="ascii"))
  dataarr.add_data(fx, XMLWriters.PointWriter)

  cell      = piece.add_elem(XMLElement('Cells'))
  dataarr     = cell.add_elem(XMLElement('DataArray', type="Int64", Name="connectivity", Format="ascii"))
  dataarr.add_data(tp, tp.vtkelementtype.write)
  dataarr     = cell.add_elem(XMLElement('DataArray', type="Int64", Name="offsets", Format="ascii"))
  dataarr.add_data(np.arange(tp.nc, tp.nc*(tp.ne+1), tp.nc), XMLWriters.IntegerWriter)
  dataarr     = cell.add_elem(XMLElement('DataArray', type="Int8",  Name="types", Format="ascii"))
  dataarr.add_data(np.full((tp.ne,), tp.vtkelementtype.vtkelementid), XMLWriters.IntegerWriter)

  points    = piece.add_elem(XMLElement('PointData', Scalars="scalars"))
  for varIdx in range(len(inp.vars)):
    vfile = inp.varstemp[varIdx](time)
    if not(os.path.isfile(vfile)):
      if lastvariable[varIdx] is not None:
        vfile = lastvariable[varIdx]
      else:
        sys.exit(">>>ERROR: Unexpect problem, file was found during check but not when being imported: {}".format(lastvariable[varIdx]))
    else:
      lastvariable[varIdx] = vfile
    if inp.binary:
      fv = read_D_binary(vfile)
      # print(fv.shape)
    else:
      fv = np.loadtxt(vfile,  skiprows=1, dtype=float)
    fvnn = fv.shape[0]
    if (fv.ndim == 1):
      fv = fv[:, np.newaxis]
    fvnd = fv.shape[1]
    if (fnn != fvnn):
      raise AssertionError(">>>ERROR: Invalid number of nodes for "+str(inp.vars)+".")
    # append zero column if need be
    if (fvnd == 2):
      z       = np.zeros((fvnn,1), dtype=float)
      fv      = np.append(fv, z, axis=1)
      fvnd    = 3
    dataarr     = points.add_elem(XMLElement('DataArray', type="Float64", Name=f"{inp.vars[varIdx]}", NumberOfComponents=f"{fvnd}", Format="ascii"))
    dataarr.add_data(fv, XMLWriters.FloatArrWriter)

  with open(foutfile, "w") as fout:
    vtkfile.write(fout)

  if inp.useCompression:
    compress_vtu(foutfile, method=compress_method, verbose=inp.verbose)
  if inp.progress:
    if bart is not None:
      bart.next()

def main_cli(args=None) -> None:
  ################################################################################################
  # Get the commandline arguments
  args = main_parser.parse_args(args=args)
  # if args.variablenames == []:sys.exit(">>>ERROR: no variable names are supplied to be processed!!!")
  ################################################################################################
  # The main Script
  print_header()
  inp = get_inputs(args)
  check_variables(inp)
  print_input_info(inp)
  ################################################################################################
  # read topology and get number of elements, number of nodes per elements
  tp  = LoadTopology(inp)
  export_boundary(tp, inp)
  ################################################################################################
  # now convert all requested files, looping over all requested times
  lastvariable = [None for _ in inp.vars]
  lastspace    = None
  lastdeform   = None
  bart         = None
  if inp.cores is None:
    if args.subauto:
      if inp.progress: bart = progress_bar('Processing', max=len(inp.index))
      for time in inp.index:
        jlist = glob.glob(f'*-{time}.*.D')
        for j in jlist:
          result = re.search('-(.*).D', j)
          export_data_iter(tp, inp, result.group(1), lastvariable, lastspace, lastdeform, bart)
    elif args.subiter is not None:
      if inp.progress: bart = progress_bar('Processing', max=len(inp.index) * (args.subiter[1] - args.subiter[0] + 1)//args.subiter[2])
      for time in inp.index:
        export_data_iter(tp, inp, time, lastvariable, lastspace, lastdeform,  bart)
        for j in range(args.subiter[0], args.subiter[1] + 1, args.subiter[2]):
          export_data_iter(tp, inp, str(time)+'.'+str(j), lastvariable, lastspace, lastdeform)
    else:
      if inp.progress:
        bart = progress_bar('Processing', max=len(inp.index))
      for time in inp.index:
        export_data_iter(tp, inp, time, lastvariable, lastspace, lastdeform,  bart)
  else:
    with futures.ProcessPoolExecutor(inp.cores) as exec:
      future_jobs=[]
      if args.subauto:
        if inp.progress: bart = progress_bar('Processing', max=len(inp.index))
        for time in inp.index:
          future_jobs.append(exec.submit(export_data_iter, tp, inp, time, lastvariable, lastspace, lastdeform))
          jlist = glob.glob(f'*-{time}.*.D')
          for j in jlist:
            result = re.search('-(.*).D', j)
            future_jobs.append(exec.submit(export_data_iter, tp, inp, str(j), lastvariable, lastspace, lastdeform))
      elif args.subiter is not None:
        if inp.progress: bart = progress_bar('Processing', max=len(inp.index0) * (args.subiter[1] - args.subiter[0] + 1)//args.subiter[2])
        for time in inp.index:
          future_jobs.append(exec.submit(export_data_iter, tp, inp, time, lastvariable, lastspace, lastdeform))
          for j in range(args.subiter[0], args.subiter[1] + 1, args.subiter[2]):
            future_jobs.append(exec.submit(export_data_iter, tp, inp, str(time)+'.'+str(j), lastvariable, lastspace, lastdeform))
      else:
        if inp.progress: bart = progress_bar('Processing', max=len(inp.index))
        for time in inp.index:
          future_jobs.append(exec.submit(export_data_iter, tp, inp, time, lastvariable, lastspace, lastdeform))
      for _ in futures.as_completed(future_jobs):
        bart.next()
  print("################################################################################################")
  if args.time_series is not None:
    from .make_vtu_series import import_time_data, print_cmd_header, xml_write_header, xml_write_content, xml_write_footer
    _, time_series = import_time_data(args.time_series)
    print_cmd_header(inp)
    bar = progress_bar('Processing', max=inp.nt)
    with open(os.path.join(inp.outfolder, inp.prefix+".pvd"), "w") as f:
      xml_write_header(f)
      for i in inp.index:
        xml_write_content(f, os.path.join(inp.outfolder, f"{inp.prefix}-{i}.vtu"), time_series[i])
        bar.next()
      xml_write_footer(f)
    bar.finish()
    print("    The process is complete!")
    print("################################################################################################")

if __name__ == "__main__":
  main_cli()


import pytest
from src.realty.landwatch import Query
from src.realty.landwatch.details import Listing_Details

@pytest.fixture(scope="module")
def reasonable_query():
    q = Query()
    return q.get_response()

@pytest.fixture(scope="module")
def reasonable_results():
    q = Query()
    return q.get_results()['propertyResults']

class TestQuery:

    @staticmethod
    def test_status_code(reasonable_query):
        assert reasonable_query.status_code == 200

    @staticmethod
    def test_expected_json_keys(reasonable_query):
        assert list(
            reasonable_query.json().keys()
        ) == ['activeFilters', 'amChartMapData', 'breadCrumbSchema', 'brokerDetails', 'carouselCounts', 'collectionPageSchema', 'dataLayerSearchResponse', 'filterSections', 'footer', 'headlineAd', 'routeContext', 'searchResults', 'searchUI', 'seoLinkSections', 'seoTextSection', 'seoTextSection2', 'siteId']

    @staticmethod
    def test_expected_results(reasonable_query):
        assert isinstance(
            reasonable_query.json(
            )['searchResults']['propertyResults'],
            list
        )

    @staticmethod
    def test_more_than_zero_results(reasonable_query):
        assert len(reasonable_query.json(
        )['searchResults']['propertyResults']) > 0

    @staticmethod
    def test_expected_result_keys(reasonable_query):
        # just required ones
        expected_keys = {'accountId', 'acres', 'acresDisplay', 'adTargetingCountyId', 'address', 'auctionDate', 'baths', 'bathsDisplay', 'bedsDisplay', 'beds', 'brokerCompany', 'brokerName', 'canonicalUrl', 'city', 'cityID', 'companyLogoDocumentId', 'county', 'countyId', 'countyLabel', 'description', 'encodedBoundaryPoints', 'externalSourceId', 'halfBaths', 'halfBathsDisplay', 'hasHouse', 'hasVideo', 'hasVirtualTour', 'homesqft', 'homesqftDisplay', 'imageCount', 'imageAltTextDisplay',
                         'id', 'isALC', 'isDiamond', 'isFirstFreeListing', 'isGold', 'isHeadlineAd', 'isLiked', 'isPlatinum', 'isShowcase', 'lake', 'latitude', 'listHubListingKey', 'listingLevel', 'listingLevelTitle', 'longitude', 'partnerId', 'portraitDocumentId', 'price', 'priceChange', 'priceDisplay', 'propertyTypes', 'propertyTypesLabel', 'schemaData', 'shortPrice', 'siteListingId', 'state', 'stateAbbreviation', 'stateCode', 'stateId', 'status', 'thumbnailDocumentId', 'title', 'types', 'zip'}

        results = reasonable_query.json(
        )['searchResults']['propertyResults']

        assert all([
            expected_keys - set(r.keys()) == set() for r in results
        ])

class TestListingDetails:
    @staticmethod
    @pytest.fixture(scope="class")
    def listing_subset(reasonable_results):
        return [
            Listing_Details(lr) for lr in reasonable_results[:3]
        ]

    @staticmethod
    def test_init(listing_subset):
        # just a basic check if init doesn't run into errors
        listing_subset


#!/usr/bin/env python3
import pathlib, acts, acts.examples
from acts.examples.simulation import (
    addParticleGun,
    MomentumConfig,
    EtaConfig,
    ParticleConfig,
    addPythia8,
    addFatras,
    ParticleSelectorConfig,
    addDigitization,
)
from acts.examples.reconstruction import (
    addSeeding,
    SeedingAlgorithm,
    TruthSeedRanges,
    addKalmanTracks,
    addCKFTracks,
    CKFPerformanceConfig,
    TrackSelectorRanges,
    addAmbiguityResolution,
    AmbiguityResolutionConfig,
    addVertexFitting,
    VertexFinder,
)
from common import getOpenDataDetectorDirectory
from acts.examples.odd import getOpenDataDetector

import uproot

u = acts.UnitConstants
geoDir = getOpenDataDetectorDirectory()

oddMaterialMap = geoDir / "data/odd-material-maps.root"
oddDigiConfig = geoDir / "config/odd-digi-smearing-config.json"
oddSeedingSel = geoDir / "config/odd-seeding-config.json"
oddMaterialDeco = acts.IMaterialDecorator.fromFile(oddMaterialMap)

detector, trackingGeometry, decorators = getOpenDataDetector(
    geoDir, mdecorator=oddMaterialDeco
)
field = acts.ConstantBField(acts.Vector3(0.0, 0.0, 2.0 * u.T))

multiplicity=10
particles=4

data = {
  VertexFinder.Iterative: {
    'event_vertices': [],
    'reco_vertices': []
  },
  VertexFinder.AMVF: {
    'event_vertices': [],
    'reco_vertices': []
  },
}

vertexing_label = {
  VertexFinder.Iterative: 'IVF',
  VertexFinder.AMVF: 'AMVF',
}

outputDir = pathlib.Path.cwd() / "output"

for vertexing in [VertexFinder.Iterative, VertexFinder.AMVF]:
  rnd = acts.examples.RandomNumbers(seed=42)

  s = acts.examples.Sequencer(events=1, skip=1, numThreads=-1, outputDir=str(outputDir))

  addParticleGun(
      s,
      MomentumConfig(1.0 * u.GeV, 10.0 * u.GeV, transverse=True),
      EtaConfig(-3.0, 3.0, uniform=True),
      ParticleConfig(particles, acts.PdgParticle.eMuon, randomizeCharge=True),
      vtxGen=acts.examples.GaussianVertexGenerator(
          stddev=acts.Vector4(12.5 * u.um, 12.5 * u.um, 55.5 * u.mm, 0.0 * u.ns),
          mean=acts.Vector4(0, 0, 0, 0),
      ),
      multiplicity=multiplicity,
      rnd=rnd,
      outputDirRoot=outputDir,
  )

  addFatras(
      s,
      trackingGeometry,
      field,
      rnd=rnd,
      outputDirRoot=outputDir,
  )

  addDigitization(
      s,
      trackingGeometry,
      field,
      digiConfigFile=oddDigiConfig,
      rnd=rnd,
      outputDirRoot=outputDir,
  )

  """
  addSeeding(
      s,
      trackingGeometry,
      field,
      #ParticleSmearingSigmas(d0=20*u.um, d0PtA=0, d0PtB=0, z0=20*u.um, z0PtA=0, z0PtB=0),
      seedingAlgorithm=SeedingAlgorithm.TruthSmeared,
      geoSelectionConfigFile=oddSeedingSel,
      outputDirRoot=outputDir,
  )

  addKalmanTracks(
      s,
      trackingGeometry,
      field,
  )
  """

  addSeeding(
      s,
      trackingGeometry,
      field,
      geoSelectionConfigFile=oddSeedingSel,
      outputDirRoot=outputDir,
  )

  addCKFTracks(
      s,
      trackingGeometry,
      field,
      CKFPerformanceConfig(ptMin=0.0, nMeasurementsMin=6),
      TrackSelectorRanges(pt=(1.0 * u.GeV, None), absEta=(None, 3.0), removeNeutral=True),
      outputDirRoot=outputDir,
  )

  addAmbiguityResolution(
      s,
      AmbiguityResolutionConfig(maximumSharedHits=3),
      CKFPerformanceConfig(ptMin=0.0, nMeasurementsMin=6),
      outputDirRoot=outputDir,
  )

  addVertexFitting(
      s,
      field,
      TrackSelectorRanges(pt=(0.5 * u.GeV, None), loc0=(-4.0 * u.mm, 4.0 * u.mm), absEta=(None, 3.0), removeNeutral=True),
      vertexFinder=vertexing,
      outputDirRoot=outputDir,
      logLevel=acts.logging.Level.VERBOSE,
  )

  s.run()

  particles_data = uproot.open(outputDir / 'particles.root')
  particles_data = particles_data['particles'].arrays(library='pd')
  for _,(x,y,z,px,py,pz) in particles_data[['vx','vy','vz','px','py','pz']].iterrows():
    data[vertexing]['event_vertices'].append((x,y,z,px,py,pz))

  vertex_data = uproot.open(outputDir / 'performance_vertexing.root')
  vertex_data = vertex_data['vertexing'].arrays(library='pd')
  for _,(x,y,z) in vertex_data[['recoX','recoY','recoZ']].iterrows():
    data[vertexing]['reco_vertices'].append((x,y,z))

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec

fig = plt.figure()
gs = gridspec.GridSpec(2, 1, height_ratios=[1, 1])

ax0 = plt.subplot(gs[0])
ax0.set_xlabel('z')
ax0.set_ylabel('x')

ax1 = plt.subplot(gs[1], sharex=ax0)
ax1.set_xlabel('z')
ax1.set_ylabel('y')

vertices = np.array(data[VertexFinder.Iterative]['event_vertices'])
p = vertices[:,3:6] / np.linalg.norm(vertices[:,3:6], axis=-1)[:,np.newaxis]
ax0.quiver(vertices[:,2], vertices[:,0], p[:,2], p[:,0], angles='uv', units='inches')
ax1.quiver(vertices[:,2], vertices[:,1], p[:,2], p[:,1], angles='uv', units='inches')
ax0.scatter(vertices[:,2], vertices[:,0], label='true')
ax1.scatter(vertices[:,2], vertices[:,1], label='true')

for vertexing in [VertexFinder.Iterative, VertexFinder.AMVF]:
  vertices = np.array(data[vertexing]['reco_vertices'])
  if len(vertices) == 0:
    continue
  ax0.scatter(vertices[:,2], vertices[:,0], label=vertexing_label[vertexing])
  ax1.scatter(vertices[:,2], vertices[:,1], label=vertexing_label[vertexing])

plt.legend()
plt.show()


from typing import List, Dict
from types import FunctionType
from Script.UI.Moudle import draw, panel
from Script.Core import (
    get_text,
    cache_control,
    game_type,
    flow_handle,
    value_handle,
    text_handle,
    constant,
    py_cmd,
)
from Script.Design import map_handle, attr_text, character_move
from Script.Config import game_config

_: FunctionType = get_text._
""" 翻译api """
cache: game_type.Cache = cache_control.cache
""" 游戏缓存数据 """
line_feed = draw.NormalDraw()
""" 换行绘制对象 """
line_feed.text = "\n"
line_feed.width = 1

class SeeMapPanel:
    """
    用于查看当前地图界面面板对象
    Keyword arguments:
    width -- 绘制宽度
    """

    def __init__(self, width: int):
        """初始化绘制对象"""
        self.width: int = width
        """ 绘制的最大宽度 """
        character_data: game_type.Character = cache.character_data[0]
        self.now_map: List[str] = map_handle.get_map_for_path(character_data.position)
        """ 当前查看的地图坐标 """

    def draw(self):
        """绘制对象"""
        move_menu_panel_data = {
            0: MapSceneNameDraw(self.now_map, self.width),
            # 1: UsefulSceneNamePanel(self.now_map, self.width),
            # 2: entertainmentSceneNamePanel(self.now_map, self.width),
            3: CollectionSceneNamePanel(self.now_map, self.width),
        }
        move_menu_panel = MoveMenuPanel(self.width)
        while 1:
            line_feed.draw()
            if cache.now_panel_id != constant.Panel.SEE_MAP:
                break
            map_path_str = map_handle.get_map_system_path_str_for_list(self.now_map)
            map_data: game_type.Map = cache.map_data[map_path_str]
            map_name = attr_text.get_map_path_text(self.now_map)
            title_draw = draw.TitleLineDraw(_("当前区块:") + map_name, self.width)
            title_draw.draw()
            now_draw_list: game_type.MapDraw = map_data.map_draw
            character_data: game_type.Character = cache.character_data[0]
            character_scene_name = map_handle.get_map_scene_id_for_scene_path(
                self.now_map, character_data.position
            )
            # print(f"debug self.now_map = {self.now_map}, map_path_str = {map_path_str}，map_name = {map_name}, character_data.position = {character_data.position}, character_scene_id = {character_scene_name}")
            return_list = []
            index = 0
            for now_draw_line in now_draw_list.draw_text:
                fix_width = int((self.width - now_draw_line.width) / 2)
                fix_text = " " * fix_width
                fix_draw = draw.NormalDraw()
                fix_draw.text = fix_text
                fix_draw.width = fix_width
                # print("fix_width:",fix_width)
                fix_draw.draw()
                for draw_text in now_draw_line.draw_list:
                    # print(f"debug draw_text.text = {draw_text.text}")

                    # 首先需要是地点按钮
                    if "is_button" in draw_text.__dict__ and draw_text.is_button:

                        # 获取地点路径
                        scene_path = map_handle.get_scene_path_for_map_scene_id(
                            self.now_map, draw_text.text
                        )
                        full_scene_str = map_handle.get_map_system_path_str_for_list(scene_path)
                        # print(f"debug scene_path = {scene_path}，draw_text.text = {draw_text.text}, full_scene_str = {full_scene_str}")

                        # 如果不是玩家所在的地点，则绘制按钮
                        if draw_text.text != character_scene_name:
                            # 如果当前地点可以进入则正常绘制
                            if map_handle.judge_scene_name_open(full_scene_str):
                                now_draw = draw.Button(
                                    draw_text.text, draw_text.text, cmd_func=self.move_now, args=(scene_path,)
                                )
                                # 如果是有NPC在那么显示为绿色
                                if len(cache.scene_data[full_scene_str].character_list):
                                    now_draw.normal_style = "green"
                            # 如果当前地点不可进入则绘制灰色按钮
                            else:
                                now_draw = draw.Button(
                                    draw_text.text, draw_text.text,normal_style="deep_gray", cmd_func=self.move_now, args=(scene_path,)
                                )
                            now_draw.width = self.width
                            now_draw.draw()
                            return_list.append(now_draw.return_text)

                        else:
                            # 如果是玩家所在的地点，则高亮显示文本
                            now_draw = draw.NormalDraw()
                            now_draw.style = "gold_enrod"
                            now_draw.text = draw_text.text
                            now_draw.width = self.width
                            now_draw.draw()
                    # 如果不是地点按钮，则正常绘制文本
                    else:
                        now_draw = draw.NormalDraw()
                        now_draw.style = draw_text.style
                        now_draw.text = draw_text.text
                        now_draw.width = self.width
                        now_draw.draw()
                line_feed.draw()
            path_edge = map_data.path_edge
            scene_path = path_edge[character_scene_name].copy()
            if character_scene_name in scene_path:
                del scene_path[character_scene_name]

            # 当前位置相邻地点
            # scene_path_list = list(scene_path.keys())
            # if len(scene_path_list):
            #     line = draw.LineDraw(".", self.width)
            #     line.draw()
            #     message_draw = draw.NormalDraw()
            #     message_draw.text = _("你可以从这里前往:\n")
            #     message_draw.width = self.width
            #     message_draw.draw()
            #     draw_list = []
            #     for scene in scene_path_list:
            #         load_scene_data = map_handle.get_scene_data_for_map(map_path_str, scene)
            #         now_scene_path = map_handle.get_map_system_path_for_str(load_scene_data.scene_path)
            #         now_draw = draw.CenterButton(
            #             f"[{load_scene_data.scene_name}]",
            #             load_scene_data.scene_name,
            #             self.width / 4,
            #             cmd_func=self.move_now,
            #             args=(now_scene_path,),
            #         )
            #         return_list.append(now_draw.return_text)
            #         draw_list.append(now_draw)
            #     draw_group = value_handle.list_of_groups(draw_list, 8)
            #     for now_draw_list in draw_group:
            #         for now_draw in now_draw_list:
            #             now_draw.draw()
            #         line_feed.draw()

            scene_id_list = list(path_edge.keys())
            now_index = len(scene_id_list)
            index = now_index
            move_menu_panel.update()
            move_menu_panel.draw()
            return_list.extend(move_menu_panel.return_list)
            if move_menu_panel.now_type in move_menu_panel_data:
                now_move_menu = move_menu_panel_data[move_menu_panel.now_type]
                now_move_menu.update(self.now_map, index)
                now_move_menu.draw()
                now_index = now_move_menu.end_index + 1
                return_list.extend(now_move_menu.return_list)
            line = draw.LineDraw("=", self.width)
            line.draw()
            if self.now_map != []:
                now_id = text_handle.id_index(now_index)
                now_text = now_id + _("查看上级地图")
                up_button = draw.CenterButton(
                    now_text, str(now_index), self.width / 3, cmd_func=self.up_map
                )
                up_button.draw()
                return_list.append(up_button.return_text)
                now_index += 1
            else:
                now_draw = draw.NormalDraw()
                now_draw.text = " " * int(self.width / 3)
                now_draw.width = self.width / 3
                now_draw.draw()
            back_id = text_handle.id_index(now_index)
            now_text = back_id + _("返回")
            back_button = draw.CenterButton(now_text, str(now_index), self.width / 3)
            back_button.draw()
            return_list.append(back_button.return_text)
            now_index += 1
            character_map = map_handle.get_map_for_path(character_data.position)
            if character_map != self.now_map:
                now_id = text_handle.id_index(now_index)
                now_text = now_id + _("查看下级地图")
                down_button = draw.CenterButton(
                    now_text, str(now_index), self.width / 3, cmd_func=self.down_map
                )
                down_button.draw()
                return_list.append(down_button.return_text)
            line_feed.draw()
            yrn = flow_handle.askfor_all(return_list)
            py_cmd.clr_cmd()
            if yrn == back_button.return_text:
                cache.now_panel_id = constant.Panel.IN_SCENE
                break

    def up_map(self):
        """将当前地图切换为上级地图"""
        py_cmd.clr_cmd()
        up_map_path = map_handle.get_map_for_path(self.now_map)
        self.now_map = up_map_path

    def down_map(self):
        """将当前地图切换为下级地图"""
        py_cmd.clr_cmd()
        character_position = cache.character_data[0].position
        down_map_scene_id = map_handle.get_map_scene_id_for_scene_path(self.now_map, character_position)
        self.now_map.append(down_map_scene_id)

    def move_now(self, scene_path: List[str]):
        """
        控制角色移动至指定场景
        Keyword arguments:
        scene_path -- 目标场景路径
        """
        py_cmd.clr_cmd()
        line_feed.draw()
        cache.wframe_mouse.w_frame_skip_wait_mouse = 1
        character_data: game_type.Character = cache.character_data[0]
        flag_map_open = False
        # 从大地图移动到另一个区块时，不关闭地图面板，且切换到下级地图面板
        if character_data.position[-2] != scene_path[-2] and scene_path[-1] == "0":
            flag_map_open = True
        character_move.own_charcter_move(scene_path)
        if flag_map_open:
            cache.now_panel_id = constant.Panel.SEE_MAP
            self.down_map()

class MoveMenuPanel:
    """
    快捷移动菜单面板
    Keyword arguments:
    width -- 绘制宽度
    """

    def __init__(self, width: int):
        """初始化绘制对象"""
        self.width: int = width
        """ 最大绘制宽度 """
        self.return_list: List[str] = []
        """ 监听的按钮列表 """
        self.now_type: int = 0
        """ 当前的移动菜单类型 """
        self.draw_list: List[draw.NormalDraw] = []
        """ 绘制的对象列表 """
        self.move_type_id_data: Dict[str, int] = {
            game_config.config_move_menu_type[i].name: i for i in game_config.config_move_menu_type
        }
        """ 移动类型名字对应配表id """

    def update(self):
        """更新绘制面板"""
        line = draw.LineDraw(".", self.width)
        self.draw_list = []
        self.return_list = []
        self.draw_list.append(line)
        menu_draw = panel.CenterDrawButtonListPanel()
        move_name_list = [
            game_config.config_move_menu_type[i].name for i in game_config.config_move_menu_type
        ]
        move_name_draw_list = [f"[{name}]" for name in move_name_list]
        menu_draw.set(
            move_name_draw_list,
            move_name_list,
            self.width,
            len(game_config.config_move_menu_type),
            move_name_draw_list[self.now_type],
            self.change_type,
        )
        self.draw_list.append(menu_draw)
        self.return_list.extend(menu_draw.return_list)

    def change_type(self, to_type: str):
        """
        改变当前快捷移动菜单类型
        Keyword arguments:
        to_type -- 指定的新类型id
        """
        self.now_type = self.move_type_id_data[to_type]
        py_cmd.clr_cmd()

    def draw(self):
        """绘制面板"""
        for now_draw in self.draw_list:
            now_draw.draw()
        line = draw.LineDraw("-.-", self.width)
        line.draw()

class MapSceneNameDraw:
    """
    绘制指定地图地图场景id对应场景名列表
    Keyword arguments:
    now_map -- 地图路径
    width -- 绘制宽度
    """

    def __init__(self, now_map: List[str], width: int):
        self.width: int = width
        """ 绘制的最大宽度 """
        self.now_map: List[str] = now_map
        """ 当前查看的地图坐标 """
        self.return_list: List[str] = []
        """ 当前面板的按钮返回 """
        self.end_index: int = 0
        """ 结束按钮id """

    def update(self, now_map: List[str], start_index: int):
        """
        更新当前面板对象
        Keyword arguments:
        now_map -- 当前地图
        start_index -- 起始按钮id
        """
        self.now_map = now_map

    def draw(self):
        """绘制面板"""
        self.return_list = []
        map_path_str = map_handle.get_map_system_path_str_for_list(self.now_map)
        map_data: game_type.Map = cache.map_data[map_path_str]
        path_edge = map_data.path_edge
        scene_id_list = list(path_edge.keys())
        if len(scene_id_list):
            character_data: game_type.Character = cache.character_data[0]
            character_scene_id = map_handle.get_map_scene_id_for_scene_path(
                self.now_map, character_data.position
            )
            scene_path = path_edge[character_scene_id].copy()
            if character_scene_id in scene_path:
                del scene_path[character_scene_id]
            draw_list = []
            for scene_id in scene_id_list:
                load_scene_data = map_handle.get_scene_data_for_map(map_path_str, scene_id)
                now_scene_path = map_handle.get_map_system_path_for_str(load_scene_data.scene_path)

                # now_id_text = f"{scene_id}:{load_scene_data.scene_name}"
                # 如果编号=0的话为出口，单独标注，其他的不标注序号
                if scene_id == character_scene_id:
                    continue
                elif scene_id == '0':
                    now_id_text = f"→0:{load_scene_data.scene_name}"
                else:
                    now_id_text = f"→{load_scene_data.scene_name}"

                now_draw = draw.LeftButton(
                    now_id_text, now_id_text, self.width, cmd_func=self.move_now, args=(now_scene_path,)
                )
                self.return_list.append(now_draw.return_text)
                draw_list.append(now_draw)
            draw_group = value_handle.list_of_groups(draw_list, 8)
            now_width_index = 0
            for now_draw_list in draw_group:
                if len(now_draw_list) > now_width_index:
                    now_width_index = len(now_draw_list)
            now_width = self.width / now_width_index
            for now_draw_list in draw_group:
                for now_draw in now_draw_list:
                    now_draw.width = now_width
                    now_draw.draw()
                line_feed.draw()
        self.end_index = len(scene_id_list) - 1

    def move_now(self, scene_path: List[str]):
        """
        控制角色移动至指定场景
        Keyword arguments:
        scene_path -- 目标场景路径
        """
        py_cmd.clr_cmd()
        line_feed.draw()
        cache.wframe_mouse.w_frame_skip_wait_mouse = 1
        character_move.own_charcter_move(scene_path)

class UsefulSceneNamePanel:
    """
    绘制功能地点快捷寻路场景按钮列表
    Keyword arguments:
    now_map -- 地图路径
    width -- 绘制宽度
    """

    def __init__(self, now_map: List[str], width: int):
        self.width: int = width
        """ 绘制的最大宽度 """
        self.now_map: List[str] = now_map
        """ 当前查看的地图坐标 """
        self.return_list: List[str] = []
        """ 当前面板的按钮返回 """
        self.end_index: int = 0
        """ 结束按钮id """
        self.handle_panel = panel.PageHandlePanel(
            self.get_path_list(), ScenePathNameMoveDraw, 20, 3, self.width, 1
        )
        self.end_index = self.handle_panel.end_index

    def get_path_list(self) -> list:
        """
        获取功能地点场景路径列表
        Return arguments:
        list -- 路径列表
        """
        character_data: game_type.Character = cache.character_data[0]
        # if character_data.classroom == "":
        #     class_room_path = []
        # else:
        #     class_room_path = map_handle.get_map_system_path_for_str(character_data.classroom)
        # office_room_path = character_data.officeroom
        # square_path = ["2"]
        # swim_path = ["14", "0"]
        # big_restaurant_path = ["10", "0", "0"]
        # small_restaurant_path = ["16", "0", "0"]
        # multi_media_class_room_a_path = ["1", "3", "1"]
        # multi_media_class_room_b_path = ["1", "3", "2"]
        # music_class_room_path = ["1", "4", "1"]
        # shop_path = ["11"]
        # dormitory_path = map_handle.get_map_system_path_for_str(character_data.dormitory)
        path_list = [
            # dormitory_path,
            # class_room_path,
            # office_room_path,
            # square_path,
            # swim_path,
            # big_restaurant_path,
            # small_restaurant_path,
            # multi_media_class_room_a_path,
            # multi_media_class_room_b_path,
            # music_class_room_path,
            # shop_path,
        ]
        path_list = [i for i in path_list if len(i)]
        return path_list

    def update(self, now_map: List[str], start_index: int):
        """
        更新当前面板对象
        Keyword arguments:
        now_map -- 当前地图
        start_index -- 起始按钮id
        """
        self.now_map = now_map
        self.handle_panel.text_list = self.get_path_list()
        self.handle_panel.button_start_id = start_index
        self.handle_panel.update()
        self.end_index = self.handle_panel.end_index

    def draw(self):
        """绘制面板"""
        self.handle_panel.draw()
        self.return_list = self.handle_panel.return_list

class ScenePathNameMoveDraw:
    """
    按场景路径绘制场景名和移动按钮
    Keyword arguments:
    text -- 场景路径
    width -- 最大宽度
    is_button -- 绘制按钮
    num_button -- 绘制数字按钮
    button_id -- 数字按钮的id
    """

    def __init__(self, text: List[str], width: int, is_button: bool, num_button: bool, button_id: int):
        """初始化绘制对象"""
        self.draw_text = ""
        """ 场景路径绘制的文本 """
        self.width: int = width
        """ 最大宽度 """
        self.is_button: bool = is_button
        """ 绘制按钮 """
        self.num_button: bool = num_button
        """ 绘制数字按钮 """
        self.button_id: int = button_id
        """ 数字按钮的id """
        self.button_return: str = str(button_id)
        """ 按钮返回值 """
        self.draw_list: List[draw.NormalDraw] = []
        """ 绘制的对象列表 """
        self.scene_path: List[str] = text
        """ 对应场景路径 """
        path_text = attr_text.get_scene_path_text(text)
        button_text = f"[{path_text}]"
        button_text = text_handle.align(button_text, text_width=width)
        name_draw = draw.Button(button_text, path_text, cmd_func=self.move_now)
        self.draw_text = button_text
        name_draw.width = width
        self.now_draw = name_draw
        """ 绘制的对象 """
        self.button_return = path_text

    def draw(self):
        """绘制对象"""
        self.now_draw.draw()

    def move_now(self):
        """
        控制角色移动至指定场景
        Keyword arguments:
        scene_path -- 目标场景路径
        """
        py_cmd.clr_cmd()
        line_feed.draw()
        cache.wframe_mouse.w_frame_skip_wait_mouse = 1
        character_move.own_charcter_move(self.scene_path)

# class entertainmentSceneNamePanel:
#     """
#     绘制娱乐地点对象所在场景快捷寻路按钮列表
#     Keyword arguments:
#     now_map -- 地图路径
#     width -- 绘制宽度
#     """

#     def __init__(self, now_map: List[str], width: int):
#         self.width: int = width
#         """ 绘制的最大宽度 """
#         self.now_map: List[str] = now_map
#         """ 当前查看的地图坐标 """
#         self.return_list: List[str] = []
#         """ 当前面板的按钮返回 """
#         character_data: game_type.Character = cache.character_data[0]
#         self.handle_panel = panel.PageHandlePanel(
#             [k for k in character_data.social_contact_data if character_data.social_contact_data[k]],
#             SocialSceneNameDraw,
#             20,
#             3,
#             self.width,
#             1,
#         )
#         self.end_index = self.handle_panel.end_index
#         """ 结束按钮id """

#     def update(self, now_map: List[str], start_index: int):
#         """
#         更新当前面板对象
#         Keyword arguments:
#         now_map -- 当前地
#         start_index -- 起始按钮id
#         """
#         self.now_map = now_map
#         self.handle_panel.button_start_id = start_index
#         self.handle_panel.update()
#         self.end_index = self.handle_panel.end_index

#     def draw(self):
#         """绘制面板"""
#         self.handle_panel.draw()
#         self.return_list = self.handle_panel.return_list

class SocialSceneNameDraw:
    """
    按角色id绘制角色所在场景名和移动按钮
    Keyword arguments:
    text -- 角色id
    width -- 最大宽度
    is_button -- 绘制按钮
    num_button -- 绘制数字按钮
    button_id -- 数字按钮的id
    """

    def __init__(self, text: int, width: int, is_button: bool, num_button: bool, button_id: int):
        """初始化绘制对象"""
        self.draw_text = ""
        """ 场景路径绘制文本 """
        self.width: int = width
        """ 最大宽度 """
        self.is_button: bool = is_button
        """ 绘制按钮 """
        self.num_button: bool = num_button
        """ 绘制数字按钮 """
        self.button_id: int = button_id
        """ 数字按钮的id """
        self.button_return: str = str(button_id)
        """ 按钮返回值 """
        self.draw_list: List[draw.NormalDraw] = []
        """ 绘制的对象列表 """
        character_data: game_type.Character = cache.character_data[text]
        self.scene_path: List[str] = character_data.position
        """ 角色所在场景 """
        path_text = attr_text.get_scene_path_text(self.scene_path)
        button_text = f"{character_data.name}:[{path_text}]"
        button_text = text_handle.align(button_text, text_width=width)
        name_draw = draw.Button(button_text, character_data.name, cmd_func=self.move_now)
        name_draw.width = width
        self.now_draw = name_draw
        """ 绘制的对象 """
        self.button_return = character_data.name

    def draw(self):
        """绘制对象"""
        self.now_draw.draw()

    def move_now(self):
        """
        控制角色移动至指定场景
        Keyword arguments:
        scene_path -- 目标场景路径
        """
        py_cmd.clr_cmd()
        line_feed.draw()
        cache.wframe_mouse.w_frame_skip_wait_mouse = 1
        character_move.own_charcter_move(self.scene_path)

class CollectionSceneNamePanel:
    """
    绘制收藏对象所在场景快捷寻路按钮列表
    Keyword arguments:
    now_map -- 地图路径
    width -- 绘制宽度
    """

    def __init__(self, now_map: List[str], width: int):
        self.width: int = width
        """ 绘制的最大宽度 """
        self.now_map: List[str] = now_map
        """ 当前查看的地图坐标 """
        self.return_list: List[str] = []
        """ 当前面板的按钮返回 """
        character_data: game_type.Character = cache.character_data[0]
        self.handle_panel = panel.PageHandlePanel(
            list(character_data.collection_character),
            SocialSceneNameDraw,
            10,
            2,
            self.width,
            1,
        )
        self.end_index = self.handle_panel.end_index
        """ 结束按钮id """

    def update(self, now_map: List[str], start_index: int):
        """
        更新当前面板对象
        Keyword arguments:
        now_map -- 当前地图
        start_index -- 起始按钮id
        """
        self.now_map = now_map
        self.handle_panel.button_start_id = start_index
        self.handle_panel.update()
        self.end_index = self.handle_panel.end_index

    def draw(self):
        """绘制面板"""
        self.handle_panel.draw()
        self.return_list = self.handle_panel.return_list