from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QMainWindow, QGridLayout, QWidget, QFileDialog
from pathlib import Path

from src.controllers.formats import Formats
from src.controllers.resolution_controller import ResolutionController
from src.models.enums import Channels
from src.views.channel_view import ChannelView
from src.models.texture import Texture
from src.controllers.custom_widgets import Button, HorizontalSeparator

class MainWindow(QMainWindow):
    """ This object is the main window ui """
    def __init__(self):
        QMainWindow.__init__(self)

        self.r_channel = ChannelView(_channel_type=Channels.RED, _default_value=150)
        self.g_channel = ChannelView(_channel_type=Channels.GREEN, _default_value=150)
        self.b_channel = ChannelView(_channel_type=Channels.BLUE, _default_value=150)
        self.a_channel = ChannelView(_channel_type=Channels.ALPHA, _default_value=255)
        self.separator = HorizontalSeparator(_color='181818')
        self.resolution_controller = ResolutionController(_default_resolution=(1024, 1024))
        self.export_button = Button(_text="Export", _color_normal='009bfc', _color_hover='0079ca',
                                    _color_pressed='0069bd', _color_disabled='505050')
        self.export_button.clicked.connect(self.open_export_dialogue_box)

        self.setup_ui()

    def setup_ui(self):
        _grid_spacing = 20
        _grid_margin = 20

        self.setStyleSheet("""background: #222222;""")
        _grid = QGridLayout()
        _grid.setAlignment(Qt.AlignLeft)
        _grid.setSpacing(_grid_spacing)
        _grid.setContentsMargins(_grid_margin, _grid_margin, _grid_margin, _grid_margin)
        self.setLayout(_grid)

        main_widget = QWidget()
        main_widget.setLayout(_grid)
        self.setCentralWidget(main_widget)

        _grid.addWidget(self.r_channel, 0, 0)
        _grid.addWidget(self.g_channel, 0, 1)
        _grid.addWidget(self.b_channel, 0, 2)
        _grid.addWidget(self.a_channel, 0, 3)
        _grid.addWidget(self.separator, 4, 0, 1, 4)
        _grid.addWidget(self.resolution_controller, 5, 0, 1, 2)
        _grid.addWidget(self.export_button, 5, 2, 1, 2)

    def open_export_dialogue_box(self):
        """ Open a dialogue box to set an export location and export format """
        _file_path = QFileDialog.getSaveFileName(self, 'Save File', "", Formats().get_export_formats())
        _path = Path(_file_path[0])
        _format = Formats().get_format(_format_extension=_path.suffix)

        if _path != '' and _format != '':
            self.export_texture(_path=_path, _format=_format)

    def export_texture(self, _format: dict, _path: Path):
        """ Create a texture and export it at the desired path """
        _new_texture = Texture(_channel_r=self.r_channel.channel_data.get_data(),
                               _channel_g=self.g_channel.channel_data.get_data(),
                               _channel_b=self.b_channel.channel_data.get_data(),
                               _channel_a=self.a_channel.channel_data.get_data(),
                               _format=_format,
                               _resolution=self.resolution_controller.get_resolution(),
                               _quality=95,
                               _subsampling=0)
        _new_texture.save_texture(_full_path=fr"{_path}")


from django.test import TestCase, override_settings
from rest_framework.test import APIClient
from rest_framework import status
from .models import Entity, Feature, Measurement
import random
from django.utils.timezone import now
from datetime import timedelta as td

class Apex_Test(TestCase):
    def add_measurement_data(self):
        """Adds Entity / Feature data to test"""
        entity = "ENTITY"
        _entity, created = Entity.objects.get_or_create(name=entity)
        feature = "SENSOR"
        _feature, created = Feature.objects.get_or_create(name=feature)

        # One random value every minute for the last hour
        _now = now()
        _time = _now - td(hours=1)
        while _time < _now:
            value = random.uniform(0, 80)
            Measurement.objects.create(
                time=_time, entity=_entity, feature=_feature, value=value
            )
            _time = _time + td(minutes=1)

    def setUp(self):
        self.add_measurement_data()
        self.assertEqual(60, Measurement.objects.count())
        self.client = APIClient()

    @override_settings(APEX_DEFAULT_LAST="2h", APEX_DEFAULT_RESOLUTION="1m")
    def test_apex_sensor_data_retrieval(self):
        """Test the correctness of apex data retrieval"""
        response = self.client.get("/api/measurement/apex/?entity=ENTITY")
        self.assertEqual(status.HTTP_200_OK, response.status_code)
        data = response.json()
        self.assertEqual(1, len(data))
        self.assertEqual("SENSOR", data[0]["name"])
        self.assertEqual(60, len(data[0]["data"]))

    def test_apex_sensor_data_retrieval_with_parameters(self):
        """Test the correctness of apex data retrieval with parameters"""
        response = self.client.get(
            "/api/measurement/apex/?entity=ENTITY&last=2h&resolution=15m"
        )
        self.assertEqual(status.HTTP_200_OK, response.status_code)
        data = response.json()
        self.assertEqual(1, len(data))
        self.assertEqual("SENSOR", data[0]["name"])
        self.assertEqual(5, len(data[0]["data"]))

    def test_add_measurement_data_at_same_time(self):
        _entity, created = Entity.objects.get_or_create(name="entity")
        _feature1, created = Feature.objects.get_or_create(name="sensor1")
        _feature2, created = Feature.objects.get_or_create(name="sensor2")

        _now = now()
        _time = _now - td(minutes=2)

        Measurement.objects.create(
            time=_time, entity=_entity, feature=_feature1, value=1.0
        )
        Measurement.objects.create(
            time=_time, entity=_entity, feature=_feature2, value=1.0
        )


from collections import deque
# BFS
v = [0, 1, 2, 3, 4, 5, 6]
edges = [(0, 1), (0, 3), (1, 0), (1, 3), (1, 2), (1, 5), (1, 6), (2, 1), (2, 3), (2, 4), (2, 5), (3, 0), (3, 2),
         (3, 4), (4, 2), (4, 3), (4, 6), (5, 1), (5, 2), (6, 1), (6, 4)]
adj = {i: [] for i in v}
for i, j in edges:
    adj[i].append(j)

def BFS(adj, s):
    visited = {}
    res = [s]
    for key, value in adj.items():
        visited[key] = False
    visited[s] = True
    q = deque()
    q.append(s)
    while q:
        vertex = q.popleft()
        for i in adj[vertex]:
            if not visited[i]:
                visited[i] = True
                q.append(i)
                res.append(i)
    return res

bfsPath = BFS(adj, 0)
print(bfsPath)

#
#
#
#
#
#
# DFS
def DFS(adj, s):
    visited = {}
    res = []
    for key, value in adj.items():
        visited[key] = False
    stack = [s]
    while stack:
        # print(stack)
        vertex = stack.pop()
        if visited[vertex]:
            continue
        res.append(vertex)
        visited[vertex] = True
        l = adj[vertex][::-1]
        for i in l:
            if not visited[i]:
                stack.append(i)
    return res

print()
dfsPath = DFS(adj, 0)
print(dfsPath)

#
#
#
#
#
#
# DFS Recursive
global recursiveVisit, result
recursiveVisit = {i: False for i in v}
result = []

def DFSRecursive(adj, s):
    global recursiveVisit, result
    if recursiveVisit[s]:
        return
    recursiveVisit[s] = True
    result.append(s)
    for i in adj[s]:
        DFSRecursive(adj, i)

print()
DFSRecursive(adj, 0)
print(result)

#
#
#
#
#
#
# DFS Recursive Classification
global recursiveVisited, resultDFS, nodeStart, nodeEnd, time, vertexStack, treeEdge, backEdge, forwardEdge
recursiveVisited = {i: False for i in v}
nodeStart = {}
nodeEnd = {}
time = 1
resultDFS = []
vertexStack = [0]
treeEdge = []
backEdge = []
forwardEdge = []

def DFSRecursiveNode(adj, s):
    global recursiveVisited, resultDFS, nodeStart, nodeEnd, time, vertexStack, treeEdge, backEdge, forwardEdge
    nodeStart[s] = time
    recursiveVisited[s] = True
    resultDFS.append(s)
    time += 1
    for i in adj[s]:
        if not recursiveVisited[i]:
            vertexStack.append(i)
            treeEdge.append((s, i))
            DFSRecursiveNode(adj, i)
        elif i in vertexStack:
            backEdge.append((s, i))
        elif nodeStart[s] < nodeStart[i]:
            forwardEdge.append((s, i))
    vertexStack.pop()
    time += 1
    nodeEnd[s] = time

print()
DFSRecursiveNode(adj, 0)
print('DFSPath:', resultDFS)
print('TreeEdge:', treeEdge)
print('BackEdge:', backEdge)
print('Node Start Time: {}\nNode End Time: {}'.format(nodeStart, nodeEnd))
print(forwardEdge)


import nmap
import colorama
from pyfiglet import Figlet
from tabulate import tabulate

colorama.init()

# Print banner
f = Figlet(font='slant')
print(colorama.Fore.BLUE + f.renderText("NMAP Scanner"))
print("By 477")

# Get IP range to scan
try:
    ip_range = input(colorama.Fore.WHITE + "Enter IP range to scan (ex: 192.168.1.1-255): ")
except KeyboardInterrupt:
    print(colorama.Fore.RED + "\nProgram execution canceled.")
    exit()

# Scan hosts for open ports
try:
    nm = nmap.PortScanner()
    nm.scan(hosts=ip_range, arguments='-p 1-65535 -sV -T4')

    # Print live hosts and their open ports
    headers = ['Host', 'Port', 'Service', 'Product', 'Version', 'Extra Info']
    table = []
    for host in nm.all_hosts():
        if nm[host].state() == 'up':
            for port in nm[host]['tcp']:
                state = nm[host]['tcp'][port]['state']
                if state == 'open':
                    service = nm[host]['tcp'][port]['name']
                    product = nm[host]['tcp'][port]['product']
                    version = nm[host]['tcp'][port]['version']
                    extra_info = nm[host]['tcp'][port]['extrainfo']
                    table.append([host, port, service, product, version, extra_info])

    print(colorama.Fore.GREEN + f"\nFound {len(table)} open ports on {len(nm.all_hosts())} hosts.")
    if len(table) > 0:
        print(tabulate(table, headers=headers, tablefmt='fancy_grid'))

except nmap.PortScannerError as e:
    print(colorama.Fore.RED + "\nNmap error occurred:")
    print(e)
except Exception as e:
    print(colorama.Fore.RED + "\nAn error occurred:")
    print(e)


from pygame import draw, Rect, image, transform

