from typing import Dict, List, Optional, Tuple, Union

from flask import (
    abort, g, jsonify, render_template, request, make_response, Response
)
from werkzeug.exceptions import (
    BadRequest, Forbidden, HTTPException, InternalServerError, NotFound
    )
from plot_weather import (BAD_REQUEST_IMAGE_DATA,
                          INTERNAL_SERVER_ERROR_IMAGE_DATA, DebugOutRequest,
                          app, app_logger, app_logger_debug)
from plot_weather.dao.weathercommon import WEATHER_CONF
from plot_weather.dao.weatherdao import WeatherDao
from plot_weather.dao.devicedao import DeviceDao, DeviceRecord
from plot_weather.db.sqlite3conv import DateFormatError, strdate2timestamp
from plot_weather.plotter.plotterweather import (
    ImageDateType, gen_plot_image, ImageDateParams, ParamKey
)
from werkzeug.datastructures import Headers, MultiDict
import psycopg2
from psycopg2.pool import SimpleConnectionPool
from psycopg2.extensions import connection
import plot_weather.util.dateutil as date_util

APP_ROOT: str = app.config["APPLICATION_ROOT"]

# エラーメッセージの内容 ※messages.confで定義
MSG_REQUIRED: str = app.config["MSG_REQUIRED"]
MSG_INVALID: str = app.config["MSG_INVALID"]
MSG_NOT_FOUND: str = app.config["MSG_NOT_FOUND"]
# ヘッダー
# トークン ※携帯端末では必須, 一致 ※ない場合は不一致とみなす
# messages.conf で定義済み
# 端末サイズ情報 ※携帯端末では必須, 形式は 幅x高さx密度
MSG_PHONE_IMG: str = "phone image size"
REQUIRED_PHONE_IMG: str = f"401,{MSG_PHONE_IMG} {MSG_REQUIRED}"
INVALID_PHONE_IMG: str = f"402,{MSG_PHONE_IMG} {MSG_INVALID}"
# リクエストパラメータ
PARAM_DEVICE: str = "device_name"
PARAM_START_DAY: str = "start_day"
PARAM_BOFORE_DAYS: str = "before_days"
PARAM_YEAR_MONTH: str = "year_month"
# リクエストパラメータエラー時のコード: 421番台以降
# デバイス名: 必須, 長さチェック (1-20byte), 未登録
DEVICE_LENGTH: int = 20
#  デバイスリスト取得クリエスと以外の全てのリクエスト
REQUIRED_DEVICE: str = f"421,{PARAM_DEVICE} {MSG_REQUIRED}"
INVALIDD_DEVICE: str = f"422,{PARAM_DEVICE} {MSG_INVALID}"
DEVICE_NOT_FOUND: str = f"423,{PARAM_DEVICE} {MSG_NOT_FOUND}"
# 期間指定画像取得リクエスト
#  (1)検索開始日["start_day"]: 任意 ※未指定ならシステム日付を検索開始日とする
#     日付形式(ISO8601: YYYY-mm-dd), 10文字一致
INVALID_START_DAY: str = f"431,{PARAM_START_DAY} {MSG_INVALID}"
#  (2)検索開始日から N日前 (1,2,3,7日): 必須
REQUIRED_BOFORE_DAY: str = f"433,{PARAM_BOFORE_DAYS} {MSG_REQUIRED}"
INVALID_BOFORE_DAY: str = f"434,{PARAM_BOFORE_DAYS} {MSG_INVALID}"
# 月間指定画像取得リクエスト
#   年月: 必須, 形式(YYYY-mm), 7文字一致
REQUIRED_YEAR_MONTH: str = f"435,{PARAM_YEAR_MONTH} {MSG_REQUIRED}"
INVALID_YEAR_MONTH: str = f"436,{PARAM_YEAR_MONTH} {MSG_INVALID}"

# エラーメッセージを格納する辞書オブジェクト定義
MSG_DESCRIPTION: str = "error_message"
# 固定メッセージエラー辞書オブジェクト
ABORT_DICT_UNMATCH_TOKEN: Dict[str, str] = {MSG_DESCRIPTION: app.config["UNMATCH_TOKEN"]}
# 可変メッセージエラー辞書オブジェクト: ""部分を置き換える
ABORT_DICT_BLANK_MESSAGE: Dict[str, str] = {MSG_DESCRIPTION: ""}

def get_connection() -> connection:
    if 'db' not in g:
        conn_pool: SimpleConnectionPool = app.config["postgreSQL_pool"]
        g.db: connection = conn_pool.getconn()
        g.db.set_session(readonly=True, autocommit=True)
        if app_logger_debug:
            app_logger.debug(f"g.db:{g.db}")
    return g.db

@app.teardown_appcontext
def close_connection(exception=None) -> None:
    db: connection = g.pop('db', None)
    if app_logger_debug:
        app_logger.debug(f"db:{db}")
    if db is not None:
        app.config["postgreSQL_pool"].putconn(db)

@app.route(APP_ROOT, methods=["GET"])
def index() -> str:
    """本日データ表示画面 (初回リクエストのみ)

    :return: 本日データ表示HTMLページ (matplotlibでプロットした画像含む)
    """
    if app_logger_debug:
        app_logger.debug(request.path)
    try:
        conn: connection = get_connection()
        # 年月日リスト取得
        dao = WeatherDao(conn, logger=app_logger)
        yearMonthList: List[str] = dao.getGroupbyMonths(
            device_name=WEATHER_CONF["DEVICE_NAME"],
            start_date=WEATHER_CONF["STA_YEARMONTH"],
        )
        if app_logger_debug:
            app_logger.debug(f"yearMonthList:{yearMonthList}")
        # 本日データプロット画像取得
        image_date_params = ImageDateParams(ImageDateType.TODAY)
        img_base64_encoded: str = gen_plot_image(
            conn, image_date_params=image_date_params, logger=app_logger
        )
    except Exception as exp:
        app_logger.error(exp)
        abort(InternalServerError.codde, InternalServerError(original_exception=exp))

    strToday: str = app.config.get("STR_TODAY", "")
    titleSuffix: str = app.config.get("TITLE_SUFFIX", "")
    defaultMainTitle: str = strToday + titleSuffix
    return render_template(
        "showplotweather.html",
        ip_host=app.config["SERVER_NAME"],
        app_root_url=APP_ROOT,
        path_get_today="/gettoday",
        path_get_month="/getmonth/",
        str_today=strToday,
        title_suffix=titleSuffix,
        info_today_update_interval=app.config.get("INFO_TODAY_UPDATE_INTERVAL"),
        default_main_title=defaultMainTitle,
        year_month_list=yearMonthList,
        img_src=img_base64_encoded,
    )

@app.route("/plot_weather/gettoday", methods=["GET"])
def getTodayImage() -> Response:
    """本日データ取得リクエスト(2回以降) JavaScriptからのリクエスト想定

    :return: jSON形式(matplotlibでプロットした画像データ(形式: png)のbase64エンコード済み文字列)
         (出力内容) jSON('data:image/png;base64,... base64encoded data ...')
    """
    if app_logger_debug:
        app_logger.debug(request.path)
    try:
        conn: connection = get_connection()
        # 本日データプロット画像取得
        image_date_params = ImageDateParams(ImageDateType.TODAY)
        img_base64_encoded: str = gen_plot_image(
            conn, image_date_params, logger=app_logger
        )
    except psycopg2.Error as db_err:
        app_logger.error(db_err)
        abort(InternalServerError.code, _set_errormessage(f"559,{db_err}"))
    except Exception as exp:
        app_logger.error(exp)
        return _createErrorImageResponse(InternalServerError.code)

    return _createImageResponse(img_base64_encoded)

@app.route("/plot_weather/getmonth/<yearmonth>", methods=["GET"])
def getMonthImage(yearmonth) -> Response:
    """要求された年月の月間データ取得

    :param yearmonth str: 年月 (例) 2022-01
    :return: jSON形式(matplotlibでプロットした画像データ(形式: png)のbase64エンコード済み文字列)
         (出力内容) jSON('data:image/png;base64,... base64encoded data ...')
    """
    if app_logger_debug:
        app_logger.debug(request.path)
    try:
        # リクエストパラメータの妥当性チェック: "YYYY-mm" + "-01"
        chk_yyyymmdd = yearmonth + "-01"
        # 日付チェック(YYYY-mm-dd): 日付不正の場合例外スロー
        strdate2timestamp(chk_yyyymmdd, raise_error=True)
        conn: connection = get_connection()
        # 指定年月(year_month)データプロット画像取得
        image_date_params = ImageDateParams(ImageDateType.YEAR_MONTH)
        param: Dict[ParamKey, str] = image_date_params.getParam()
        param[ParamKey.YEAR_MONTH] = yearmonth
        image_date_params.setParam(param)
        img_base64_encoded: str = gen_plot_image(
            conn, image_date_params, logger=app_logger
        )
    except DateFormatError as dfe:
        # BAD Request
        app_logger.warning(dfe)
        return _createErrorImageResponse(BadRequest.code)
    except psycopg2.Error as db_err:
        # DBエラー
        app_logger.error(db_err)
        abort(InternalServerError.code, _set_errormessage(f"559,{db_err}"))
    except Exception as exp:
        # バグ, DBサーバーダウンなど想定
        app_logger.error(exp)
        return _createErrorImageResponse(InternalServerError.code)

    return _createImageResponse(img_base64_encoded)

@app.route("/plot_weather/getlastdataforphone", methods=["GET"])
def getLastDataForPhone() -> Response:
    """最新の気象データを取得する (スマートホン専用)
       [仕様変更] 2023-09-09
         (1) リクエストパラメータ追加
            device_name: デバイス名 ※必須

    :param: request parameter: device_name="xxxxx"
    """
    if app_logger_debug:
        app_logger.debug(request.path)
        # Debug output request.headers or request.arg or both
        _debugOutRequestObj(request, debugout=DebugOutRequest.HEADERS)

    # トークン必須
    headers: Headers = request.headers
    if not _matchToken(headers):
        abort(Forbidden.code, ABORT_DICT_UNMATCH_TOKEN)

    # デバイス名必須
    param_device_name: str = _checkDeviceName(request.args)
    try:
        conn: connection = get_connection()
        # 現在時刻時点の最新の気象データ取得
        dao = WeatherDao(conn, logger=app_logger)
        rec_count: int
        row: Optional[Tuple[str, float, float, float, float]]
        # デバイス名に対応する最新のレコード取得
        row = dao.getLastData(device_name=param_device_name)
        if row:
            rec_count = 1
            measurement_time, temp_out, temp_in, humid, pressure = row
            return _responseLastDataForPhone(
                measurement_time, temp_out, temp_in, humid, pressure, rec_count)
        else:
            # デバイス名に対応するレコード無し
            rec_count = 0
            return _responseLastDataForPhone(None, None, None, None, None, rec_count)
    except psycopg2.Error as db_err:
        app_logger.error(db_err)
        abort(InternalServerError.code, _set_errormessage(f"559,{db_err}"))
    except Exception as exp:
        app_logger.error(exp)
        abort(InternalServerError.code, description=str(exp))

