import datetime
import os
import subprocess
import base64
from pathlib import Path
import shutil
import pandas as pd
import signal
import requests

from baselayer.app.env import load_env
from baselayer.app.model_util import status, create_tables, drop_tables
from social_tornado.models import TornadoStorage
from skyportal.models import init_db, Base, DBSession, Source, User
from skyportal.model_util import setup_permissions, create_token
from skyportal.tests import api
from baselayer.tools.test_frontend import verify_server_availability


if __name__ == "__main__":
    """Insert test data"""
    env, cfg = load_env()
    basedir = Path(os.path.dirname(__file__)) / ".."

    with status(f"Connecting to database {cfg['database']['database']}"):
        init_db(**cfg["database"])

    with status("Dropping all tables"):
        drop_tables()

    with status("Creating tables"):
        create_tables()

    for model in Base.metadata.tables:
        print("    -", model)

    with status(f"Creating permissions"):
        setup_permissions()

    with status(f"Creating dummy users"):
        super_admin_user = User(
            username="testuser@cesium-ml.org", role_ids=["Super admin"]
        )
        group_admin_user = User(
            username="groupadmin@cesium-ml.org", role_ids=["Super admin"]
        )
        full_user = User(username="fulluser@cesium-ml.org", role_ids=["Full user"])
        view_only_user = User(
            username="viewonlyuser@cesium-ml.org", role_ids=["View only"]
        )
        DBSession().add_all(
            [super_admin_user, group_admin_user, full_user, view_only_user]
        )

        for u in [super_admin_user, group_admin_user, full_user, view_only_user]:
            DBSession().add(
                TornadoStorage.user.create_social_auth(u, u.username, "google-oauth2")
            )

    with status("Creating token"):
        token = create_token(
            [
                "Manage groups",
                "Manage sources",
                "Upload data",
                "Comment",
                "Manage users",
            ],
            super_admin_user.id,
            "load_demo_data token",
        )

    def assert_post(endpoint, data):
        response_status, data = api("POST", endpoint, data, token)
        if not response_status == 200 and data["status"] == "success":
            raise RuntimeError(
                f'API call to {endpoint} failed with status {status}: {data["message"]}'
            )
        return data

    with status("Launching web app & executing API calls"):
        try:
            response_status, data = api("GET", "sysinfo", token=token)
            app_already_running = True
        except requests.ConnectionError:
            app_already_running = False
            web_client = subprocess.Popen(
                ["make", "run"], cwd=basedir, preexec_fn=os.setsid
            )

        server_url = f"http://localhost:{cfg['ports.app']}"
        print()
        print(f"Waiting for server to appear at {server_url}...")

        try:
            verify_server_availability(server_url)
            print("App running - continuing with API calls")

            with status("Creating dummy group & adding users"):
                data = assert_post(
                    "groups",
                    data={
                        "name": "Stream A",
                        "group_admins": [
                            super_admin_user.username,
                            group_admin_user.username,
                        ],
                    },
                )
                group_id = data["data"]["id"]

                for u in [view_only_user, full_user]:
                    data = assert_post(
                        f"groups/{group_id}/users/{u.username}", data={"admin": False}
                    )

            with status("Creating dummy instruments"):
                data = assert_post(
                    "telescope",
                    data={
                        "name": "Palomar 1.5m",
                        "nickname": "P60",
                        "lat": 33.3633675,
                        "lon": -116.8361345,
                        "elevation": 1870,
                        "diameter": 1.5,
                        "group_ids": [group_id],
                    },
                )
                telescope1_id = data["data"]["id"]

                data = assert_post(
                    "instrument",
                    data={
                        "name": "P60 Camera",
                        "type": "phot",
                        "band": "optical",
                        "telescope_id": telescope1_id,
                    },
                )
                instrument1_id = data["data"]["id"]

                data = assert_post(
                    "telescope",
                    data={
                        "name": "Nordic Optical Telescope",
                        "nickname": "NOT",
                        "lat": 28.75,
                        "lon": 17.88,
                        "elevation": 1870,
                        "diameter": 2.56,
                        "group_ids": [group_id],
                    },
                )
                telescope2_id = data["data"]["id"]

                data = assert_post(
                    "instrument",
                    data={
                        "name": "ALFOSC",
                        "type": "both",
                        "band": "optical",
                        "telescope_id": telescope2_id,
                    },
                )

            with status("Creating dummy sources"):
                SOURCES = [
                    {
                        "id": "14gqr",
                        "ra": 353.36647,
                        "dec": 33.646149,
                        "redshift": 0.063,
                        "group_ids": [group_id],
                        "comments": [
                            "No source at transient location to R>26 in LRIS imaging",
                            "Strong calcium lines have emerged.",
                        ],
                    },
                    {
                        "id": "16fil",
                        "ra": 322.718872,
                        "dec": 27.574113,
                        "redshift": 0.0,
                        "group_ids": [group_id],
                        "comments": ["Frogs in the pond", "The eagle has landed"],
                    },
                ]

                (basedir / "static/thumbnails").mkdir(parents=True, exist_ok=True)
                for source_info in SOURCES:
                    comments = source_info.pop("comments")

                    data = assert_post("sources", data=source_info)
                    assert data["data"]["id"] == source_info["id"]

                    for comment in comments:
                        data = assert_post(
                            "comment",
                            data={"source_id": source_info["id"], "text": comment},
                        )

                    phot_file = basedir / "skyportal/tests/data/phot.csv"
                    phot_data = pd.read_csv(phot_file)

                    data = assert_post(
                        "photometry",
                        data={
                            "source_id": source_info["id"],
                            "time_format": "iso",
                            "time_scale": "utc",
                            "instrument_id": instrument1_id,
                            "observed_at": phot_data.observed_at.tolist(),
                            "mag": phot_data.mag.tolist(),
                            "e_mag": phot_data.e_mag.tolist(),
                            "lim_mag": phot_data.lim_mag.tolist(),
                            "filter": phot_data["filter"].tolist(),
                        },
                    )

                    spec_file = os.path.join(
                        os.path.dirname(os.path.dirname(__file__)),
                        "skyportal",
                        "tests",
                        "data",
                        "spec.csv",
                    )
                    spec_data = pd.read_csv(spec_file)
                    for i, df in spec_data.groupby("instrument_id"):
                        data = assert_post(
                            "spectrum",
                            data={
                                "source_id": source_info["id"],
                                "observed_at": str(datetime.datetime(2014, 10, 24)),
                                "instrument_id": 1,
                                "wavelengths": df.wavelength.tolist(),
                                "fluxes": df.flux.tolist(),
                            },
                        )

                    for ttype in ["new", "ref", "sub"]:
                        fname = f'{source_info["id"]}_{ttype}.png'
                        fpath = basedir / f"skyportal/tests/data/{fname}"
                        thumbnail_data = base64.b64encode(
                            open(os.path.abspath(fpath), "rb").read()
                        )
                        data = assert_post(
                            "thumbnail",
                            data={
                                "source_id": source_info["id"],
                                "data": thumbnail_data,
                                "ttype": ttype,
                            },
                        )

                    source = Source.query.get(source_info["id"])
                    source.add_linked_thumbnails()
        finally:
            if not app_already_running:
                print("Terminating web app")
                os.killpg(os.getpgid(web_client.pid), signal.SIGTERM)