class Bird:
    def __init__(self, scr, width, height, r):
        self.scr = scr
        self.width = width
        self.height = height

        self.x = self.width/2
        self.r = r
        self.y = self.height-self.r

        self.vy = 0
        self.gravity = 0.3

        # imaage
        self.img = image.load("img/android.png")
        self.img = transform.scale(self.img, (self.r, self.r))

    def update(self):
        self.y += self.vy
        self.vy += self.gravity

    def up(self, num=10):
        self.vy = -num

    def move(self, num):
        self.x += num
        if self.x+self.r < 0:
            self.x = self.width-self.r
        elif self.x >= self.width:
            self.x = 0
        else:
            pass

    def constrain(self, var, minmum, maximum):
        if var <= minmum:
            return minmum
        elif var >= maximum:
            return maximum
        else:
            return var

    def show(self):
        self.y = self.constrain(self.y, 100, self.height-self.r)
        # draw.rect(self.scr, (100,100,100), Rect(self.x, self.y, self.r, self.r))
        self.scr.blit(self.img, (self.x, self.y))


import datetime
import decimal
import json

from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.test import TestCase, override_settings
from django.utils import timezone
from faker import Factory as FakerFactory
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.models import ContentType

from experimenter.experiments.forms import (
    BugzillaURLField,
    DSIssueURLField,
    ChangeLogMixin,
    CustomModelMultipleChoiceField,
    ExperimentArchiveForm,
    ExperimentCommentForm,
    ExperimentObjectivesForm,
    ExperimentOverviewForm,
    ExperimentReviewForm,
    ExperimentRisksForm,
    ExperimentStatusForm,
    ExperimentSubscribedForm,
    ExperimentResultsForm,
    JSONField,
    NormandyIdForm,
    ExperimentTimelinePopulationForm,
    ExperimentOrderingForm,
)
from experimenter.experiments.models import Experiment
from experimenter.base.tests.factories import CountryFactory, LocaleFactory
from experimenter.experiments.tests.factories import ExperimentFactory, UserFactory
from experimenter.bugzilla.tests.mixins import MockBugzillaMixin
from experimenter.experiments.tests.mixins import MockTasksMixin, MockRequestMixin

from experimenter.notifications.models import Notification

faker = FakerFactory.create()

class TestJSONField(TestCase):

    def test_jsonfield_accepts_valid_json(self):
        valid_json = json.dumps({"a": True, 2: ["b", 3, 4.0]})
        field = JSONField()
        cleaned = field.clean(valid_json)
        self.assertEqual(cleaned, valid_json)

    def test_jsonfield_rejects_invalid_json(self):
        invalid_json = "{this isnt valid"
        field = JSONField()

        with self.assertRaises(ValidationError):
            field.clean(invalid_json)

@override_settings(BUGZILLA_HOST="https://bugzilla.example.com/")
class TestBugzillaURLField(TestCase):

    def test_accepts_bugzilla_url(self):
        field = BugzillaURLField()
        bugzilla_url = "{base}/show_bug.cgi?id=123".format(base=settings.BUGZILLA_HOST)
        cleaned = field.clean(bugzilla_url)
        self.assertEqual(cleaned, bugzilla_url)

    def test_rejects_non_show_bug_bugzilla_url(self):
        field = BugzillaURLField()
        bugzilla_url = "{base}/123".format(base=settings.BUGZILLA_HOST)

        with self.assertRaises(ValidationError):
            field.clean(bugzilla_url)

    def test_rejects_non_bugzilla_url(self):
        field = BugzillaURLField()

        with self.assertRaises(ValidationError):
            field.clean("www.example.com")

@override_settings(DS_ISSUE_HOST="https://jira.example.com/browse/")
class TestDSIssueURLField(TestCase):

    def test_accepts_ds_url_field(self):
        field = DSIssueURLField()
        ds_url = "{base}DS-1234".format(base=settings.DS_ISSUE_HOST)
        cleaned = field.clean(ds_url)
        self.assertEqual(cleaned, ds_url)

    def test_rejects_wrong_project_name(self):
        field = DSIssueURLField()
        ds_url = "{base}AA-1234".format(base=settings.DS_ISSUE_HOST)
        with self.assertRaises(ValidationError):
            field.clean(ds_url)

class TestChangeLogMixin(MockRequestMixin, TestCase):

    def test_mixin_creates_change_log_with_request_user_on_save(self):

        class TestForm(ChangeLogMixin, forms.ModelForm):

            class Meta:
                model = Experiment
                fields = ("name",)

        data = ExperimentFactory.attributes()
        form = TestForm(request=self.request, data=data)

        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertEqual(experiment.changes.count(), 1)

        change = experiment.changes.get()
        self.assertEqual(change.changed_by, self.user)

    def test_mixin_sets_old_and_new_status(self):
        old_status = Experiment.STATUS_DRAFT
        new_status = Experiment.STATUS_REVIEW
        experiment = ExperimentFactory.create_with_status(old_status)

        self.assertEqual(experiment.changes.count(), 1)

        class TestForm(ChangeLogMixin, forms.ModelForm):

            class Meta:
                model = Experiment
                fields = ("status",)

        form = TestForm(
            request=self.request, data={"status": new_status}, instance=experiment
        )
        self.assertTrue(form.is_valid())

        form.save()

        self.assertEqual(experiment.changes.count(), 2)

        change = experiment.changes.latest()

        self.assertEqual(change.old_status, old_status)
        self.assertEqual(change.new_status, new_status)

    def test_changelog_not_produced_when_no_change(self):

        experiment = ExperimentFactory.create_with_status(
            target_status=Experiment.STATUS_DRAFT
        )
        num_of_changes = experiment.changes.count()

        class TestForm(ChangeLogMixin, forms.ModelForm):

            class Meta:
                model = Experiment
                fields = ("name",)

        form = TestForm(
            request=self.request, data={"name": experiment.name}, instance=experiment
        )
        self.assertTrue(form.is_valid())
        form.save()
        experiment = Experiment.objects.get(id=experiment.id)
        self.assertEqual(experiment.changes.count(), num_of_changes)

    def test_changelog_values(self):
        experiment = Experiment()
        experiment.save()

        country1 = CountryFactory(code="CA", name="Canada")
        country2 = CountryFactory(code="US", name="United States")
        locale1 = LocaleFactory(code="da", name="Danish")
        locale2 = LocaleFactory(code="de", name="German")

        data = {
            "proposed_start_date": timezone.now().date(),
            "proposed_duration": 20,
            "population_percent": "10",
            "firefox_min_version": "56.0",
            "firefox_max_version": "58.0",
            "firefox_channel": Experiment.CHANNEL_BETA,
            "client_matching": "en-us",
            "platform": Experiment.PLATFORM_WINDOWS,
            "locales": [locale1, locale2],
            "countries": [country1, country2],
        }

        form = ExperimentTimelinePopulationForm(
            request=self.request, data=data, instance=experiment
        )
        self.assertTrue(form.is_valid())
        experiment = form.save()
        latest_changes = experiment.changes.latest()

        expected_data = {
            "locales": {
                "new_value": ["da", "de"],
                "old_value": None,
                "display_name": "Locales",
            },
            "platform": {
                "new_value": "All Windows",
                "old_value": None,
                "display_name": "Platform",
            },
            "countries": {
                "new_value": ["CA", "US"],
                "old_value": None,
                "display_name": "Countries",
            },
            "client_matching": {
                "new_value": "en-us",
                "old_value": None,
                "display_name": "Population Filtering",
            },
            "firefox_channel": {
                "new_value": "Beta",
                "old_value": None,
                "display_name": "Firefox Channel",
            },
            "proposed_duration": {
                "new_value": 20,
                "old_value": None,
                "display_name": "Proposed Total Duration (days)",
            },
            "population_percent": {
                "new_value": "10.0000",
                "old_value": None,
                "display_name": "Population Percentage",
            },
            "firefox_min_version": {
                "new_value": "56.0",
                "old_value": None,
                "display_name": "Firefox Min Version",
            },
            "firefox_max_version": {
                "new_value": "58.0",
                "old_value": None,
                "display_name": "Firefox Max Version",
            },
            "proposed_start_date": {
                "new_value": timezone.now().date().strftime("%Y-%m-%d"),
                "old_value": None,
                "display_name": "Proposed Start Date",
            },
        }

        self.assertEqual(expected_data, latest_changes.changed_values)