@app.route("/plot_weather/getfirstregisterdayforphone", methods=["GET"])
def getFirstRegisterDayForPhone() -> Response:
    """デバイスの観測データの初回登録日を取得する (スマートホン専用)
       [仕様追加] 2023-09-13

           :param: request parameter: device_name="xxxxx"
    """
    if app_logger_debug:
        app_logger.debug(request.path)
        # Debug output request.headers or request.arg or both
        _debugOutRequestObj(request, debugout=DebugOutRequest.HEADERS)

    # トークン必須
    headers: Headers = request.headers
    if not _matchToken(headers):
        abort(Forbidden.code, ABORT_DICT_UNMATCH_TOKEN)

    # デバイス名必須
    param_device_name: str = _checkDeviceName(request.args)
    try:
        conn: connection = get_connection()
        dao = WeatherDao(conn, logger=app_logger)
        # デバイス名に対応する初回登録日取得
        first_register_day: Optional[str] = dao.getFisrtRegisterDay(param_device_name)
        if app_logger_debug:
            app_logger.debug(f"first_register_day[{type(first_register_day)}]: {first_register_day}")
        if first_register_day:
            return _responseFirstRegisterDayForPhone(first_register_day, 1)
        else:
            # デバイス名に対応するレコード無し
            return _responseFirstRegisterDayForPhone(None, 0)
    except psycopg2.Error as db_err:
        app_logger.error(db_err)
        abort(InternalServerError.code, _set_errormessage(f"559,{db_err}"))
    except Exception as exp:
        app_logger.error(exp)
        abort(InternalServerError.code, description=str(exp))

@app.route("/plot_weather/gettodayimageforphone", methods=["GET"])
def getTodayImageForPhone() -> Response:
    """本日データ画像取得リクエスト (スマートホン専用)
       [仕様変更] 2023-09-09
         (1) リクエストパラメータ追加
            device_name: デバイス名 ※必須
         (2) レスポンスにレコード件数を追加 ※0件エラーの抑止

    :param: request parameter: device_name="xxxxx"
    :return: jSON形式(matplotlibでプロットした画像データ(形式: png)のbase64エンコード済み文字列)
         (出力内容) jSON('data:': 'img_src':'image/png;base64,... base64encoded data ...',
                         'rec_count':xxx)
    """
    if app_logger_debug:
        app_logger.debug(request.path)
        _debugOutRequestObj(request, debugout=DebugOutRequest.HEADERS)

    # トークン必須
    headers: Headers = request.headers
    if not _matchToken(headers):
        abort(Forbidden.code, ABORT_DICT_UNMATCH_TOKEN)

    # デバイス名必須
    param_device_name: str = _checkDeviceName(request.args)

    # 表示領域サイズ+密度は必須: 形式(横x縦x密度)
    str_img_size: str = _checkPhoneImageSize(headers)
    try:
        conn: connection = get_connection()
        image_date_params = ImageDateParams(ImageDateType.TODAY)
        param: Dict[ParamKey, str] = image_date_params.getParam()
        param[ParamKey.PHONE_SIZE] = str_img_size
        image_date_params.setParam(param)
        rec_count: int
        img_base64_encoded: str
        rec_count, img_base64_encoded = gen_plot_image(
            conn, param_device_name, image_date_params, logger=app_logger
        )
        return _responseImageForPhone(rec_count, img_base64_encoded)
    except psycopg2.Error as db_err:
        app_logger.error(db_err)
        abort(InternalServerError.code, _set_errormessage(f"559,{db_err}"))
    except Exception as exp:
        app_logger.error(exp)
        abort(InternalServerError.code, description=str(exp))

@app.route("/plot_weather/getbeforedaysimageforphone", methods=["GET"])
def getBeforeDateImageForPhone() -> Response:
    """過去経過日指定データ画像取得リクエスト (スマートホン専用)
       [仕様変更] 2023-09-09
         (1) リクエストパラメータ追加
            device_name: デバイス名 ※必須
            start_day: 検索開始日(iso8601形式) ※任意
         (2) レスポンスにレコード件数を追加 ※0件エラーの抑止

    :param: request parameter: ?device_name=xxxxx&start_day=2023-05-01&before_days=(2|3|7)
    :return: jSON形式(matplotlibでプロットした画像データ(形式: png)のbase64エンコード済み文字列)
         (出力内容) jSON('data:': 'img_src':'image/png;base64,... base64encoded data ...',
                         'rec_count':xxx)
    """
    if app_logger_debug:
        app_logger.debug(request.path)
        _debugOutRequestObj(request, debugout=DebugOutRequest.BOTH)

    # トークン必須
    headers = request.headers
    if not _matchToken(headers):
        abort(Forbidden.code, ABORT_DICT_UNMATCH_TOKEN)

    # デバイス名 ※必須チェック
    param_device_name: str = _checkDeviceName(request.args)
    # 検索開始日 ※任意、指定されている場合はISO8601形式チェック
    str_start_day: Optional[str] = _checkStartDay(request.args)
    if str_start_day is None:
        # 検索開始日がない場合は当日を設定
        str_start_day = date_util.getTodayIsoDate()
    # Check before_days query parameter
    str_before_days: str = _checkBeforeDays(request.args)

    # 表示領域サイズ+密度は必須: 形式(横x縦x密度)
    str_img_size: str = _checkPhoneImageSize(headers)
    try:
        conn: connection = get_connection()
        image_date_params = ImageDateParams(ImageDateType.RANGE)
        param: Dict[ParamKey, str] = image_date_params.getParam()
        param[ParamKey.START_DAY] = str_start_day
        param[ParamKey.BEFORE_DAYS] = str_before_days
        param[ParamKey.PHONE_SIZE] = str_img_size
        image_date_params.setParam(param)
        rec_count: int
        img_base64_encoded: str
        rec_count, img_base64_encoded = gen_plot_image(
            conn, param_device_name, image_date_params, logger=app_logger
        )
        return _responseImageForPhone(rec_count,img_base64_encoded)
    except psycopg2.Error as db_err:
        app_logger.error(db_err)
        abort(InternalServerError.code, _set_errormessage(f"559,{db_err}"))
    except Exception as exp:
        app_logger.error(exp)
        abort(InternalServerError.code, description=str(exp))

@app.route("/plot_weather/get_devices", methods=["GET"])
def getDevices() -> Response:
    """センサーディバイスリスト取得リクエスト

    :return: JSON形式(idを除くセンサーディバイスリスト)
         (出力内容) JSON({"data":{"devices":[...]}')
    """
    if app_logger_debug:
        app_logger.debug(request.path)

    devices_with_dict: List[Dict]
    try:
        conn: connection = get_connection()
        dao: DeviceDao = DeviceDao(conn, logger=app_logger)
        devices: List[DeviceRecord] = dao.get_devices()
        devices_with_dict = DeviceDao.to_dict_without_id(devices)
        resp_obj: Dict[str, Dict] = {
            "data": {"devices": devices_with_dict},
            "status": {"code": 0, "message": "OK"}
        }
        return _make_respose(resp_obj, 200)
    except psycopg2.Error as db_err:
        app_logger.error(db_err)
        abort(InternalServerError.code, _set_errormessage(f"559,{db_err}"))
    except Exception as exp:
        app_logger.error(exp)
        abort(InternalServerError.code, description=str(exp))

def _debugOutRequestObj(request, debugout=DebugOutRequest.ARGS) -> None:
    if debugout == DebugOutRequest.ARGS or debugout == DebugOutRequest.BOTH:
        app_logger.debug(f"reqeust.args: {request.args}")
    if debugout == DebugOutRequest.HEADERS or debugout == DebugOutRequest.BOTH:
        app_logger.debug(f"request.headers: {request.headers}")

def _matchToken(headers: Headers) -> bool:
    """トークン一致チェック
    :param headers: request header
    :return: if match token True, not False.
    """
    token_value: str = app.config.get("HEADER_REQUEST_PHONE_TOKEN_VALUE", "!")
    req_token_value: Optional[str] = headers.get(
        key=app.config.get("HEADER_REQUEST_PHONE_TOKEN_KEY", "!"),
        type=str,
        default=""
    )
    if req_token_value != token_value:
        app_logger.warning("Invalid request token!")
        return False
    return True

def _checkPhoneImageSize(headers: Headers) -> str:
    """
    ヘッダーに表示領域サイズ+密度([width]x[height]x[density])をつけてくる
    ※1.トークンチェックを通過しているのでセットされている前提で処理
    ※2.途中でエラー (Androidアプリ側のBUG) ならExceptionで補足されJSONでメッセージが返却される
    :param headers: request header
    :return: (imageWidth, imageHeight, density)
    """
    img_size: str = headers.get(
        app.config.get("HEADER_REQUEST_IMAGE_SIZE_KEY", ""), type=str, default=""
    )
    if app_logger_debug:
        app_logger.debug(f"Phone imgSize: {img_size}")
    if len(img_size) == 0:
        abort(BadRequest.code, _set_errormessage(REQUIRED_PHONE_IMG))

    sizes: List[str] = img_size.split("x")
    try:
        img_wd: int = int(sizes[0])
        img_ht: int = int(sizes[1])
        density: float = float(sizes[2])
        if app_logger_debug:
            app_logger.debug(f"imgWd: {img_wd}, imgHt: {img_ht}, density: {density}")
        return img_size
    except Exception as exp:
        # ログには例外メッセージ
        app_logger.warning(f"[phone image size] {exp}")
        abort(BadRequest.code, _set_errormessage(INVALID_PHONE_IMG))

def _checkBeforeDays(args: MultiDict) -> str:
    # QueryParameter: before_days in (1,2,3,7)
    # before_days = args.get("before_days", default=-1, type=int)
    # args.get(key): keyが無い場合も キーが有る場合で数値以外でも -1 となり必須チェックができない
    # before_days = args.pop("before_days"): TypeError: 'ImmutableMultiDict' objects are immutable
    if len(args.keys()) == 0 or PARAM_BOFORE_DAYS not in args.keys():
        abort(BadRequest.code, _set_errormessage(REQUIRED_BOFORE_DAY))

    before_days = args.get(PARAM_BOFORE_DAYS, default=-1, type=int)
    if before_days not in [1,2,3,7]:
        abort(BadRequest.code, _set_errormessage(INVALID_BOFORE_DAY))

    return  str(before_days)

def _checkDeviceName(args: MultiDict) -> str:
    """デバイス名チェック
        パラメータなし: abort(BadRequest)
        該当レコードなし: abort(NotFound)
    return デバイス名
    """
    # 必須チェック
    if len(args.keys()) == 0 or PARAM_DEVICE not in args.keys():
        abort(BadRequest.code, _set_errormessage(REQUIRED_DEVICE))

    # 長さチェック: 1 - 20
    param_device_name: str = args.get(PARAM_DEVICE, default="", type=str)
    chk_size: int = len(param_device_name)
    if chk_size < 1 or chk_size > DEVICE_LENGTH:
        abort(BadRequest.code, _set_errormessage(INVALIDD_DEVICE))

    # 存在チェック
    if app_logger_debug:
        app_logger.debug("requestParam.device_name: " + param_device_name)

    exists: bool = False
    try:
        conn: connection = get_connection()
        dao: DeviceDao = DeviceDao(conn, logger=app_logger)
        exists = dao.exists(param_device_name)
    except Exception as exp:
        app_logger.error(exp)
        abort(InternalServerError.code, description=str(exp))

    if exists is True:
        return param_device_name
    else:
        abort(BadRequest.code, _set_errormessage(DEVICE_NOT_FOUND))

def _checkStartDay(args: MultiDict) -> Optional[str]:
    """検索開始日の形式チェック
        パラメータなし: OK
        パラメータ有り: ISO8601形式チェック
    return 検索開始日 | None
    """
    if len(args.keys()) == 0 or PARAM_START_DAY not in args.keys():
        return None

    # 形式チェック
    param_start_day: str = args.get(PARAM_START_DAY, default="", type=str)
    if app_logger_debug:
        app_logger.debug(f"start_day: {param_start_day}")
    valid: bool = date_util.checkIso8601Date(param_start_day)
    if valid is True:
        return param_start_day
    else:
        # 不正パラメータ
        abort(BadRequest.code, _set_errormessage(INVALID_START_DAY))