[((22, 15, 22, 25), 'baselayer.app.env.load_env', 'load_env', ({}, {}), '()', False, 'from baselayer.app.env import load_env\n'), ((25, 9, 25, 72), 'baselayer.app.model_util.status', 'status', ({(25, 16, 25, 71): 'f"""Connecting to database {cfg[\'database\'][\'database\']}"""'}, {}), '(f"Connecting to database {cfg[\'database\'][\'database\']}")', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((26, 8, 26, 34), 'skyportal.models.init_db', 'init_db', ({}, {}), "(**cfg['database'])", False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), ((28, 9, 28, 38), 'baselayer.app.model_util.status', 'status', ({(28, 16, 28, 37): '"""Dropping all tables"""'}, {}), "('Dropping all tables')", False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((29, 8, 29, 21), 'baselayer.app.model_util.drop_tables', 'drop_tables', ({}, {}), '()', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((31, 9, 31, 34), 'baselayer.app.model_util.status', 'status', ({(31, 16, 31, 33): '"""Creating tables"""'}, {}), "('Creating tables')", False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((32, 8, 32, 23), 'baselayer.app.model_util.create_tables', 'create_tables', ({}, {}), '()', False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((37, 9, 37, 40), 'baselayer.app.model_util.status', 'status', ({(37, 16, 37, 39): 'f"""Creating permissions"""'}, {}), "(f'Creating permissions')", False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((38, 8, 38, 27), 'skyportal.model_util.setup_permissions', 'setup_permissions', ({}, {}), '()', False, 'from skyportal.model_util import setup_permissions, create_token\n'), ((40, 9, 40, 40), 'baselayer.app.model_util.status', 'status', ({(40, 16, 40, 39): 'f"""Creating dummy users"""'}, {}), "(f'Creating dummy users')", False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((41, 27, 43, 9), 'skyportal.models.User', 'User', (), '', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), ((44, 27, 46, 9), 'skyportal.models.User', 'User', (), '', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), ((47, 20, 47, 83), 'skyportal.models.User', 'User', (), '', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), ((48, 25, 50, 9), 'skyportal.models.User', 'User', (), '', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), ((60, 9, 60, 33), 'baselayer.app.model_util.status', 'status', ({(60, 16, 60, 32): '"""Creating token"""'}, {}), "('Creating token')", False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((61, 16, 71, 9), 'skyportal.model_util.create_token', 'create_token', ({(62, 12, 68, 13): "['Manage groups', 'Manage sources', 'Upload data', 'Comment', 'Manage users']", (69, 12, 69, 31): 'super_admin_user.id', (70, 12, 70, 34): '"""load_demo_data token"""'}, {}), "(['Manage groups', 'Manage sources', 'Upload data', 'Comment',\n    'Manage users'], super_admin_user.id, 'load_demo_data token')", False, 'from skyportal.model_util import setup_permissions, create_token\n'), ((74, 32, 74, 66), 'skyportal.tests.api', 'api', ({(74, 36, 74, 42): '"""POST"""', (74, 44, 74, 52): 'endpoint', (74, 54, 74, 58): 'data', (74, 60, 74, 65): 'token'}, {}), "('POST', endpoint, data, token)", False, 'from skyportal.tests import api\n'), ((81, 9, 81, 58), 'baselayer.app.model_util.status', 'status', ({(81, 16, 81, 57): '"""Launching web app & executing API calls"""'}, {}), "('Launching web app & executing API calls')", False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((23, 19, 23, 44), 'os.path.dirname', 'os.path.dirname', ({(23, 35, 23, 43): '__file__'}, {}), '(__file__)', False, 'import os\n'), ((83, 36, 83, 70), 'skyportal.tests.api', 'api', (), '', False, 'from skyportal.tests import api\n'), ((96, 12, 96, 50), 'baselayer.tools.test_frontend.verify_server_availability', 'verify_server_availability', ({(96, 39, 96, 49): 'server_url'}, {}), '(server_url)', False, 'from baselayer.tools.test_frontend import verify_server_availability\n'), ((51, 8, 51, 19), 'skyportal.models.DBSession', 'DBSession', ({}, {}), '()', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), ((57, 16, 57, 86), 'social_tornado.models.TornadoStorage.user.create_social_auth', 'TornadoStorage.user.create_social_auth', ({(57, 55, 57, 56): 'u', (57, 58, 57, 68): 'u.username', (57, 70, 57, 85): '"""google-oauth2"""'}, {}), "(u, u.username, 'google-oauth2')", False, 'from social_tornado.models import TornadoStorage\n'), ((87, 25, 89, 13), 'subprocess.Popen', 'subprocess.Popen', (), '', False, 'import subprocess\n'), ((99, 17, 99, 62), 'baselayer.app.model_util.status', 'status', ({(99, 24, 99, 61): '"""Creating dummy group & adding users"""'}, {}), "('Creating dummy group & adding users')", False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((117, 17, 117, 53), 'baselayer.app.model_util.status', 'status', ({(117, 24, 117, 52): '"""Creating dummy instruments"""'}, {}), "('Creating dummy instruments')", False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((167, 17, 167, 49), 'baselayer.app.model_util.status', 'status', ({(167, 24, 167, 48): '"""Creating dummy sources"""'}, {}), "('Creating dummy sources')", False, 'from baselayer.app.model_util import status, create_tables, drop_tables\n'), ((56, 12, 56, 23), 'skyportal.models.DBSession', 'DBSession', ({}, {}), '()', False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), ((204, 32, 204, 54), 'pandas.read_csv', 'pd.read_csv', ({(204, 44, 204, 53): 'phot_file'}, {}), '(phot_file)', True, 'import pandas as pd\n'), ((228, 32, 228, 54), 'pandas.read_csv', 'pd.read_csv', ({(228, 44, 228, 53): 'spec_file'}, {}), '(spec_file)', True, 'import pandas as pd\n'), ((256, 29, 256, 64), 'skyportal.models.Source.query.get', 'Source.query.get', ({(256, 46, 256, 63): "source_info['id']"}, {}), "(source_info['id'])", False, 'from skyportal.models import init_db, Base, DBSession, Source, User\n'), ((261, 26, 261, 52), 'os.getpgid', 'os.getpgid', ({(261, 37, 261, 51): 'web_client.pid'}, {}), '(web_client.pid)', False, 'import os\n'), ((222, 40, 222, 65), 'os.path.dirname', 'os.path.dirname', ({(222, 56, 222, 64): '__file__'}, {}), '(__file__)', False, 'import os\n'), ((234, 51, 234, 82), 'datetime.datetime', 'datetime.datetime', ({(234, 69, 234, 73): '2014', (234, 75, 234, 77): '10', (234, 79, 234, 81): '24'}, {}), '(2014, 10, 24)', False, 'import datetime\n'), ((245, 33, 245, 55), 'os.path.abspath', 'os.path.abspath', ({(245, 49, 245, 54): 'fpath'}, {}), '(fpath)', False, 'import os\n')]