@override_settings(
    BUGZILLA_HOST="https://bugzilla.example.com/",
    DS_ISSUE_HOST="https://jira.example.com/browse/",
)
class TestExperimentOverviewForm(MockRequestMixin, TestCase):

    def setUp(self):
        super().setUp()
        ds_url = "{base}DS-123".format(base=settings.DS_ISSUE_HOST)
        bug_url = "{base}show_bug.cgi?id=123".format(base=settings.BUGZILLA_HOST)
        self.related_exp = ExperimentFactory.create()

        self.data = {
            "type": Experiment.TYPE_PREF,
            "name": "A new experiment!",
            "short_description": "Let us learn new things",
            "data_science_issue_url": ds_url,
            "owner": self.user.id,
            "analysis_owner": self.user.id,
            "engineering_owner": "Lisa the Engineer",
            "public_name": "A new public experiment!",
            "public_description": "Let us learn new public things",
            "related_to": [self.related_exp],
            "feature_bugzilla_url": bug_url,
            "related_work": "Designs: https://www.example.com/myproject/",
        }

    def test_minimum_required_fields_for_experiment(self):
        bug_url = "https://jira.example.com/browse/DO-123"

        data = {
            "type": Experiment.TYPE_PREF,
            "owner": self.user.id,
            "name": "A new experiment!",
            "short_description": "Let us learn new things",
            "data_science_issue_url": bug_url,
            "public_name": "Public Name",
            "public_description": "Public Description",
        }
        form = ExperimentOverviewForm(request=self.request, data=data)

        self.assertTrue(form.is_valid())

        experiment = form.save()

        self.assertEqual(experiment.owner, self.user)
        self.assertEqual(experiment.status, experiment.STATUS_DRAFT)
        self.assertEqual(experiment.name, data["name"])
        self.assertEqual(experiment.slug, "a-new-experiment")
        self.assertEqual(experiment.short_description, data["short_description"])
        self.assertEqual(experiment.public_name, data["public_name"])
        self.assertEqual(experiment.public_description, data["public_description"])
        self.assertEqual(experiment.changes.count(), 1)

    def test_minimum_required_fields_for_rollout(self):
        data = {
            "type": Experiment.TYPE_ROLLOUT,
            "owner": self.user.id,
            "name": "A new experiment!",
            "short_description": "Let us learn new things",
        }
        form = ExperimentOverviewForm(request=self.request, data=data)
        self.assertTrue(form.is_valid())

        experiment = form.save()

        self.assertEqual(experiment.owner, self.user)
        self.assertEqual(experiment.status, experiment.STATUS_DRAFT)
        self.assertEqual(experiment.name, self.data["name"])
        self.assertEqual(experiment.slug, "a-new-experiment")
        self.assertEqual(experiment.short_description, self.data["short_description"])
        self.assertEqual(experiment.changes.count(), 1)

    def test_form_creates_experiment(self):
        form = ExperimentOverviewForm(request=self.request, data=self.data)
        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertEqual(experiment.owner, self.user)
        self.assertEqual(experiment.engineering_owner, self.data["engineering_owner"])
        self.assertEqual(experiment.status, experiment.STATUS_DRAFT)
        self.assertEqual(experiment.name, self.data["name"])
        self.assertEqual(experiment.slug, "a-new-experiment")
        self.assertEqual(experiment.short_description, self.data["short_description"])
        self.assertTrue(self.related_exp in experiment.related_to.all())

        self.assertEqual(experiment.changes.count(), 1)
        change = experiment.changes.get()
        self.assertEqual(change.old_status, None)
        self.assertEqual(change.new_status, experiment.status)
        self.assertEqual(change.changed_by, self.request.user)

    def test_empty_slug_raises_error(self):
        self.data["name"] = "#"

        form = ExperimentOverviewForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())

    def test_unique_name_with_same_slug_raises_error(self):
        ExperimentFactory.create(slug="slug")
        self.data["name"] = "slug#"

        form = ExperimentOverviewForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())

    def test_ds_issue_url_required_for_non_rollout(self):
        self.data["type"] = Experiment.TYPE_PREF
        del self.data["data_science_issue_url"]

        form = ExperimentOverviewForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())

    def test_bugzilla_url_optional_for_rollout(self):
        self.data["type"] = Experiment.TYPE_ROLLOUT
        del self.data["data_science_issue_url"]

        form = ExperimentOverviewForm(request=self.request, data=self.data)

        self.assertTrue(form.is_valid())

    def test_invalid_bugzilla_url(self):
        self.data["data_science_issue_url"] = "https://example.com/notbugzilla"

        form = ExperimentOverviewForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())

class TestExperimentTimelinePopulationForm(MockRequestMixin, TestCase):

    def setUp(self):
        super().setUp()

        self.data = {
            "proposed_start_date": timezone.now().date(),
            "proposed_duration": 20,
            "proposed_enrollment": 10,
            "population_percent": "10.0",
            "firefox_channel": Experiment.CHANNEL_NIGHTLY,
            "firefox_min_version": "67.0",
            "firefox_max_version": "69.0",
            "locales": [],
            "countries": [],
            "platform": Experiment.PLATFORM_WINDOWS,
            "client_matching": "en-us",
        }

    def test_no_fields_required(self):
        experiment = ExperimentFactory.create()
        data = {
            "proposed_start_date": "",
            "proposed_duration": "",
            "proposed_enrollment": "",
            "population_percent": "",
            "firefox_channel": "",
            "firefox_min_version": "",
            "firefox_max_version": "",
            "locales": [],
            "countries": [],
            "platform": "",
            "client_matching": "",
        }
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=data, instance=experiment
        )
        self.assertTrue(form.is_valid())
        experiment = form.save()

    def test_form_saves_experiment_timeline_population_data(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, countries=[], locales=[]
        )
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )

        self.assertEqual(experiment.changes.count(), 1)

        experiment = form.save()

        self.assertEqual(experiment.proposed_start_date, self.data["proposed_start_date"])
        self.assertEqual(experiment.proposed_duration, self.data["proposed_duration"])
        self.assertEqual(experiment.proposed_enrollment, self.data["proposed_enrollment"])
        self.assertEqual(experiment.population_percent, decimal.Decimal("10.000"))
        self.assertEqual(experiment.firefox_channel, self.data["firefox_channel"])
        self.assertEqual(experiment.firefox_min_version, self.data["firefox_min_version"])
        self.assertEqual(experiment.firefox_max_version, self.data["firefox_max_version"])
        self.assertEqual(experiment.platform, self.data["platform"])
        self.assertEqual(experiment.client_matching, self.data["client_matching"])

        self.assertEqual(experiment.changes.count(), 2)

    def test_form_saves_rollout_timeline_population_data(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT,
            type=Experiment.TYPE_ROLLOUT,
            countries=[],
            locales=[],
        )

        data = {
            "proposed_start_date": timezone.now().date(),
            "proposed_duration": 20,
            "rollout_playbook": Experiment.ROLLOUT_PLAYBOOK_LOW_RISK,
            "firefox_channel": Experiment.CHANNEL_NIGHTLY,
            "firefox_min_version": "67.0",
            "firefox_max_version": "69.0",
            "locales": [],
            "countries": [],
        }

        form = ExperimentTimelinePopulationForm(
            request=self.request, data=data, instance=experiment
        )

        self.assertEqual(experiment.changes.count(), 1)

        experiment = form.save()

        self.assertEqual(experiment.proposed_start_date, data["proposed_start_date"])
        self.assertEqual(experiment.firefox_min_version, data["firefox_min_version"])
        self.assertEqual(experiment.firefox_max_version, data["firefox_max_version"])
        self.assertEqual(experiment.firefox_channel, data["firefox_channel"])
        self.assertEqual(experiment.population_percent, decimal.Decimal("25.000"))

        self.assertEqual(experiment.changes.count(), 2)

    def test_enrollment_must_be_less_or_equal_duration(self):
        self.data["proposed_enrollment"] = 2
        self.data["proposed_duration"] = 1

        form = ExperimentTimelinePopulationForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())

    def test_large_duration_is_invalid(self):
        self.data["proposed_duration"] = Experiment.MAX_DURATION + 1

        form = ExperimentTimelinePopulationForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())

    def test_large_enrollment_duration_is_invalid(self):
        self.data["proposed_enrollment"] = Experiment.MAX_DURATION + 1

        form = ExperimentTimelinePopulationForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())

    def test_start_date_must_be_greater_or_equal_to_current_date(self):
        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        self.data["proposed_start_date"] = timezone.now().date() - datetime.timedelta(
            days=1
        )

        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertFalse(form.is_valid())

    def test_locales_choices(self):
        locale1 = LocaleFactory(code="sv-SE", name="Swedish")
        locale2 = LocaleFactory(code="fr", name="French")
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, countries=[], locales=[]
        )
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertEqual(
            list(form.fields["locales"].choices),
            [
                (CustomModelMultipleChoiceField.ALL_KEY, "All locales"),
                (locale2.code, str(locale2)),
                (locale1.code, str(locale1)),
            ],
        )

    def test_locales_initials(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, locales=[]
        )
        locale1 = LocaleFactory(code="sv-SE", name="Swedish")
        locale2 = LocaleFactory(code="fr", name="French")
        experiment.locales.add(locale1)
        experiment.locales.add(locale2)
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertEqual(form.initial["locales"], [locale2, locale1])

    def test_locales_initials_all_locales(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, locales=[]
        )
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertEqual(
            form.initial["locales"], [CustomModelMultipleChoiceField.ALL_KEY]
        )

    def test_clean_locales(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, num_variants=0
        )
        locale1 = LocaleFactory(code="sv-SE", name="Swedish")
        locale2 = LocaleFactory(code="fr", name="French")
        self.data["locales"] = [locale2.code, locale1.code]
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertTrue(form.is_valid())
        self.assertEqual(set(form.cleaned_data["locales"]), set([locale2, locale1]))

    def test_clean_locales_all(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, num_variants=0
        )
        locale1 = LocaleFactory(code="sv-SE", name="Swedish")
        locale2 = LocaleFactory(code="fr", name="French")
        self.data["locales"] = [
            locale2.code,
            CustomModelMultipleChoiceField.ALL_KEY,
            locale1.code,
        ]
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertTrue(form.is_valid())
        self.assertEqual(list(form.cleaned_data["locales"]), [])

    def test_clean_unrecognized_locales(self):
        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        self.data["locales"] = ["xxx"]
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertTrue(not form.is_valid())
        self.assertTrue(form.errors["locales"])

    def test_countries_choices(self):
        country1 = CountryFactory(code="SV", name="Sweden")
        country2 = CountryFactory(code="FR", name="France")

        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, countries=[], locales=[]
        )
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertEqual(
            list(form.fields["countries"].choices),
            [
                (CustomModelMultipleChoiceField.ALL_KEY, "All countries"),
                (country2.code, str(country2)),
                (country1.code, str(country1)),
            ],
        )

    def test_countries_initials(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, countries=[]
        )
        country1 = CountryFactory(code="SV", name="Sweden")
        country2 = CountryFactory(code="FR", name="France")
        experiment.countries.add(country1)
        experiment.countries.add(country2)
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertEqual(form.initial["countries"], [country2, country1])

    def test_countries_initials_all(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, num_variants=0, countries=[]
        )
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertEqual(
            form.initial["countries"], [CustomModelMultipleChoiceField.ALL_KEY]
        )

    def test_clean_countries(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, num_variants=0
        )
        country1 = CountryFactory(code="SV", name="Sweden")
        country2 = CountryFactory(code="FR", name="France")
        self.data["countries"] = [country1.code, country2.code]
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertTrue(form.is_valid())
        self.assertEqual(
            # form.cleaned_data["countries"] is a QuerySet to exhaust it.
            list(form.cleaned_data["countries"]),
            [country2, country1],
        )

    def test_clean_countries_all(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, num_variants=0
        )
        country1 = CountryFactory(code="SV", name="Sweden")
        country2 = CountryFactory(code="FR", name="France")
        self.data["countries"] = [
            country1.code,
            CustomModelMultipleChoiceField.ALL_KEY,
            country2.code,
        ]
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertTrue(form.is_valid())
        self.assertEqual(list(form.cleaned_data["countries"]), [])

    def test_clean_unrecognized_countries(self):
        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        self.data["countries"] = ["xxx"]
        form = ExperimentTimelinePopulationForm(
            request=self.request, data=self.data, instance=experiment
        )
        self.assertTrue(not form.is_valid())
        self.assertTrue(form.errors["countries"])

    def test_form_is_invalid_if_population_percent_below_0(self):
        self.data["population_percent"] = "-1"
        form = ExperimentTimelinePopulationForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())
        self.assertIn("population_percent", form.errors)

    def test_form_is_invalid_if_population_percent_above_100(self):
        self.data["population_percent"] = "101"
        form = ExperimentTimelinePopulationForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())
        self.assertIn("population_percent", form.errors)

    def test_form_is_invalid_if_firefox_max_is_lower_than_min(self):
        self.data["firefox_min_version"] = "66.0"
        self.data["firefox_max_version"] = "64.0"
        form = ExperimentTimelinePopulationForm(request=self.request, data=self.data)
        self.assertFalse(form.is_valid())
        self.assertIn("firefox_max_version", form.errors)

    def test_form_is_valid_if_firefox_max_is_equal_to_min(self):
        self.data["firefox_min_version"] = "66.0"
        self.data["firefox_max_version"] = "66.0"
        form = ExperimentTimelinePopulationForm(request=self.request, data=self.data)
        self.assertTrue(form.is_valid())