def _createImageResponse(img_src: str) -> Response:
    """画像レスポンスを返却する (JavaScript用)"""
    resp_obj = {"status": "success", "data": {"img_src": img_src}}
    return _make_respose(resp_obj, 200)

def _createErrorImageResponse(err_code) -> Response:
    """エラー画像レスポンスを返却する (JavaScript用)"""
    resp_obj = {"status": "error", "code": err_code}
    if err_code == BadRequest.code:
        resp_obj["data"] = {"img_src": BAD_REQUEST_IMAGE_DATA}
    elif err_code == InternalServerError.code:
        resp_obj["data"] = {"img_src": INTERNAL_SERVER_ERROR_IMAGE_DATA}
    return _make_respose(resp_obj, err_code)

def _responseLastDataForPhone(
        mesurement_time: str,
        temp_out: float,
        temp_in: float,
        humid: float,
        pressure: float,
        rec_count: int
        ) -> Response:
    """気象データの最終レコードを返却する (スマホアプリ用)"""
    resp_obj: Dict[str, Dict[str, Union[str, float]]] = {
        "status":
            {"code": 0, "message": "OK"},
        "data": {
            "measurement_time": mesurement_time,
            "temp_out": temp_out,
            "temp_in": temp_in,
            "humid": humid,
            "pressure": pressure,
            "rec_count": rec_count
        }
    }
    return _make_respose(resp_obj, 200)

def _responseFirstRegisterDayForPhone(
        first_day: Optional[str],
        rec_count: int
        ) -> Response:
    """気象データの初回登録日を返却する (スマホアプリ用)"""
    resp_obj: Dict[str, Dict[str, Union[str, int]]] = {
        "status":
            {"code": 0, "message": "OK"},
        "data": {
            "first_register_day": first_day,
            "rec_count": rec_count
        }
    }
    return _make_respose(resp_obj, 200)

def _responseImageForPhone(rec_count: int, img_src: str) -> Response:
    """Matplotlib生成画像を返却する (スマホアプリ用)
       [仕様変更] 2023-09-09
         レスポンスにレコード件数を追加 ※0件エラーの抑止
    """
    resp_obj: Dict[str, Dict[str, Union[int, str]]] = {
        "status": {"code": 0, "message": "OK"},
        "data": {
            "img_src": img_src,
            "rec_count": rec_count
         }
    }
    return _make_respose(resp_obj, 200)

def _set_errormessage(message: str) -> Dict:
    ABORT_DICT_BLANK_MESSAGE[MSG_DESCRIPTION] = message
    return ABORT_DICT_BLANK_MESSAGE

# Request parameter check error.
@app.errorhandler(BadRequest.code)
# Token error.
@app.errorhandler(Forbidden.code)
# Device not found.
@app.errorhandler(NotFound.code)
@app.errorhandler(InternalServerError.code)
def error_handler(error: HTTPException) -> Response:
    app_logger.warning(f"error_type:{type(error)}, {error}")
    # Bugfix: 2023-09-06
    err_msg: str
    if isinstance(error.description, dict):
        # アプリが呼び出すabort()の場合は辞書オブジェクト
        err_msg = error.description["error_message"]
    else:
        # Flaskが出す場合は HTTPException)
        err_msg = error.description
    resp_obj: Dict[str, Dict[str, Union[int, str]]] = {
        "status": {"code": error.code, "message": err_msg}
    }
    return _make_respose(resp_obj, error.code)

def _make_respose(resp_obj: Dict, resp_code) -> Response:
    response = make_response(jsonify(resp_obj), resp_code)
    response.headers["Content-Type"] = "application/json"
    return response


import numpy
from sklearn.metrics import cohen_kappa_score, classification_report
import torch
from torch.autograd import Variable
from tqdm import tqdm
import torch.nn as nn
from sklearn.metrics import cohen_kappa_score, classification_report
from models import FitNet_4
from torch import optim
import numpy as np

def evaluation(test_dataloader, model, class_names, epoch, criterion):
    eval_loss_list = []
    eval_acc = 0
    pred_list = []
    GT_list = []
    pbar_test = tqdm(test_dataloader, total=len(test_dataloader))
    with torch.no_grad():
        for image, label in pbar_test:
            image = Variable(image).cuda()
            label = Variable(label).cuda()
            out = model(image)
            loss = criterion(out, label)
            eval_loss_list.append(loss.item())
            _, pred = torch.max(out, 1)
            num_correct = (pred == label).sum()
            pred_list.extend(pred.cpu().numpy().tolist())
            GT_list.extend(label.cpu().numpy().tolist())
            eval_acc += num_correct.item()
            pbar_test.set_description("Testing:epoch{} loss:{}".format(epoch, loss.item()))
    epoch_test_acc = eval_acc / len(pbar_test)
    print(
        "Testing:epoch{} finished! Total loss:{}".format(epoch,
                                                                      np.mean(eval_loss_list)))
    print(classification_report(y_true=GT_list, y_pred=pred_list, target_names=class_names))
    kappa = cohen_kappa_score(y1=pred_list, y2=GT_list)
    print("Kappa:{}".format(kappa))


from threading import Thread
import time

import classifierAlexa
import classifier_pyqt5

def main():
    try:
        classifierAlexa_thread = Thread(target=classifierAlexa.app.run)
        classifierAlexa_thread.start()

        time.sleep(1)
        classifier_thread = Thread(target=classifier_pyqt5.startApp())
        classifier_thread.start()
    except Exception:
        print("Unknown exception occurred!")
        raise

if __name__ == '__main__':
    main()


import numpy as np
from PIL import Image

def normalize(x):
    min = np.min(x)
    max = np.max(x)
    print(min,max)
    return ((x - min)/(max - min) * 255).astype(int)

W=np.load('./data/W.npy')
b=np.load('./data/b.npy')
zero = np.zeros(W.shape)
nag = normalize(np.minimum(W,0))
pos = normalize(np.maximum(W,0))
print("shape", nag[:,0].reshape((28,28)).shape)
ns = nag[:,0].reshape((28,28))
print(np.expand_dims(ns, axis=2))
'''w, h = 512, 512
data = np.zeros((h, w, 3), dtype=np.uint8)
data[256, 256] = [255, 0, 0]
img = Image.fromarray(data, 'RGB')
img.save('my.png')
img.show()'''

#print(nag[nag != 255], pos[pos != 1])

#print(nag[nag != 0],pos[pos != 0], pos_m, nag_m, np.sum(nag), np.sum(pos))
#print(W.shape)
#print(W[W != 0])


import cv2 as cv
import numpy as np

from process import Resize, NormalizeImage

class PicoDetProcess():
    def __init__(self,
                 trainsize=[320,320],
                 mean=[0.485,0.456,0.406],
                 std=[0.229,0.224,0.225],
                 score_threshold=0.4,
                 nms_threshold=0.5
                 ):
        self.score_threshold = score_threshold
        self.nms_threshold = nms_threshold
        self.resize =Resize(trainsize)
        self.normalizeImage = NormalizeImage(mean = mean,std =std)

    def preprocess(self, images):
        input_im_lst = []
        input_im_info_lst = []
        for im in images:
            im, im_info = self.processim(im)
            input_im_lst.append(im)
            input_im_info_lst.append(im_info)
        inputs = self.create_inputs(input_im_lst, input_im_info_lst)
        return inputs

    def create_inputs(self, imgs, im_info):
        """generate input for different model type
        Args:
            imgs (list(numpy)): list of images (np.ndarray)
            im_info (list(dict)): list of image info
        Returns:
            inputs (dict): input of model
        """
        inputs = {}
        im_shape = []
        scale_factor = []
        if len(imgs) == 1:
            inputs['image'] = np.array((imgs[0], )).astype('float32')
            inputs['im_shape'] = np.array(
                (im_info[0]['im_shape'], )).astype('float32')
            inputs['scale_factor'] = np.array(
                (im_info[0]['scale_factor'], )).astype('float32')
            return inputs

        for e in im_info:
            im_shape.append(np.array((e['im_shape'], )).astype('float32'))
            scale_factor.append(np.array((e['scale_factor'], )).astype('float32'))

        inputs['im_shape'] = np.concatenate(im_shape, axis=0)
        inputs['scale_factor'] = np.concatenate(scale_factor, axis=0)

        imgs_shape = [[e.shape[1], e.shape[2]] for e in imgs]
        max_shape_h = max([e[0] for e in imgs_shape])
        max_shape_w = max([e[1] for e in imgs_shape])
        padding_imgs = []
        for img in imgs:
            im_c, im_h, im_w = img.shape[:]
            padding_im = np.zeros(
                (im_c, max_shape_h, max_shape_w), dtype=np.float32)
            padding_im[:, :im_h, :im_w] = img
            padding_imgs.append(padding_im)
        inputs['image'] = np.stack(padding_imgs, axis=0)
        return inputs

    def processim(self, im):
            # process image by preprocess_ops
        im_info = {
            'scale_factor': np.array(
                [1., 1.], dtype=np.float32),
            'im_shape': None,
        }
        im_info['im_shape'] = np.array(im.shape[:2], dtype=np.float32)
        im_info['scale_factor'] = np.array([1., 1.], dtype=np.float32)
        im = cv.cvtColor(im, cv.COLOR_BGR2RGB)
        im,im_info = self.resize(im,im_info)
        im,im_info = self.normalizeImage(im,im_info)
        # im = im.transpose((2, 0, 1)).copy()
        return im, im_info

    def postprocess(self, inputs, scale_factor):
        bboxs = inputs['bboxes']
        scores = inputs['scores']
        bbox,score = self.nms(bboxs[0],scores[0][0])
        for box in bbox:
            box[0] = box[0] / scale_factor[1]
            box[1] = box[1] / scale_factor[0]
            box[2] = box[2] / scale_factor[1]
            box[3] = box[3] / scale_factor[0]
        outputs =  dict(bboxes=np.array(bbox), scores=np.array(score))
        return outputs

    def nms(self, bounding_boxes, confidence_score):
        '''
        :param bounding_boxes: 候选框列表，[左上角坐标, 右下角坐标], [min_x, min_y, max_x, max_y], 原点在图像左上角
        :param confidence_score: 候选框置信度
        :param threshold: IOU阈值
        :return: 抑制后的bbox和置信度
        '''
        picked = []

        for i in range(confidence_score.shape[-1]):
            if confidence_score[i] > self.score_threshold:
                    picked.append(i)
        bounding_boxes = bounding_boxes[picked,:]
        confidence_score = confidence_score[picked]
        # 如果没有bbox，则返回空列表
        if len(bounding_boxes) == 0:
            return [], []

        # bbox转为numpy格式方便计算
        boxes = np.array(bounding_boxes)

        # 分别取出bbox的坐标
        start_x = boxes[:, 0]
        start_y = boxes[:, 1]
        end_x = boxes[:, 2]
        end_y = boxes[:, 3]

        # 置信度转为numpy格式方便计算
        score = np.array(confidence_score)  # [0.9  0.75 0.8  0.85]

        # 筛选后的bbox和置信度
        picked_boxes = []
        picked_score = []

        # 计算每一个框的面积
        areas = (end_x - start_x + 1) * (end_y - start_y + 1)

        # 将score中的元素从小到大排列，提取其对应的index(索引)，然后输出到order
        order = np.argsort(score)   # [1 2 3 0]

        # Iterate bounding boxes
        while order.size > 0:

            # The index of largest confidence score
            # 取出最大置信度的索引
            index = order[-1]

            # Pick the bounding box with largest confidence score
            # 将最大置信度和最大置信度对应的框添加进筛选列表里
            picked_boxes.append(bounding_boxes[index])
            picked_score.append(confidence_score[index])

            # 求置信度最大的框与其他所有框相交的长宽，为下面计算相交面积做准备
            # 令左上角为原点，
            # 两个框的左上角坐标x取大值，右下角坐标x取小值，小值-大值+1==相交区域的长度
            # 两个框的左上角坐标y取大值，右下角坐标y取小值，小值-大值+1==相交区域的高度
            # 这里可以在草稿纸上画个图，清晰明了
            x1 = np.maximum(start_x[index], start_x[order[:-1]])
            x2 = np.minimum(end_x[index], end_x[order[:-1]])
            y1 = np.maximum(start_y[index], start_y[order[:-1]])
            y2 = np.minimum(end_y[index], end_y[order[:-1]])

            # 计算相交面积，当两个框不相交时，w和h必有一个为0，面积也为0
            w = np.maximum(0.0, x2 - x1 + 1)
            h = np.maximum(0.0, y2 - y1 + 1)
            intersection = w * h

            # 计算IOU
            ratio = intersection / (areas[index] + areas[order[:-1]] - intersection)

            # 保留小于阈值的框的索引
            left = np.where(ratio < self.nms_threshold)
            # 根据该索引修正order中的索引（order里放的是按置信度从小到大排列的索引）
            order = order[left]

        return picked_boxes, picked_score


