#!/usr/bin/env python
"""Test file for the physbiblio.argParser module.

This file is part of the physbiblio package.
"""
import sys
import traceback
import unittest
from io import StringIO
from unittest.mock import MagicMock, patch

from PySide6.QtWidgets import QApplication

try:
    from physbiblio import __version__, __version_date__
    from physbiblio.argParser import *
    from physbiblio.config import pbConfig
    from physbiblio.database import pBDB
    from physbiblio.export import pBExport
    from physbiblio.gui.mainWindow import MainWindow
    from physbiblio.gui.setuptests import *
    from physbiblio.setuptests import *
except ImportError:
    print("Could not find physbiblio and its modules!")
    raise
except Exception:
    print(traceback.format_exc())

@patch("logging.Logger.debug")
@patch("logging.Logger.info")
@patch("logging.Logger.warning")
@patch("logging.Logger.exception")
class TestParser(unittest.TestCase):
    """Tests for main parser and stuff"""

    @patch("sys.stdout", new_callable=StringIO)
    def assert_in_stdout_sysexit(self, function, expected_output, mock_stdout):
        """Catch and if test stdout of the function contains a string"""
        with self.assertRaises(SystemExit):
            function()
        string = mock_stdout.getvalue()
        for text in expected_output:
            self.assertIn(text, string)

    @patch("sys.stderr", new_callable=StringIO)
    def assert_in_stderr_sysexit(self, function, expected_output, mock_stderr):
        """Catch and if test stderr of the function contains a string"""
        with self.assertRaises(SystemExit):
            function()
        string = mock_stderr.getvalue()
        for text in expected_output:
            self.assertIn(text, string)

    def test_global(self, *args):
        """Test that the options are recognised correctly"""
        parser = setParser()
        for opt in ("-v", "--version"):
            with self.assertRaises(SystemExit):
                parser.parse_args([opt])
            self.assert_in_stdout_sysexit(
                lambda: parser.parse_args([opt]),
                ["PhysBiblio %s (%s)" % (__version__, __version_date__)],
            )
        for opt in ("-h", "--help"):
            with self.assertRaises(SystemExit):
                parser.parse_args([opt])
            self.assert_in_stdout_sysexit(
                lambda: parser.parse_args([opt]),
                [
                    "usage: PhysBiblio.exe [-h] [-p ",
                    "{citations,clean,cli,daily,dates,duplicates,export,test,tex,update,weekly,gui}",
                ],
            )
        for opt in ("-p", "--profile"):
            profile = list(pbConfig.profiles.keys())[0]
            with patch("physbiblio.cli.cli", autospec=True) as mock, patch(
                "logging.Logger.info"
            ) as _i:
                parser.parse_args([opt, "%s" % profile, "cli"])
                self.assertIn(
                    "Starting with profile '%s', database" % profile, _i.call_args[0][0]
                )
                args = parser.parse_args([opt, "%s" % profile, "cli"])
                args.func(args)
                mock.assert_called_once_with()

    def test_subcommands_ok(self, *args):
        """Test that the options are recognised correctly"""
        parser = setParser()
        tests = [
            ["citations", call_citationCount],
            ["gui", call_gui],
            ["test", call_tests],
        ]
        for sub, func in tests:
            args = parser.parse_args([sub])
            self.assertIs(args.func, func)
        tests = [
            [
                "clean",
                "physbiblio.database.Entries.cleanBibtexs",
                [],
                ([pBDB.bibs], {"startFrom": 0}),
            ],
            [
                "clean",
                "physbiblio.database.Entries.cleanBibtexs",
                ["-s", "100"],
                ([pBDB.bibs], {"startFrom": 100}),
            ],
            [
                "clean",
                "physbiblio.database.Entries.cleanBibtexs",
                ["--startFrom", "1000"],
                ([pBDB.bibs], {"startFrom": 1000}),
            ],
            ["cli", "physbiblio.cli.cli", [], ([], {})],
            [
                "daily",
                "physbiblio.database.Entries.getDailyInfoFromOAI",
                [],
                (
                    [
                        pBDB.bibs,
                        (datetime.date.today() - datetime.timedelta(1)).strftime(
                            "%Y-%m-%d"
                        ),
                        datetime.date.today().strftime("%Y-%m-%d"),
                    ],
                    {},
                ),
            ],
            [
                "dates",
                "physbiblio.database.Entries.getDailyInfoFromOAI",
                ["2018-04-01", "2018-05-01"],
                ([pBDB.bibs, "2018-04-01", "2018-05-01"], {}),
            ],
            [
                "duplicates",
                "physbiblio.database.Entries.checkDuplicates",
                [],
                ([pBDB.bibs], {}),
            ],
            [
                "export",
                "physbiblio.export.PBExport.exportAll",
                ["testname"],
                ([pBExport, "testname"], {"citations": False}),
            ],
            [
                "export",
                "physbiblio.export.PBExport.exportAll",
                ["testname", "-c"],
                ([pBExport, "testname"], {"citations": True}),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": True,
                        "overwrite": False,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "-o"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": True,
                        "overwrite": True,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "--overwrite"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": True,
                        "overwrite": True,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "--ov"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": True,
                        "overwrite": True,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "-s"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": False,
                        "overwrite": False,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "--save"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": False,
                        "overwrite": False,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "--sa"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": False,
                        "overwrite": False,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "f3", "--sa"],
                (
                    [pBExport, ["f2", "f3"], "f1"],
                    {
                        "autosave": False,
                        "overwrite": False,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "-u"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": True,
                        "overwrite": False,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": True,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "--updateExisting"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": True,
                        "overwrite": False,
                        "removeUnused": False,
                        "reorder": False,
                        "updateExisting": True,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "-r"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": True,
                        "overwrite": False,
                        "removeUnused": True,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "--removeUnused"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": True,
                        "overwrite": False,
                        "removeUnused": True,
                        "reorder": False,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "tex",
                "physbiblio.export.PBExport.exportForTexFile",
                ["f1", "f2", "--reorder"],
                (
                    [pBExport, ["f2"], "f1"],
                    {
                        "autosave": True,
                        "overwrite": False,
                        "removeUnused": False,
                        "reorder": True,
                        "updateExisting": False,
                    },
                ),
            ],
            [
                "update",
                "physbiblio.database.Entries.searchOAIUpdates",
                [],
                ([pBDB.bibs], {"startFrom": 0, "force": False}),
            ],
            [
                "update",
                "physbiblio.database.Entries.searchOAIUpdates",
                ["-f"],
                ([pBDB.bibs], {"startFrom": 0, "force": True}),
            ],
            [
                "update",
                "physbiblio.database.Entries.searchOAIUpdates",
                ["--force"],
                ([pBDB.bibs], {"startFrom": 0, "force": True}),
            ],
            [
                "update",
                "physbiblio.database.Entries.searchOAIUpdates",
                ["-s", "100"],
                ([pBDB.bibs], {"startFrom": 100, "force": False}),
            ],
            [
                "update",
                "physbiblio.database.Entries.searchOAIUpdates",
                ["--startFrom", "100"],
                ([pBDB.bibs], {"startFrom": 100, "force": False}),
            ],
            [
                "weekly",
                "physbiblio.database.Entries.getDailyInfoFromOAI",
                [],
                (
                    [
                        pBDB.bibs,
                        (datetime.date.today() - datetime.timedelta(7)).strftime(
                            "%Y-%m-%d"
                        ),
                        datetime.date.today().strftime("%Y-%m-%d"),
                    ],
                    {},
                ),
            ],
        ]
        with patch(
            "physbiblio.database.PhysBiblioDB.commit", autospec=True
        ) as _comm_mock:
            for sub, func, options, expected in tests:
                with patch(func, autospec=True) as mock:
                    args = parser.parse_args([sub] + options)
                    args.func(args)
                    mock.assert_called_once_with(*expected[0], **expected[1])

    def test_subcommands_fail(self, *args):
        """Test that the options are recognised correctly."""
        parser = setParser()
        tests = [
            ["clean", ["-f"]],
            ["clean", ["-s"]],
            ["clean", ["-s", "abc"]],
            ["clean", ["--startfrom", "1000"]],
            ["cli", ["-t"]],
            ["daily", ["date"]],
            ["dates", ["date1"]],
            ["export", ["testname1", "testname2"]],
            ["gui", ["-p"]],
            ["test", ["-f"]],
            ["tex", ["f1"]],
            ["tex", ["f1", "f2", "--overWrite"]],
            ["tex", ["f1", "f2", "-save"]],
            ["update", ["-o"]],
            ["update", ["-s", "abc"]],
            ["update", ["--startFrom"]],
            ["weekly", ["any"]],
        ]
        with patch(
            "physbiblio.database.PhysBiblioDB.commit", autospec=True
        ) as _comm_mock:
            for sub, options in tests:
                with self.assertRaises(SystemExit):
                    args = parser.parse_args([sub] + options)

    def test_subcommand_tests(self, *args):
        """Test that the options are recognised correctly"""
        from physbiblio.setuptests import skipTestsSettings

        oldSettings = skipTestsSettings.copy()
        parser = setParser()
        tests = [
            [["test"], False, False, False, False],
            [["test", "-d"], True, False, False, False],
            [["test", "--database"], True, False, False, False],
            [["test", "-g"], False, True, False, False],
            [["test", "--gui"], False, True, False, False],
            [["test", "-l"], False, False, True, False],
            [["test", "--long"], False, False, True, False],
            [["test", "-o"], False, False, False, True],
            [["test", "--online"], False, False, False, True],
            [["test", "-d", "--online"], True, False, False, True],
        ]
        for options, _db, _gui, _lon, _onl in tests:
            skipTestsSettings.default()
            with patch("unittest.runner.TextTestRunner.run", autospec=True) as _run:
                args = parser.parse_args(options)
                call_tests(args)
                self.assertEqual(skipTestsSettings.db, _db)
                self.assertEqual(skipTestsSettings.gui, _gui)
                self.assertEqual(skipTestsSettings.long, _lon)
                self.assertEqual(skipTestsSettings.online, _onl)
                self.assertEqual(_run.call_count, 1)
        with self.assertRaises(SystemExit):
            args = parser.parse_args(["test", "-a"])
        skipTestsSettings = oldSettings

    def test_call_citationCount(self, *args):
        """test call_citationCount"""
        with patch(
            "physbiblio.database.Entries.getAll",
            return_value=[{"inspire": "a"}, {"inspire": "b"}],
        ) as _ga, patch("physbiblio.database.Entries.citationCount") as _cc, patch(
            "physbiblio.databaseCore.PhysBiblioDBCore.commit"
        ) as _c:
            call_citationCount("args")
            _ga.assert_called_once_with()
            _cc.assert_called_once_with(["a", "b"])
            _c.assert_called_once_with()

    def test_call_clean(self, *args):
        """test call_clean"""
        args = NameSpace()
        args.startFrom = 12
        with patch("physbiblio.database.Entries.cleanBibtexs") as _f, patch(
            "physbiblio.databaseCore.PhysBiblioDBCore.commit"
        ) as _c:
            call_clean(args)
            _f.assert_called_once_with(startFrom=12)
            _c.assert_called_once_with()

    def test_call_cli(self, *args):
        """test call_cli"""
        with patch("physbiblio.cli.cli") as _f:
            call_cli(args)
            _f.assert_called_once_with()

    def test_call_duplicates(self, *args):
        """test call_duplicates"""
        with patch(
            "physbiblio.database.Entries.checkDuplicates", return_value="abcd"
        ) as _f, patch("physbiblio.database.Entries.printDuplicates") as _p:
            call_duplicates(args)
            _f.assert_called_once_with()
            _p.assert_called_once_with("abcd")

    def test_call_export(self, *args):
        """test call_export"""
        args = NameSpace()
        args.filename = "abc"
        args.citations = True
        with patch("physbiblio.export.PBExport.exportAll") as _f:
            call_export(args)
            _f.assert_called_once_with("abc", citations=True)

    def test_call_tex(self, *args):
        """test call_tex"""
        args = NameSpace()
        args.texFiles = ["a", "b", "c"]
        args.bibFile = "bib"
        args.overwrite = "o"
        args.save = "s"
        args.updateExisting = "u"
        args.removeUnused = "r"
        args.reorder = "d"
        with patch("physbiblio.export.PBExport.exportForTexFile") as _f:
            call_tex(args)
            _f.assert_called_once_with(
                ["a", "b", "c"],
                "bib",
                overwrite="o",
                autosave="s",
                updateExisting="u",
                removeUnused="r",
                reorder="d",
            )

    def test_call_update(self, *args):
        """test call_update"""
        args = NameSpace()
        args.startFrom = 12
        args.force = "f"
        with patch("physbiblio.database.Entries.searchOAIUpdates") as _f, patch(
            "physbiblio.databaseCore.PhysBiblioDBCore.commit"
        ) as _c:
            call_update(args)
            _f.assert_called_once_with(startFrom=12, force="f")
            _c.assert_called_once_with()

    def test_cumulativeInspireDates(self, *args):
        """test cumulativeInspireDates"""
        with patch("physbiblio.database.Entries.getDailyInfoFromOAI") as _f, patch(
            "physbiblio.databaseCore.PhysBiblioDBCore.commit"
        ) as _c:
            cumulativeInspireDates("a", "b")
            _f.assert_called_once_with("a", "b")
            _c.assert_called_once_with()

    def test_call_dates(self, *args):
        """test call_dates"""
        args = NameSpace()
        args.start = "s"
        args.end = "e"
        with patch("physbiblio.argParser.cumulativeInspireDates") as _f:
            call_dates(args)
            _f.assert_called_once_with("s", "e")

    def test_call_daily(self, *args):
        """test call_daily"""
        with patch("physbiblio.argParser.cumulativeInspireDates") as _f:
            call_daily("args")
            _f.assert_called_once_with(
                (datetime.date.today() - datetime.timedelta(1)).strftime("%Y-%m-%d"),
                datetime.date.today().strftime("%Y-%m-%d"),
            )

    def test_call_weekly(self, *args):
        """test call_weekly"""
        with patch("physbiblio.argParser.cumulativeInspireDates") as _f:
            call_weekly("args")
            _f.assert_called_once_with(
                (datetime.date.today() - datetime.timedelta(7)).strftime("%Y-%m-%d"),
                datetime.date.today().strftime("%Y-%m-%d"),
            )

    def test_call_gui(self, *args):
        """test the call_gui function"""
        mw = MainWindow()
        mw.show = MagicMock()
        mw.raise_ = MagicMock()
        mw.recentChanges = MagicMock()
        mw.errormessage = MagicMock()
        with patch(
            "physbiblio.argParser.QApplication", return_value=globalQApp
        ) as _qa, patch(
            "physbiblio.gui.mainWindow.MainWindow", return_value=mw
        ) as _mw, patch(
            "physbiblio.config.ConfigurationDB.update"
        ) as _u, patch(
            "physbiblio.config.GlobalDB.commit"
        ) as _c, patch(
            "sys.exit"
        ) as _e:
            with patch.dict(
                pbConfig.params, {"openSinceLastUpdate": __version__}, clear=False
            ):
                call_gui([])
            _qa.assert_called_once_with(sys.argv)
            _mw.assert_called_once_with()
            mw.show.assert_called_once_with()
            mw.raise_.assert_called_once_with()
            _e.assert_called_once_with(0)
            self.assertEqual(mw.recentChanges.call_count, 0)
            with patch.dict(
                pbConfig.params,
                {"openSinceLastUpdate": __version__ + "abc"},
                clear=False,
            ):
                call_gui([])
            mw.recentChanges.assert_called_once_with()
            _u.assert_called_once_with("openSinceLastUpdate", __version__)
            _c.assert_called_once_with()

    @classmethod
    def tearDownModule(self):
        sys.excepthook = sys.__excepthook__

if __name__ == "__main__":
    unittest.main()


"""Tests for linear solver information operators."""

import pathlib

import numpy as np
from pytest_cases import parametrize_with_cases

from probnum.linalg.solvers import LinearSolverState, information_ops

case_modules = (pathlib.Path(__file__).parent / "cases").stem
cases_information_ops = case_modules + ".information_ops"
cases_states = case_modules + ".states"

@parametrize_with_cases("info_op", cases=cases_information_ops)
@parametrize_with_cases("state", cases=cases_states, has_tag=["has_action"])
def test_returns_ndarray_or_scalar(
    info_op: information_ops.LinearSolverInformationOp, state: LinearSolverState
):
    observation = info_op(state)
    assert isinstance(observation, np.ndarray) or np.isscalar(observation)


import os
from setuptools import setup, find_packages

def readfile(filename):
    """ Utility Function to Read the Readme File """
    return open(os.path.join(os.path.dirname(__file__), filename)).read()

setup(
    name="databrowse",
    author="Tyler Lesthaeghe, Nathan Scheirer",
    author_email="tylerl@iastate.edu, scheirer@iastate.edu",
    description="An Extensible Data Management Platform",
    keywords="databrowse data management",
    url="http://limatix.org",
    version='0.8.4',
    packages=find_packages(exclude=['tests', 'test_*']),
    package_data={'': ['*.conf', '*.xml']},
    include_package_data=True,
    license="BSD-3",
    long_description=readfile('README.md'),
    test_suite='nose.collector',
    zip_safe=False,
    classifiers=[
        "Development Status :: 5 - Production/Stable",
        "Environment :: Web Environment",
        "Intended Audience :: Science/Research",
        "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
        "Natural Language :: English",
        "Operating System :: OS Independent",
        "Programming Language :: Python :: 2",
        "Programming Language :: JavaScript",
        "Topic :: Internet :: WWW/HTTP :: WSGI :: Application",
        "Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware",
        "Topic :: Scientific/Engineering :: Information Analysis",
        "Topic :: Software Development :: Libraries",
        "Topic :: Utilities"
    ],
    entry_points={
        'console_scripts': [
            'databrowse = cefdatabrowse.cefdatabrowse:main'
        ]
    }
)


"""
A simple Python/matplotlib implementation of Conway's Game of Life.

Author: Maosen Hu
"""

import sys, argparse
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

from src.conway import Conway

# main() function
def main():
    # Command line args are in sys.argv[1], sys.argv[2] ..
    # sys.argv[0] is the script name itself and can be ignored
    # parse arguments
    parser = argparse.ArgumentParser(description="Runs Conway's Game of Life simulation.")
    # add arguments
    parser.add_argument('--grid-size', dest='N', required=False)
    parser.add_argument('--mov-file', dest='movfile', required=False)
    parser.add_argument('--interval', dest='interval', required=False)
    parser.add_argument('--glider', action='store_true', required=False)
    parser.add_argument('--gosper', action='store_true', required=False)
    args = parser.parse_args()

    conway = Conway()
    # set grid size
    conway.setGridSize(100)
    if args.N and int(args.N) > 8:
        conway.setGridSize(int(args.N))

    # set animation update interval
    updateInterval = 50
    if args.interval:
        updateInterval = int(args.interval)

    # check if "glider" demo flag is specified
    grid = conway.initGrid()
    if args.glider:
        conway.addGlider(1, 1, grid)
    elif args.gosper:
        conway.addGosperGliderGun(10, 10, grid)
    else:
        # populate grid with random on/off - more off than on
        grid = conway.randomGrid()

    # set up animation
    fig, ax = plt.subplots()
    img = ax.imshow(grid, interpolation='nearest')
    ani = animation.FuncAnimation(fig, conway.update, fargs=(img, grid),
                                  frames = 10,
                                  interval=updateInterval,
                                  save_count=50)

    # # of frames?
    # set output file
    if args.movfile:
        ani.save(args.movfile, fps=30, extra_args=['-vcodec', 'libx264'])

    plt.show()

# call main
if __name__ == '__main__':
    main()


# -*- coding: utf-8 -*-
"""
Created on Tue Apr  3 18:45:20 2018

@author: Abhinav
"""

#Install flask- A web dev framework in "anaconda prompt"
#pip install Flask==0.12.2

#____________________________________________________________________________________
#PACKAGES

import datetime #Block needs a timestamp
import hashlib  #Hash the blocks
import json    #Retrieve JSON attributes
from flask import Flask,jsonify #For web applications and returning messages in Postman respectively
import requests # Catch the right nodes, older nodes have been synced
from uuid import uuid4
from urllib.parse import urlparse

#_____________________ _______________________________________________________________

#PROGRAMMING TIPS: Since blockchain is a huge application ,
    #that involves decentralized network connections, always code in CLASSES and not FUNCTIONS

#----------------------------------------------BUILD BLOCKCHAIN------------------------------

    # Attention: Proof is Nonce in this case
class Blockchain:

    #Init method is important, similar to constructor , to initialize variables.
    def __init__(self):
        self.chain = [] #Initialize Blockchain variable- This will hold different blocks.
        self.transactions =[] #Transactions make blockchain cryptocurrency. They need to be before createBlock function else chain will be initialized without transactions.
        self.createBlock(proof = 1, prev_hash='0')
        #METHOD ABOVE is GENESIS Block generator that will accept:
        #proof i.e sr.no (which will be 1 since genesis block is the first block)
        #and previous_hash as parameters.
        #NOTE: prev_hash accepts string variables because SHA256 only accepts encoded strings.
        self.nodes=set()

    #Method to create block, that is declared above
    def createBlock(self, proof,prev_hash):

        #Dictionary to hold following variables of a block: sr_no, prev_hash, hash, data
        block={
                'index': len(self.chain) +1,
                'timestamp': str(datetime.datetime.now()),
                'proof': proof,
                'prev_hash': prev_hash,
                'transactions': self.transactions}
        #After adding the transactions in the block, list should be empty
        self.transactions =[]
        #Add block to the blockchain
        self.chain.append(block)
        return block

    def getLastBlock(self):
        return self.chain[-1]   #-1 is the last element in a list

    #This method defines, problem statement and the solution to the problem
    def workProof(self, prev_proof):
        new_proof = 1
        #To check if proof is valid
        isProof = False

        while (isProof is False):
            #Defining problem- check if current proof has four 0s in the start.
            #If the statement is true, isProof = True

            #Function below produces a hashcode by adding previous proof and new proof
            #Operation should be ASYMMETRICAL
                #Symmetrical => a+b (cause a+b = b+a)
                #Asymmetrical => a-b (a-b != b-a)
            #STEP 1:
            #PRoduce a hashcode
            hash_operation = hashlib.sha256(str(new_proof**2 - prev_proof**2).encode()).hexdigest()

            #STEP 2: Check if hashcode has 4 0s:
            #MIND WELL , its [:4] for it checks elements from 0 to 3 (excluding 4)
            if(hash_operation[:4] == '0000'):
                isProof = True
            else:
                new_proof +=1   #Increment new proof
            #End of while loop
        return new_proof #Return proof (NONCE)

    #End of function

    def hashFunction(self,block):
        encoded_block = json.dumps(block,sort_keys = True).encode()
        return hashlib.sha256(encoded_block).hexdigest()

    #Check if Blockchain is valid by Checking:
        #if prev_hash property of a block is similar to hash of previous block
        # Proo(Nonce) is valid

    def checkValidBlockChain(self, chain):
        #Variable initializations before while loop
        prev_block = chain[0]   #First block in BlockChain
        block_index = 1 #Block index in the dictionary. Starts with 1

        #Here we go with the iterations BABAY!!!!!!
        while(block_index <len(chain)):
            block = chain[block_index]

            #Check COndition 1!
            #Don't get confused with this line, read carefully:
                #If Block of previous hash is not equal to hash of previous block.
            if(block['prev_hash']!= self.hashFunction(prev_block)):
                return False

            #Check Condition 2
            prev_proof = prev_block['proof']
            proof = block['proof']
            hash_operation = hashlib.sha256(str(proof**2 - prev_proof**2).encode()).hexdigest()
            #Check first 4 characters are 0000
            if (hash_operation[:4]!='0000'):
                return False
            #increment in loop
            prev_block = block
            block_index +=1
        #If everything is valid
        return True

        #Method to add transaction: It'll hold 3 important elements- Sender,Reciever, Amount
    def addTransaction(self,sender, receiver,amt):
        self.transactions.append({'sender':sender,
                                  'receiver':receiver,
                                  'amount': amt})
        #Need to append the transaction in the latest block that'll be mined. For this we need the last block mined
        prev_block = self.getLastBlock()
        return prev_block['index'] +1 #Returns index of the new block
    #NOTE: FOR THE FUNCTION ABOVE, BLOCK NEEDS TO BE MINED FIRST TO ADD TRANSACTIONS.

    #Will accept node and its address
    def addNode(self,address):
        #Parse the address of the node
        parsed_url=urlparse(address) #Parse url and retrieves a set of properties i.e protocol=http, netloc=ip address + port
        self.nodes.add(parsed_url.netloc) #add only netloc cause that's what we need
   #Solves the consesus problem i.e replacing a shorter chain with larger chain
    def chainChecker(self):
        nw=self.nodes #Network of all the nodes
        lc = None #Longest chain
        max_len=len(self.chain)
        for node in nw:
             res=requests.get(f'http://{node}/get_all_blocks')
             #Check if the chain retrieved from node is valid, Valid request returns 200 response
             if(res.status_code==200):
                 length= res.json()['blocks']
                 chain= res.json()['blockchain']

                 if length > max_len and self.checkValidBlockChain(chain):
                     max_len=length
                     lc=chain
        if lc:
            self.chain=lc
            return True
        return False


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import time
import pygame
import random

from pygame.locals import *

random.seed()

pygame.init()
screen = pygame.display.set_mode((600, 500))
pygame.display.set_caption("Draw lines")

font = pygame.font.Font(None, 60)

def draw_lines(fixed=False):
	width = 8

	if fixed:
		color = 255, 255, 0
		pygame.draw.line(screen, color, (100, 100), (300, 300), width)
		return None

	color = random.randint(0, 255), random.randint(0,255), random.randint(0, 10)

	x1 = random.randint(0, 600)
	y1 = random.randint(0, 500)

	x2 = random.randint(0, 600)
	y2 = random.randint(0, 500)

	pygame.draw.line(screen, color, (x1, y1), (x2, y2), width)

line = 0
while True:
	for evt in pygame.event.get():
		if evt.type in (QUIT, KEYDOWN):
			sys.exit()

	screen.fill((0, 0, 120))

	if line <= 1000:
		draw_lines()
		line += 1
		screen.blit(
			font.render("line: {0}".format(line), True, (255, 255, 0)),
			(50, 50)
			)

		time.sleep(0.01)
	else:
		draw_lines(True)

	pygame.display.update()


from bs4 import BeautifulSoup
import requests
from datetime import datetime
import replace_words
import google_translate

now = datetime.now()
year = now.year
month = now.month
day = now.day

url = input('crypto news: ')
resp = requests.get(url)
soup = BeautifulSoup(resp.text, 'html.parser')
print(resp.status_code)
print('-'*20)

# 排除不必要的內容
if soup.find_all('div', 'disclaimer'):
    for i in soup.find_all('div', 'disclaimer'):
        i.extract()
if soup.find_all(class_='sc-bdfBwQ iumvLK'):
    for i in soup.find_all(class_='sc-bdfBwQ iumvLK'):
        i.extract()
if soup.find_all('em'):
    for i in soup.find_all('em'):
        i.extract()

title = soup.find('h1', 'sc-bdfBwQ Text-msjfkz-0 Heading-juwhnu-0 hoXOTC dAnDNe')
title = title.text
print(title)
summary = soup.find('div', 'sc-bdfBwQ ArticleContent__ArticleSummaryBox-sc-18n1x4l-1 ihJtHv dwcfaP')
summary = summary.text
print(summary)
article = soup.find('article', 'sc-bdfBwQ Container-sc-4c5vqs-0 ArticleContent__ArticleContainer-sc-18n1x4l-0 cdVqKV khdEkh ljODsN')
article = article.text
print(article)
print('-'*20)

unedited = google_translate.translate_text(text = f'{title}\n{summary}\n{article}')
result = replace_words.replacement_func(unedited)

path = f'/Users/jacobhuang/news/{month}/{title}.txt'
with open(path,'a') as file:
    file.writelines(f'{url}\n\n{title}\n\n{result}')


import os

from astropy.io import ascii as asc
import astropy.units as u

import numpy as np
from matplotlib import pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages

from utilities_az.visualization import zscale
from utilities_az import define_filters

class SnecAnalysis(object):
    def __init__(self, snname, base_dir, S2_start, S2_end, ni_mass,
                 ni_mixing, masses, energies, time_offsets,
                 Kvalues, radii, fig_dir=None):
        self.name = snname
        self.base_dir = base_dir
        self.S2_start = S2_start
        self.S2_end = S2_end
        self.ni_mass = ni_mass
        self.ni_mixing = ni_mixing
        self.masses = masses
        self.energies = energies
        self.time_offsets = time_offsets
        self.Kvalues = Kvalues
        self.radii = radii
        self.fig_dir = fig_dir
        self.chisq = None

    def get_breakout_time(self, model_dir):
        ofile = open(os.path.join(model_dir, 'info.dat'), 'r')
        all_lines = ofile.readlines()
        if len(all_lines)>6:
            time_breakout = float((all_lines[5].split('=')[1]).strip('seconds\n'))
        else: #SN never got to breakout
            time_breakout = None
        return time_breakout

    def prepare_model_data(self, model_dir):
        model_mag_tbdata = asc.read(os.path.join(model_dir,'magnitudes.dat'),
                                    names=['time', 'temp', 'weird_mag',
                                           'u', 'g', 'r', 'i', 'z', 'U',
                                           'B', 'V', 'R', 'I'])
        #Observers call t_explosion the time when the explosion is first visible, therefore
        #the models should use this same conventions
        time_breakout = self.get_breakout_time(model_dir)
        if time_breakout is not None:
            model_mag_tbdata['time'] = ((model_mag_tbdata['time']-time_breakout)*u.second).to(u.day).value
        else:
            model_mag_tbdata=None
        return model_mag_tbdata

    def calc_chisq(self, sn_lc):
        skip_filters = []
        start_now = True
        if not os.path.exists('chisq_table.txt'):
            ofile = open('chisq_table.txt', 'w')
        else:
            ofile = open('chisq_table.txt', 'r')
            all_lines = ofile.readlines()
            if len(all_lines) > 0:
                last_line = all_lines[-1]
                split_last_line = last_line.split(',')
                last_complete_dir = os.path.join(self.base_dir,
                                 'Ni_mass_{:1.4f}'.format(float(split_last_line[0])),
                                 'Ni_mixing_{:1.1f}'.format(float(split_last_line[1])),
                                 'M{:2.1f}'.format(float(split_last_line[2])),
                                 'E_{:1.3f}'.format(float(split_last_line[3])),
                                 'K_{:2.1f}'.format(float(split_last_line[4])),
                                 'R_{}'.format(int(split_last_line[5])),
                                 'Data')
                start_now = False
        with open('chisq_table.txt', 'a') as output_ofile:
            with open('missing_mag_files.txt', 'a') as missing_ofile:
                chisq = np.ones((len(self.ni_mass),
                                 len(self.ni_mixing),
                                 len(self.masses),
                                 len(self.energies),
                                 len(self.Kvalues),
                                 len(self.radii),
                                 len(self.time_offsets)))*np.nan
                for ni_mindx, i_ni_mass in enumerate(self.ni_mass):
                    for ni_indx, i_ni_mix in enumerate(self.ni_mixing):
                        for mindx, imass in enumerate(self.masses):
                            for eindx, ienergy in enumerate(self.energies):
                                for kindx, idensity in enumerate(self.Kvalues):
                                    for rindx, iradius in enumerate(self.radii):
                                        #read in model data
                                        #TODO: make this and prep_snec look at the same piece of code
                                        model_dir = os.path.join(self.base_dir,
                                                                 'Ni_mass_{:1.4f}'.format(i_ni_mass),
                                                                 'Ni_mixing_{:1.1f}'.format(i_ni_mix),
                                                                 'M{:2.1f}'.format(imass),
                                                                 'E_{:1.3f}'.format(ienergy),
                                                                 'K_{:2.1f}'.format(idensity),
                                                                 'R_{}'.format(int(iradius)),
                                                                 'Data')
                                        if start_now is False:
                                            if model_dir != last_complete_dir:
                                                continue
                                            else:
                                                start_now = True
                                                continue
                                        if not os.path.exists(os.path.join(model_dir, 'magnitudes.dat')):
                                            missing_ofile.write("Missing Mag File: NiMass={},NiMix={},M={},E={},K={}, R={}\n".format(i_ni_mass, i_ni_mix, imass, ienergy, idensity, iradius))
                                            continue
                                        model_mag_tbdata = self.prepare_model_data(model_dir)
                                        if model_mag_tbdata is not None: #Successful explosion model
                                            chisq_filters = []
                                            #Loop over time shifts
                                            for tindx, toffset in enumerate(self.time_offsets):
                                                #import pdb; pdb.set_trace()
                                                #Loop over filters
                                                for ifilter in sn_lc.abs_mag.keys():
                                                    if (ifilter in model_mag_tbdata.colnames) and (ifilter in ['g', 'r', 'i', 'V']):
                                                        pre_fall_indx = (sn_lc.phase[ifilter]+toffset <=self.S2_end)
                                                        if len(sn_lc.phase[ifilter][pre_fall_indx]) > 5:
                                                            if model_mag_tbdata['time'][-1]-toffset > self.S2_end:
                                                                interp_mod_mag = np.interp(sn_lc.phase[ifilter][pre_fall_indx]+toffset,
                                                                                model_mag_tbdata['time'],
                                                                                model_mag_tbdata[ifilter])
                                                                chisq_tmp = np.sum(((sn_lc.abs_mag[ifilter][pre_fall_indx]-interp_mod_mag)/sn_lc.abs_mag_err[ifilter][pre_fall_indx])**2)
                                                                chisq_filters.append(chisq_tmp)
                                                            else:
                                                                missing_ofile.write("Failed (LC too short) Model: NiMass={},NiMix={},M={},E={},K={}, R={}\n".format(i_ni_mass, i_ni_mix, imass, ienergy, idensity, iradius))
                                                                chisq_filters.append(1E10) #if a model doesn't explode it should never be the best model

                                                        else:
                                                            skip_filters.append(ifilter)
                                                    else:
                                                        skip_filters.append(ifilter)
                                                chisq[ni_mindx, ni_indx, mindx, eindx, kindx, rindx, tindx] = np.sum(np.array(chisq_filters))
                                                output_ofile.write('{},{},{},{},{},{},{},{}\n'.format(i_ni_mass, i_ni_mix, imass, ienergy, idensity, iradius, toffset, chisq[ni_mindx, ni_indx, mindx, eindx, kindx, rindx, tindx]))
                                                output_ofile.flush()
                                        else:
                                            missing_ofile.write("Failed (unexploded) Model: NiMass={},NiMix={},M={},E={},K={}, R={}\n".format(i_ni_mass, i_ni_mix, imass, ienergy, idensity, iradius))
        print('Skipped Filters {} b/c <5 points in fit region'.format(set(skip_filters)))
        self.min_indx_base_mod = np.where(chisq == np.nanmin(chisq))
        self.best_ni_mass = self.ni_mass[self.min_indx_base_mod[0][0]]
        self.best_ni_mix = self.ni_mixing[self.min_indx_base_mod[1][0]]
        self.best_mass = self.masses[self.min_indx_base_mod[2][0]]
        self.best_energy = self.energies[self.min_indx_base_mod[3][0]]
        self.best_Kvalue = self.Kvalues[self.min_indx_base_mod[4][0]]
        self.best_radius = self.radii[self.min_indx_base_mod[5][0]]
        self.best_time_offset = self.time_offsets[self.min_indx_base_mod[6][0]]
        print('Best chi square for Ni mixing={}, Ni mass = {}, mass={}, energy={}, density = {}, radius = {}, time offset={}'.format(
                    self.best_ni_mass,
                    self.best_ni_mix,
                    self.best_mass,
                    self.best_energy,
                    self.best_Kvalue,
                    self.best_radius,
                    self.best_time_offset))

        self.best_model_dir = os.path.join(self.base_dir,
                                         'Ni_mass_{:1.4f}'.format(self.best_ni_mass),
                                         'Ni_mixing_{:1.1f}'.format(self.best_ni_mix),
                                         'M{:2.1f}'.format(self.best_mass),
                                         'E_{:1.3f}'.format(self.best_energy),
                                         'K_{:2.1f}'.format(self.best_Kvalue),
                                         'R_{}'.format(int(self.best_radius)),
                                         'Data')

        self.best_model_tbdata = self.prepare_model_data(self.best_model_dir)
        self.model_chisq = chisq

    def get_best_model(self, return_model=False):
        if not hasattr(self,'tbdata'):
            self.read_chisq()
        best_model_row = np.nanargmin(self.tbdata['chisq'])
        self.best_ni_mass = self.tbdata['ni_mass'][best_model_row]
        self.best_ni_mix = self.tbdata['ni_mixing'][best_model_row]
        self.best_mass = self.tbdata['mass'][best_model_row]
        self.best_energy = self.tbdata['energy'][best_model_row]
        self.best_Kvalue = self.tbdata['kvalue'][best_model_row]
        self.best_radius = self.tbdata['radius'][best_model_row]
        #self.best_Kvalue = 30.0
        #self.best_radius = 2400
        self.best_time_offset = self.tbdata['time_offset'][best_model_row]
        self.best_model_dir = os.path.join(self.base_dir,
                                         'Ni_mass_{:1.4f}'.format(self.best_ni_mass),
                                         'Ni_mixing_{:1.1f}'.format(self.best_ni_mix),
                                         'M{:2.1f}'.format(self.best_mass),
                                         'E_{:1.3f}'.format(self.best_energy),
                                         'K_{:2.1f}'.format(self.best_Kvalue),
                                         'R_{}'.format(int(self.best_radius)),
                                         'Data')
        if return_model is True:
            self.best_model_tbdata = self.prepare_model_data(self.best_model_dir)
        print('Best chi square for Ni mixing={}, Ni mass = {}, mass={}, energy={}, density = {}, radius = {}, time offset={}'.format(
                    self.best_ni_mass,
                    self.best_ni_mix,
                    self.best_mass,
                    self.best_energy,
                    self.best_Kvalue,
                    self.best_radius,
                    self.best_time_offset))

    def plot_lightcurve(self, sn_lc, band='all'):
        self.get_best_model(return_model=True)
        if band == 'all':
            bands = sn_lc.abs_mag.keys()
        elif len(band) == 1:
            bands = [band]
        else:
            bands = band
        offset = 0
        filter_dict = define_filters.define_filters()
        fig = plt.figure()
        ax = fig.add_subplot(1,1,1)
        for iband in filter_dict.keys():
            if (iband in bands) and (iband in self.best_model_tbdata.colnames):
                l = ax.errorbar(sn_lc.phase[iband]+self.best_time_offset, sn_lc.abs_mag[iband]-offset, sn_lc.abs_mag_err[iband], fmt='o', linestyle='none', label='{} data+{}'.format(iband, offset))
                ax.plot(self.best_model_tbdata['time'], self.best_model_tbdata[iband]-offset, color=l[0].get_color(), ls='--', label='{}+{}'.format(iband, offset))

        ax.set_xlabel('Phase (days)')
        ax.set_ylabel('Absolute Magnitude + Offset')
        ax.set_title('Light Curve and SNEC Models for {}'.format(self.name))
        ax.set_ylim(ax.get_ylim()[::-1])
        ax.legend(loc=3, ncol=2)
        plt.tight_layout()
        plt.savefig(os.path.join(self.fig_dir, '{}_snec_run1.pdf'.format(self.name)))

    def read_chisq(self):
        #import pdb; pdb.set_trace()
        self.tbdata = asc.read('chisq_table.txt', names=['ni_mass', 'ni_mixing', 'mass', 'energy', 'kvalue', 'radius', 'time_offset', 'chisq'])

    def plot_2D(self,plot_ni_mass=False, plot_ni_mix=False, plot_mass=False, plot_energy=False,
                plot_Kvalue=False, plot_radius = False, plot_time_offset=False):
        axis1 = None
        axis2 = None
        if plot_ni_mass is False:
            global_indx = (self.tbdata['ni_mass']==self.best_ni_mass)
            fixed_param_str = 'Ni Mass={}'.format(self.best_ni_mass)
        else:
            axis1='ni_mass'
            axis1_label = 'Ni Mass (M$_\odot$)'
            parameter1 = self.ni_mass
            fixed_param_str = ''
            global_indx = np.ones(len(self.tbdata), dtype=bool)
        if plot_ni_mix is False:
            global_indx = global_indx & (self.tbdata['ni_mixing']==self.best_ni_mix)
            fixed_param_str = '{}, Ni Mix={}'.format(fixed_param_str, self.best_ni_mix)
        else:
            if axis1 is None:
                axis1 = 'ni_mix'
                parameter1 = self.ni
                axis1_label = 'Ni Core Mixing (Msun)'
            else:
                axis2 = 'ni_mix'
                axis2_label = 'Ni Core Mixing (M$_\odot$)'
        if plot_mass is False:
            global_indx = global_indx & (self.tbdata['mass']==self.best_mass)
            fixed_param_str = '{}, Mass={}'.format(fixed_param_str, self.best_mass)
        else:
            if axis1 is None:
                axis1 = 'mass'
                axis1_label = 'Progenitor Mass (M$_\odot$)'
                parameter1 = self.masses
            elif axis2 is None:
                axis2 = 'mass'
                axis2_label = 'Progenitor Mass (M$_\odot$)'
                parameter2 = self.masses
            else:
                sys.exit('More than 2 axes specified')
        if plot_energy is False:
            global_indx = global_indx & (self.tbdata['energy']==self.best_energy)
            fixed_param_str = '{}, Energy={}'.format(fixed_param_str, self.best_energy)
        else:
            if axis1 is None:
                axis1 = 'energy'
                axis1_label = 'Explosion Energy ($x10^{51}$ ergs)'
                parameter1 = self.energies
            elif axis2 is None:
                axis2 = 'energy'
                parameter2 =self.energies
                axis2_label = 'Explosion Energy ($x10^{51}$ ergs)'
            else:
                sys.exit('More than 2 axes specified')
        if plot_Kvalue is False:
            global_indx = global_indx & (self.tbdata['kvalue']==self.best_Kvalue)
            fixed_param_str = '{}, K Value={}'.format(fixed_param_str, self.best_Kvalue)
        else:
            if axis1 is None:
                axis1 = 'kvalue'
                axis1_label = 'CSM Density ($x10^{17}$ g/cm)'
                parameter1 = self.Kvalues
            elif axis2 is None:
                axis2 = 'kvalue'
                axis2_label = 'CSM Density ($x10^{17}$ g/cm)'
                parameter2 = self.Kvalues
            else:
                sys.exit('More than 2 axes specified')
        if plot_radius is False:
            global_indx = global_indx &  (self.tbdata['radius']==self.best_radius)
            fixed_param_str = '{}, CSM Radius={}'.format(fixed_param_str, self.best_radius)
        else:
            if axis1 is None:
                axis1 = 'radius'
                axis1_label = 'CSM Radial Extent (R$_\odot$)'
                parameter1 = self.radii
            elif axis2 is None:
                axis2 = 'radius'
                axis2_label = 'CSM Radial Extent (R$_\odot$)'
                parameter2 = self.radii
            else:
                sys.exit('More than 2 axes specified')
        if plot_time_offset is False:
            global_indx = global_indx &  (self.tbdata['time_offset']==self.best_time_offset)
            fixed_param_str = '{}, Time Off={}'.format(fixed_param_str, self.best_time_offset)
        else:
            if axis1 is None:
                sys.exit('You much choose 2 axes to plot')
            elif axis2 is None:
                axis2 = 'time_offset'
                parameter2 = self.best_time_offset
                axis2_label = 'Time Offset from Explosion Epoch (days)'
            else:
                sys.exit('More than 2 axes specified')
        plot_tbdata = self.tbdata[global_indx]
        chisq = np.empty((len(parameter1), len(parameter2)))*np.nan
        parameter1.sort() #just in case listed out of order
        parameter2.sort() #just in case listed out of order
        for indx1, val1 in enumerate(parameter1):
            for indx2, val2 in enumerate(parameter2):
                if np.all((plot_tbdata[axis1]==val1)&(plot_tbdata[axis2]==val2)==False):
                    pass
                else:
                    chisq[indx1, indx2] = plot_tbdata['chisq'][(plot_tbdata[axis1]==val1)&(plot_tbdata[axis2]==val2)]
        fig = plt.figure()
        ax = fig.add_subplot(111)
        im = ax.imshow(chisq, interpolation='nearest', aspect='auto', cmap=plt.get_cmap('viridis'))
        ax.set_xlabel(axis2_label)
        ax.set_ylabel(axis1_label)
        ax.set_title('Best Chi Square for {} and {}'.format(axis1, axis2))
        fig.suptitle(fixed_param_str)
        fig.colorbar(mappable=im)
        ax.set_xticklabels([0]+['{}'.format(i) for i in parameter2])
        ax.set_yticklabels([0]+['{}'.format(i) for i in parameter1])
        plt.savefig(os.path.join(self.fig_dir, 'chisq_{}_{}.pdf'.format(axis1,axis2)))
        #ax.contour(np.log10(chisq), cmap=plt.get_cmap('Reds'))

# Write every step
# Check that file exists
# Write issues to log file
# Restart from last check


import subprocess
from typing import List, Iterator

def output(args: List[str], decode_ascii: bool = True, strip: bool = True) -> Iterator[str]:
    with subprocess.Popen(args, stdout=subprocess.PIPE) as process:
        for line in process.stdout.readlines():
            if decode_ascii:
                line = line.decode('ascii')
            if strip:
                line = line.strip()
            yield line


#!/usr/bin/env python3
import sys
import json

suffix_name = '_' + sys.argv[1] if len(sys.argv) > 1 else ""
tot_cnt = 0
chr_rm_cnt = 0
sm_rm_cnt = 0
big_rm_cnt = 0
flip_rm_cnt = 0
span_rm = 0
span_kept = 0

known_chrs = [f"chr{n}" for n in range(1, 23)]
known_chrs.extend(["chrY", "chrX"])

for line in sys.stdin:
    tot_cnt += 1
    chrom, start, end  = line.strip().split('\t')
    start = int(start)
    end = int(end)
    span = abs(end - start)
    if chrom not in known_chrs:
        chr_rm_cnt += 1
        span_rm += span
        continue
    if end <= start:
        flip_rm_cnt += 1
        span_rm += span
        continue
    if abs(end - start) < 10:
        sm_rm_cnt += 1
        span_rm += span
        continue
    if span > 50000:
        big_rm_cnt += 1
        span_rm += span
        continue
    span_kept += span
    print(f"{chrom}\t{start}\t{end}")

span_total = span_kept + span_rm
span_pct = span_rm / span_total * 100
rm_cnt = sm_rm_cnt + big_rm_cnt + flip_rm_cnt + chr_rm_cnt
pct_rm = rm_cnt / tot_cnt * 100

sys.stderr.write(f"""Count removed {rm_cnt} of {tot_cnt} = {tot_cnt - rm_cnt} ({pct_rm:.2f}%)
Span removed {span_rm} of {span_total} = {span_kept} ({span_pct:.2f}%)
Chromosome removed: {chr_rm_cnt}
Small removed: {sm_rm_cnt}
Big removed: {big_rm_cnt}
Coord remove: {flip_rm_cnt}
""")

out = {"total": tot_cnt,
       "removed": rm_cnt,
       "removed_pct": pct_rm,
       "chrom_rm": chr_rm_cnt,
       "small_rm": sm_rm_cnt,
       "big_rm": big_rm_cnt,
       "coord_rm": flip_rm_cnt,
       "span_total": span_total,
       "span_rm": span_rm,
       "span_kept": span_kept}
json.dump(out, open(f"merging_stats{suffix_name}.json", 'w'), indent=4)


import facebook

#You have to enter in values for the following variables:  token, pagetitle, pagedescription, albumname, and albumnumber.  Dont change anything else.

#input your access code, in quotes. (get this from https://developers.facebook.com/tools/explorer/). This class of code expires after two hours.
#Be sure to check 'user-photos' under 'select permissions' before clicking 'get access token'.

token = "your code"

# set this to what you want the title of the html page to be, in quotes
pagetitle = "My Album"

# This is the text at the top of the page, eg "Photos from My Summer Abroad". In quotes.
pagedescription = "Photos from My Summer Abroad"

# This is the name of the album, eg, "Summer2016", if the pic folder is "Summer2016@@".
albumname = "Summer2016"

albumnumber = "album_number"
#To get albumnumber its the numerical string following the a ,
# but only before the first dot, so if the whole thing is
 # #media_set?set=a.10291136899892709.6736476374
                             #then its:
                             #10291136899892709

graph = facebook.GraphAPI(access_token=token, version=2.7)

htmlstring = """

<!DOCTYPE HTML>

<meta charset="utf-8">

   <TITLE>""" + pagetitle + """</TITLE>

""" + pagedescription +  """

"""

photodict = graph.get_object(id=albumnumber, fields="photos.limit(200)")

photolist = [x['id'] for x in photodict['photos']['data']]

for photo in photolist:

    photocapt = graph.get_object(id=photo, fields="tags,name")

    if "name" in photocapt:
        caption = photocapt['name']

        if "tags" in photocapt:

            tagslist = [x["name"] for x in photocapt['tags']['data']]
            tagstring = ", ".join(tagslist)

            htmlstring += """

""" + caption + """

            """

        else:

            htmlstring += """

""" + caption + """

            """

    else:

        if "tags" in photocapt:

            tagslist = [x["name"] for x in photocapt['tags']['data']]
            tagstring = ", ".join(tagslist)

            htmlstring += """

""" + tagstring + """

            """

        else:

            htmlstring += """

            """

htmlstring += """

Go to:

My Photo
    Gallery

My Home Page

"""
#The above assumes you want to situate this page within a larger website with a main photo galleries page and a home page

Html_file = open(albumname + ".html","w")
Html_file.write(htmlstring)
Html_file.close()

# guide to facebook-sdk python package at http://facebook-sdk.readthedocs.io/en/latest/api.html


'''
各式雜項功能
'''

import numpy as np
import matplotlib.pyplot as plt
import json
import sys
import copy
sys.path.append("../")

def rotationJsonDataParser(jsonDict: dict, jointCount: int):
    '''
    target format:
    x list:[x1, x2, x3, ...]
    knee: {x list, y list, z list}
    left upper leg, left knee, right upper leg, right knee
    '''
    timeSeries=jsonDict['results']
    axisNames = list(timeSeries[0]['data'][0].keys())
    parsedRotationData=[{_axis: [] for _axis in axisNames} for i in range(jointCount)]
    for jointIdx in range(jointCount):
        for oneData in timeSeries:
            for _axis in axisNames:
                parsedRotationData[jointIdx][_axis].append(oneData['data'][jointIdx][_axis])
    return parsedRotationData

def readHandPerformance(filePath = '../HandRotationOuputFromHomePC/leftFrontKickStream.json', isFromUnity=False):
    '''
    讀取hand performance rotation data
    '''
    handJointsRotations=None
    with open(filePath, 'r') as fileOpen:
        rotationJson=None
        if isFromUnity:
            rotationJson=json.load(fileOpen)['results']
        else:
            rotationJson=json.load(fileOpen)
        handJointsRotations = rotationJsonDataParser({'results': rotationJson}, jointCount=4)    # For python output
    return handJointsRotations

def cropHandPerformance(handPerformanceData, startInd, endInd):
    numJoint = len(handPerformanceData)
    catAxis = handPerformanceData[0].keys()
    # timeCount = len(handPerformanceData[0]['x'])

    data = copy.deepcopy(handPerformanceData)
    for _joint in range(numJoint):
        for _axis in catAxis:
            data[_joint][_axis] = data[_joint][_axis][startInd:endInd+1]
    return data

def cropHandPerfJointWise(handPerformanceData, intervals):
    numJoint = len(handPerformanceData)
    catAxis = handPerformanceData[0].keys()

    data = copy.deepcopy(handPerformanceData)
    for _joint in range(numJoint):
        for _axis in catAxis:
            data[_joint][_axis] = data[_joint][_axis][intervals[_joint][0]:intervals[_joint][1]+1]
    return data

def handPerformanceToMatrix(handPerformanceData, usedJointAxis):
    '''
    Make hand rotation data to a matrix with d * t dimension
    每一行是所有維度的資料, 依序是joint 0 x, joint 0 y, joint 0 z, joint 1 x, .....
    總共的行數是多少個時間點
    :usedJointAxis: 使用到的joint axis. List of pairs of joint and axis. e.g. [[0, 'x'], [2, 'y']]
    '''
    numFeat = len(usedJointAxis)
    timeCount = len(handPerformanceData[0]['x'])
    targetMat = np.zeros((numFeat, timeCount))
    for i, _pair in enumerate(usedJointAxis):
        _joint=_pair[0]
        _axis=_pair[1]
        targetMat[i, :] = handPerformanceData[_joint][_axis]
    return targetMat

def scaleToReferenceControlSequence(signal, referenceControlSequence):
    '''
    使用min max scaling將input signal根據reference control sequence轉換到[-1, 1]
    大於或是小於reference control sequence的數值, 使用control sequence的最大最小值代替
    '''
    refMin = np.min(referenceControlSequence)
    refMax = np.max(referenceControlSequence)
    signal = np.copy(signal)
    signal[signal>refMax] = refMax
    signal[signal<refMin] = refMin
    signal = -1 + (signal-refMin)*(1-(-1)) / (refMax-refMin)
    return signal

if __name__=='__main__':
    data = readHandPerformance()
    cropData = cropHandPerformance(data, 1430, 1530)
    dataMat = handPerformanceToMatrix(cropData, [[0, 'x'], [1, 'x']])
    print(dataMat.shape)
    pass


from collections import defaultdict
import re
from itertools import groupby

def ref_len(cigar_string):
	"""
	Given a CIGAR string, return the number of
	bases consumed in the reference sequence.
	"""
	ref_consuming_ops = {"M", "D", "N", "=", "X"}
	result = 0
	cig_iter = groupby(cigar_string, lambda chr: chr.isdigit())
	for _, length_digits in cig_iter:
		length = int(''.join(length_digits))
		op = next(next(cig_iter)[1])
		if op in ref_consuming_ops:
			result += length
	return result

def subset_cigar_string(full_cigar, alignment_pos, ref_start, ref_stop):
	"""
	This function will return the cigar string that overlaps
	the reference coordinates from ref_start to ref_stop inclusive.
	For now we assume that the alignment fully covers this region.
	This function is using the 1-based coordinate system.
	So, we assume that alignment_pos is 1-based (as it should be from SAM),
	and that ref_start and ref_stop are 1-based.
	"""
	consumes_ref = {"M", "D", "N", "=", "X"}
	assert alignment_pos <= ref_start
	X = re.finditer("(\d+)([MIDNSHPX=])", full_cigar)
	previous_block_stop = alignment_pos - 1
	subset_cigar = ""
	overlapping_region = False
	for block in X:
		cigar_digits = int(block.group(1))
		cigar_operation = block.group(2)
		if cigar_operation in consumes_ref:
			block_start = previous_block_stop + 1
			block_stop = block_start + cigar_digits - 1
			#if we overlap the region of interest
			if block_stop >= ref_start:
				overlapping_region = True
				overlap_start = max(ref_start, block_start)
				overlap_stop = min(ref_stop, block_stop)
				overlap_length = overlap_stop - overlap_start + 1
				subset_cigar += f"{overlap_length}{cigar_operation}"
			#if we have finished the region of interest
			if block_stop >= ref_stop:
				break
		else:
			if overlapping_region:
				subset_cigar += f"{cigar_digits}{cigar_operation}"
			block_stop = previous_block_stop
		previous_block_stop = block_stop
	return subset_cigar

transcript_junction_to_intron = {}
with open(snakemake.input[0], "r") as input_intron_file:
	for line in input_intron_file:
		geneid, transcript, junction, intron = line.strip().split()
		transcript_junction_to_intron[(transcript, junction)] = intron

readname_to_introns = defaultdict(set)
intron_to_transcripts = defaultdict(set)
with open(snakemake.input[1], "r") as input_spannedjunctions_file:
	for line in input_spannedjunctions_file:
		geneie, transcript, junction, readname = line.strip().split()
		if (transcript, junction) in transcript_junction_to_intron:
			chrom = "_".join(transcript.split("|")[1].split("_")[:2])
			intron = transcript_junction_to_intron[(transcript, junction)]
			readname_to_introns[readname].add(intron)
			intron_to_transcripts[intron].add(transcript)

def cigar_missing_percent(cigar):
	consumes_ref = {"M", "D", "N", "=", "X"}
	X = re.finditer("(\d+)([MIDNSHPX=])", cigar)
	numerator = 0
	denominator = 0
	for block in X:
		cigar_digits = int(block.group(1))
		cigar_operation = block.group(2)
		if cigar_operation in consumes_ref:
			denominator += cigar_digits
			if cigar_operation in {"N", "D"}:
				numerator += cigar_digits
	missing_percent = numerator/denominator
	return missing_percent

with open(snakemake.input[2], "r") as input_subsetsam_file, open(snakemake.output[0], "w") as output_file:
	for line in input_subsetsam_file:
		if not line.startswith("@"):
			geneid, readname, _, chrom, pos, _, cigar = line.strip().split()[:7]
			pos = int(pos)
			introns = readname_to_introns[readname]
			cigar_ref_len = ref_len(cigar)
			for intron in introns:
				intron_chrom = intron.split(":")[0]
				if intron_chrom == chrom:
					intron_start, intron_stop = intron.split(":")[1].split("-")
					intron_start, intron_stop = int(intron_start), int(intron_stop)
					#if the read spans across the entire intron
					if pos <= intron_start and intron_stop <= pos + cigar_ref_len:
						transcripts = intron_to_transcripts[intron]
						region_cigar = subset_cigar_string(cigar, pos, intron_start, intron_stop)
						missing_percent = cigar_missing_percent(region_cigar)
						for transcript in transcripts:
							output_file.write(f"{geneid}\t{transcript}\t{intron}\t{readname}\t{missing_percent}\t{region_cigar}\n")
					else:
						output_file.write("Read doesn't span across entire intron.\n")


from dataclasses import dataclass, field
from typing import Any, Dict, List, Tuple

import numpy as np
import pandas as pd
import polars as pl
from more_itertools import first_true

from src.data_generating_process.domain_parameters import (
    BaseVariableNames,
    BernoulliBias,
    CensoredVariables,
    Coefficients,
    CorrellationMatrix,
    InteractionVariableNames,
    RndBernoulliBias,
    RndCensoredVariables,
    RndCoefficients,
    RndCorrelationMatrix,
    RndTransformationExponent,
    TransformationExponent,
)

# Defaults
NUMBER_OF_RND_CENSORED_FEATURES = 6
NUMBER_OF_BASE_FEATURES = 30
PROB_OF_ZERO_BASE_COEFF = 0.8
VALUE_RANGE_BASE_COEFF = 10
PROB_OF_ZERO_INTERACTION_COEFF = 0.5
VALUE_RANGE_INTERACTION_COEFF = 10
COEFF_SAME_SIGN = True
CORRELATION_VALUE_RANGE = 0.5
CORRELATION_SAME_SIGN = False
BIAS_VALUE_RANGE = 0.3
EXPONENT_LOWER = 0.5
EXPONENT_UPPPER = 2
PROB_OF_CENSORING = 0.2

@dataclass
class BaseTerm:
    name: str
    coef: float

@dataclass
class IntrTerm:
    name1: str
    name2: str
    coef: float

@dataclass
class Domain:
    base_coeff_cnt: int
    base_variable_names: BaseVariableNames
    intr_variable_names: InteractionVariableNames
    correllation_matrix: RndCorrelationMatrix | CorrellationMatrix
    base_coefficients: RndCoefficients | Coefficients
    intr_coefficients: RndCoefficients | Coefficients
    bias: RndBernoulliBias | BernoulliBias
    transformation_exponent: RndTransformationExponent | TransformationExponent
    censored_variables: RndCensoredVariables | CensoredVariables

    @property
    def base_terms(self) -> List[BaseTerm]:
        base_vars = [
            BaseTerm(name=name, coef=coef)
            for name, coef in zip(
                self.base_variable_names.base_var_names,
                self.base_coefficients.coefficients,
            )
        ]
        return base_vars

    @property
    def intr_terms(self) -> List[IntrTerm]:
        intr_terms = [
            IntrTerm(name1=name1, name2=name2, coef=coef)
            for name1, name2, coef in zip(
                self.intr_variable_names.intr_separ_names[0],
                self.intr_variable_names.intr_separ_names[1],
                self.intr_coefficients.coefficients,
            )
        ]
        return intr_terms

@dataclass
class SamplingParameters:
    n: int
    seed: int

class DomainSampler:
    """
    Samples data from a given domain
    """

    @staticmethod
    def generate_X(domain: Domain, n: int, sample_seed: int) -> pl.DataFrame:
        """_summary_

        Args:
            domain (Domain): domain to sample from (provides number of features and feature names)
            n (int): number of samples to draw
            sample_seed (int): seed for reproducible sampling of data

        Returns:
            pl.DataFrame: Polars.DataFrame with multi-normal feature data (X)
        """
        rng = np.random.default_rng(seed=sample_seed)
        mean_vector = [0] * domain.base_coeff_cnt

        data = rng.multivariate_normal(
            mean=mean_vector,
            cov=domain.correllation_matrix.matrix,
            size=n,
            check_valid="warn",
            tol=0.01,
        )
        feature_names = domain.base_variable_names.base_var_names
        pl_df = pl.from_numpy(data, schema=feature_names, orient="row")

        return pl_df

    @staticmethod
    def transform_data(
        domain: Domain,
        X: pl.DataFrame,
    ) -> pl.DataFrame:
        """
        Transforms X with the transformation exponent obtained from the domain

        Args:
            domain (Domain): domain to sample from
            X (pl.DataFrame): untransformed feature data

        Returns:
            pl.DataFrame: Polars.DataFrame with transformed feature data
        """
        # raise feature data to the given power
        feature_names = domain.base_variable_names.base_var_names
        exponent = domain.transformation_exponent.exponent
        transformation_tasks = [
            (pl.col(f).sign() * pl.col(f).abs().pow(exponent)).alias(f)
            for f in feature_names
        ]
        X_powered = X.select(transformation_tasks)

        # re-scale the to have variance 1
        scales = X_powered.std()
        scale_tasks = [pl.col(n) / scales[n][0] for n in X_powered.columns]
        X_rescaled = X_powered.select(scale_tasks)

        return X_rescaled

    @staticmethod
    def aggregate(domain: Domain, X: pl.DataFrame) -> pl.DataFrame:
        """Aggregation function of the reverse logistic regression:
            Multiply each value of X and the interactions
            with the respective coefficient and sum them up.

        Args:
            domain (Domain): domain to sample from
            X (pl.DataFrame): feature data
        Returns:
            pl.DataFrame: with one newly added column containing
            the aggregated value
        """
        # prep multiplication of feature values with their coefficients
        base_multiplications = [
            (f.coef * pl.col(f.name)).alias(f.name) for f in domain.base_terms
        ]

        # prep multiplications of interactions (feat1 * feat2) with the coefficient
        interaction_multiplications = [
            (f.coef * pl.col(f.name1) * pl.col(f.name2)).alias(f"{f.name1}{f.name2}")
            for f in domain.intr_terms
        ]

        all_multiplications = base_multiplications + interaction_multiplications

        multiply_sum_across = (
            X.select(all_multiplications)  # do all the multiplication
            .fold(lambda s1, s2: s1 + s2)  # sum across
            .alias("agg")  # rename the Series
        )

        return pl.DataFrame(multiply_sum_across)

    @staticmethod
    def sigmoid_link(X: pl.DataFrame) -> pl.DataFrame:
        """
        Adds the class probability to X.

        Args:
            X (pl.DataFrame): feature data
        Returns:
            pl.DataFrame: with one newly added column ('link')
            containing the class probability

        """

        link = X.with_columns(
            (
                (pl.col("agg").exp() / (pl.col("agg").exp() + 1)).round(decimals=3)
                # e^x / (e^x + 1)
            ).alias("link")
        )
        return link

    @staticmethod
    def bernoulli_label(
        domain: Domain, X: pl.DataFrame, sample_seed: int
    ) -> pl.DataFrame:
        """Uses Bernoulli sampling to add a class label to the features

        Args:
            domain (Domain): domain to sample from, provides the Bias
            X (pl.DataFrame): feature data with probabilities
            sample_seed (int): for reproducibility

        Returns:
            pl.DataFrame: with one newly added column ('class')
            containing the class label
        """

        def bernoulli(X: pl.DataFrame, sample_seed: int) -> pl.DataFrame:
            """Helper function for sampling"""
            rng = np.random.default_rng(seed=sample_seed)

            probs = X.select(pl.col("biased_prob")).to_numpy().flatten()
            picks = (rng.uniform(size=None) < probs) * 1
            picks = pl.from_numpy(picks, schema=["class"])
            new = pl.concat([X, picks], how="horizontal")
            return new

        b = domain.bias.bias
        X_class = X.with_columns(
            [
                # add bias to probability but stay within [0,1]
                pl.when(pl.col("link") + b > 1)
                .then(1)
                .when(pl.col("link") + b < 0)
                .then(0)
                .otherwise(pl.col("link") + b)
                .alias("biased_prob"),
            ]
        )
        X_class = bernoulli(X=X_class, sample_seed=sample_seed)

        return X_class

    @staticmethod
    def generate_y(domain: Domain, X: pl.DataFrame, sample_seed: int) -> pl.DataFrame:
        """
        Reverse logistic regression to add class labels

        Args:
            domain (Domain): domain to sample from
            X (pl.DataFrame): feature matrix
            sample_seed (int): for reproducibility

        Returns:
            pl.DataFrame: with newly added columns for aggregtion, link, probability and class
        """
        X_agg = DomainSampler.aggregate(domain=domain, X=X)
        X_link = DomainSampler.sigmoid_link(X=X_agg)
        X_class = DomainSampler.bernoulli_label(
            domain=domain, X=X_link, sample_seed=sample_seed
        )
        return X_class

    @staticmethod
    def generate_data(domain: Domain, sample: SamplingParameters) -> pd.DataFrame:
        """
        Generates a dataset consisting of features (X) and labels (y).
        The distributional properties of the data are determined by the Domain.
        The size of the data is determined by the SamplingParameters

        Args:
            domain (Domain): domain to sample from
            sample (SamplingParameters): provides sample size and sample seed
        """
        n = sample.n
        sample_seed = sample.seed

        X = DomainSampler.generate_X(domain=domain, n=n, sample_seed=sample_seed)
        X = DomainSampler.transform_data(domain=domain, X=X)
        y = DomainSampler.generate_y(domain=domain, X=X, sample_seed=sample_seed)
        X = DomainSampler.censor_data(domain=domain, X=X)

        result = X.to_pandas()
        result["y"] = y.select(pl.col("class")).to_pandas()
        return result

    @staticmethod
    def censor_data(domain: Domain, X: pl.DataFrame) -> pl.DataFrame:
        n = X.shape[0]
        zeroes = [
            pl.zeros(n=n).alias(v)
            for v in domain.censored_variables.censored__variables
        ]
        censored_X = X.with_columns(zeroes)
        return censored_X

@dataclass
class DomainParameters:
    """Specifies the paramaters of a Domain.
    DomainGenerator class uses the DomainParameters class
    to generate the described domain.

    If parameters are provided, they will be used.
    If they are None, they will be generated randomly by the DomainGenerator
    """

    base_coeff_cnt: int
    base_variable_names: BaseVariableNames | None = field(init=False)
    intr_variable_names: InteractionVariableNames | None = field(init=False)
    correllation_matrix: CorrellationMatrix | RndCorrelationMatrix | None = field(
        default=None
    )
    base_coefficients: Coefficients | RndCoefficients | None = field(default=None)
    base_coefficients_prob_of_zero: float | None = field(default=None)
    intr_coefficients: Coefficients | RndCoefficients | None = field(default=None)
    intr_coefficients_prob_of_zero: float | None = field(default=None)
    coeff_same_sign: bool | None = field(default=None)
    bias: BernoulliBias | RndBernoulliBias | None = field(default=None)
    transformation_exponent: TransformationExponent | RndTransformationExponent | None = field(
        default=None
    )
    censored_variables: CensoredVariables | RndCensoredVariables | None = field(
        default=None
    )
    number_of_censored_variables: int | None = field(default=None)

    def __post_init__(self):
        self.base_variable_names = BaseVariableNames(self.base_coeff_cnt)
        self.intr_variable_names = InteractionVariableNames(self.base_coeff_cnt)

class DomainGenerator:
    """
    Generate a Domain using the provided parameters.
    Replaces missing parameters with random parameters using reasonable defaults

    Returns:
        _type_: _description_
    """

    @staticmethod
    def get_base_coeff_cnt(params: DomainParameters):
        return first_true(
            [params.base_coeff_cnt, NUMBER_OF_BASE_FEATURES],
            pred=lambda x: x is not None,
        )

    @staticmethod
    def get_base_variable_names(params: DomainParameters):
        return params.base_variable_names

    @staticmethod
    def get_intr_variable_names(params: DomainParameters):
        return params.intr_variable_names

    @staticmethod
    def get_correllation_matrix(params: DomainParameters, seed: int):
        return first_true(
            [
                params.correllation_matrix,
                RndCorrelationMatrix(
                    params.base_coeff_cnt,
                    seed=seed,
                    same_sign=CORRELATION_SAME_SIGN,
                    value_range=CORRELATION_VALUE_RANGE,
                ),
            ],
            pred=lambda x: x is not None,
        )

    @staticmethod
    def get_base_coefficients(params: DomainParameters, seed: int):
        base_coefficients_prob_of_zero = first_true(
            [params.base_coefficients_prob_of_zero, PROB_OF_ZERO_BASE_COEFF],
            pred=lambda x: x is not None,
        )

        same_sign = first_true(
            [params.coeff_same_sign, COEFF_SAME_SIGN],
            pred=lambda x: x is not None,
        )

        return first_true(
            [
                params.base_coefficients,
                RndCoefficients(
                    size=params.base_coeff_cnt,
                    seed=seed,
                    prob_of_zero=base_coefficients_prob_of_zero,
                    same_sign=same_sign,
                ),
            ],
            pred=lambda x: x is not None,
        )

    @staticmethod
    def get_intr_coefficients(params: DomainParameters, seed: int):
        intr_coefficients_prob_of_zero = first_true(
            [params.intr_coefficients_prob_of_zero, PROB_OF_ZERO_INTERACTION_COEFF],
            pred=lambda x: x is not None,
        )

        same_sign = first_true(
            [params.coeff_same_sign, COEFF_SAME_SIGN],
            pred=lambda x: x is not None,
        )

        return first_true(
            [
                params.intr_coefficients,
                RndCoefficients(
                    size=len(params.intr_variable_names.intr_joint_name),
                    seed=seed,
                    prob_of_zero=intr_coefficients_prob_of_zero,
                    same_sign=same_sign,
                ),
            ],
            pred=lambda x: x is not None,
        )

    @staticmethod
    def get_censored_variables(params: DomainParameters, seed: int):
        number_of_censored_variables = first_true(
            [
                params.number_of_censored_variables,
                round(PROB_OF_CENSORING * params.base_coeff_cnt),
            ],
            pred=lambda x: x is not None,
        )
        return first_true(
            [
                params.censored_variables,
                RndCensoredVariables(
                    seed=seed,
                    censored_size=number_of_censored_variables,
                    total_size=params.base_coeff_cnt,
                ),
            ],
            pred=lambda x: x is not None,
        )

    @staticmethod
    def get_bias(params: DomainParameters, seed: int):
        return first_true(
            [params.bias, RndBernoulliBias(seed=seed, value_range=BIAS_VALUE_RANGE)],
            pred=lambda x: x is not None,
        )

    @staticmethod
    def get_transformation_exponent(params: DomainParameters, seed: int):
        return first_true(
            [
                params.transformation_exponent,
                RndTransformationExponent(
                    seed=seed,
                    exponent_range_lower=EXPONENT_LOWER,
                    exponent_range_upper=EXPONENT_UPPPER,
                ),
            ],
            pred=lambda x: x is not None,
        )

    @classmethod
    def get_domain(cls, params: DomainParameters, seed: int):
        """
        Creates a domain accorind to the specified params. If domain parameters are not
        provided, they will be replaced by a random parameter, generated using seed.

        Args:
            params (DomainParameters): povides fixed parameters for the domain
            seed (int): seed for creating random missing parameters that are not part of params

        Returns:
            Domain: domain with parameters are specified or random
        """
        base_coeff_cnt = cls.get_base_coeff_cnt(params)
        base_variable_names = cls.get_base_variable_names(params)
        intr_variable_names = cls.get_intr_variable_names(params)
        correllation_matrix = cls.get_correllation_matrix(params, seed)
        base_coefficients = cls.get_base_coefficients(params, seed)
        intr_coefficients = cls.get_intr_coefficients(params, seed)
        bias = cls.get_bias(params, seed)
        transformation_exponent = cls.get_transformation_exponent(params, seed)
        censored_variables = cls.get_censored_variables(params, seed)

        return Domain(
            base_coeff_cnt=base_coeff_cnt,
            base_variable_names=base_variable_names,
            intr_variable_names=intr_variable_names,
            correllation_matrix=correllation_matrix,
            base_coefficients=base_coefficients,
            intr_coefficients=intr_coefficients,
            bias=bias,
            transformation_exponent=transformation_exponent,
            censored_variables=censored_variables,
        )


# -*- coding: utf-8 -*-
# created by YeFei<316606233@qq.com>
# since 2018/12/19
import os
import logging
from PIL import Image
from django.http import Http404
from django.conf import settings
from django.views.static import serve
from .helpers import check_thumb_sign, MODES
from .settings import THUMB_ROOT, THUMB_JPEG_QUALITY

logger = logging.getLogger(__name__)

def thumb(request, path, sign, width=0, height=0, mode=0):
    if not THUMB_ROOT:
        raise Http404('E.005')

    width, height, mode = int(width), int(height), int(mode)
    if not check_thumb_sign(sign, path, width, height, mode):
        raise Http404('E.004')

    thumb_filename = os.path.join(str(mode), '%dx%d' % (width, height), sign, path)
    thumb_full_path = os.path.join(THUMB_ROOT, thumb_filename)
    if not os.path.exists(thumb_full_path):
        source_full_path = os.path.join(settings.MEDIA_ROOT, path)
        if not os.path.isfile(source_full_path):
            raise Http404('E.001')

        try:
            im = Image.open(source_full_path)
        except IOError as e:
            logger.warning('upload thumb open image error: %s', e)
            raise Http404('E.002')

        # if im.mode not in ('RGB', 'RGBA'):
        #   im = im.convert(mode='RGB')

        mode_func = MODES.get(mode)
        if mode_func is None:
            raise Http404('E.006')

        try:
            im = mode_func(im, width, height)
        except Exception:
            raise Http404('E.007')

        directory = os.path.dirname(thumb_full_path)
        if not os.path.exists(directory):
            try:
                os.makedirs(directory)
            except OSError:
                pass

        if not os.path.isdir(directory):
            logger.warning('upload thumb save image error: %s', "%s exists and is not a directory." % directory)
            raise Http404('E.003')

        params = {}
        if im.format == 'JPEG':
            params['quality'] = THUMB_JPEG_QUALITY

        im.save(thumb_full_path, **params)

    return serve(request, thumb_filename, document_root=THUMB_ROOT)