class TestExperimentObjectivesForm(MockRequestMixin, TestCase):

    def test_no_fields_required(self):
        experiment = ExperimentFactory.create()
        form = ExperimentObjectivesForm(
            request=self.request, data={}, instance=experiment
        )
        self.assertTrue(form.is_valid())

    def test_form_saves_objectives(self):
        created_experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        self.assertEqual(created_experiment.changes.count(), 1)

        data = {
            "objectives": "The objective is to experiment!",
            "analysis": "Lets analyze the results!",
            "survey_required": True,
            "survey_urls": "example.com",
            "survey_instructions": "Here are the launch instructions.",
        }

        form = ExperimentObjectivesForm(
            request=self.request, data=data, instance=created_experiment
        )

        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertEqual(experiment.objectives, data["objectives"])
        self.assertEqual(experiment.analysis, data["analysis"])
        self.assertTrue(experiment.survey_required)
        self.assertEqual(experiment.survey_urls, data["survey_urls"])
        self.assertEqual(experiment.survey_instructions, data["survey_instructions"])

        self.assertEqual(experiment.changes.count(), 2)

class TestExperimentRisksForm(MockRequestMixin, TestCase):

    valid_data = {
        "risk_partner_related": False,
        "risk_brand": True,
        "risk_fast_shipped": True,
        "risk_confidential": True,
        "risk_release_population": True,
        "risk_revenue": True,
        "risk_data_category": True,
        "risk_external_team_impact": True,
        "risk_telemetry_data": True,
        "risk_ux": True,
        "risk_security": True,
        "risk_revision": True,
        "risk_technical": True,
        "risk_technical_description": "It's complicated",
        "risks": "There are some risks",
        "testing": "Always be sure to test!",
        "test_builds": "Latest build",
        "qa_status": "It ain't easy being green",
    }

    def test_no_fields_required(self):
        experiment = ExperimentFactory.create()
        form = ExperimentRisksForm(request=self.request, data={}, instance=experiment)
        self.assertTrue(form.is_valid())

    def test_form_saves_risks(self):
        created_experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        self.assertEqual(created_experiment.changes.count(), 1)

        data = self.valid_data.copy()
        form = ExperimentRisksForm(
            request=self.request, data=data, instance=created_experiment
        )

        self.assertTrue(form.is_valid())

        experiment = form.save()
        self.assertFalse(experiment.risk_partner_related)
        self.assertTrue(experiment.risk_brand)
        self.assertTrue(experiment.risk_fast_shipped)
        self.assertTrue(experiment.risk_confidential)
        self.assertTrue(experiment.risk_release_population)
        self.assertTrue(experiment.risk_technical)
        self.assertEqual(
            experiment.risk_technical_description, data["risk_technical_description"]
        )
        self.assertEqual(experiment.risks, data["risks"])
        self.assertEqual(experiment.testing, data["testing"])
        self.assertEqual(experiment.test_builds, data["test_builds"])
        self.assertEqual(experiment.qa_status, data["qa_status"])

        self.assertEqual(experiment.changes.count(), 2)

class TestExperimentResultsForm(MockRequestMixin, TestCase):

    def test_form_saves_results(self):
        experiment = ExperimentFactory.create()
        self.assertEqual(experiment.changes.count(), 0)
        data = {
            "results_url": "https://example.com",
            "results_initial": "Initially, all went well.",
            "results_lessons_learned": "Lessons were learned.",
        }

        form = ExperimentResultsForm(request=self.request, data=data, instance=experiment)

        self.assertTrue(form.is_valid())

        experiment = form.save()

        self.assertEqual(experiment.results_url, "https://example.com")
        self.assertEqual(experiment.results_initial, "Initially, all went well.")

        self.assertEqual(experiment.changes.count(), 1)

class TestExperimentReviewForm(
    MockRequestMixin, MockBugzillaMixin, MockTasksMixin, TestCase
):

    def test_form_saves_reviews(self):
        user = UserFactory.create()
        content_type = ContentType.objects.get_for_model(Experiment)
        experiment_model_permissions = Permission.objects.filter(
            content_type=content_type, codename__startswith="can_check"
        )
        for permission in experiment_model_permissions:
            user.user_permissions.add(permission)

        self.request.user = user

        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_REVIEW)
        self.assertEqual(experiment.changes.count(), 2)

        self.assertFalse(experiment.review_science)
        self.assertFalse(experiment.review_engineering)
        self.assertFalse(experiment.review_qa_requested)
        self.assertFalse(experiment.review_intent_to_ship)
        self.assertFalse(experiment.review_bugzilla)
        self.assertFalse(experiment.review_qa)
        self.assertFalse(experiment.review_relman)
        self.assertFalse(experiment.review_advisory)
        self.assertFalse(experiment.review_legal)
        self.assertFalse(experiment.review_ux)
        self.assertFalse(experiment.review_security)
        self.assertFalse(experiment.review_vp)
        self.assertFalse(experiment.review_data_steward)
        self.assertFalse(experiment.review_comms)
        self.assertFalse(experiment.review_impacted_teams)

        data = {
            "review_science": True,
            "review_engineering": True,
            "review_qa_requested": True,
            "review_intent_to_ship": True,
            "review_bugzilla": True,
            "review_qa": True,
            "review_relman": True,
            "review_advisory": True,
            "review_legal": True,
            "review_ux": True,
            "review_security": True,
            "review_vp": True,
            "review_data_steward": True,
            "review_comms": True,
            "review_impacted_teams": True,
        }

        form = ExperimentReviewForm(request=self.request, data=data, instance=experiment)

        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertTrue(experiment.review_science)
        self.assertTrue(experiment.review_engineering)
        self.assertTrue(experiment.review_qa_requested)
        self.assertTrue(experiment.review_intent_to_ship)
        self.assertTrue(experiment.review_bugzilla)
        self.assertTrue(experiment.review_qa)
        self.assertTrue(experiment.review_relman)
        self.assertTrue(experiment.review_advisory)
        self.assertTrue(experiment.review_legal)
        self.assertTrue(experiment.review_ux)
        self.assertTrue(experiment.review_security)
        self.assertTrue(experiment.review_vp)
        self.assertTrue(experiment.review_data_steward)
        self.assertTrue(experiment.review_comms)
        self.assertTrue(experiment.review_impacted_teams)

        self.assertEqual(experiment.changes.count(), 3)

    def test_added_reviews_property(self):
        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_REVIEW)
        self.assertEqual(experiment.changes.count(), 2)

        data = {"review_bugzilla": True, "review_science": True}

        form = ExperimentReviewForm(request=self.request, data=data, instance=experiment)

        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertEqual(len(form.added_reviews), 2)
        self.assertEqual(len(form.removed_reviews), 0)
        self.assertIn(form.fields["review_bugzilla"].label, form.added_reviews)
        self.assertIn(form.fields["review_science"].label, form.added_reviews)

        self.assertEqual(experiment.changes.count(), 3)

    def test_removed_reviews_property(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_REVIEW, review_bugzilla=True, review_science=True
        )

        self.assertEqual(experiment.changes.count(), 2)

        data = {"review_bugzilla": False, "review_science": False}

        form = ExperimentReviewForm(request=self.request, data=data, instance=experiment)

        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertEqual(len(form.added_reviews), 0)
        self.assertEqual(len(form.removed_reviews), 2)
        self.assertIn(form.fields["review_bugzilla"].label, form.removed_reviews)
        self.assertIn(form.fields["review_science"].label, form.removed_reviews)

        self.assertEqual(experiment.changes.count(), 3)

    def test_required_reviews(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_REVIEW,
            type=Experiment.TYPE_PREF,
            review_relman=True,
            review_science=True,
        )

        form = ExperimentReviewForm(request=self.request, data={}, instance=experiment)

        self.assertEqual(
            set([f.name for f in form.required_reviews]),
            set(
                [
                    "review_science",
                    "review_advisory",
                    "review_engineering",
                    "review_qa_requested",
                    "review_intent_to_ship",
                    "review_bugzilla",
                    "review_qa",
                    "review_relman",
                ]
            ),
        )

    def test_required_reviews_when_a_risk_partner_related_is_true(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_REVIEW,
            review_relman=True,
            review_science=True,
            risk_partner_related=True,
        )

        form = ExperimentReviewForm(request=self.request, data={}, instance=experiment)

        self.assertIn(form["review_vp"], form.required_reviews)
        self.assertIn(form["review_legal"], form.required_reviews)
        self.assertNotIn(form["review_vp"], form.optional_reviews)
        self.assertNotIn(form["review_legal"], form.optional_reviews)

    def test_optional_reviews(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_REVIEW, review_relman=True, review_science=True
        )

        form = ExperimentReviewForm(request=self.request, data={}, instance=experiment)

        self.assertEqual(
            form.optional_reviews,
            [
                form["review_comms"],
                form["review_data_steward"],
                form["review_impacted_teams"],
                form["review_legal"],
                form["review_security"],
                form["review_ux"],
                form["review_vp"],
            ],
        )

    def test_optional_reviews_when_a_risk_option_is_true(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_REVIEW,
            review_relman=True,
            review_science=True,
            risk_partner_related=True,
        )

        form = ExperimentReviewForm(request=self.request, data={}, instance=experiment)

        self.assertNotIn(form["review_vp"], form.optional_reviews)
        self.assertIn(form["review_vp"], form.required_reviews)

    def test_required_reviews_for_rollout(self):
        experiment = ExperimentFactory.create(type=Experiment.TYPE_ROLLOUT)

        form = ExperimentReviewForm(self.request, instance=experiment)

        self.assertEqual(
            set([f.name for f in form.required_reviews]),
            set(
                [
                    "review_qa",
                    "review_intent_to_ship",
                    "review_qa_requested",
                    "review_advisory",
                    "review_relman",
                ]
            ),
        )

    def test_optional_reviews_for_rollout(self):
        experiment = ExperimentFactory.create(type=Experiment.TYPE_ROLLOUT)

        form = ExperimentReviewForm(self.request, instance=experiment)

        self.assertEqual(
            set([f.name for f in form.optional_reviews]),
            set(
                [
                    "review_impacted_teams",
                    "review_ux",
                    "review_legal",
                    "review_security",
                    "review_vp",
                    "review_comms",
                    "review_data_steward",
                ]
            ),
        )

    def test_cannot_check_review_relman_without_permissions(self):
        user_1 = UserFactory.create()
        user_2 = UserFactory.create()

        content_type = ContentType.objects.get_for_model(Experiment)
        permission = Permission.objects.get(
            content_type=content_type, codename="can_check_relman_signoff"
        )
        user_1.user_permissions.add(permission)

        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_REVIEW)

        self.request.user = user_2
        form = ExperimentReviewForm(
            request=self.request, data={"review_relman": True}, instance=experiment
        )

        self.assertTrue(form.is_valid())
        experiment = form.save()
        self.assertFalse(experiment.review_relman)

        self.request.user = user_1

        form = ExperimentReviewForm(
            request=self.request, data={"review_relman": True}, instance=experiment
        )

        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertTrue(experiment.review_relman)

    def test_cannot_check_review_qa_without_permissions(self):
        user_1 = UserFactory.create()
        user_2 = UserFactory.create()

        content_type = ContentType.objects.get_for_model(Experiment)
        permission = Permission.objects.get(
            content_type=content_type, codename="can_check_QA_signoff"
        )
        user_1.user_permissions.add(permission)
        self.assertTrue(user_1.has_perm("experiments.can_check_QA_signoff"))
        self.assertFalse(user_2.has_perm("experiments.can_check_QA_signoff"))

        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_REVIEW)

        self.request.user = user_2
        form = ExperimentReviewForm(
            request=self.request, data={"review_qa": True}, instance=experiment
        )

        self.assertTrue(form.is_valid())
        experiment = form.save()
        self.assertFalse(experiment.review_qa)

        self.request.user = user_1

        form = ExperimentReviewForm(
            request=self.request, data={"review_qa": True}, instance=experiment
        )

        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertTrue(experiment.review_qa)