import copy
from typing import List, Optional

def deep_merge_dicts(original: dict, new_dict: dict) -> dict:
    """
    Overview:
        Merge two dicts by calling ``deep_update``
    Arguments:
        - original (:obj:`dict`): Dict 1.
        - new_dict (:obj:`dict`): Dict 2.
    Returns:
        - merged_dict (:obj:`dict`): A new dict that is d1 and d2 deeply merged.
    """
    original = original or {}
    new_dict = new_dict or {}
    merged = copy.deepcopy(original)
    if new_dict:  # if new_dict is neither empty dict nor None
        deep_update(merged, new_dict, True, [])
    return merged

def deep_update(
    original: dict,
    new_dict: dict,
    new_keys_allowed: bool = False,
    whitelist: Optional[List[str]] = None,
    override_all_if_type_changes: Optional[List[str]] = None
):
    """
    Overview:
        Update original dict with values from new_dict recursively.
    Arguments:
        - original (:obj:`dict`): Dictionary with default values.
        - new_dict (:obj:`dict`): Dictionary with values to be updated
        - new_keys_allowed (:obj:`bool`): Whether new keys are allowed.
        - whitelist (:obj:`Optional[List[str]]`):
            List of keys that correspond to dict
            values where new subkeys can be introduced. This is only at the top
            level.
        - override_all_if_type_changes(:obj:`Optional[List[str]]`):
            List of top level
            keys with value=dict, for which we always simply override the
            entire value (:obj:`dict`), if the "type" key in that value dict changes.

    .. note::

        If new key is introduced in new_dict, then if new_keys_allowed is not
        True, an error will be thrown. Further, for sub-dicts, if the key is
        in the whitelist, then new subkeys can be introduced.
    """
    whitelist = whitelist or []
    override_all_if_type_changes = override_all_if_type_changes or []

    for k, value in new_dict.items():
        if k not in original and not new_keys_allowed:
            raise RuntimeError("Unknown config parameter `{}`. Base config have: {}.".format(k, original.keys()))

        # Both original value and new one are dicts.
        if isinstance(original.get(k), dict) and isinstance(value, dict):
            # Check old type vs old one. If different, override entire value.
            if k in override_all_if_type_changes and \
                    "type" in value and "type" in original[k] and \
                    value["type"] != original[k]["type"]:
                original[k] = value
            # Whitelisted key -> ok to add new subkeys.
            elif k in whitelist:
                deep_update(original[k], value, True)
            # Non-whitelisted key.
            else:
                deep_update(original[k], value, new_keys_allowed)
        # Original value not a dict OR new value not a dict:
        # Override entire value.
        else:
            original[k] = value
    return original


import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')

class DynamicEvolutionStats:
    def __init__(self, seeds, specialist_type):
        self.seeds = seeds
        self.specialist_type = specialist_type
        self.init_data()

    def init_data(self):
        self.data = {}
        for seed in self.seeds:
            self.data[seed] = pd.DataFrame(columns=['generation', 'score', 'cycle'])

    def get_data(self, suffix='score'):
        for seed in self.seeds:
            df = pd.read_csv(f'../../data/specialist/dynamic_evolution/{self.specialist_type}/{seed}_{suffix}.csv')
            self.data[seed] = pd.concat([self.data[seed], df]).query("generation >= 1600")

    def get_seed(self, seed):
        return self.data.get(seed)

    def describe_seeds(self):
        describe = []
        for seed in self.seeds:
            df = self.get_seed(seed)
            describe.append([
                df.score.mean(),
                len(df.query('cycle == "score"')),
                len(df.query('cycle == "fit"')),
            ])

        return pd.DataFrame(
            describe,
            columns=['score', 'score_time', 'fit_time']
        )

    def plot_seeds_scatter(self):
        for seed in self.seeds:
            df = self.get_seed(seed)
            plt.scatter(df.generation, df.score, s=1)
        plt.legend(self.seeds)
        plt.title(f'All Seeds Specialist Score')
        plt.xlabel('generation')
        plt.ylabel('score')
        plt.show()

    def describe_score(self):
        df = self.describe_seeds()
        plt.boxplot(df.score, labels=['mean'])
        plt.title(f'All Seeds Specialist Score Mean')
        plt.ylabel('score')
        plt.show()

    def describe_cycles(self):
        df = self.describe_seeds()
        plt.boxplot(df[['score_time', 'fit_time']], labels=['score_time', 'fit_time'])
        plt.title(f'All Seeds Specialist Cycles')
        plt.xlabel('process')
        plt.ylabel('cycles')
        plt.show()


from django.shortcuts import render
from .forms import getData, getTraningInfo
import requests
from bs4 import BeautifulSoup
from datetime import date, datetime
import folium
import geocoder
# Create your views here.

runs = {}

def calculate_difference(key):
    run_date_str = runs[key]["date"][:10] + " 0:0:0"
    today = datetime.now()

    run_day = datetime.strptime(run_date_str, "%Y-%m-%d %H:%M:%S")
    difference = (run_day - today).days
    minus = 0
    if today.weekday() != 0:
        minus += 7 - today.weekday()

    return difference - minus

def calculate_distance(key):
    distance = ''
    i = 0
    while runs[key]["distance"][i] != "k":
        distance += runs[key]["distance"][i]
        i += 1
    return int(distance)

def calculate_speed(hour, minutes, distance):
    minutes += hour*60
    speed = minutes / distance
    return speed

def speed_to_str(speed):
    minutes = speed // 1
    sek = speed - minutes
    sek *= 60
    if sek < 10:
        sek = "0" + str(round(sek))
    else:
        sek = str(round(sek))
    minutes = str(round(minutes))
    return minutes + ":" + sek

def basic_introduction(weeks):

    plan4 = {
        '1': {'pon': 'odpoczynek', 'wt': 'bieg 10 min', 'sr': 'opdoczynek', 'czw': 'bieg 10 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 15 min'},
        '2': {'pon': 'odpoczynek', 'wt': 'bieg 15 min', 'sr': 'opdoczynek', 'czw': 'bieg 15 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 20 min'},
        '3': {'pon': 'odpoczynek', 'wt': 'bieg 20 min', 'sr': 'opdoczynek', 'czw': 'bieg 25 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 25 min'},
        '4': {'pon': 'odpoczynek', 'wt': 'bieg 25 min', 'sr': 'opdoczynek', 'czw': 'bieg 30 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 30 min'}}

    plan5 = {
        '1': {'pon': 'odpoczynek', 'wt': 'bieg 10 min', 'sr': 'opdoczynek', 'czw': 'bieg 10 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 10 min'},
        '2': {'pon': 'odpoczynek', 'wt': 'bieg 15 min', 'sr': 'opdoczynek', 'czw': 'bieg 15 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 15 min'},
        '3': {'pon': 'odpoczynek', 'wt': 'bieg 20 min', 'sr': 'opdoczynek', 'czw': 'bieg 20 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 20 min'},
        '4': {'pon': 'odpoczynek', 'wt': 'bieg 25 min', 'sr': 'opdoczynek', 'czw': 'bieg 25 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 25 min'},
        '5': {'pon': 'odpoczynek', 'wt': 'bieg 30 min', 'sr': 'opdoczynek', 'czw': 'bieg 30 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 30 min'}}

    plan2 = {
        '1': {'pon': 'odpoczynek', 'wt': 'bieg 10 min', 'sr': 'opdoczynek', 'czw': 'bieg 15 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 15 min'},
        '2': {'pon': 'odpoczynek', 'wt': 'bieg 15 min', 'sr': 'opdoczynek', 'czw': 'bieg 20 min', 'pt': 'odpoczynek',
              'weekend': 'bieg 30 min'},
    }
    if weeks == 4:
        return plan4, weeks - 4, 5
    elif weeks == 5:
        return plan5, weeks - 5, 6
    else:
        print("tu jestem")

        return plan2, 2, 3

def introduction(weeks, actual_week, distance, week, mode, plan, weeks_for_introduction=0):

    mins = 0
    if mode == "Basic":
        # pocztatkowy dystans dla basic
        mins = 2
        if distance < 11:
            if weeks_for_introduction == 0:
                # ilosc tygodni na dostosowanie dystansu
                weeks_for_introduction = 4

        elif distance < 22:
            if weeks_for_introduction == 0:
                weeks_for_introduction = 10

        else:
            if weeks_for_introduction == 0:
                weeks_for_introduction = 15
            # jako ze dystans jest bardzo duzy trening odbywa sie na max 3/4 jego wartosic
            distance *= 0.75

    if mode == "Medium":
        mins = 5
        if distance < 22:
            if weeks_for_introduction == 0:
                weeks_for_introduction = 4
        else:
            if weeks_for_introduction == 0:
                weeks_for_introduction = 10
            distance *= 0.75

    if mode == "Advance":
        mins = 10
        if weeks_for_introduction == 0:
            weeks_for_introduction = 10
        distance *= 0.75

    # ilosc kilometrow jaka zwiekszamy co kazdy tydzien
    jump = (distance - mins) / (weeks_for_introduction - 1)

    # iterowanie przez kazdy tydzien traningu wprowadzajacego
    for i in range(actual_week, actual_week + weeks_for_introduction):
        plan[str(i)] = {}
        weeks -= 1
        # iterowanie przez kazdy dzien tygodnia (weekend jako 1 dzien czyli mozna se wybrac sob lub nd)
        for day in range(0, len(week)):
            if day % 2 == 0:
                plan[str(i)][week[day]] = "odpoczynek"
            elif (day == 1 or day == 3) and mins > 5:
                plan[str(i)][week[day]] = "bieg na " + str(round(mins / 2)) + "km"
            else:
                plan[str(i)][week[day]] = "bieg na " + str(round(mins)) + "km"
        mins += jump

    #aktualizowanie aktualnego tygonia
    actual_week += weeks_for_introduction

    return plan, weeks, actual_week