class TestExperimentStatusForm(
    MockBugzillaMixin, MockRequestMixin, MockTasksMixin, TestCase
):

    def test_form_allows_valid_state_transition_and_creates_changelog(self):
        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        form = ExperimentStatusForm(
            request=self.request,
            data={"status": experiment.STATUS_REVIEW},
            instance=experiment,
        )
        self.assertTrue(form.is_valid())
        experiment = form.save()
        self.assertEqual(experiment.status, experiment.STATUS_REVIEW)
        change = experiment.changes.latest()
        self.assertEqual(change.old_status, experiment.STATUS_DRAFT)
        self.assertEqual(change.new_status, experiment.STATUS_REVIEW)

    def test_form_rejects_invalid_state_transitions(self):
        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        form = ExperimentStatusForm(
            request=self.request,
            data={"status": experiment.STATUS_LIVE},
            instance=experiment,
        )
        self.assertFalse(form.is_valid())

    def test_sets_bugzilla_id_when_draft_becomes_review(self):
        experiment = ExperimentFactory.create_with_status(
            Experiment.STATUS_DRAFT, bugzilla_id=None
        )
        form = ExperimentStatusForm(
            request=self.request,
            data={"status": experiment.STATUS_REVIEW},
            instance=experiment,
        )
        self.assertTrue(form.is_valid())
        experiment = form.save()
        self.mock_tasks_create_bug.delay.assert_called_with(self.user.id, experiment.id)

    def test_adds_bugzilla_comment_and_normandy_slug_when_becomes_ship(self):
        experiment = ExperimentFactory.create_with_status(
            target_status=Experiment.STATUS_REVIEW,
            type=Experiment.TYPE_PREF,
            name="Experiment Name",
            slug="experiment-slug",
            firefox_min_version="57.0",
            firefox_max_version="",
            firefox_channel=Experiment.CHANNEL_NIGHTLY,
            bugzilla_id="12345",
        )
        self.assertEqual(experiment.normandy_slug, None)

        form = ExperimentStatusForm(
            request=self.request,
            data={"status": experiment.STATUS_SHIP},
            instance=experiment,
        )

        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertEqual(
            experiment.normandy_slug, "pref-experiment-name-nightly-57-bug-12345"
        )
        self.mock_tasks_update_experiment_bug.delay.assert_called_with(
            self.user.id, experiment.id
        )

class TestExperimentCommentForm(MockRequestMixin, TestCase):

    def test_form_creates_comment(self):
        text = "hello"
        section = Experiment.SECTION_OVERVIEW
        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        form = ExperimentCommentForm(
            request=self.request,
            data={"experiment": experiment.id, "section": section, "text": text},
        )
        self.assertTrue(form.is_valid())
        comment = form.save()
        self.assertEqual(comment.experiment, experiment)
        self.assertEqual(comment.section, section)
        self.assertEqual(comment.created_by, self.user)
        self.assertEqual(comment.text, text)

    def test_section_must_be_valid(self):
        text = "hello"
        section = "invalid section"
        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        form = ExperimentCommentForm(
            request=self.request,
            data={"experiment": experiment.id, "section": section, "text": text},
        )
        self.assertFalse(form.is_valid())
        self.assertIn("section", form.errors)

    def test_text_is_required(self):
        text = ""
        section = Experiment.SECTION_OVERVIEW
        experiment = ExperimentFactory.create_with_status(Experiment.STATUS_DRAFT)
        form = ExperimentCommentForm(
            request=self.request,
            data={"experiment": experiment.id, "section": section, "text": text},
        )
        self.assertFalse(form.is_valid())
        self.assertIn("text", form.errors)

class TestExperimentArchiveForm(MockRequestMixin, MockTasksMixin, TestCase):

    def test_form_flips_archive_bool(self):

        experiment = ExperimentFactory.create(archived=False)

        form = ExperimentArchiveForm(self.request, instance=experiment, data={})
        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.assertEqual(self.mock_tasks_update_bug_resolution.delay.call_count, 1)
        self.assertTrue(experiment.archived)
        self.assertEqual(experiment.changes.latest().message, "Archived Delivery")

        form = ExperimentArchiveForm(self.request, instance=experiment, data={})
        self.assertTrue(form.is_valid())

        experiment = form.save()
        self.assertEqual(self.mock_tasks_update_bug_resolution.delay.call_count, 2)
        self.assertFalse(experiment.archived)
        self.assertEqual(experiment.changes.latest().message, "Unarchived Delivery")

    def test_form_stays_unarchived_when_live(self):
        self.assertEqual(Notification.objects.count(), 0)
        experiment = ExperimentFactory.create(
            archived=False, status=Experiment.STATUS_LIVE
        )

        form = ExperimentArchiveForm(self.request, instance=experiment, data={})
        self.assertTrue(form.is_valid())
        experiment = form.save()

        self.mock_tasks_update_bug_resolution.delay.assert_not_called()
        self.assertFalse(experiment.archived)
        self.assertEqual(Notification.objects.count(), 1)

class TestExperimentSubscribedForm(MockRequestMixin, TestCase):

    def test_form_adds_subscribers(self):
        experiment = ExperimentFactory.create()

        self.assertFalse(self.user in experiment.subscribers.all())

        form = ExperimentSubscribedForm(self.request, instance=experiment, data={})
        self.assertTrue(form.is_valid())

        experiment = form.save()
        self.assertTrue(self.user in experiment.subscribers.all())

    def test_form_removes_subscribers(self):
        experiment = ExperimentFactory.create(subscribers=[self.user])

        self.assertTrue(self.user in experiment.subscribers.all())

        form = ExperimentSubscribedForm(self.request, instance=experiment, data={})
        self.assertTrue(form.is_valid())

        experiment = form.save()
        self.assertFalse(self.user in experiment.subscribers.all())

class TestNormandyIdForm(MockRequestMixin, TestCase):

    def test_form_not_valid_with_bad_main_id(self):
        experiment = ExperimentFactory.create()

        form = NormandyIdForm(
            self.request,
            instance=experiment,
            data={"normandy_id": "aaaa", "other_normandy_ids": "434"},
        )

        self.assertFalse(form.is_valid())

    def test_form_not_valid_with_bad_other_ids(self):
        experiment = ExperimentFactory.create()

        form = NormandyIdForm(
            self.request,
            instance=experiment,
            data={"normandy_id": "4343", "other_normandy_ids": "434, aaa"},
        )

        self.assertFalse(form.is_valid())

    def test_form_not_valid_when_other_ids_duplicate_main_id(self):
        experiment = ExperimentFactory.create()

        form = NormandyIdForm(
            self.request,
            instance=experiment,
            data={"normandy_id": "4343", "other_normandy_ids": "4343"},
        )

        self.assertFalse(form.is_valid())

    def test_form_valid_when_other_ids_formatted_correctly(self):
        experiment = ExperimentFactory.create()

        form = NormandyIdForm(
            self.request,
            instance=experiment,
            data={"normandy_id": "4343", "other_normandy_ids": "4323, 5671"},
        )

        self.assertTrue(form.is_valid())

    def test_form_changelog_has_message(self):
        experiment = ExperimentFactory.create()

        form = NormandyIdForm(
            self.request,
            instance=experiment,
            data={"normandy_id": "4343", "other_normandy_ids": "443"},
        )

        self.assertTrue(form.is_valid())

        form.save()

        self.assertTrue(experiment.changes.latest().message, "Recipe ID(s) Added")

class TestExperimentOrderingForm(TestCase):

    def test_accepts_valid_ordering(self):
        ordering = ExperimentOrderingForm.ORDERING_CHOICES[1][0]
        form = ExperimentOrderingForm({"ordering": ordering})
        self.assertTrue(form.is_valid())

    def test_rejects_invalid_ordering(self):
        form = ExperimentOrderingForm({"ordering": "invalid ordering"})
        self.assertFalse(form.is_valid())


from numbers import Real

import numpy as np
import theano.tensor as tt
import theano

from pymc3.distributions.distribution import (
    Distribution,
    Discrete,
    Continuous,
    draw_values,
    generate_samples,
)
from pymc3.distributions import transforms
from pymc3.distributions.dist_math import bound

__all__ = ["Bound"]

class _Bounded(Distribution):
    def __init__(self, distribution, lower, upper, default, *args, **kwargs):
        self.lower = lower
        self.upper = upper
        self._wrapped = distribution.dist(*args, **kwargs)

        if default is None:
            defaults = self._wrapped.defaults
            for name in defaults:
                setattr(self, name, getattr(self._wrapped, name))
        else:
            defaults = ("_default",)
            self._default = default

        super().__init__(
            shape=self._wrapped.shape,
            dtype=self._wrapped.dtype,
            testval=self._wrapped.testval,
            defaults=defaults,
            transform=self._wrapped.transform,
        )

    def logp(self, value):
        logp = self._wrapped.logp(value)
        bounds = []
        if self.lower is not None:
            bounds.append(value >= self.lower)
        if self.upper is not None:
            bounds.append(value <= self.upper)
        if len(bounds) > 0:
            return bound(logp, *bounds)
        else:
            return logp

    def _random(self, lower, upper, point=None, size=None):
        lower = np.asarray(lower)
        upper = np.asarray(upper)
        if lower.size > 1 or upper.size > 1:
            raise ValueError(
                "Drawing samples from distributions with "
                "array-valued bounds is not supported."
            )
        total_size = np.prod(size)
        samples = []
        s = 0
        while s < total_size:
            sample = np.atleast_1d(
                self._wrapped.random(point=point, size=total_size)
            ).flatten()

            select = sample[np.logical_and(sample >= lower, sample <= upper)]
            samples.append(select)
            s += len(select)
        if size is not None:
            return np.reshape(np.concatenate(samples)[:total_size], size)
        else:
            return samples[0]

    def random(self, point=None, size=None):
        if self.lower is None and self.upper is None:
            return self._wrapped.random(point=point, size=size)
        elif self.lower is not None and self.upper is not None:
            lower, upper = draw_values([self.lower, self.upper], point=point, size=size)
            return generate_samples(
                self._random, lower, upper, point, dist_shape=self.shape, size=size
            )
        elif self.lower is not None:
            lower = draw_values([self.lower], point=point, size=size)
            return generate_samples(
                self._random, lower, np.inf, point, dist_shape=self.shape, size=size
            )
        else:
            upper = draw_values([self.upper], point=point, size=size)
            return generate_samples(
                self._random, -np.inf, upper, point, dist_shape=self.shape, size=size
            )

class _DiscreteBounded(_Bounded, Discrete):
    def __init__(self, distribution, lower, upper, transform="infer", *args, **kwargs):
        if transform == "infer":
            transform = None
        if transform is not None:
            raise ValueError("Can not transform discrete variable.")

        if lower is None and upper is None:
            default = None
        elif lower is not None and upper is not None:
            default = (lower + upper) // 2
        if upper is not None:
            default = upper - 1
        if lower is not None:
            default = lower + 1

        super().__init__(
            distribution, lower, upper, default, *args, transform=transform, **kwargs
        )

class _ContinuousBounded(_Bounded, Continuous):
    r"""
    An upper, lower or upper+lower bounded distribution

    Parameters
    ----------
    distribution : pymc3 distribution
        Distribution to be transformed into a bounded distribution
    lower : float (optional)
        Lower bound of the distribution, set to -inf to disable.
    upper : float (optional)
        Upper bound of the distribibution, set to inf to disable.
    tranform : 'infer' or object
        If 'infer', infers the right transform to apply from the supplied bounds.
        If transform object, has to supply .forward() and .backward() methods.
        See pymc3.distributions.transforms for more information.
    """

    def __init__(self, distribution, lower, upper, transform="infer", *args, **kwargs):
        dtype = kwargs.get("dtype", theano.config.floatX)

        if lower is not None:
            lower = tt.as_tensor_variable(lower).astype(dtype)
        if upper is not None:
            upper = tt.as_tensor_variable(upper).astype(dtype)

        if transform == "infer":
            if lower is None and upper is None:
                transform = None
                default = None
            elif lower is not None and upper is not None:
                transform = transforms.interval(lower, upper)
                default = 0.5 * (lower + upper)
            elif upper is not None:
                transform = transforms.upperbound(upper)
                default = upper - 1
            else:
                transform = transforms.lowerbound(lower)
                default = lower + 1
        else:
            default = None

        super().__init__(
            distribution, lower, upper, default, *args, transform=transform, **kwargs
        )

class Bound:
    r"""
    Create a Bound variable object that can be applied to create
    a new upper, lower, or upper and lower bounded distribution.

    The resulting distribution is not normalized anymore. This
    is usually fine if the bounds are constants. If you need
    truncated distributions, use `Bound` in combination with
    a :class:`~pymc3.model.Potential` with the cumulative probability function.

    The bounds are inclusive for discrete distributions.

    Parameters
    ----------
    distribution : pymc3 distribution
        Distribution to be transformed into a bounded distribution.
    lower : float or array like, optional
        Lower bound of the distribution.
    upper : float or array like, optional
        Upper bound of the distribution.

    Examples
    --------
    .. code-block:: python

        with pm.Model():
            NegativeNormal = pm.Bound(pm.Normal, upper=0.0)
            par1 = NegativeNormal('par`', mu=0.0, sigma=1.0, testval=-0.5)
            # you can use the Bound object multiple times to
            # create multiple bounded random variables
            par1_1 = NegativeNormal('par1_1', mu=-1.0, sigma=1.0, testval=-1.5)

            # you can also define a Bound implicitly, while applying
            # it to a random variable
            par2 = pm.Bound(pm.Normal, lower=-1.0, upper=1.0)(
                    'par2', mu=0.0, sigma=1.0, testval=1.0)
    """

    def __init__(self, distribution, lower=None, upper=None):
        if isinstance(lower, Real) and lower == -np.inf:
            lower = None
        if isinstance(upper, Real) and upper == np.inf:
            upper = None

        if not issubclass(distribution, Distribution):
            raise ValueError('"distribution" must be a Distribution subclass.')

        self.distribution = distribution
        self.lower = lower
        self.upper = upper

    def __call__(self, name, *args, **kwargs):
        if "observed" in kwargs:
            raise ValueError(
                "Observed Bound distributions are not supported. "
                "If you want to model truncated data "
                "you can use a pm.Potential in combination "
                "with the cumulative probability function. See "
                "pymc3/examples/censored_data.py for an example."
            )

        transform = kwargs.pop("transform", "infer")
        if issubclass(self.distribution, Continuous):
            return _ContinuousBounded(
                name,
                self.distribution,
                self.lower,
                self.upper,
                transform,
                *args,
                **kwargs
            )
        elif issubclass(self.distribution, Discrete):
            return _DiscreteBounded(
                name,
                self.distribution,
                self.lower,
                self.upper,
                transform,
                *args,
                **kwargs
            )
        else:
            raise ValueError("Distribution is neither continuous nor discrete.")

    def dist(self, *args, **kwargs):
        if issubclass(self.distribution, Continuous):
            return _ContinuousBounded.dist(
                self.distribution, self.lower, self.upper, *args, **kwargs
            )

        elif issubclass(self.distribution, Discrete):
            return _DiscreteBounded.dist(
                self.distribution, self.lower, self.upper, *args, **kwargs
            )
        else:
            raise ValueError("Distribution is neither continuous nor discrete.")


import sys
import argparse

import torch
import torch.nn as nn
import torch.nn.functional as F

from torchtext import data

from transformers import AutoTokenizer
from transformers import BertForSequenceClassification

from sklearn.metrics import confusion_matrix, classification_report

def define_argparse():

    p = argparse.ArgumentParser()

    p.add_argument('--model_fn', required=True)
    p.add_argument('--gpu_id', type=int, default=-1)
    p.add_argument('--batch_size', type=int, default=32)

    config = p.parse_args()

    return config

def read_text():

    lines = []
    y_trues = []

    for line in sys.stdin:
        if line.strip() != '':
            lines += [line.strip().split('\t')[1]]
            y_trues += [line.strip().split('\t')[0]]

    return lines, y_trues

def main(config):
    saved_data = torch.load(
        config.model_fn,
        map_location='cpu' if config.gpu_id < 0 else f'cuda:{config.gpu_id}',
    )

    train_config = saved_data['config']
    bert_best = saved_data['bert']
    index_to_label = saved_data['classes']

    lines, y_trues = read_text()

    with torch.no_grad():
        tokenizer = AutoTokenizer.from_pretrained(train_config.pretrained_model_name)
        model = BertForSequenceClassification.from_pretrained(
            train_config.pretrained_model_name,
            num_labels=len(index_to_label)
        )
        model.load_state_dict(bert_best)

        if config.gpu_id >= 0:
            model.cuda(config.gpu_id)
        device = next(model.parameters()).device

        model.eval()

        y_hats = []
        for idx in range(0, len(lines), config.batch_size):
            mini_batch = tokenizer(
                lines[idx:idx+config.batch_size],
                padding=True,
                truncation=True,
                return_tensors='pt'
            )

            x = mini_batch['input_ids']
            x = x.to(device)
            mask = mini_batch['attention_mask']
            mask = mask.to(device)

            y_hat = F.softmax(model(x, attention_mask=mask)[0], dim=-1)
            y_hats += [y_hat]

        y_hats = torch.cat(y_hats, dim=0)

        probs, indices = y_hats.cpu().topk(1)
        y_hat_labels = [index_to_label[idx] for idx in indices.squeeze().numpy()]

        print(classification_report(y_trues, y_hat_labels, digits=4))

if __name__ == "__main__":
    config = define_argparse()
    main(config)


from google.appengine.ext import db
import webapp2
from google.appengine.api import users, mail

from main import BaseHandler
from models import *

class AdminListHandler(BaseHandler):
  def get(self):
    user = users.get_current_user()
    if not user:
      self.redirect("/login")
    elif not UserInfo.isAdmin(user.user_id()):
      self.redirect("/")
    else:
      rqs = ScheduleRequest.all()
      template_args ={
        'user': user,
        'rqs': rqs,
        'timetable': timetable
      }
      self.render_template("adminlist.html", **template_args)

  def post(self):
    arqs = self.request.get_all("approve")
    drqs = self.request.get_all("deny")
    parqs = []
    pdrqs = []
    for rq in arqs:
      if rq in drqs: drqs.remove(rq)
      rq = db.get(rq)
      parqs.append(rq)
      accepted = RoomSchedule(roomnum=rq.roomnum, userid=rq.userid,role=rq.role,
                              startdate=rq.startdate,
                              starttime=rq.starttime,endtime=rq.endtime,
                              deletekey=rq.deletekey, reserved=True)
      accepted.put()
      sender_address = "Room Scheduling Notification <notification@roomscheduler490.appspotmail.com>"
      subject = "Your request has been approved"
      body = """
      Your request of room %s has been approved.
      """ % rq.roomnum
      user_address = rq.useremail
      mail.send_mail(sender_address, user_address, subject, body)
      rq.delete()

    for rq in drqs:
      rq = db.get(rq)
      pdrqs.append(rq)
      sender_address = "Room Scheduling Notification <notification@roomscheduler490.appspotmail.com>"
      subject = "Your request has been denied"
      body = """
      Your request of room %s has been denied.
      """ % rq.roomnum
      user_address = rq.useremail
      mail.send_mail(sender_address, user_address, subject, body)
      rq.delete()

    template_args = {
      'user': users.get_current_user(),
      'arqs': parqs,
      'drqs': pdrqs,
      'timetable': timetable
    }
    self.render_template("adminsuccess.html", **template_args)