def full_training(weeks, actual_week, distance, week, mode, plan, speed):
    # range (actual_week, actual_week + weeks)

    if mode == "Basic":

        # min predkosc po introduction ktora jest zwiekszana z klejnymi tygodniami
        min_speed = 10
        if distance >= 22:
            distance *= 0.75

    elif mode == "Medium":
        min_speed = 8
        if distance >= 22:
            distance *= 0.75

    else:
        min_speed = 7
        if distance >= 22:
            distance *= 0.75

    # zwiekszanie predkosci co tydzien o jump
    jump = (min_speed - speed) / weeks
    for i in range(actual_week, actual_week + weeks):
        plan[str(i)] = {}
        min_speed -= jump
        weeks -= 1
        actual_week += 1
        for day in range(0, len(week)):
            if day % 2 == 0:
                plan[str(i)][week[day]] = "odpoczynek"
            elif day == 1 and 5 < distance < 11:
                plan[str(i)][week[day]] = "bieg na " + str(round(distance / 2)) + "km w czasie " + \
                                          speed_to_str(min_speed * 0.7) + " min/km"
            elif day == 1 and 5 < distance < 22:
                plan[str(i)][week[day]] = "bieg na " + str(round(distance / 2)) + "km w czasie " + \
                                          speed_to_str(min_speed * 0.8) + " min/km"
            elif day == 1 and 5 < distance:
                plan[str(i)][week[day]] = "bieg na " + str(round(distance / 2)) + "km w czasie " + \
                                          speed_to_str(min_speed * 0.9) + " min/km"
            elif day == 3 and mode != "Advance":
                plan[str(i)][week[day]] = "bieg interwalowy: 5x (bieg 1.5 min na maksimum mozliwosci + " \
                                          "2 min wolnego truchtu) + wybiganie na " + str(round(distance / 2)) + "km"
            elif day == 3:
                plan[str(i)][week[day]] = "bieg interwalowy: 5x (bieg 1.5 min na maksimum mozliwosci pod gorke + " \
                                          "2 min wolnego truchtu z gorki) + wybiganie na " + \
                                          str(round(distance / 2)) + "km"
            else:
                plan[str(i)][week[day]] = "bieg na " + str(distance) + "km w czasie " + speed_to_str(min_speed) + \
                                          " min/km"

    return plan, weeks, actual_week

def home(request):
    global runs
    runs = {}
    if request.method == "POST":
        runs = {}

        # pobranie danych z forms po wcisnieciu przycisku
        form = getData(request.POST)

        # ustawienie zmiennej na global w celu modyfikacji dict runs
        if form.is_valid():

            # pobieranie danych
            city = form.cleaned_data["city"]
            date_from_wrong = form.cleaned_data["date_from"]
            date_to_wrong = form.cleaned_data["date_to"]
            distance_from = form.cleaned_data["distance_from"]
            distance_to = form.cleaned_data["distance_to"]

            # zamiana daty
            if date_from_wrong is not None:
                date_from_correct = str(date_from_wrong.year) + "-" + str(date_from_wrong.month) + "-" + \
                                    str(date_from_wrong.day)
            else:
                date_from_correct = ""

            if date_to_wrong is not None:
                date_to_correct = str(date_to_wrong.year) + "-" + str(date_to_wrong.month) + "-" + \
                                    str(date_to_wrong.day)
            else:
                date_to_correct = ""

            # wyczyszczenie input-ow
            form = getData()

            # pobranie danych ze strony
            url = "https://run-log.com/events/?terms=" + city + "&date_from=" + date_from_correct + \
                  "&date_to=" + date_to_correct + "&distance_from=" + str(distance_from) + \
                  "&distance_to=" + str(distance_to) + "&location_radius=&action="
            website = requests.get(url)
            result = website.text
            doc = BeautifulSoup(result, "html.parser")
            table = doc.tbody
            trs = table.contents
            i = 0

            # iterowanie po kazdym elemenecie tabeli z danymi zawodow
            for tr in trs:
                i += 1

                # sprawdzenie czy w tabeli istenieja biegi oraz czy nie sprawdzania jest pusty wiersz
                if i % 2 == 0 and i <= 10 and len(tr.contents) >= 10:
                    run = {}
                    date, name, distance, shit, location = tr.contents[1::2]
                    run["date"] = date.text
                    run["distance"] = distance.text.strip()
                    run["location"] = location.text
                    run["number"] = i/2
                    name = name.a.string

                    # wyszukiwanie linkow do obrazu dla kazdego miasta w ktorym jest bieg
                    r = requests.get(
                        'https://commons.wikimedia.org/w/index.php?search=' + run["location"]
                        + '&title=Special:MediaSearch&go=Go&type=image')
                    result = r.text
                    doc = BeautifulSoup(result, "html.parser")
                    images = doc.find('a', {'class': 'sdms-image-result'})
                    print(images)
                    if not images:
                        run["image"] = "#"
                    else:
                        r = requests.get(images['href'])
                        result = r.text
                        doc = BeautifulSoup(result, "html.parser")
                        doc2 = doc.find('div', {'class': 'mw-body-content'})
                        image = doc2.find('img')
                        run["image"] = image['src']

                        # w wypadku wystapnie biegu z taka sama nazwa dodanie numerka do nazyw
                        if name in runs:
                            runs[name+" ("+str(i/2)[0]+")"] = run
                        else:
                            runs[name] = run
    else:
        form = getData()
    return render(request, "runsite/home.html", {"Data": form, "Runs": runs})