from __future__ import annotations

from typing import TYPE_CHECKING
from discord.components import SelectOption

from discord.ui.select import select

from .constants import Embed

if TYPE_CHECKING:
    from typing import Mapping, List, Optional, Tuple, Union, Any

    from .bot import Bot

import discord
from discord.ui.select import Select
from discord.ext import commands, menus
from discord.ext.menus.views import ViewMenuPages

USE_HELP = "Use ``{0.clean_prefix}help <command/module>`` for more help!"

class HelpViewMenu(ViewMenuPages):
    def __init__(self, source, **kwargs):
        super().__init__(source, delete_message_after=True)
        self.msg: Optional[discord.Message] = kwargs.get("message")
        self.select: Optional[Select] = kwargs.get("select")

    async def send_initial_message(self, ctx, channel):
        page = await self._source.get_page(0)
        kwargs = await self._get_kwargs_from_page(page)
        view = self.build_view()

        if view and self.select:
            view.add_item(self.select)

        if self.msg:
            await self.msg.edit(**kwargs, view=view)
            return self.msg
        return await channel.send(**kwargs, view=view)

    def should_add_reactions(self):
        return True

    def build_view(self):
        view = super().build_view()
        setattr(view, "ctx", self.ctx)

        async def interaction_check(interaction: discord.Interaction):
            if interaction.user == view.ctx.author:  # type: ignore
                return True
            await interaction.response.send_message(
                "You do not own this menu!", ephemeral=True
            )
            return False

        setattr(view, "interaction_check", interaction_check)
        return view

class FormatBotHelp(menus.ListPageSource):
    def __init__(self, entries, ctx: commands.Context, *, per_page: Optional[int] = 5):
        super().__init__(entries, per_page=per_page)
        self.ctx = ctx

    async def format_page(
        self,
        menu: ViewMenuPages,
        entries: List[Tuple[commands.Cog, List[commands.Command]]],
    ):
        embed = Embed(title=f"Help!", description=USE_HELP.format(self.ctx))

        for cog, _commands in entries:
            embed.add_field(
                name=f"{cog.qualified_name}",
                value=f"{' '.join(f'``{cmd}``' for cmd in _commands) or 'No commands'}",
                inline=False,
            )

        if self.get_max_pages() > 1:
            embed.set_footer(text=f"Page {menu.current_page+1}/{self.get_max_pages()}")

        return embed

class FormatHelp(menus.ListPageSource):
    """A listpagesource that generates for both cog and commands, since they are roughly the same"""

    def __init__(
        self,
        entries,
        ctx: commands.Context,
        *,
        per_page: Optional[int] = 5,
        group: Union[commands.Cog, commands.Group],
    ):
        super().__init__(entries, per_page=per_page)
        self.ctx = ctx
        self.group = group

    async def format_page(
        self, menu: menus.MenuPages, entries: List[commands.Command]
    ) -> Embed:
        embed = Embed(
            title=f"Help for {self.group.qualified_name}!",
            description=f"{USE_HELP.format(self.ctx)} \n\n",
        )
        assert isinstance(embed.description, str)

        if isinstance(self.group, commands.Group):
            embed.description += f"{self.group.help}"
        elif isinstance(self.group, commands.Cog):
            embed.description += f"{self.group.description}"

        for command in entries:
            name = f"{command} {command.signature}"
            value = command.help or "No help"

            if isinstance(command, commands.Group):
                value = f"[Group] {value}"

            embed.add_field(name=name, value=value, inline=False)

        if self.get_max_pages() > 1:
            embed.set_footer(text=f"Page {menu.current_page+1}/{self.get_max_pages()}")

        return embed

class HelpCogSelect(Select):
    def __init__(self, help_command: HelpCommand, cogs: List[commands.Cog]):
        self.help_command = help_command
        options = [
            SelectOption(
                label=cog.qualified_name,
                description=cog.description[:99] if cog.description else None,
                value=cog.qualified_name,
                default=False,
            )
            for cog in cogs
            if len(cog.get_commands()) > 0
        ]
        super().__init__(options=options)

    async def callback(self, interaction: discord.Interaction):
        setattr(self.help_command, "message", interaction.message)
        cog = self.help_command.context.bot.get_cog(self.values[0])  # type: ignore
        await self.help_command.send_cog_help(cog)

class HelpCommand(commands.HelpCommand):
    sctx: commands.Context

    async def filter_commands(self, commands, *, sort=False, key=None):
        assert self.context is not None  # appease the linter

        if await self.context.bot.is_owner(self.context.author):
            return commands

        return await super().filter_commands(commands, sort=sort, key=key)

    async def get_filtered_cogs(self) -> List[commands.Cog]:
        """Returns a list of filtered cogs.
        This checks if cog has > 1 cmd and the return of :meth:`filter_commands` is truthy.

        Returns:
            List[commands.Cog]: A list of the cogs.
        """
        return [
            cog
            for cog in self.context.bot.cogs.values()  # type: ignore
            if len(cog.get_commands()) > 0
            and await self.filter_commands(cog.get_commands())
        ]

    async def send_bot_help(
        self, mapping: Mapping[Optional[commands.Cog], List[commands.Command]]
    ):
        assert self.context is not None  # appease the linter

        filtered: List[Tuple[commands.Cog, List[commands.Command]]] = []

        for k, v in mapping.items():
            _filtered = await self.filter_commands(v)

            if _filtered and k:
                filtered.append((k, _filtered))

        if not filtered:
            return await self.get_destination().send("You cannot use any commands!")

        await HelpViewMenu(
            FormatBotHelp(filtered, self.context),
            select=HelpCogSelect(self, await self.get_filtered_cogs()),
        ).start(self.context)

    async def send_group_help(self, group: commands.Group):
        assert self.context is not None  # appease the linter

        filtered = await self.filter_commands(list(group.commands))

        if not filtered:
            return await self.send_command_help(group)

        await HelpViewMenu(
            source=FormatHelp(filtered, self.context, group=group)
        ).start(self.context)

    async def send_cog_help(self, cog: commands.Cog):
        assert self.context is not None  # appease the linter

        filtered = await self.filter_commands(cog.get_commands())

        if not filtered:
            return await self.get_destination().send(
                "You cannot use any commands in this module!"
            )

        await HelpViewMenu(
            source=FormatHelp(filtered, self.context, group=cog),
            message=getattr(self, "message", None),
            select=HelpCogSelect(self, await self.get_filtered_cogs()),
        ).start(self.context)

    async def send_command_help(self, command: commands.Command):
        assert self.context is not None  # appease the linter

        embed = Embed(
            title=f"Help for {command}!", description=USE_HELP.format(self.context)
        )

        embed.add_field(
            name="Help", value=command.help or "No help given", inline=False
        )

        embed.add_field(name="Can run", value=await command.can_run(self.context))

        embed.add_field(name="Signature", value=command.signature or "No parameters")

        embed.add_field(
            name="Aliases",
            value=" ".join(f"``{a}``" for a in command.aliases) or "None",
        )

        await self.get_destination().send(embed=embed)

def setup(bot: Bot):
    bot.help_command = HelpCommand()

def teardown(bot: Bot):
    bot.help_command = commands.DefaultHelpCommand()


import pygame #Κανει εισαγωγη ολη την pygame!
from pygame.locals import * #Κανει καπιες σημαντικες μεταβλητες/σταθερες import στο προγραμμα μας

import random

pygame.init() #Αρχικοποιεί το Pygame

screen = pygame.display.set_mode((745,480),DOUBLEBUF,32)
candy = pygame.image.load("candy.png")
gun = pygame.image.load("gun.png")
gk = pygame.image.load("ab.png")
bg = pygame.image.load("bg.jpg")

x=325
y=400

xgk = 290
ygk = 80

onshoot = False
counter = 0
move = True
movebucket=True
speed = 6
score = 0
lives = 6
while True:
    screen.fill((0,0,0))
    screen.blit(bg,(0,0))

    screen.blit(candy,(x,y))
    screen.blit(gun,(310,385))
    screen.blit(gk,(xgk,ygk))

    if move==True and movebucket==True:
        xgk+=speed
        if (xgk>=542):
            move=False
    if move==False and movebucket==True:
        xgk-=speed
        if (xgk<=50):
            move=True

    if movebucket==False and xgk!=x:
        lives-=1
        movebucket=True
        onshoot = False
        y=400

    if lives<=0:
        movebucket=False
        onshoot = False
        break #(!)
    speed=lives

    print("LIV : ",lives)

    for event in pygame.event.get():
        if pygame.mouse.get_pressed() == (True,False,False):
            onshoot=True

    if onshoot==True:
        counter+=1
        y = y - 6
        if y<=100:
            onshoot = False
            movebucket=False

    pygame.display.update()


"""
Non-equivariant ResNet and U-net
"""

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

def conv(input_channels, output_channels, kernel_size, stride):
    return nn.Sequential(
        nn.Conv2d(input_channels, output_channels, kernel_size = kernel_size,
                  stride = stride, padding=(kernel_size - 1) // 2),
        nn.ReLU()
    )

def deconv(input_channels, output_channels):
    return nn.Sequential(
        nn.ConvTranspose2d(input_channels, output_channels, kernel_size = 4,
                           stride = 2, padding=1),
        nn.ReLU()
    )

class Unet(nn.Module):
    def __init__(self, input_channels, output_channels, kernel_size):
        super(Unet, self).__init__()
        self.input_channels = input_channels
        self.conv1 = conv(input_channels, 64, kernel_size=kernel_size, stride=2)
        self.conv2 = conv(64, 128, kernel_size=kernel_size, stride=2)
        self.conv2_1 = conv(128, 128, kernel_size=kernel_size, stride=1)
        self.conv3 = conv(128, 256, kernel_size=kernel_size, stride=2)
        self.conv3_1 = conv(256, 256, kernel_size=kernel_size, stride=1)
        self.conv4 = conv(256, 512, kernel_size=kernel_size, stride=2)
        self.conv4_1 = conv(512, 512, kernel_size=kernel_size, stride=1)

        self.deconv3 = deconv(512, 128)
        self.deconv2 = deconv(384, 64)
        self.deconv1 = deconv(192, 32)
        self.deconv0 = deconv(96, 16)

        self.output_layer = nn.Conv2d(16 + input_channels, output_channels, kernel_size=kernel_size,
                                      stride = 1, padding=(kernel_size - 1) // 2)

    def forward(self, x):
        out_conv1 = self.conv1(x)
        out_conv2 = self.conv2_1(self.conv2(out_conv1))
        out_conv3 = self.conv3_1(self.conv3(out_conv2))
        out_conv4 = self.conv4_1(self.conv4(out_conv3))

        out_deconv3 = self.deconv3(out_conv4)
        concat3 = torch.cat((out_conv3, out_deconv3), 1)
        out_deconv2 = self.deconv2(concat3)
        concat2 = torch.cat((out_conv2, out_deconv2), 1)
        out_deconv1 = self.deconv1(concat2)
        concat1 = torch.cat((out_conv1, out_deconv1), 1)
        out_deconv0 = self.deconv0(concat1)
        concat0 = torch.cat((x, out_deconv0), 1)
        out = self.output_layer(concat0)
        return out

class Resblock(nn.Module):
    def __init__(self, input_channels, hidden_dim, kernel_size):
        super(Resblock, self).__init__()
        self.layer1 = nn.Sequential(
            nn.Conv2d(input_channels, hidden_dim, kernel_size = kernel_size, padding = (kernel_size-1)//2),
            nn.BatchNorm2d(hidden_dim),
            nn.LeakyReLU()
        )
        self.layer2 = nn.Sequential(
            nn.Conv2d(hidden_dim, hidden_dim, kernel_size = kernel_size, padding = (kernel_size-1)//2),
            nn.BatchNorm2d(hidden_dim),
            nn.LeakyReLU()
        )

        if input_channels != hidden_dim:
            self.upscale = nn.Sequential(
                nn.Conv2d(input_channels, hidden_dim, kernel_size = kernel_size, padding = (kernel_size-1)//2),
                nn.LeakyReLU()
                )
        self.input_channels = input_channels
        self.hidden_dim = hidden_dim

    def forward(self, xx):
        out = self.layer1(xx)
        if self.input_channels != self.hidden_dim:
            out = self.layer2(out) + self.upscale(xx)
        else:
            out = self.layer2(out) + xx
        return out

class ResNet(nn.Module):
    def __init__(self, input_channels, output_channels, kernel_size):
        super(ResNet, self).__init__()
        layers = [Resblock(input_channels, 64, kernel_size), Resblock(64, 64, kernel_size)]
        layers += [Resblock(64, 128, kernel_size), Resblock(128, 128, kernel_size)]
        layers += [Resblock(128, 256, kernel_size), Resblock(256, 256, kernel_size)]
        layers += [Resblock(256, 512, kernel_size), Resblock(512, 512, kernel_size)]
        layers += [nn.Conv2d(512, output_channels, kernel_size = kernel_size, padding = (kernel_size-1)//2)]
        self.model = nn.Sequential(*layers)

    def forward(self, xx):
        out = self.model(xx)
        return out


import heapq
import sys
from collections import defaultdict

class Dijkstras:

    def find_shortest(self, adjs, N):
        graph = defaultdict(list)
        for u, v, w in adjs:
            graph[u].append((v, w))

        distance = [sys.maxsize] * N
        distance[0] = 0
        pq = [[0, 0]]  # cost, node
        visited = [False] * N
        while pq:
            curr_dist, node = heapq.heappop(pq)
            visited[node] = True
            for nei, nei_cost in graph[node]:
                if not visited[nei]:
                    distance[nei] = min(distance[nei], curr_dist + nei_cost)
                    heapq.heappush(pq, (distance[nei], nei))
        return distance

if __name__ == '__main__':
    init = Dijkstras()
    conn = [
        [0, 1, 3],
        [0, 2, 2],
        [1, 2, 8],
        [1, 5, 2],
        [2, 4, 7],
        [3, 1, 9],
        [3, 5, 1],
        [5, 7, 1],
        [6, 2, 6],
        [6, 4, 7],
        [7, 5, 7],
        [7, 6, 2],
    ]
    print(init.find_shortest(conn, 8))


from typing import Optional
from urllib.parse import urljoin
from datetime import date

class Customer:
    def __init__(self,
        id: str,
        dateCreated: date,
        name: str,
        cpfCnpj: str,
        **kwargs
    ) -> None:

        self.id                   = id
        self.dateCreated          = dateCreated if type(dateCreated) == date else date.fromisoformat(dateCreated)
        self.name                 = name
        self.cpfCnpj              = cpfCnpj
        self.email                = kwargs.get('email', None)
        self.phone                = kwargs.get('phone', None)
        self.mobilePhone          = kwargs.get('mobilePhone', None)
        self.address              = kwargs.get('address', None)
        self.addressNumber        = kwargs.get('addressNumber', None)
        self.complement           = kwargs.get('complement', None)
        self.province             = kwargs.get('province', None)
        self.postalCode           = kwargs.get('postalCode', None)
        self.externalReference    = kwargs.get('externalReference', None)
        self.notificationDisabled = kwargs.get('notificationDisabled', None)
        self.additionalEmails     = kwargs.get('additionalEmails', None)
        self.municipalInscription = kwargs.get('municipalInscription', None)
        self.stateInscription     = kwargs.get('stateInscription', None)
        self.observations         = kwargs.get('observations', None)

    def __repr__(self) -> str:
        return f'Customer(id={self.id}, name={self.name}, email={self.email})'


import numpy as np

import torch
import torch.nn as nn

import torchvision.models as models
import torchvision.datasets as datasets
import torchvision.transforms as T

from tqdm import tqdm

def accuracy(output, target):
    with torch.no_grad():
        pred = output.argmax(-1)
        # print(pred, target)
        # print(pred.shape, target.shape)
        correct = (pred == target).float().sum() / len(target)
        return correct

transform = T.Compose([
    T.Resize(224),
    T.CenterCrop(224),
    T.ToTensor(),
    T.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

val_ds = datasets.ImageFolder("imagenet/val", transform=transform)
val = torch.utils.data.DataLoader(val_ds, batch_size=8, shuffle=False, num_workers=4)

# model = models.mobilenet_v2()
model = torch.load("checkpoints/model.pt")

criterion = nn.CrossEntropyLoss()

model.eval()
accur, losses = [], []
with torch.no_grad():
    for images, target in tqdm(val):

        output = model(images)
        loss = criterion(output, target)

        acc = accuracy(output, target)
        accur.append(acc)

    print(f'Accuracy Val: {np.round(np.mean(accur), 3)}')


import json
import os
import random
from collections import defaultdict
from typing import List, Optional

from pytorch_lightning import LightningDataModule
from torch.utils.data import DataLoader, Dataset

class GAD_Dataset(Dataset):
    def __init__(self, data_dir, split: str):
        with open(os.path.join(data_dir, f"{split}.json")) as f:
            """
            'id': '0'
            'sentence': 'this study proposes that A/A genotype at position -607 in @GENE$ gene can be used as a new genetic maker in Thai population for predicting @DISEASE$ development.'
            'label': '1'
            """
            self.data = [
                json.loads(line)
                for line in f
            ]
            for d in self.data:
                if d["label"] == '0':
                    d["label"] = 'no answer'
                else: # '1'
                    d["label"] = 'has answer'
                d["sentence"] = d["sentence"][0].upper() + d["sentence"][1:]
                assert "@GENE$" in d["sentence"] and "@DISEASE$" in d["sentence"]
                # unused
                d["entity1"] = ["@GENE$", "GENE", -1, -1]
                d["entity2"] = ["@DISEASE$", "DISEASE", -1, -1]

    def __getitem__(self, idx) -> dict:
        ret: dict = self.data[idx]
        ret["dataset_name"] = "GAD"
        return ret

    def __len__(self):
        return len(self.data)

    @staticmethod
    def collate_fn(samples: List[dict]):
        ret = defaultdict(list)
        for sample in samples:
            for k, v in sample.items():
                ret[k].append(v)
        ret["LABEL_VERBALIZER"] = DataModule.LABEL_VERBALIZER
        return ret

def hypo_template(w1, w2, verbalized, abstain=True, **kwargs):
    """
    w1 and w2 must be @GENE#, @DISEASE#
    verbalized either no answer or has answer
    """
    assert abstain
    if verbalized == "no answer":
        hypo = f"There is no relation between {w1} and {w2}."
    else:
        hypo = f"{w1} and {w2} are correlated."
    return hypo[0].upper() + hypo[1:]

class DataModule(LightningDataModule):
    LABEL_VERBALIZER = {
        "has answer": "has answer",
        "no answer": "no answer"
    }

    def __init__(self, data_dir, adaptive_neg_sample: bool = False,
                 # dataloader
                 batch_size: int = 64, num_workers: int = 0, pin_memory: bool = False,
                 **kwargs):
        super().__init__()
        assert os.path.exists(data_dir)
        self.datasets = {}
        self.save_hyperparameters(logger=False)

    def setup(self, stage: Optional[str] = None):
        for split in ["train", "dev", "test"]:
            self.datasets[split] = GAD_Dataset(self.hparams.data_dir, split)

    def load_dataloader(self, split: str):
        return DataLoader(
            self.datasets[split],
            num_workers=self.hparams.num_workers,
            pin_memory=self.hparams.pin_memory,
            collate_fn=GAD_Dataset.collate_fn,
            batch_size=self.hparams.batch_size,
            shuffle=(split == "train"),
        )

    def train_dataloader(self):
        return self.load_dataloader("train")

    def val_dataloader(self):
        return self.load_dataloader("dev")

    def test_dataloader(self):
        return self.load_dataloader("test")

    def predict_dataloader(self):
        return [self.train_dataloader(), self.test_dataloader()]

    @staticmethod
    def random_neg_sample(batch: dict, num_negs: int = 1) -> dict:
        """
        randomly sample 1 from all possible labels
        :return: new batch with field "neg_label" List[List[str]] where List[str] is num_negs-len neg samples
        """
        neg = [
            random.sample(list(filter(
                lambda l: l != label,
                DataModule.LABEL_VERBALIZER
            )), k=num_negs)
            for label in batch["label"]
        ]
        assert len(neg) == len(batch["label"])
        batch["neg_label"] = neg
        return batch