def run_plan(request):

    # pobranie url storny (zawiera index dictionary z dpowiednimi zawodami)
    url = int(request.build_absolute_uri()[22])
    key = list(runs.keys())[url-1]
    working = 1
    # oblicznie ile dni oraz tygodni jest do zawodow
    days = calculate_difference(key)
    weeks = days//7
    week = ['pon', 'wt', 'sr', 'cw', 'pt', 'weekend']
    plan = {}
    # konwertowanie dystansu ze slownika na typ int (pomijanie metrow)
    distance = calculate_distance(key)

    # generowanie mapy ze znacznikiem lokalizacji biegu
    try:
        location = geocoder.location(runs[key]['location'])
        lat = location.lat
        lng = location.lng
        mapa = folium.Map(location=[lat, lng], zoom_start=12)
        folium.Marker([lat, lng]).add_to(mapa)
    except:
        location = geocoder.osm('PL')
        lat = location.lat
        lng = location.lng
        mapa = folium.Map(location=[lat, lng], zoom_start=12)

    mapa = mapa._repr_html_()
    if request.method == "POST":
        working = 1

        # pobranie danych z forms po wcisnieciu przycisku
        form = getTraningInfo(request.POST)

        if form.is_valid():
            type_of_training = form.cleaned_data["type"]
            time_hours = form.cleaned_data["time_hours"]
            if time_hours:
                try:
                    time_hours = int(time_hours)
                except ValueError:
                    working = 0
                    time_hours = 0
            else:
                time_hours = 0
            time_minutes = form.cleaned_data["time_minutes"]
            if time_minutes:
                try:
                    time_minutes = int(time_minutes)
                except ValueError:
                    working = 0
                    time_minutes = 0
            else:
                time_minutes = 0
            speed = calculate_speed(time_hours, time_minutes, distance)
            if time_minutes < 0 or time_hours < 0 or speed < 2.5:
                working = 0

            form = getTraningInfo()
            if type_of_training == "Basic":
                speed *= 1.2
                if weeks <= 3:
                    print("nie da sie wygnerowa traningu1")
                    working = 0
                elif weeks <= 20:

                    #pierwszy tryb (najkrotrze zawody)
                    if distance < 11:
                        if weeks < 6:
                            plan, weeks, actual_week = basic_introduction(weeks)
                        elif weeks >= 6:
                            # pamietaj 6 - 2(basic_introduction)
                            # pamietaj ze full_training() z tych weekow co zostaly po introduction
                            plan, dif, actual_week = basic_introduction(2)
                            print(weeks)
                            # odjecie od pozostalych tygodni juz wykorzystanych
                            weeks -= dif
                            plan, weeks, actual_week = introduction(weeks, actual_week, distance, week,
                                                                    type_of_training, plan)
                            print(plan)
                            print(weeks)
                            print(actual_week)
                            plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                     type_of_training, plan, speed)
                            print(plan)
                            print(weeks)
                            print(actual_week)

                    #drugi tryb (srednio dlugie zaowdy)
                    elif distance < 22:
                        if weeks < 12:
                            print("nie da sie wygenerowac treningu2")
                            working = 0
                        elif weeks >= 12:

                            plan, dif, actual_week = basic_introduction(2)
                            weeks -= dif
                            print(weeks)
                            plan, weeks, actual_week = introduction(weeks, actual_week, distance, week,
                                                                    type_of_training, plan)
                            print(plan)
                            print(weeks)
                            print(actual_week)
                            plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                     type_of_training, plan, speed)
                            print(plan)
                            print(weeks)
                            print(actual_week)

                    #trzeci tryb(dlugie zawody)
                    else:
                        if weeks < 17:
                            print("nie da sie wygenerowac treningu2")
                            working = 0
                        if weeks >= 17:
                            plan, dif, actual_week = basic_introduction(2)
                            weeks -= dif
                            plan, weeks, actual_week = introduction(weeks, actual_week, distance, week,
                                                                    type_of_training, plan)

                            print(plan)
                            print(weeks)
                            print(actual_week)
                            plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                     type_of_training, plan, speed)
                            print(plan)
                            print(weeks)
                            print(actual_week)
                # ----------------------------------
                else:
                    if distance < 11:

                        # wyliczenie na korym tygoniu konczy sie introducion (+2 by uwzglednic basic_introdution)
                        weeks_for_introduction = round((weeks * 0.2)//1 + 2)
                        plan, dif, actual_week = basic_introduction(2)
                        weeks -= dif
                        plan, weeks, actual_week = introduction(weeks, actual_week, distance, week,
                                                                type_of_training, plan, weeks_for_introduction)

                        print(plan)
                        print(weeks)
                        print(actual_week)
                        plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                 type_of_training, plan, speed)
                        print(plan)
                        print(weeks)
                        print(actual_week)
                    elif distance < 22:
                        weeks_for_introduction = round((weeks * 0.5) // 1 + 2)
                        plan, dif, actual_week = basic_introduction(2)
                        weeks -= dif
                        plan, weeks, actual_week = introduction(weeks, actual_week, distance, week,
                                                                type_of_training, plan, weeks_for_introduction)

                        print(plan)
                        print(weeks)
                        print(actual_week)
                        plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                 type_of_training, plan, speed)
                        print(plan)
                        print(weeks)
                        print(actual_week)
                    else:
                        weeks_for_introduction = round((weeks * 0.75) // 1 + 2)

                        plan, dif, actual_week = basic_introduction(2)
                        weeks -= dif
                        plan, weeks, actual_week = introduction(weeks, actual_week, distance, week,
                                                                type_of_training, plan, weeks_for_introduction)
                        print(plan)
                        print(weeks)
                        print(actual_week)
                        plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                 type_of_training, plan, speed)
                        print(plan)
                        print(weeks)
                        print(actual_week)
            elif type_of_training == "Medium":
                if weeks <= 3:
                    print("nie da sie wygnerowa traningu1")
                    working = 0
                elif distance < 11:

                    # dla malego dystansu w trybie medium nie ma introduction
                    plan, weeks, actual_week = full_training(weeks, 1, distance, week,
                                                             type_of_training, {}, speed)
                    print(plan)
                    print(weeks)
                    print(actual_week)

                elif weeks <= 20:

                    if distance < 22:
                        if weeks < 4:
                            print("nie da sie wygnerowa traningu2")
                            working = 0
                        else:
                            plan, weeks, actual_week = introduction(weeks, 1, distance, week,
                                                                    type_of_training, {})
                            print(plan)
                            print(weeks)
                            print(actual_week)
                            plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                     type_of_training, plan, speed)
                            print(plan)
                            print(weeks)
                            print(actual_week)
                    else:
                        if weeks < 10:
                            print("nie da sie wygnerowa traningu2")
                            working = 0
                        else:
                            plan, weeks, actual_week = introduction(weeks, 1, distance, week,
                                                                    type_of_training, {})
                            print(plan)
                            print(weeks)
                            print(actual_week)
                            print(speed)
                            plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                     type_of_training, plan, speed)
                            print(plan)
                            print(weeks)
                            print(actual_week)
                else:
                    if distance < 22:
                        weeks_for_introduction = round((weeks * 0.2) // 1)
                        plan, weeks, actual_week = introduction(weeks, 1, distance, week,
                                                                type_of_training, {}, weeks_for_introduction)
                        print(plan)
                        print(weeks)
                        print(actual_week)
                        plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                 type_of_training, plan, speed)
                        print(plan)
                        print(weeks)
                        print(actual_week)
                    else:
                        weeks_for_introduction = round((weeks * 0.5) // 1)
                        plan, weeks, actual_week = introduction(weeks, 1, distance, week,
                                                                type_of_training, {}, weeks_for_introduction)
                        print(plan)
                        print(weeks)
                        print(actual_week)
                        plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                                 type_of_training, plan, speed)
                        print(plan)
                        print(weeks)
                        print(actual_week)

            else:
                speed *= 0.9
                if weeks <= 3:
                    print("nie da sie wygnerowa traningu1")
                    working = 0
                elif distance < 22:

                    # dla malego dystansu oraz sredniego w trybie advance nie ma introduction
                    plan, weeks, actual_week = full_training(weeks, 1, distance, week,
                                                             type_of_training, {}, speed)
                    print(plan)
                    print(weeks)
                    print(actual_week)

                elif weeks < 10:
                    print("nie da sie wygnerowa traningu3")
                    working = 0
                elif weeks <= 20:

                    plan, weeks, actual_week = introduction(weeks, 1, distance, week,
                                                            type_of_training, {})

                    #print(plan)
                    print(weeks)
                    print(actual_week)
                    print(speed)
                    plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                             type_of_training, plan, speed)
                    print(plan)
                    print(weeks)
                    print(actual_week)
                else:
                    weeks_for_introduction = round((weeks * 0.5) // 1)
                    plan, weeks, actual_week = introduction(weeks, 1, distance, week,
                                                            type_of_training, {}, weeks_for_introduction)
                    #print(plan)
                    print(weeks)
                    print(actual_week)
                    print(speed)
                    plan, weeks, actual_week = full_training(weeks, actual_week, distance, week,
                                                             type_of_training, plan, speed)
                    print(plan)
                    print(weeks)
                    print(actual_week)

        if working == 0:
            plan = {}
        for name, values in plan.items():
            print(name)
            print(values)
    else:
        form = getTraningInfo()
    return render(request, "runsite/runPlan.html", {"Forms": form, "Key": key, "Run": runs[key], "Mapa": mapa,
                                                    "Plan": plan, "Working": working})


named_params = {
    "Rest_time_T": float,
    "Duration_step": float,
    "Record_every_dT": float,
    "Record_every_dE": float,
    "Record_every_dI": float,
    "E_Range": int,
    "I_Range": int,
    "Current_step": float,
    "Voltage_step": float,
    "Scan_Rate": float,
    "vs_initial": bool,
    "Test1_Config": int,
    "Test1_Value": float,
    "Test2_Config": int,
    "Test2_Value": float,
    "Test3_Config": int,
    "Test3_Value": float,
    "Exit_Cond": int,
    "N_Cycles": int,
    "Step_number": int,
    "Scan_number": int,
    "loop_N_times": int,
    "protocol_number": int,
    "Begin_measuring_I": float,
    "End_measuring_I": float,
    "Begin_measuring_E": float,
    "End_measuring_E": float,
}

I_ranges = {
    "keep": -1,
    "100 pA": 0,
    "1 nA": 1,
    "10 nA": 2,
    "100 nA": 3,
    "1 uA": 4,
    "10 uA": 5,
    "100 uA": 6,
    "1 mA": 7,
    "10 mA": 8,
    "100 mA": 9,
    "1 A": 10,
    "booster": 11,
    "auto": 12,
}

E_ranges = {
    "+-2.5 V": 0,
    "+-5.0 V": 1,
    "+-10 V": 2,
    "auto": 3,
}

datatypes = {
    "VMP3": {
        "OCV": ["t_high", "t_low", "Ewe", "Ece"],
        "CPLIMIT": ["t_high", "t_low", "Ewe", "I", "cycle"],
        "CALIMIT": ["t_high", "t_low", "Ewe", "I", "cycle"],
        "PDYNLIMIT": ["t_high", "t_low", "Ec", "<I>", "<Ewe>", "cycle"],
        "GDYNLIMIT": ["t_high", "t_low", "Ic", "<I>", "<Ewe>", "cycle"],
    },
    "SP-300": {
        "OCV": ["t_high", "t_low", "Ewe"],
        "CPLIMIT": ["t_high", "t_low", "Ewe", "I", "cycle"],
        "CALIMIT": ["t_high", "t_low", "Ewe", "I", "cycle"],
        "PDYNLIMIT": ["t_high", "t_low", "<I>", "<Ewe>", "cycle"],
        "GDYNLIMIT": ["t_high", "t_low", "<I>", "<Ewe>", "cycle"],
    },
}

techfiles = {
    "VMP3": {
        "open_circuit_voltage": "ocv.ecc",
        "constant_current": "cplimit.ecc",
        "constant_voltage": "calimit.ecc",
        "sweep_voltage": "vscanlimit.ecc",
        "sweep_current": "iscanlimit.ecc",
        "loop": "loop.ecc",
    },
    "SP-300": {
        "open_circuit_voltage": "ocv4.ecc",
        "constant_current": "cplimit4.ecc",
        "constant_voltage": "calimit4.ecc",
        "sweep_voltage": "vscanlimit4.ecc",
        "sweep_current": "iscanlimit4.ecc",
        "loop": "loop4.ecc",
    },
}


from Crypto.Util.number import  getPrime
from Crypto.Util.number import inverse
import hashlib
import socket
from threading import Thread

host = 'localhost'
port = 6000

mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try :
    mysocket.connect((host, port))

except socket.error :
    print("connexion echouer avec le serveur ")
    exit()

print("connexion etablie avec le serveur")

def gen_rsa_keypair (bits):
    p=getPrime(bits//2)
    q=getPrime(bits//2)
    n=p*q
    e=655337
    d=inverse(e, (p-1)*(q-1))
    return((e,n), (d,n)) #cle pub et cle priv

key  = gen_rsa_keypair(256)

def rsa(m,key):
    return pow(m,key[0],key[1])

def rsa_enc(msg, key):
    m = int.from_bytes(msg.encode('utf-8'),'big')
    c = rsa(m, key)
    return c

def rsa_dec(msg, key):
    txt_clair = rsa(msg, key)
    return txt_clair.to_bytes((txt_clair.bit_length()+7) // 8,'big').decode('utf-8')

class Send(Thread):

    def __init__(self,arg):
        Thread.__init__(self)
        #super(Send, self).__init__()
        self.arg = arg

    def run(self):
        continuer = True

        while(continuer):

            message = input()

            message1 = self.arg.sendall(repr(key[0]).encode('utf8'))#cle

            try:
                enchifrer = rsa_enc(message, key[0])

                #print("enchiffreeer = ",enchifrer)

                #self.arg.send(repr(enchifrer).encode('utf-8'))
                dechiffrer = rsa_dec(enchifrer, key[1])
                #print("dechiffrer ", dechiffrer)
                self.arg.send(dechiffrer.encode('utf-8'))

            except socket.error:
                continuer = False
                break

        self.arg.close()

class receive(Thread):

    def __init__(self,arg):
        Thread.__init__(self)
       # super(receive, self).__init__()
        self.arg = arg

    def run(self):

        continuer = True

        while(continuer):
            try:

                message = self.arg.recv(1024).decode('utf-8')
            except socket.error:
                    continuer = False
                    break

            else :
                print(">>>>>> {0}".format(message))

        self.arg.close()

if __name__ == "__main__":

    sn = Send(mysocket)
    sn.start()

    rv = receive(mysocket)
    rv.start()


from collections import ChainMap

import yaml

with open('eve_static_data/invFlags.yaml') as flags_file:
    INV_FLAGS = {item['flagID']: item for item in yaml.full_load(flags_file)}

INVENTORY_POSITIONS = [
    *range(92, 99+1),  # Rigs
    *range(27, 34+1),  # High Slots
    *range(19, 26+1),  # Med Slots
    *range(11, 18+1),  # Low Slots
    0                # Everything Else
]


import logging
log = logging.getLogger(__name__)

import re
import requests

from bs4 import BeautifulSoup
try:
    # Python 2 has a standard urlparse library
    from urlparse import urlparse, ParseResult
except:
    # Python 3 has the same library hidden in urllib.parse
    from urllib.parse import urlparse, ParseResult

MAX_FILEIZE =  2**19  # bytes; this is .5MB
MAX_CONNECTIONTIME = 20  # in seconds

RE_bad_title = re.compile(
    """(?:<title>|<title>)(.*)(?:<?/title>|(?:<)?/title>)""", re.I)

REGEX_doctype = re.compile("^\s*<!DOCTYPE[^>]*>", re.IGNORECASE)

RE_whitespace = re.compile("\s+")

PARSE_SAFE_FILES = ('html', 'txt', 'json', 'htm', 'xml',
                    'php', 'asp', 'aspx', 'ece', 'xhtml', 'cfm', 'cgi')

# based on DJANGO
# https://github.com/django/django/blob/master/django/core/validators.py
# not testing ipv6 right now, because rules are needed for ensuring they are correct
RE_VALID_HOSTNAME = re.compile(
    r'(?:'
        r'(?P<ipv4>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})'  # ...or ipv4
        r'|'
        #  r'(?P<ipv6>\[?[A-F0-9]*:[A-F0-9:]+\]?)'  # ...or ipv6
        #  r'|'
        r'(?P<localhost>localhost)'  # localhost...
        r'|'
        r'(?P<domain>([A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}\.?|[A-Z0-9-]{2,}(?<!-)\.?))'  # domain...
        r'(?P<port>:\d+)?'  # optional port
    r')', re.IGNORECASE)

RE_PORT = re.compile(
    r'^'
    r'(?P<main>.+)'
    r':'
    r'(?P<port>\d+)'
    r'$', re.IGNORECASE
)

RE_DOMAIN_NAME = re.compile(
    r"""(^
            (?:
                [A-Z0-9]
                (?:
                    [A-Z0-9-]{0,61}
                    [A-Z0-9]
                )?
                \.
            )+
            (?:
                [A-Z]{2,6}\.?
                |
                [A-Z0-9-]{2,}
            (?<!-)\.?)
        $)""",
    re.VERBOSE | re.IGNORECASE)

RE_IPV4_ADDRESS = re.compile(
    r'^(\d{1,3})\.(\d{1,3}).(\d{1,3}).(\d{1,3})$'  # grab 4 octets
)

RE_ALL_NUMERIC = re.compile("^[\d\.]+$")

def is_parsed_valid_url(parsed, require_public_netloc=True, http_only=True):
    """returns bool
        `http_only`
            defaults True
            requires http or https for the scheme
    """
    assert isinstance(parsed, ParseResult)
    log.debug("is_parsed_valid_url = %s", parsed)
    if not all((parsed.scheme, parsed.netloc)):
        log.debug(" FALSE - missing `scheme` or `netloc`")
        return False
    if http_only:
        if parsed.scheme not in ('http', 'https'):
            log.debug(" FALSE - invalid `scheme`")
            return False
    if require_public_netloc:
        log.debug(" validating netloc")
        _netloc_match = RE_VALID_HOSTNAME.match(parsed.netloc)
        if not _netloc_match:
            log.debug(" did not match regex")
            return False

        # we may assign these
        _netloc_clean = parsed.netloc
        _port = None

        _netloc_ported = RE_PORT.match(parsed.netloc)
        if _netloc_ported:
            _netloc_ported_groudict = _netloc_ported.groupdict()
            _netloc_clean = _netloc_ported_groudict['main']
            _port = _netloc_ported_groudict['port']

        _netloc_groudict = _netloc_match.groupdict()
        if _netloc_groudict['ipv4'] is not None:
            octets = RE_IPV4_ADDRESS.match(_netloc_clean)
            if octets:
                log.debug(" validating against ipv4")
                for g in octets.groups():
                    g = int(g)
                    if int(g) > 255:
                        log.debug(" invalid ipv4; encountered an octect > 255")
                        return False
                log.debug(" valid ipv4")
                return True
            log.debug(" invalid ipv4")
            return False
        else:
            if _netloc_clean == 'localhost':
                log.debug(" localhost!")
                return True

            if RE_ALL_NUMERIC.match(_netloc_clean):
                log.debug(" This only has numeric characters. "
                          "this is probably a fake or typo ip address.")
                return False
            if _port:
                try:
                    _port = int(_port)
                    if parsed.port != _port:
                        log.debug(" netloc.port does not match our regex _port")
                        return False
                except:
                    raise
                    log.debug(" _port is not an int")
                    return False
            if RE_DOMAIN_NAME.match(_netloc_clean):
                log.debug(" valid public domain name format")
                return True
        log.debug(" this appears to be invalid")
        return False
    return True

def is_parsed_valid_relative(parsed):
    """returns bool"""
    assert isinstance(parsed, ParseResult)
    if parsed.path and not any((parsed.scheme, parsed.hostname)):
        return True
    return False

def parsed_to_relative(parsed):
    """turns a parsed url into a full relative url"""
    assert isinstance(parsed, ParseResult)
    _path = parsed.path
    # cleanup, might be unnecessary now
    if _path and _path[0] != "/":
        # prepend a slash
        _path = "/%s" % _path
    if parsed.query:
        _path += "?" + parsed.query
    if parsed.fragment:
        _path += "#" + parsed.fragment
    return _path

def is_url_valid(url, require_public_netloc=None):
    """
    tries to parse a url. if valid returns `ParseResult`
    (boolean eval is True); if invalid returns `False`
    """
    if url is None:
        return False
    parsed = urlparse(url)
    if is_parsed_valid_url(parsed, require_public_netloc=require_public_netloc):
        return parsed
    return False

def url_to_absolute_url(url_test, url_fallback=None, require_public_netloc=None):
    """
        returns an "absolute url" if we have one.
        if we don't, it tries to fix the current url based on the fallback

        this shouldn't be needed, but it is.

        called by:

            MetadataParser.absolute_url()
            MetadataParser.get_discrete_url()

        args:
            `url_test` - the url to return/fix
            `url_fallback` - a fallback url.  this is returned in VERY bad
                errors. in "not so bad" errors, this is parsed and used as the
                base to construct a new url.
            `require_public_netloc` - requires the hostname/netloc to be a
                valid IPV4 or public dns domain name
    """
    if url_test is None and url_fallback is not None:
        return url_fallback

    parsed = urlparse(url_test)

    _path = parsed.path
    if _path:
        # sanity check
        # some stock plugins create invalid urls/files like '/...' in meta-data
        if _path[0] != "/":
            # prepend a slash
            _path = "/%s" % _path
        known_invalid_plugins = ['/...', ]
        if _path in known_invalid_plugins:
            return url_fallback

    # finally, fix the path
    # this isn't nested, because we could have kwargs
    _path = parsed_to_relative(parsed)

    if not _path:
        # so if our _path is BLANK, fuck it.
        # this can happen if someone puts in "" for the canonical
        return url_fallback

    rval = None

    # we'll use a placeholder for a source 'parsed' object that has a domain...
    parsed_domain_source = None

    # if we have a valid URL (OMFG, PLEASE)...
    if is_parsed_valid_url(parsed, require_public_netloc=require_public_netloc):
        parsed_domain_source = parsed
    else:
        # ok, the URL isn't valid
        # can we re-assemble it
        if url_fallback:
            parsed_fallback = urlparse(url_fallback)
            if is_parsed_valid_url(
                parsed_fallback,
                require_public_netloc=require_public_netloc
            ):
                parsed_domain_source = parsed_fallback
    if parsed_domain_source:
        rval = "%s://%s%s" % (
            parsed_domain_source.scheme,
            parsed_domain_source.netloc, _path)
    return rval

class NotParsable(Exception):

    def __init__(self, message='', raised=None, code=None):
        self.message = message
        self.raised = raised
        self.code = code

    def __str__(self):
        return "ApiError: %s | %s | %s" % (self.message, self.code, self.raised)

class NotParsableFetchError(NotParsable):
    pass

class MetadataParser(object):
    """
    turns text or a URL into a dict of dicts, extracting as much relevant
    metadata as possible.

    the 'keys' will be either the 'name' or 'property' attribute of the node.

    we EXPECT/REQUIRE a `head` in the document.

    the attribute's prefix are removed when storing into it's bucket
    eg:
        og:title -> 'og':{'title':''}

    metadata is stored into subgroups:

    page
        extracted from page elements
        saved into MetadataParser.metadata['page']
        example:
            <head><title>Awesome</title></head>
            MetadataParser.metadata = {'page': {'title':'Awesome'}}

    opengraph
        has 'og:' prefix
        saved into MetadataParser.metadata['og']
        example:
            <meta property="og:title" content="Awesome"/>
            MetadataParser.metadata = {'og': {'og:title':'Awesome'}}

    dublin core
        has 'dc:' prefix
        saved into MetadataParser.metadata['dc']
        example:
            <meta property="dc:title" content="Awesome"/>
            MetadataParser.metadata = {'dc': {'dc:title':'Awesome'}}

    meta
        has no prefix
        saved into MetadataParser.metadata['meta']
        example:
            <meta property="title" content="Awesome"/>
            MetadataParser.metadata = {'meta': {'dc:title':'Awesome'}}

    NOTE:
        passing in ssl_verify=False will turn off ssl verification checking
        in the requests library.
        this can be necessary on development machines
    """
    url = None
    url_actual = None
    strategy = None
    metadata = None
    LEN_MAX_TITLE = 255
    only_parse_file_extensions = None
    require_public_netloc = None
    force_doctype = None
    requests_timeout = None

    # allow for the beautiful_soup to be saved
    soup = None

    og_minimum_requirements = ['title', 'type', 'image', 'url']
    twitter_sections = ['card', 'title', 'site', 'description']
    strategy = ['og', 'dc', 'meta', 'page']

    def __init__(
        self,
        url=None, html=None, strategy=None, url_data=None, url_headers=None,
        force_parse=False, ssl_verify=True, only_parse_file_extensions=None,
        force_parse_invalid_content_type=False, require_public_netloc=True,
        force_doctype=False, requests_timeout=None,
    ):
        """
        creates a new `MetadataParser` instance.

        kwargs:
            `url`
                url to parse
            `html`
                instead of a url, parse raw html
            `strategy`
                default: None
                sets default metadata strategy (['og', 'dc', 'meta', 'page'])
                see also `MetadataParser.get_metadata()`
            `url_data`
                data passed to `requests` library as `params`
            `url_headers`
                data passed to `requests` library as `headers`
            `force_parse`
                default: False
                force parsing invalid content
            `ssl_verify`
                default: True
                disable ssl verification, sometimes needed in development
            `only_parse_file_extensions`
                default: None
                set a list of valid file extensions.
                see `metadata_parser.PARSE_SAFE_FILES` for an example list
            `force_parse_invalid_content_type`
                default: False
                force parsing invalid content types
                by default this will only parse text/html content
            `require_public_netloc`
                default: True
                require a valid `netloc` for the host.  if `True`, valid hosts
                must be a properly formatted public domain name, IPV4 address
                or "localhost"
            `force_doctype`
                default: False
                if set to true, will replace a doctype with 'html'
                why? some cms give a bad doctype (like nasa.gov)
                which can break lxml/bsd
            `requests_timeout`
                default: None
                if set, proxies the value into `requests.get` as `timeout`

        """
        self.metadata = {
            'og': {},
            'meta': {},
            'dc': {},
            'page': {},
            'twitter': {}
        }
        if strategy:
            self.strategy = strategy
        if url is not None:
            url = url.strip()
        self.url = url
        self.url_actual = url
        self.ssl_verify = ssl_verify
        self.soup = None
        self.force_doctype = force_doctype
        self.response = None
        self.response_headers = {}
        self.require_public_netloc = require_public_netloc
        self.requests_timeout = requests_timeout
        if only_parse_file_extensions is not None:
            self.only_parse_file_extensions = only_parse_file_extensions
        if html is None:
            html = self.fetch_url(
                url_data=url_data, url_headers=url_headers,
                force_parse=force_parse,
                force_parse_invalid_content_type=force_parse_invalid_content_type
            )
        self.parser(html, force_parse=force_parse)

    def is_opengraph_minimum(self):
        """
        returns true/false if the page has the minimum amount of opengraph tags
        """
        return all([hasattr(self, attr)
                   for attr in self.og_minimum_requirements])

    def fetch_url(
        self,
        url_data=None, url_headers=None, force_parse=False,
        force_parse_invalid_content_type=False
    ):
        """
        fetches the url and returns it.
        this was busted out so you could subclass.
        """
        # should we even download/parse this?
        if not force_parse and self.only_parse_file_extensions is not None:
            parsed = urlparse(self.url)
            path = parsed.path
            if path:
                url_fpath = path.split('.')
                if len(url_fpath) == 0:
                    # i have no idea what this file is, it's likely using a
                    # directory index
                    pass
                elif len(url_fpath) > 1:
                    url_fext = url_fpath[-1]
                    if url_fext in self.only_parse_file_extensions:
                        pass
                    else:
                        raise NotParsable("I don't know what this file is")

        # borrowing some ideas from
        # http://code.google.com/p/feedparser/source/browse/trunk/feedparser/feedparser.py#3701
        if not url_headers:
            url_headers = {}

        # if someone does usertracking with sharethis.com, they get a hashbang
        # like this: http://example.com/page#.UHeGb2nuVo8
        # that fucks things up.
        url = self.url.split('#')[0]

        r = None
        try:
            # requests gives us unicode and the correct encoding, yay
            r = requests.get(
                url, params=url_data, headers=url_headers,
                allow_redirects=True, verify=self.ssl_verify,
                timeout=self.requests_timeout, stream=True,
            )
            content_type = None
            if 'content-type' in r.headers:
                content_type = r.headers['content-type']
                # content type can have a character encoding in it...
                content_type = [i.strip() for i in content_type.split(';')]
                content_type = content_type[0].lower()

            if (
                (
                    (content_type is None)
                    or
                    (content_type != 'text/html')
                )
                and
                (not force_parse_invalid_content_type)
            ):
                raise NotParsable("I don't know what type of file this is! "
                                  "content-type:'[%s]" % content_type)

            # okay, now we need to read
            ## TODO
            ## TODO
            ## TODO
            ## TODO

            html = r.text
            self.response = r

            # lowercase all of the HTTP headers for comparisons per RFC 2616
            self.response_headers = dict((k.lower(), v)
                                         for k, v in r.headers.items())
            self.url_actual = r.url

            if r.status_code != 200:
                raise NotParsableFetchError(
                    message="Status Code is not 200",
                    code=r.status_code
                )

        except requests.exceptions.RequestException as error:
            raise NotParsableFetchError(
                message="Error with `requests` library.  Inspect the `raised`"
                        " attribute of this error.",
                raised=error
            )

        return html

    def absolute_url(self, link=None):
        """
        makes the url absolute, as sometimes people use a relative url. sigh.
        """
        url_fallback = self.url_actual or self.url or None
        return url_to_absolute_url(
            link,
            url_fallback=url_fallback,
            require_public_netloc=self.require_public_netloc
        )

    def parser(self, html, force_parse=False):
        """parses the html
        """
        if not isinstance(html, BeautifulSoup):
            # clean the html?
            if self.force_doctype:
                html = REGEX_doctype.sub("<!DOCTYPE html>", html)
            try:
                doc = BeautifulSoup(html, "lxml")
            except:
                doc = BeautifulSoup(html, "html.parser")
        else:
            doc = html

        # let's ensure that we have a real document...
        if not doc or not doc.html or not doc.html.head:
            return

        # stash the bs4 doc for further operations
        self.soup = doc

        ogs = doc.html.head.findAll(
            'meta',
            attrs={'property': re.compile(r'^og')}
        )
        for og in ogs:
            try:
                self.metadata['og'][og['property'][3:]] = og['content'].strip()
            except (AttributeError, KeyError):
                pass
            except:
                log.debug("Ran into a serious error parsing `og`")
                pass

        twitters = doc.html.head.findAll(
            'meta',
            attrs={'name': re.compile(r'^twitter')}
        )
        for twitter in twitters:
            try:
                self.metadata['twitter'][
                    twitter['name'][8:]] = twitter['value'].strip()
            except (AttributeError, KeyError):
                pass

        # pull the text off the title
        try:
            _title_text = doc.html.head.title.text
            if len(_title_text) > self.LEN_MAX_TITLE:
                _title_text = _title_text[:self.LEN_MAX_TITLE]
            self.metadata['page']['title'] = _title_text

        except AttributeError:
            pass

        # is there an image_src?
        images = doc.findAll(
            'link',
            attrs={'rel': re.compile("^image_src$", re.I)}
        )
        if images:
            image = images[0]
            if image.has_attr("href"):
                img_url = image['href'].strip()
                self.metadata['page']['image'] = img_url
            elif image.has_attr("content"):
                img_url = image['content'].strip()
                self.metadata['page']['image'] = img_url
            else:
                pass

        # figure out the canonical url
        canonicals = doc.findAll(
            'link',
            attrs={'rel': re.compile("^canonical$", re.I)}
        )
        if canonicals:
            canonical = canonicals[0]
            if canonical.has_attr("href"):
                link = canonical['href'].strip()
                self.metadata['page']['canonical'] = link
            elif canonical.has_attr("content"):
                link = canonical['content'].strip()
                self.metadata['page']['canonical'] = link
            else:
                pass

        # pull out all the metadata
        meta = doc.html.head.findAll(name='meta')
        for m in meta:
            try:
                k = None
                v = None
                attrs = m.attrs
                k = None
                if 'name' in attrs:
                    k = 'name'
                elif 'property' in attrs:
                    k = 'property'
                elif 'http-equiv' in attrs:
                    k = 'http-equiv'
                if k:
                    k = attrs[k].strip()
                    if 'content' in attrs:
                        v = attrs['content'].strip()
                    if (len(k) > 3) and (k[:3] == 'dc:'):
                        self.metadata['dc'][k[3:]] = v
                    else:
                        self.metadata['meta'][k] = v
            except AttributeError:
                pass

    def get_metadata(self, field, strategy=None):
        """
            looks for the field in various stores.  defaults to the core
            strategy, though you may specify a certain item.  if you search for
            'all' it will return a dict of all values.
        """
        if strategy:
            _strategy = strategy
        else:
            _strategy = self.strategy
        if _strategy == 'all':
            rval = {}
            for store in self.metadata:
                if field in self.metadata[store]:
                    rval[store] = self.metadata[store][field]
            return rval
        for store in _strategy:
            if store in self.metadata:
                if field in self.metadata[store]:
                    return self.metadata[store][field]
        return None

    def get_discrete_url(
        self,
        og_first=True, canonical_first=False, allow_invalid=False
    ):
        """convenience method.
            if `allow_invalid` is True, it will return the raw data.
            if `allow_invalid` is False (default), it will try to correct
                the data (relative to absolute) or reset to None.
        """
        og = self.get_metadata('url', strategy=['og'])
        canonical = self.get_metadata('canonical', strategy=['page'])

        if not allow_invalid:

            # fallback url is used to drop a domain
            url_fallback = self.url_actual or self.url or None

            if og and not is_url_valid(
                og,
                require_public_netloc=self.require_public_netloc
            ):
                # try making it absolute
                og = url_to_absolute_url(
                    og,
                    url_fallback=url_fallback,
                    require_public_netloc=self.require_public_netloc
                )
                if not is_url_valid(
                    og,
                    require_public_netloc=self.require_public_netloc
                ):
                    # set to NONE if invalid
                    og = None

            if canonical and not is_url_valid(
                canonical,
                require_public_netloc=self.require_public_netloc
            ):
                # try making it absolute
                canonical = url_to_absolute_url(
                    canonical,
                    url_fallback=url_fallback,
                    require_public_netloc=self.require_public_netloc
                )
                if not is_url_valid(
                    canonical,
                    require_public_netloc=self.require_public_netloc
                ):
                    # set to NONE if invalid
                    canonical = None

        rval = []
        if og_first:
            rval = (og, canonical)
        elif canonical_first:
            rval = (canonical, og)

        for i in rval:
            if i:
                return i

        return self.absolute_url()

    def get_metadata_link(self, field, strategy=None):
        """sometimes links are bad; this tries to fix them.  most useful for meta images"""
        # `_value` will be our raw value
        _value = self.get_metadata(field, strategy=strategy)
        if not _value:
            return None
        # `value` will be our clean value
        # remove whitespace, because some bad blogging platforms add in whitespace by printing elements on multiple lines. d'oh!
        value = RE_whitespace.sub('', _value)

        # if the url is valid, RETURN IT
        if is_url_valid(value, require_public_netloc=self.require_public_netloc):
            return value

        # fallback url is used to drop a domain
        url_fallback = self.url_actual or self.url or None

        # try making it absolute
        value_fixed = url_to_absolute_url(
            value,
            url_fallback = url_fallback,
            require_public_netloc = self.require_public_netloc
        )
        if is_url_valid(value_fixed, require_public_netloc=self.require_public_netloc):
            return value_fixed

        return None


#!/usr/bin/env python
import argparse

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="split blast results by organism")
    parser.add_argument("blast", type=argparse.FileType("r"))
    args = parser.parse_args()

    blast = [x.split("\t") for x in args.blast.readlines()]

    for row in blast:
        if "<>" in row[24]:
            for i in row[24].strip().split("<>"):
                row_copy = row
                row_copy[24] = i
                print("\t".join(row_copy).strip())
        else:
            print("\t".join(row).strip())


def plunder(city, people_to_kill, gold_to_steal):
    town = [t for t in towns if t.name == city][0]
    town.population -= people_to_kill
    town.gold -= gold_to_steal
    print(f"{city} plundered! {gold_to_steal} gold stolen, {people_to_kill} citizens killed.")
    if town.population <= 0 or town.gold <= 0:
        towns.remove(town)
        print(f"{city} has been wiped off the map!")

def prosper(city, gold_to_add):
    town = [t for t in towns if t.name == city][0]
    if gold_to_add < 0:
        print("Gold added cannot be a negative number!")
    else:
        town.gold += gold_to_add
        print(f"{gold_to_add} gold added to the city treasury. {city} now has {town.gold} gold.")

class Town:
    def __init__(self, name, population, gold):
        self.name = name
        self.population = population
        self.gold = gold

    def __repr__(self):
        return f"{self.name} -> Population: {self.population} citizens, Gold: {self.gold} kg"

towns = []
while True:
    command = input()
    if command == "Sail":
        break
    tokens = command.split("||")
    current_town = tokens[0]
    current_population = int(tokens[1])
    current_gold = int(tokens[2])

    if towns:
        existing_town = [t for t in towns if t.name == current_town]
        if existing_town:
            existing_town[0].population += current_population
            existing_town[0].gold += current_gold
            continue
    town = Town(current_town, current_population, current_gold)
    towns.append(town)

while True:
    command = input()
    if command == "End":
        break
    tokens = command.split("=>")
    if tokens[0] == "Plunder":
        plunder(tokens[1], int(tokens[2]), int(tokens[3]))
    elif tokens[0] == "Prosper":
        prosper(tokens[1], int(tokens[2]))

sorted_towns = sorted(towns, key=lambda t: (-t.gold, t.name))

if towns:
    print(f"Ahoy, Captain! There are {len(towns)} wealthy settlements to go to:")
    for town in sorted_towns:
        print(town)
else:
    print("Ahoy, Captain! All targets have been plundered and destroyed!")


import pika
import sys

conn = pika.BlockingConnection(pika.URLParameters('amqp://guest:guest@localhost:25672/%2F'))
channel = conn.channel()

channel.exchange_declare(exchange='direct_logs', exchange_type='direct')

severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
message = ' '.join(sys.argv[2:]) or "Hello World!"
channel.basic_publish(
    exchange='direct_logs', routing_key=severity, body=message
)
print(f" [*] Sent {severity}:{message}")
conn.close()


from django.conf.urls import url

from network.views import views_auth
from network.views import views_app

urlpatterns = [

    # Main page
    url(r'^home/(?P<msg>.*)$', views_auth.main_page, name="Home"),
    # url(r'$', views_auth.main_page, name="Home"),
    # Auth urls
    url(r'^login/(?P<info>.*)$', views_auth.login_page, name="Login"),
    url(r'^logout', views_auth.logout_page, name="Logout"),
    url(r'^registration', views_auth.registration_page),

    # App urls
    url(r'^userpage/(?P<usr_id>.[0-9])', views_app.user_page, name="UserPage"),
    url(r'^userpage/wall/new_record', views_app.new_wall_record, name="NewWallRecord"),
    url(r'^userpage/wall/new_like', views_app.new_like, name="AddLike"),
    url(r'^userpage/wall/new_comment', views_app.new_comment, name="AddComment"),
    url(r'^userpage/wall/delete_post', views_app.delete_post, name="DeletePost"),

    url(r'^error/', views_app.error_page, name="UserPage"),
    url(r'^im/', views_app.mail_page, name="UserMail"),
    url(r'^send_msg/(?P<user_id>.[0-9])', views_app.send_msg, name="SendMessage"),
    url(r'^friend_request/', views_app.send_friend_request, name="FriendRequest"),
    url(r'^friends/', views_app.user_friends, name="Friends"),
    url(r'^delete_friend/', views_app.delete_friend, name="Delete Friend"),

    url(r'^sent/', views_app.user_sent_msgs, name="Sent msgs"),

    url(r'^requests/', views_app.user_requests, name="Friend requests"),

    url(r'^accept_request/', views_app.accept_request, name="Accept_request"),
    url(r'^decline_request/', views_app.decline_request, name="Decline_request")

]


import numpy as np

def linan(s1, s2):
    try:
        a1, b1, c1 = map(float, s1.split(" "))
        a2, b2, c2 = map(float, s2.split(" "))
        a = np.array([[a1, b1], [a2, b2]])
        b = np.array([c1, c2])
        solution = np.linalg.solve(a, b)
        return f"{solution[0]} {solution[1]}"
    except np.linalg.LinAlgError as e:
        return "Нет решений"

str1 = input()
str2 = input()
print(linan(str1, str2))