import datetime
import json
from typing import List, Optional

from libcloud.common.base import ConnectionKey, JsonResponse
from libcloud.common.types import InvalidCredsError, ProviderError
from libcloud.compute.base import (
    KeyPair,
    Node,
    NodeAuthPassword,
    NodeAuthSSHKey,
    NodeDriver,
    NodeImage,
    NodeLocation,
    NodeSize,
    NodeState,
    T_Auth,
)
from libcloud.dns.base import DNSDriver, Record, Zone
from libcloud.dns.types import (
    RecordAlreadyExistsError,
    RecordDoesNotExistError,
    RecordError,
    RecordType,
    ZoneDoesNotExistError,
    ZoneError,
)
from libcloud.utils.publickey import get_pubkey_openssh_fingerprint
from libcloud.utils.py3 import httplib

class VscaleJsonResponse(JsonResponse):
    def parse_error(self) -> str:
        http_code = int(self.status)
        if http_code == httplib.FORBIDDEN:
            raise InvalidCredsError("Invalid credentials")

        body = super().parse_error()

        if http_code == httplib.INTERNAL_SERVER_ERROR:
            return body["error"]["message"]

        if http_code in (httplib.CONFLICT, httplib.NOT_FOUND):
            raise ProviderError(value=body["error"], http_code=http_code)

        return body["error"]

    def success(self):
        # При успешном DELETE возвращается NO CONTENT
        if self.status == httplib.NO_CONTENT:
            return True
        return super().success()

class VscaleConnection(ConnectionKey):
    responseCls = VscaleJsonResponse
    host = "api.vscale.io"

    def add_default_headers(self, headers):
        headers["X-Token"] = self.key
        return headers

class VscaleDriver(NodeDriver):
    connectionCls = VscaleConnection
    name = "Vscale"
    website = "https://vscale.io/"
    NODE_STATE_MAP = {
        "started": NodeState.RUNNING,
        "stopped": NodeState.STOPPED,
        "billing": NodeState.SUSPENDED,
        "queued": NodeState.PENDING,  # в документации нет, но в API возвращает
    }

    def list_locations(self):
        response = self.connection.request("v1/locations")
        locations = []
        # there is only one possible location RU
        default_location = "RU"
        for loc in response.object:
            locations.append(
                NodeLocation(loc["id"], loc["description"], default_location, self, extra=loc),
            )
        return locations

    def list_images(self):
        images = []
        response = self.connection.request("v1/images")
        for image in response.object:
            images.append(NodeImage(image["id"], image["description"], self, extra=image))
        return images

    def list_sizes(self, location=None):
        sizes = []
        response = self.connection.request("v1/rplans")
        for plan in response.object:
            # since selectel doesnt support filtering do it manually
            if location and location not in plan["locations"]:
                continue
            # selectel doesn't provide prices for plans and bandwidth
            #  so set to 0
            extra = plan
            sizes.append(
                NodeSize(
                    id=plan["id"],
                    name=plan["id"],
                    ram=plan["memory"],
                    disk=plan["disk"],
                    price=0,
                    bandwidth=0,
                    driver=self,
                    extra=extra,
                ),
            )
        return sizes

    def list_key_pairs(self):
        response = self.connection.request("v1/sshkeys")
        key_pairs = []
        for kp in response.object:
            key_pair = KeyPair(
                name=kp["name"],
                public_key=kp["key"],
                fingerprint=get_pubkey_openssh_fingerprint(kp["key"]),
                driver=self,
                extra=dict(id=kp["id"]),
            )
            key_pairs.append(key_pair)
        return key_pairs

    def get_key_pair(self, key_name):
        key_pairs = self.list_key_pairs()
        for kp in key_pairs:
            if kp.name == key_name:
                return kp
        return None

    def create_key_pair(self, name: str, public_key: str) -> KeyPair:
        payload = {
            "key": public_key,
            "name": name,
        }
        data = json.dumps(payload)
        headers = {"Content-Type": "application/json"}
        response = self.connection.request(
            "v1/sshkeys",
            method="POST",
            headers=headers,
            data=data,
        )
        kp = response.object

        key = kp.pop("key")
        key_pair = KeyPair(
            name=kp.pop("name"),
            public_key=key,
            fingerprint=get_pubkey_openssh_fingerprint(key),
            driver=self,
            extra=kp,
        )
        return key_pair

    def delete_key_pair(self, key_pair: KeyPair):
        key_pair_id = key_pair.extra["id"]
        response = self.connection.request(f"v1/sshkeys/{key_pair_id}", method="DELETE")
        return response.status == httplib.NO_CONTENT

    def list_nodes(self):
        response = self.connection.request("v1/scalets")
        nodes = []
        for n in response.object:

            state = self.NODE_STATE_MAP.get(n["status"], NodeState.UNKNOWN)

            created = datetime.datetime.strptime(n["created"], "%d.%m.%Y %H:%M:%S")

            private_ips = []
            if n["private_address"]:
                private_ips.append(n["private_address"]["address"])

            public_ips = []
            if n["public_address"]:
                public_ips.append(n["public_address"]["address"])

            # неправильно передаётся name. для сравнения используется поле id
            image = NodeImage(n["made_from"], name=n["made_from"], driver=self)

            node = Node(
                id=n["ctid"],
                name=n["name"],
                state=state,
                public_ips=public_ips,
                private_ips=private_ips,
                driver=self,
                image=image,
                extra=n,
                created_at=created,
            )
            nodes.append(node)

        return nodes

    def start_node(self, node: Node) -> bool:
        payload = {
            "id": node.id,
        }
        data = json.dumps(payload)
        headers = {"Content-Type": "application/json;charset=UTF-8"}
        response = self.connection.request(
            f"v1/scalets/{node.id}/start",
            data=data,
            headers=headers,
            method="POST",
        )
        return response.status == httplib.OK

    def stop_node(self, node: Node) -> bool:
        payload = {
            "id": node.id,
        }
        data = json.dumps(payload)
        headers = {"Content-Type": "application/json;charset=UTF-8"}
        response = self.connection.request(
            f"v1/scalets/{node.id}/stop",
            headers=headers,
            data=data,
            method="POST",
        )
        return response.status == httplib.OK

    def destroy_node(self, node: Node) -> bool:
        headers = {"Content-Type": "application/json;charset=UTF-8"}
        response = self.connection.request(
            f"v1/scalets/{node.id}",
            headers=headers,
            method="DELETE",
        )
        return response.status == httplib.OK

    def reboot_node(self, node: Node) -> bool:
        headers = {"Content-Type": "application/json;charset=UTF-8"}
        response = self.connection.request(
            f"v1/scalets/{node.id}/restart",
            headers=headers,
            method="PATCH",
        )
        return response.status == httplib.OK

    def create_node(
        self,
        name: str,
        size: NodeSize,
        image: NodeImage,
        location: Optional[NodeLocation] = None,
        auth: T_Auth = None,
    ) -> Node:
        payload = {
            "make_from": image.id,
            "rplan": size.id,
            "do_start": True,
            "name": name,
        }
        if location:
            payload["location"] = location
        if T_Auth:
            if isinstance(T_Auth, NodeAuthSSHKey):
                payload["keys"] = [T_Auth.pubkey]
            elif isinstance(T_Auth, NodeAuthPassword):
                payload["password"] = T_Auth.password
        data = json.dumps(payload)
        headers = {"Content-Type": "application/json;charset=UTF-8"}
        response = self.connection.request(
            "v1/scalets",
            headers=headers,
            data=data,
            method="POST",
        )
        state = self.NODE_STATE_MAP.get(response.object.get("status"), NodeState.UNKNOWN)
        node = Node(
            id=response.object.get("ctid"),
            name=response.object.get("name"),
            state=state,
            public_ips=response.object.get("public_address"),
            private_ips=response.object.get("private_address"),
            driver=self,
            image=image,
            extra=response,
            created_at=response.object.get("created"),
        )
        return node

class VscaleDns(DNSDriver):
    connectionCls = VscaleConnection
    name = "Vscale"
    website = "https://vscale.io/"

    RECORD_TYPE_MAP = {
        RecordType.SOA: "SOA",
        RecordType.NS: "NS",
        RecordType.A: "A",
        RecordType.AAAA: "AAAA",
        RecordType.CNAME: "CNAME",
        RecordType.SRV: "SRV",
        RecordType.MX: "MX",
        RecordType.TXT: "TXT",
        RecordType.SPF: "SPF",
    }

    def get_zone(self, domain_id: str) -> Zone:
        response = self.connection.request(f"v1/domains/{domain_id}")

        result = response.object

        zone_id = result.pop("id")
        name = result.pop("name")
        extra = result

        zone = Zone(
            id=zone_id,
            domain=name,
            type="master",
            ttl=None,
            driver=self,
            extra=extra,
        )

        return zone

    def list_zones(self):
        response = self.connection.request("v1/domains/")
        zones = []
        for n in response.object:
            extra = dict(
                tags=n["tags"],
                create_date=n["create_date"],
                cheange_date=n["change_date"],
                user_id=n["user_id"],
            )
            zone = Zone(
                id=n["id"],
                domain=n["name"],
                type="master",
                ttl=None,
                driver=self,
                extra=extra,
            )
            zones.append(zone)
        return zones

    def create_zone(self, domain, type="master", ttl=None, extra=None) -> Zone:
        payload = {"name": domain}
        data = json.dumps(payload)
        headers = {"Content-Type": "application/json"}
        response = self.connection.request(
            "v1/domains/",
            data=data,
            headers=headers,
            method="POST",
        )

        result = response.object

        zone_id = result.pop("id")
        name = result.pop("name")
        extra = result

        zone = Zone(
            id=zone_id,
            domain=name,
            type="master",
            ttl=None,
            driver=self,
            extra=extra,
        )

        return zone

    def delete_zone(self, zone) -> bool:
        try:
            response = self.connection.request(f"v1/domains/{zone.id}", method="DELETE")
        except ProviderError as e:
            if e.value == "domain_not_found":
                raise ZoneDoesNotExistError(e.value, self, zone.id)
            raise

        return response.status == httplib.NO_CONTENT

    def list_records(self, zone: Zone) -> List[Record]:
        response = self.connection.request(f"v1/domains/{zone.id}/records/")
        result = response.object

        records = []
        for r in result:
            zone_id = r.pop("id")
            name = r.pop("name")
            record_type = r.pop("type")
            data = r.pop("content")
            ttl = r.pop("ttl", None)
            extra = r

            record = Record(
                id=zone_id,
                name=name,
                type=record_type,
                data=data,
                zone=zone,
                driver=self,
                ttl=ttl,
                extra=extra,
            )
            records.append(record)

        return records

    def get_record(self, zone_id: str, record_id: str):
        response = self.connection.request(f"v1/domains/{zone_id}/records/{record_id}")
        result = response.object

        result_id = str(result.pop("id"))
        name = result.pop("name")
        result_type = result.pop("type")
        data = result.pop("content")
        ttl = result.pop("ttl", None)
        extra = result

        zone = self.get_zone(zone_id)

        record = Record(
            id=result_id,
            name=name,
            type=result_type,
            data=data,
            zone=zone,
            driver=self,
            ttl=ttl,
            extra=extra,
        )
        return record

    def create_record(self, name, zone: Zone, type, data, extra=None):
        payload = {
            "id": zone.id,
            "name": name,
            "type": type,
            "ttl": 604800,
            "content": data,
        }
        data = json.dumps(payload)
        headers = {"Content-Type": "application/json"}
        url = f"v1/domains/{zone.id}/records/"
        try:
            response = self.connection.request(
                url,
                method="POST",
                headers=headers,
                data=data,
            )
        except ProviderError as e:
            if e.value == "record_already_exists":
                raise RecordAlreadyExistsError(e.value, self, record_id=None)
            raise

        result = response.object
        result_id = str(result.pop("id"))
        name = result.pop("name")
        result_type = result.pop("type")
        data = result.pop("content")
        ttl = result.pop("ttl")
        extra = result

        record = Record(
            id=result_id,
            name=name,
            type=result_type,
            data=data,
            zone=zone,
            driver=self,
            ttl=ttl,
            extra=extra,
        )
        return record

    def delete_record(self, record):
        response = self.connection.request(
            f"v1/domains/{record.zone.id}/records/{record.id}",
            method="DELETE",
        )
        return response.status == httplib.NO_CONTENT

    def update_record(
        self,
        record,
        name: Optional[str],
        type: Optional[RecordType],
        data: Optional[str],
        extra=None,
    ):

        payload = {}
        payload["name"] = record.name if name is None else name
        payload["type"] = record.type if type is None else type
        payload["content"] = record.data if data is None else data

        url = f"v1/domains/{record.zone.id}/records/{record.id}"
        headers = {"Content-Type": "application/json"}
        data = json.dumps(payload)
        try:
            response = self.connection.request(
                url,
                method="PUT",
                headers=headers,
                data=data,
            )
        except ProviderError as e:
            if e.value == "domain_not_found":
                raise ZoneDoesNotExistError(e.value, self, record.zone.id)
            if e.value == "record_not_found":
                raise RecordDoesNotExistError(e.value, self, record.id)
            if e.value == "record_already_exists":
                raise RecordAlreadyExistsError(e.value, self, record.id)
            if e.value in (
                "cname_record_conflict",
                "record_does_not_belong_to_domain",
                "cant_add_soa",
                "string_required",
                "bad_zone_name",
                "bad_record_name",
                "zone_name_too_long",
            ):
                raise RecordError(e.value, self, record.id)
            raise

        result = response.object
        result_id = str(result.pop("id"))
        name = result.pop("name")
        result_type = result.pop("type")
        data = result.pop("content")
        ttl = result.pop("ttl")
        extra = result

        record = Record(
            id=result_id,
            name=name,
            type=result_type,
            data=data,
            zone=record.zone,
            driver=self,
            ttl=ttl,
            extra=extra,
        )

        return record

    def update_zone(
        self,
        zone: Zone,
        domain: Optional[str],
        type: Optional[str] = "master",
        ttl: Optional[int] = None,
        extra: Optional[dict] = None,
    ) -> Zone:
        """Обновляет зону"""

        # INFO: TTL и domain нельзя обновить
        # TODO: Сделать warning на TTL и domain

        payload = {}
        if extra:
            payload.update(extra)
        if type is not None:
            payload["type"] = type

        url = f"v1/domains/{zone.id}"
        headers = {"Content-Type": "application/json"}
        data = json.dumps(payload)
        try:
            response = self.connection.request(
                url,
                method="PATCH",
                headers=headers,
                data=data,
            )
        except ProviderError as e:
            if e.value == "domain_not_found":
                raise ZoneDoesNotExistError(e.value, self, zone.id)
            if e.value == "tag_not_found":
                raise ZoneError(e.value, self, zone.id)
            raise

        result = response.object

        zone_id = result.pop("id")
        name = result.pop("name")
        extra = result

        zone = Zone(
            id=zone_id,
            domain=name,
            type="master",
            ttl=None,
            driver=self,
            extra=extra,
        )

        return zone


import os
import re
import json
import configparser
import errno
import requests

class Text2VoiceException(Exception):
    pass

def replace_text(matching_pattern, replace_characters, text):
    matched_list = re.findall(matching_pattern, text)
    for matched_characters in matched_list:
        text = text.replace(matched_characters, replace_characters)

    return text

class T2V(object):

    def __init__(self, api_key, api_endpoint, user_dictionary, speaker='show'):
        self._default_speaker = speaker
        self.api_preference = {'speaker': self._default_speaker}
        self.api_key = api_key
        self.api_endpoint = api_endpoint
        self.user_dictionary = user_dictionary

        try:
            self.generate_voice('test')
        except Text2VoiceException:
            raise Text2VoiceException('HTTP basic auth error')

    def reset(self):
        self.api_preference = {'speaker': self._default_speaker}
        return self

    def speaker(self, speaker):
        if speaker in ['show', 'haruka', 'hikari', 'takeru', 'santa', 'bear']:
            self.api_preference['speaker'] = speaker

        return self

    def volume(self, volume):
        if isinstance(volume, int):
            if volume < 50:
                volume = 50
            elif 200 < volume:
                volume = 200
            self.api_preference['volume'] = volume

        return self

    def replace_text_all(self, text):
        # Replace unnecessary characters in text such as URL, email, user, etc.
        # WARNING: DO NOT set no character in the replacement characters.
        # 現状、置き換え文字を空白にしてしまうと、入力した文字によっては置き換え後の読み上げテキスト文字列が何もなくなってしまい、エラーを起こします。
        DEFAULT_MATCHING_PATTERN = [
            ['URL', 'https?://[\w/:%#\$&\?\(\)~\.=\+\-]+', "URL"],
            ['MAIL', '[\w\-._]+@[\w\-._]+\.[A-Za-z]+', 'メール'],
            ['USER', '<@![\d]+>', 'ユーザー'],
            ['CUSTOM_EMOJI', '<:[\w]+:[\d]+>', '絵文字'],
            ['EMOJI', ':[\w]+:', '絵文字'],
        ]

        for replacement_set in DEFAULT_MATCHING_PATTERN:
            pattern = replacement_set[1]
            replace_characters = replacement_set[2]
            text = replace_text(pattern, replace_characters, text)

        # Replace user-defined characters in text
        for replacement_set in self.user_dictionary:
            pattern = replacement_set[0]
            replace_characters = replacement_set[1]
            text = replace_text(pattern, replace_characters, text)

        return text

    def generate_voice(self, text):
        output_dir = os.path.dirname(__file__) + '/cache'
        os.makedirs(output_dir, exist_ok=True)
        output_filename = "tmp.mp3"
        output = os.path.join(output_dir, output_filename)

        read_text = self.replace_text_all(text)
        self.api_preference['text'] = read_text
        result = requests.post(self.api_endpoint, data=self.api_preference, auth=(self.api_key, ""))
        if result.status_code != requests.codes.ok:
            raise Text2VoiceException('Invalid status code: %d' % result.status_code)
        with open(output, 'wb') as tmp:
            tmp.write(result.content)
        return output

# this is for test
if __name__ == '__main__':
    # load config.ini
    config_ini = configparser.ConfigParser()
    config_ini_path = '../config.ini'
    if not os.path.exists(config_ini_path):
        raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), config_ini_path)
    config_ini.read(config_ini_path, encoding='utf-8')

    voiceText_api_key = config_ini['VoiceText Web API']['API_KEY']
    voiceText_api_endpoint = config_ini['VoiceText Web API']['API_ENDPOINT']
    my_dict = json.loads(config_ini['General']['USER_DICTIONARY'])

    t2v = T2V(api_key=voiceText_api_key, api_endpoint=voiceText_api_endpoint, user_dictionary=my_dict)
    t2v.generate_voice(
        "これはテスト用に作成した音声です。https://www.google.com, hogehoge@ezweb.ne.jp, <@!123456789>, :fire:, <:fire:123456789>")
    print("OK")


import unittest
from sdk_test_case import SdkTestCase
from test_acquisition import create_test_acquisition
from test_gear import create_test_gear

import flywheel

class JobsTestCases(SdkTestCase):
    def setUp(self):
        self.group_id, self.project_id, self.session_id, self.acquisition_id = create_test_acquisition()
        self.gear_id = create_test_gear()

    def tearDown(self):
        self.fw.delete_project(self.project_id)
        self.fw.delete_group(self.group_id)
        self.fw.delete_gear(self.gear_id)

    def test_job(self):
        fw = self.fw

        gear = fw.get_gear(self.gear_id)
        self.assertIsNotNone(gear)

        poem = 'Mere anarchy is loosed upon the world,'
        fw.upload_file_to_acquisition(self.acquisition_id, flywheel.FileSpec('yeats.txt', poem))

        tag = self.rand_string()

        # Get the acquisition destination and file input
        acq = fw.get_acquisition(self.acquisition_id)
        any_file = acq.files[0]
        self.assertEqual(any_file.name, 'yeats.txt')

        # Add
        job_id = gear.run(destination=acq, tags=[tag], inputs={'any-file': any_file})
        self.assertNotEmpty(job_id)

        # Get
        r_job = fw.get_job(job_id)
        self.assertEqual(r_job.gear_id, self.gear_id)
        gear_info = r_job.gear_info
        self.assertEqual(gear_info.category, gear.category)
        self.assertEqual(gear_info.name, gear.gear.name)
        self.assertEqual(gear_info.version, gear.gear.version)
        self.assertEqual(r_job.state, 'pending')
        self.assertEqual(r_job.attempt, 1)
        self.assertIsNotNone(r_job.origin)
        self.assertEqual(r_job.origin.type, 'user')
        self.assertNotEmpty(r_job.origin.id)
        self.assertIn(tag, r_job.tags)
        self.assertTimestampBeforeNow(r_job.created)
        self.assertGreaterEqual(r_job.modified, r_job.created)

        # Modify
        tag2 = self.rand_string()
        r_job.update(tags=[tag2])

        # Check
        r_job = fw.get_job(job_id)
        self.assertEqual(r_job.state, 'pending')
        self.assertNotIn(tag, r_job.tags)
        self.assertIn(tag2, r_job.tags)

        # Get session jobs
        jobs = fw.get_session_jobs(self.session_id)
        self.assertIsNotNone(jobs)
        self.assertIsNotNone(jobs.jobs)
        self.assertEqual(1, len(jobs.jobs))

        self.assertIn(r_job, jobs.jobs)

        # Get all jobs
        jobs = fw.jobs(limit=100, sort='created:desc')
        self.assertIsNotNone(jobs)
        self.assertGreaterEqual(len(jobs), 1)
        self.assertTrue(any([ job.id == job_id for job in jobs]))

        # Cancel
        job_mod = flywheel.Job(state='cancelled')
        fw.modify_job(job_id, job_mod)

        # Check
        r_job = fw.get_job(job_id)
        self.assertEqual(r_job.state, 'cancelled')

    def test_job_queue(self):
        fw = self.fw

        poem = 'The blood-dimmed tide is loosed, and everywhere'
        fw.upload_file_to_acquisition(self.acquisition_id, flywheel.FileSpec('yeats.txt', poem))

        tag = self.rand_string()
        job = flywheel.Job(
            gear_id=self.gear_id,

            destination=flywheel.JobDestination(
                id=self.acquisition_id,
                type='acquisition'
            ),

            inputs={
                'any-file': flywheel.FileReference(
                    id=self.acquisition_id,
                    type='acquisition',
                    name='yeats.txt'
                )
            },

            tags=[tag]
        )

        # Add
        job_id = fw.add_job(job)
        self.assertNotEmpty(job_id)

        # Check
        r_job = fw.get_job(job_id)
        self.assertEqual(r_job.state, 'pending')

        # Run
        r_job = fw.get_next_job(tags=[tag])
        self.assertIsNotNone(r_job)
        self.assertEqual(r_job.id, job_id)
        self.assertIsNotNone(r_job.request)
        self.assertIn('dir', r_job.request.target)
        self.assertEqual(r_job.request.target['dir'], '/flywheel/v0')

        # Next fetch should not find any jobs
        try:
            fw.get_next_job(tags=[tag])
            self.fail('Expected an error retrieving next job')
        except flywheel.ApiException as e:
            self.assertEqual(e.status, 400)

        # Heartbeat
        fw.modify_job(job_id, {})
        r_job2 = fw.get_job(job_id)
        self.assertTimestampAfter(r_job2.modified, r_job.modified)

        # Add logs
        log1 = [ flywheel.JobLogStatement(fd=-1, msg='System message') ]
        log2 = [
            flywheel.JobLogStatement(fd=1, msg='Standard out'),
            flywheel.JobLogStatement(fd=2, msg='Standard err')
        ]
        fw.add_job_logs(job_id, log1)
        fw.add_job_logs(job_id, log2)

        # Finish
        r_job.change_state('complete')

        r_job3 = fw.get_job(job_id)
        self.assertEqual(r_job3.state, 'complete')
        self.assertTimestampAfter(r_job3.modified, r_job2.modified)

        logs = fw.get_job_logs(job_id)
        self.assertEqual(len(logs.logs), 4)
        self.assertEqual(logs.logs[1], log1[0])
        self.assertEqual(logs.logs[2], log2[0])
        self.assertEqual(logs.logs[3], log2[1])


"""
Created on Wed Nov 30 2022

@author: Syed Misba Shahriyaar
"""

import pandas as pd
from sklearn.preprocessing import LabelEncoder
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
from sklearn import metrics
from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap
from sklearn import neighbors, datasets
import seaborn as sns

# Step 1 - Load Data
dataset = pd.read_csv("iphone_purchase_dataset.csv")
X = dataset.iloc[:,:-1].values
y = dataset.iloc[:, 3].values

# Step 2 - Convert Gender to number
labelEncoder_gender =  LabelEncoder()
X[:,0] = labelEncoder_gender.fit_transform(X[:,0])

# Optional - if you want to convert X to float data type
# X = np.vstack(X[:, :]).astype(np.float)

# Step 3 - Split into training and test data
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=0)

# Step 4 - Feature Scaling
sc_X = StandardScaler()
X_train = sc_X.fit_transform(X_train)
X_test = sc_X.transform(X_test)

# Step 5 - Fit KNN Classifier
# metric = minkowski and p=2 is Euclidean Distance
# metric = minkowski and p=1 is Manhattan Distance
classifier = KNeighborsClassifier(n_neighbors=5, metric="minkowski",p=2)
classifier.fit(X_train, y_train)

# Step 6 - Make Prediction
y_pred = classifier.predict(X_test)

# Step 7 - Confusion Matrix
print('Confusion Matrix of KNN Algorithm: ')
cm = metrics.confusion_matrix(y_test, y_pred)
print(cm)
accuracy = metrics.accuracy_score(y_test, y_pred)
print("Accuracy score:",accuracy)
precision = metrics.precision_score(y_test, y_pred)
print("Precision score:",precision)
recall = metrics.recall_score(y_test, y_pred)
print("Recall score:",recall)

# Step 8 - Make New Predictions
x1 = sc_X.transform([[1,21,40000]])
x2 = sc_X.transform([[1,21,80000]])
x3 = sc_X.transform([[0,21,40000]])
x4 = sc_X.transform([[0,21,80000]])
x5 = sc_X.transform([[1,41,40000]])
x6 = sc_X.transform([[1,51,100000]])
x7 = sc_X.transform([[0,41,40000]])
x8 = sc_X.transform([[0,41,80000]])

print("Male aged 21 making $40k will buy iPhone:", classifier.predict(x1))
print("Male aged 21 making $80k will buy iPhone:", classifier.predict(x2))
print("Female aged 21 making $40k will buy iPhone:", classifier.predict(x3))
print("Female aged 21 making $80k will buy iPhone:", classifier.predict(x4))
print("Male aged 41 making $40k will buy iPhone:", classifier.predict(x5))
print("Male aged 51 making $100k will buy iPhone:", classifier.predict(x6))
print("Female aged 41 making $40k will buy iPhone:", classifier.predict(x7))
print("Female aged 41 making $80k will buy iPhone:", classifier.predict(x8))

# labelEncoder_gender =  LabelEncoder()
# X[:,0] = labelEncoder_gender.fit_transform(X[:,0])
#
# plt.rcParams["figure.figsize"] = [7.00, 3.50]
# plt.rcParams["figure.autolayout"] = True
#
# n_neighbors = 5
# h = .02
#
# cmap_light = ListedColormap(['orange', 'cyan', 'cornflowerblue'])
# cmap_bold = ['darkorange', 'c', 'darkblue']
#
#
# clf = neighbors.KNeighborsClassifier(n_neighbors, weights='uniform')
# clf.fit(X, y)
#
# x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
# y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
# xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
# np.arange(y_min, y_max, h))
# Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
# Z = Z.reshape(xx.shape)
#
# plt.figure()
#
# plt.contourf(xx, yy, Z, cmap=cmap_light)
#
# sns.scatterplot(x=X[:, 0], y=X[:, 1],
# palette=cmap_bold, alpha=1.0, edgecolor="black")
#
# plt.xlim(xx.min(), xx.max())
# plt.ylim(yy.min(), yy.max())
#
# plt.title("classification (k = %i, 'uniform' = '%s')"
# % (n_neighbors, 'uniform'))
#
# plt.xlabel('Age')
# plt.ylabel('Purchase')
#
# plt.show()


# Used cars kicks classification - Sensitivity analysis
# Data source:
# https://www.openml.org/search?type=data&sort=runs&id=41162&status=active
# https://www.kaggle.com/competitions/DontGetKicked/overview

# Source previous script
exec(open("./Scripts/04_Preprocessing.py").read())

import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.utils.class_weight import compute_class_weight
from sklearn.dummy import DummyClassifier
from sklearn.linear_model import SGDClassifier
from sklearn.kernel_approximation import RBFSampler
from sklearn.calibration import CalibratedClassifierCV
from xgboost import XGBClassifier

import torch, torchvision
import lightning.pytorch as pl
from XX_LightningClasses import TrainDataset, TestDataset, SeluDropoutModel

from itertools import product

# Set plotting options
plt.rcParams['figure.dpi'] = 300
plt.rcParams['savefig.dpi'] = 300
plt.rcParams["figure.autolayout"] = True
sns.set_style("darkgrid")

# Set Torch settings
torch.set_default_dtype(torch.float32)
torch.set_float32_matmul_precision('high')
pl.seed_everything(1923, workers = True)

# Compute class weight
classes = list(set(y_train))
class_weight = compute_class_weight("balanced", classes = classes, y = y_train)
sample_weight = np.where(y_train == 1, class_weight[1], class_weight[0])

# Create dummy classifier which predicts the prior class probabilities
model_dummy = DummyClassifier(strategy = "prior")

# Train & predict with dummy classifier
model_dummy.fit(x_train, y_train)
preds_dummy = model_dummy.predict_proba(x_test)
preds_dummy = [x[1] for x in preds_dummy]

# Create logistic regression pipeline with optimal hyperparameters
best_trial_logistic = pd.read_csv("./ModifiedData/trials_logistic.csv").iloc[0,]
pipe_logistic = Pipeline(steps = [
  ("preprocessing", pipe_process),
  ("Logistic", SGDClassifier(
      loss = "log_loss",
      penalty = "elasticnet",
      alpha = best_trial_logistic["params_reg_strength"],
      l1_ratio = best_trial_logistic["params_l1_ratio"],
      max_iter = 26,
      n_iter_no_change = 1000, # Ensure model doesn't early stop based on train loss
      verbose = 1,
      random_state = 1923
    )
  )
])

# Train & predict with logistic regression
pipe_logistic.fit(x_train, y_train, Logistic__sample_weight = sample_weight)
preds_logistic = pipe_logistic.predict_proba(x_test)
preds_logistic = [x[1] for x in preds_logistic]

# Create SVM pipeline with optimal hyperparameters
best_trial_svm = pd.read_csv("./ModifiedData/trials_svm.csv").iloc[0,]
pipe_svm = Pipeline(steps = [
  ("preprocessing", pipe_process),
  ("KernelTrick", RBFSampler(
      gamma = "scale",
      n_components = 100,
      random_state = 1923
    )
  ),
  ("SVM", CalibratedClassifierCV(
    estimator = SGDClassifier(
      loss = "hinge",
      penalty = "elasticnet",
      alpha = best_trial_svm["params_reg_strength"],
      l1_ratio = best_trial_svm["params_l1_ratio"],
      max_iter = 13,
      n_iter_no_change = 1000, # Ensure model doesn't early stop based on train loss
      verbose = 1,
      random_state = 1923
      ),
    method = "isotonic"
    )
  )
])

# Train & predict with SVM
pipe_svm.fit(x_train, y_train, SVM__sample_weight = sample_weight)
preds_svm = pipe_svm.predict_proba(x_test)
preds_svm = [x[1] for x in preds_svm]

# Create XGBoost pipeline with optimal hyperparameters
best_trial_xgb = pd.read_csv("./ModifiedData/trials_xgb.csv").iloc[0,]
pipe_xgb = Pipeline(steps = [
  ("preprocessing", pipe_process),
  ("XGBoost", XGBClassifier(
      objective = "binary:logistic",
      n_estimators = 20,
      eval_metric = "logloss",
      tree_method = "gpu_hist",
      gpu_id = 0,
      verbosity = 1,
      random_state = 1923,
      learning_rate = best_trial_xgb["params_learning_rate"],
      max_depth = best_trial_xgb["params_max_depth"],
      min_child_weight = best_trial_xgb["params_min_child_weight"],
      gamma = best_trial_xgb["params_gamma"],
      reg_alpha = best_trial_xgb["params_l1_reg"],
      reg_lambda = best_trial_xgb["params_l2_reg"],
      subsample = best_trial_xgb["params_subsample"],
      colsample_bytree = best_trial_xgb["params_colsample_bytree"]
    )
  )
])

# Train & predict with XGBoost
pipe_xgb.fit(x_train, y_train, XGBoost__sample_weight = sample_weight)
preds_xgb = pipe_xgb.predict_proba(x_test)
preds_xgb = [x[1] for x in preds_xgb]

# Define & train NN model with best parameters
best_trial_nn = pd.read_csv("./ModifiedData/trials_nn.csv").iloc[0,]
hyperparams_dict = {
      "input_size": 90,
      "n_hidden_layers": best_trial_nn["params_n_hidden_layers"],
      "hidden_size": 2 ** best_trial_nn["params_hidden_size"],
      "learning_rate": best_trial_nn["params_learning_rate"],
      "l2": best_trial_nn["params_l2"],
      "dropout": best_trial_nn["params_dropout"],
      "loss_alpha": best_trial_nn["params_loss_alpha"],
      "loss_gamma": best_trial_nn["params_loss_gamma"]
    }
model_nn = SeluDropoutModel(hyperparams_dict)

# Apply scikit preprocessing pipeline
x_tr = pipe_process.fit_transform(x_train, y_train)
x_test1 = pipe_process.transform(x_test)

# Create train & test Datasets, dataloaders
train_data = TrainDataset(x_tr, y_train)
test_data = TestDataset(x_test1)

train_loader = torch.utils.data.DataLoader(
  train_data, batch_size = 1024, num_workers = 0, shuffle = True)
test_loader = torch.utils.data.DataLoader(
  test_data, batch_size = len(test_data), num_workers = 0, shuffle = False)

# Create trainer
trainer = pl.Trainer(
      max_epochs = 9, # Best epoch from ModelingNN
      log_every_n_steps = 5, # The default is 50, but there are less training batches
      # than 50
      accelerator = "gpu", devices = "auto", precision = "16-mixed",
      logger = True,
      enable_progress_bar = True,
      enable_checkpointing = False
    )

# Train NN model
trainer.fit(model_nn, train_loader)

# Predict with NN model
y_prob = trainer.predict(model_nn, test_loader)

# Convert list of float16 Torch tensors to single float32 np.array
preds_nn = np.float32(y_prob[0].numpy().reshape(1, -1)[0])

# Retrieve prob. predictions, target labels, purchase prices in dataframes, sort in
# descending order according to prob. predictions
df_dummy = pd.DataFrame({
  "Price": x_test["VehBCost"],
  "Kick": y_test,
  "ProbKick": preds_dummy
})
df_dummy = df_dummy.sort_values("ProbKick", ascending = True)

df_logistic = pd.DataFrame({
  "Price": x_test["VehBCost"],
  "Kick": y_test,
  "ProbKick": preds_logistic
})
df_logistic = df_logistic.sort_values("ProbKick", ascending = True)

df_svm = pd.DataFrame({
  "Price": x_test["VehBCost"],
  "Kick": y_test,
  "ProbKick": preds_svm
})
df_svm = df_svm.sort_values("ProbKick", ascending = True)

df_xgb = pd.DataFrame({
  "Price": x_test["VehBCost"],
  "Kick": y_test,
  "ProbKick": preds_xgb
})
df_xgb = df_xgb.sort_values("ProbKick", ascending = True)

df_nn = pd.DataFrame({
  "Price": x_test["VehBCost"],
  "Kick": y_test,
  "ProbKick": preds_nn
})
df_nn = df_nn.sort_values("ProbKick", ascending = True)

# Define function to calculate profit-loss at given threshold prob. and number of
# cars to purchase
def calc_profit(threshold, num_purchases, df_probs):

  # Retrieve arrays of prices, labels, predicted probs
  price = df_probs["Price"].values
  kick = df_probs["Kick"].values
  prob = df_probs["ProbKick"].values

  # Retrieve n. of cars to purchase (if available at threshold)
  n = num_purchases

  # Get vector of purchase decision for the top N cars at threshold prob.
  decision = (prob[0:n] < threshold).astype(int)

  # Calculate profit/loss for each car, purchased or not
  profit = [((0.1 * price[i]) - (0.9 * price[i] * kick[i])) * decision[i] for i in range(n)]

  # Return n. of cars actually purchased, total profit / loss
  return sum(decision), sum(profit)

# Get combinations of thresholds - n. of cars to purchase
thresholds = np.arange(0, 1, 0.01)
num_buys = np.arange(0, len(y_test), 100)
thresholds_buys = list(product(thresholds, num_buys))

# Calculate n. of cars actually bought and profit at each threshold / buy combination
output_dummy = [calc_profit(x, y, df_dummy) for x, y in thresholds_buys]
decisions_dummy = [x[0] for x in output_dummy]
profits_dummy= [x[1] for x in output_dummy]

output_logistic = [calc_profit(x, y, df_logistic) for x, y in thresholds_buys]
decisions_logistic = [x[0] for x in output_logistic]
profits_logistic = [x[1] for x in output_logistic]

output_svm = [calc_profit(x, y, df_svm) for x, y in thresholds_buys]
decisions_svm = [x[0] for x in output_svm]
profits_svm = [x[1] for x in output_svm]

output_xgb = [calc_profit(x, y, df_xgb) for x, y in thresholds_buys]
decisions_xgb = [x[0] for x in output_xgb]
profits_xgb = [x[1] for x in output_xgb]

output_nn = [calc_profit(x, y, df_nn) for x, y in thresholds_buys]
decisions_nn = [x[0] for x in output_nn]
profits_nn = [x[1] for x in output_nn]

# Make long dataframes of threshold-purchase-profit values
df_long_dummy = pd.DataFrame({
  "Threshold": [x[0] for x in thresholds_buys],
  "Purchases": decisions_dummy,
  "Profits": profits_dummy,
  "Model": "Dummy"
})

df_long_logistic = pd.DataFrame({
  "Threshold": [x[0] for x in thresholds_buys],
  "Purchases": decisions_logistic,
  "Profits": profits_logistic,
  "Model": "Logistic"
})

df_long_svm = pd.DataFrame({
  "Threshold": [x[0] for x in thresholds_buys],
  "Purchases": decisions_svm,
  "Profits": profits_svm,
  "Model": "SVM"
})

df_long_xgb = pd.DataFrame({
  "Threshold": [x[0] for x in thresholds_buys],
  "Purchases": decisions_xgb,
  "Profits": profits_xgb,
  "Model": "XGBoost"
})

df_long_nn = pd.DataFrame({
  "Threshold": [x[0] for x in thresholds_buys],
  "Purchases": decisions_nn,
  "Profits": profits_nn,
  "Model": "Neural net"
})

# Combine long dataframes into one
df_long = pd.concat([df_long_dummy, df_long_logistic, df_long_svm, df_long_xgb, df_long_nn])

# Drop rows with duplicates of purchase-profit-model columns (cases where a
# probability t results in buying n number of cars, and a higher t doesn't result
# in more cars bought or a different profit value)
df_long = df_long.drop_duplicates(["Purchases", "Profits", "Model"])

# 2D lineplots of thresholds-purchases-profits
fig, ax = plt.subplots(3)
_ = fig.suptitle("Relationships between classifier threshold probability, number of cars purchased and profit")

_ = sns.lineplot(
  ax = ax[0],
  data = df_long, x = "Threshold", y = "Profits", hue = "Model")
_ = ax[0].set_xlabel("Threshold probability")
_ = ax[0].set_ylabel("Profits, $mil")

_ = sns.lineplot(
  ax = ax[1],
  data = df_long, x = "Threshold", y = "Purchases", hue = "Model", legend = False)
_ = ax[1].set_xlabel("Threshold probability (lowest value that results in N. cars purchased)")
_ = ax[1].set_ylabel("N. cars purchased")

_ = sns.lineplot(
  ax = ax[2],
  data = df_long, x = "Purchases", y = "Profits", hue = "Model", legend = False)
_ = ax[2].set_xlabel("N. cars purchased")
_ = ax[2].set_ylabel("Profits, $mil")

plt.show()
plt.savefig("./Plots/sensitivity.png", dpi = 300)
plt.close("all")

# Quasi-optimal combinations of threshold prob - n. purchases
optimal_dummy = df_long_dummy.loc[np.argmax(df_long_dummy["Profits"])]
optimal_logistic = df_long_logistic.loc[np.argmax(df_long_logistic["Profits"])]
optimal_svm = df_long_svm.loc[np.argmax(df_long_svm["Profits"])]
optimal_xgb = df_long_xgb.loc[np.argmax(df_long_xgb["Profits"])]
optimal_nn =df_long_nn.loc[np.argmax(df_long_nn["Profits"])]

df_optimal = pd.concat([
  optimal_dummy, optimal_logistic, optimal_svm, optimal_xgb, optimal_nn],
  axis = 1).transpose()

df_optimal["Profits"] = df_optimal["Profits"] / 1e6

df_optimal = df_optimal.rename({
  "Threshold": "Threshold prob.",
  "Purchases": "N. cars purchased",
  "Profits": "Profits, $mil"
}, axis = 1)

df_optimal


from django.http.response import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse, reverse_lazy
from django.views.generic import View, CreateView, DetailView
from datetime import datetime
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator

from django.views.generic.edit import UpdateView, DeleteView
from .calendar import Calendar
from .models import Diary
from .forms import DiaryCreationForm, DiaryUpdateForm

# Create your views here.
@method_decorator(login_required, 'get')
class HomeView(View):
    def get(self, request):
        now = datetime.now()
        cal = Calendar(now.year, now.month, request.user)
        html_cal = cal.formatmonth(withyear=True)
        return render(request, 'diaryapp/home.html', {'calendar':html_cal})

class DiaryCreate(View):
    def get(self, request, year, month, day):
        c_year, c_month, c_day = year, month, day
        return render(request, 'diaryapp/create.html', {'year':c_year, 'month':c_month, 'day':c_day})

class DiaryCreateCommon(CreateView):
    model = Diary
    context_object_name = 'target_diary'
    template_name = 'diaryapp/create_common.html'
    form_class = DiaryCreationForm

    def form_valid(self, form):
        temp_diary = form.save(commit=False)
        temp_diary.user = self.request.user
        temp_diary.date = datetime.strptime(f"{self.kwargs['year']}-{self.kwargs['month']}-{self.kwargs['day']}", "%Y-%m-%d")
        temp_diary.save()
        return super().form_valid(form)

    def get_context_data(self, **kwargs):
        value = super(DiaryCreateCommon, self).get_context_data(**kwargs)
        value['year'] = self.kwargs['year']
        value['month'] = self.kwargs['month']
        value['day'] = self.kwargs['day']
        return value

    def get_success_url(self):
        return reverse('diaryapp:read', kwargs={'pk':self.object.pk})

class DiarySelectFeeling(View):
    def get(self, request, year, month, day):
        c_year, c_month, c_day = year, month, day
        return render(request, 'diaryapp/select_feeling.html', {'year':c_year, 'month': c_month, 'day':c_day})

    def post(self, request, year, month, day):
        print('post까지 들어옴')
        if 'joy' in self.request.POST:
            print('joy')
            temp_diary = Diary(user=self.request.user)
            temp_diary.title = f'{month}월 {day}일 일기'
            temp_diary.date = datetime.strptime(f"{year}-{month}-{day}", "%Y-%m-%d")
            temp_diary.feeling = 'joy'
            temp_diary.save()
        elif 'sad' in self.request.POST:
            print('sad')
            temp_diary = Diary(user=self.request.user)
            temp_diary.title = f'{month}월 {day}일 일기'
            temp_diary.date = datetime.strptime(f"{year}-{month}-{day}", "%Y-%m-%d")
            temp_diary.feeling = 'sad'
            temp_diary.save()
        elif 'angry' in self.request.POST:
            print('angry')
            temp_diary = Diary(user=self.request.user)
            temp_diary.title = f'{month}월 {day}일 일기'
            temp_diary.date = datetime.strptime(f"{year}-{month}-{day}", "%Y-%m-%d")
            temp_diary.feeling = 'angry'
            temp_diary.save()
        else:
            print('fear')
            temp_diary = Diary(user=self.request.user)
            temp_diary.title = f'{month}월 {day}일 일기'
            temp_diary.date = datetime.strptime(f"{year}-{month}-{day}", "%Y-%m-%d")
            temp_diary.feeling = 'fear'
            temp_diary.save()
        print('리턴 바로앞!')
        return HttpResponseRedirect(reverse('diaryapp:select_who', kwargs={'pk':temp_diary.pk}))

class DiarySelectWho(View):
    def get(self, request, pk):
        return render(request, 'diaryapp/select_who.html', {'pk':pk})

    def post(self, request, pk):
        target_diary = Diary.objects.get(pk=pk)
        if target_diary.content:
            if 'me' in self.request.POST:
                target_diary.content += '
내가'
                target_diary.save()
            elif 'friend' in self.request.POST:
                target_diary.content += '
친구가'
                target_diary.save()
            elif 'father' in self.request.POST:
                target_diary.content += '
아빠가'
                target_diary.save()
            elif 'mother' in self.request.POST:
                target_diary.content += '
엄마가'
                target_diary.save()
            elif 'girlfriend' in self.request.POST:
                target_diary.content += '
여친이'
                target_diary.save()
            elif 'boyfriend' in self.request.POST:
                target_diary.content += '
남친이'
                target_diary.save()
            elif 'etc' in self.request.POST:
                target_diary.content += '
' + self.request.POST['self_input']
                target_diary.save()
        else:
            if 'me' in self.request.POST:
                target_diary.content = '내가'
                target_diary.save()
            elif 'friend' in self.request.POST:
                target_diary.content = '친구가'
                target_diary.save()
            elif 'father' in self.request.POST:
                target_diary.content = '아빠가'
                target_diary.save()
            elif 'mother' in self.request.POST:
                target_diary.content = '엄마가'
                target_diary.save()
            elif 'girlfriend' in self.request.POST:
                target_diary.content = '여친이'
                target_diary.save()
            elif 'boyfriend' in self.request.POST:
                target_diary.content = '남친이'
                target_diary.save()
            elif 'etc' in self.request.POST:
                target_diary.content = self.request.POST['self_input']
                target_diary.save()
        return HttpResponseRedirect(reverse('diaryapp:select_when', kwargs={'pk':pk}))

class DiarySelectWhen(View):
    def get(self, request, pk):
        return render(request, 'diaryapp/select_when.html', {'pk':pk})

    def post(self, request, pk):
        target_diary = Diary.objects.get(pk=pk)
        if 'before_noon' in self.request.POST:
            target_diary.content += ' 오전에'
            target_diary.save()
        elif 'after_noon' in self.request.POST:
            target_diary.content += ' 오후에'
            target_diary.save()
        elif 'morning' in self.request.POST:
            target_diary.content += ' 아침에'
            target_diary.save()
        elif 'noon' in self.request.POST:
            target_diary.content += ' 점심에'
            target_diary.save()
        elif 'evening' in self.request.POST:
            target_diary.content += ' 저녁에'
            target_diary.save()
        elif 'night' in self.request.POST:
            target_diary.content += ' 밤에'
            target_diary.save()
        elif 'etc' in self.request.POST:
            target_diary.content += ' ' + self.request.POST['self_input']
            target_diary.save()
        return HttpResponseRedirect(reverse('diaryapp:select_where', kwargs={'pk':pk}))

class DiarySelectWhere(View):
    def get(self, request, pk):
        return render(request, 'diaryapp/select_where.html', {'pk':pk})

    def post(self, request, pk):
        target_diary = Diary.objects.get(pk=pk)
        if 'home' in self.request.POST:
            target_diary.content += ' 집에서'
            target_diary.save()
        elif 'school' in self.request.POST:
            target_diary.content += ' 학교에서'
            target_diary.save()
        elif 'company' in self.request.POST:
            target_diary.content += ' 직장에서'
            target_diary.save()
        elif 'park' in self.request.POST:
            target_diary.content += ' 공원에서'
            target_diary.save()
        elif 'restaurant' in self.request.POST:
            target_diary.content += ' 식당에서'
            target_diary.save()
        elif 'cafe' in self.request.POST:
            target_diary.content += ' 카페에서'
            target_diary.save()
        elif 'etc' in self.request.POST:
            target_diary.content += ' ' + self.request.POST['self_input']
            target_diary.save()
        return HttpResponseRedirect(reverse('diaryapp:select_what', kwargs={'pk':pk}))

class DiarySelectWhat(View):
    def get(self, request, pk):
        return render(request, 'diaryapp/select_what.html', {'pk':pk})

    def post(self, request, pk):
        target_diary = Diary.objects.get(pk=pk)
        target_diary.content += ' ' + self.request.POST['self_input']
        target_diary.save()
        return HttpResponseRedirect(reverse('diaryapp:select_how', kwargs={'pk':pk}))

class DiarySelectHow(View):
    def get(self, request, pk):
        return render(request, 'diaryapp/select_how.html', {'pk':pk})

    def post(self, request, pk):
        target_diary = Diary.objects.get(pk=pk)
        target_diary.content += ' ' + self.request.POST['self_input']
        target_diary.save()
        return HttpResponseRedirect(reverse('diaryapp:select_why', kwargs={'pk':pk}))

class DiarySelectWhy(View):
    def get(self, request, pk):
        return render(request, 'diaryapp/select_why.html', {'pk':pk})

    def post(self, request, pk):
        target_diary = Diary.objects.get(pk=pk)
        target_diary.content += ' ' + self.request.POST['self_input']
        target_diary.save()
        return HttpResponseRedirect(reverse('diaryapp:select_continue', kwargs={'pk':pk}))

class DiarySelectContinue(View):
    def get(self, request, pk):
        return render(request, 'diaryapp/select_continue.html', {'pk':pk})

class DiaryRead(DetailView):
    model = Diary
    context_object_name = 'target_diary'
    template_name = 'diaryapp/detail.html'

class DiaryUpdateView(UpdateView):
    model = Diary
    context_object_name = 'target_diary'
    form_class = DiaryUpdateForm
    template_name = 'diaryapp/update.html'

    def get_success_url(self):
        return reverse('diaryapp:read', kwargs={'pk': self.object.pk})

class DiaryDeleteView(DeleteView):
    model = Diary
    success_url = reverse_lazy('diaryapp:home')
    template_name = 'diaryapp/delete.html'
    context_object_name = 'target_diary'


"""The plugin to setup polyphemus for use under the Apache 2 server.

This module is available as an polyphemus plugin by the name ``polyphemus.apache2``.

BaTLaB Plugin API
=================
"""
from __future__ import print_function
import os
import sys
from warnings import warn

from event import runfor

from .utils import RunControl, NotSpecified, writenewonly, newoverwrite, \
    DEFAULT_RC_FILE, DEFAULT_PLUGINS, nyansep, indent, check_cmd
from .plugins import Plugin

if sys.version_info[0] >= 3:
    basestring = str

conf_template = """
ServerName {server_name}

<VirtualHost {server_name}:{port}>
    ServerAlias www.{server_name}
    WSGIScriptAlias / {wsgi_file}
    DocumentRoot {rc_dir}

<Directory {wsgi_dir}>
        Order allow,deny
        Allow from all
    </Directory>

     # ---- Configure Logging ----

    ErrorLog {log_dir}/error.log
    LogLevel info
    CustomLog {log_dir}/access.log combined

</VirtualHost>
"""

port_template = """
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default

Listen 80

Listen {port}

<IfModule ssl_module>
	Listen 443
</IfModule>

<IfModule mod_gnutls.c>
	Listen 443
</IfModule>

"""

wsgi_template = """import polyphemus.main
import sys
sys.stdout = sys.stderr
application = polyphemus.main.setup(rc={rc!r}).rc.app
"""

class PolyphemusPlugin(Plugin):
    """This class provides functionality for seting up Polyphemus to run
    with Apache v2 and mod_wsgi.
    """

    requires = ('polyphemus.base',)

    defaultrc = RunControl(
        apache2_setup=False,
        server_name=NotSpecified,
        site_conf_file=NotSpecified,
        wsgi_file=NotSpecified,
        )

    rcdocs = {
        'apache2_setup': "Sets up polyphemus to run under Apache 2 with mod_wsgi.",
        'server_name': "The name of the website, e.g. 'polyphemus.org'.",
        'site_conf_file': ("The Apache 2 site configiration file name, defaults to "
                           "'/etc/apache2/sites-available/{server_name}.conf'"),
        'wsgi_file': ("The WSGI script file name, defaults to "
                      "'/var/www/{appname}/{appname}.wsgi'"),
        'port_file': ("The Apache 2 ports.conf file, defaults to /etc/apache2/ports.conf"),
        'log_dir': ("The directory for apache logging, defaults to /apache-logs")
        }

    def update_argparser(self, parser):
        parser.add_argument('--apache2-setup', action='store_true',
                            dest='apache2_setup', help=self.rcdocs['apache2_setup'])
        parser.add_argument('--no-apache2-setup', action='store_false',
                            dest='apache2_setup', help="Doesn't s" +
                                                    self.rcdocs['apache2_setup'][1:])
        parser.add_argument('--server-name', dest='server_name',
                            help=self.rcdocs['server_name'])
        parser.add_argument('--site-conf-file', dest='site_conf_file',
                            help=self.rcdocs['site_conf_file'])
        parser.add_argument('--wsgi-file', dest='wsgi_file',
                            help=self.rcdocs['wsgi_file'])
        parser.add_argument('--port-file',dest='port_file',
                            help=self.rcdocs['port_file'])
        parser.add_argument('--log-dir',dest='log_dir',
                            help=self.rcdocs['log_dir'])

    def setup(self, rc):
        if rc.server_name is NotSpecified:
            rc.server_name = rc.appname + '.com'
        if rc.site_conf_file is NotSpecified:
            scf = '/etc/apache2/sites-available/{0}.conf'
            rc.site_conf_file = scf.format(rc.server_name)
        rc.site_conf_file = os.path.abspath(rc.site_conf_file)
        if rc.wsgi_file is NotSpecified:
            rc.wsgi_file = '/var/www/{0}/{0}.wsgi'.format(rc.server_name)
        rc.wsgi_file = os.path.abspath(rc.wsgi_file)
        if rc.log_dir is NotSpecified:
            rc.log_dir = '/apache-logs'
        if rc.port_file is NotSpecified:
            rc.port_file = '/etc/apache2/ports.conf'
        if not rc.apache2_setup:
            return
        conf = conf_template.format(port=rc.port, server_name=rc.server_name,
                                    wsgi_file=rc.wsgi_file, rc_dir=rc.rc.rsplit('/',1)[0],
                                    wsgi_dir=rc.wsgi_file.rsplit('/',1)[0], log_dir=rc.log_dir )
        wsgi = wsgi_template.format(rc=rc.rc)
        ports = port_template.format(port=rc.port)
        newoverwrite(conf, rc.site_conf_file, verbose=rc.verbose)
        newoverwrite(wsgi, rc.wsgi_file, verbose=rc.verbose)
        if os.path.isfile(rc.port_file):
            port_file = open(rc.port_file,'r')
            port_lines = port_file.readlines()
            port_file.close()
            found = False
            for line in port_lines:
                tokens = line.split()
                if len(tokens) == 2:
                    if tokens[0] == 'Listen' and tokens[1] == str(rc.port):
                        found = True
            if not found:
                port_file = open(rc.port_file,'a')
                port_file.write('\nListen '+str(rc.port)+'\n')
                port_file.close()

        else:
            newoverwrite(ports, rc.port_file, verbose=rc.verbose)


import re
from os import remove, mkdir, geteuid, getenv
from os.path import isdir, isfile, realpath, dirname, join
from time import strftime, sleep
from pwd import getpwnam
from spwd import getspall

from gi.repository import Gtk
from gi.repository import GObject

import gettext
from gettext import gettext as _
from gettext import ngettext
import sys

#If DEVACTIVE is true, it uses files from local directory
DEVACTIVE = False

if DEVACTIVE:
    from sys import path
    path.append('.')

from common.timekprpam import *
from common.timekprcommon import *

#timekpr.conf variables (dictionary variable)
VAR = getvariables(DEVACTIVE)
VERSION = getversion()

#translation stuff
def init_locale():
    locale_domain = "timekpr" # must match locale domain set in *.ui files
    locale_path = '/usr/share/locale'

    # init python gettext
    gettext.bindtextdomain(locale_domain, locale_path)
    gettext.textdomain(locale_domain)

#Check if admin/root
def check_if_admin():
    if geteuid() != 0:
        dlg = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, _("You need to have administrative privileges to run timekpr-gui"))
        dlg.set_default_response(Gtk.ResponseType.CLOSE)
        dlg.run()
        dlg.destroy()
        exit("Error: You need to have administrative privileges to run timekpr")

#Create configuration folder if not existing
def create_dirs():
    if not isdir(VAR['TIMEKPRDIR']):
        mkdir(VAR['TIMEKPRDIR'])
    if not isdir(VAR['TIMEKPRWORK']):
        mkdir(VAR['TIMEKPRWORK'])
    if not isdir(VAR['TIMEKPRSHARED']):
        exit('Error: Could not find the shared directory %s' % VAR['TIMEKPRSHARED'])

#Check if it is a regular user, with userid within UID_MIN and UID_MAX.
def is_regular_user(user_name):
    #FIXME: Hide active user - bug #286529
    if getenv('SUDO_USER') and user_name == getenv('SUDO_USER'):
        return False

    try:
        password_database_entry = getpwnam(user_name)
        user_id = int(password_database_entry[2])
        logindefs = open('/etc/login.defs')
        uid_limits = [int(x) for x in re.compile('^UID_(?:MIN|MAX)\s+(\d+)', re.M).findall(logindefs.read())]
    except (KeyError, ValueError):
        return False

    return min(uid_limits) <= user_id <= max(uid_limits)

def rm(filename):
    try:
        remove(filename)
    except OSError:
        pass

class timekprGUI(object):

    def __init__(self):
        ui_file = join(VAR['TIMEKPRSHARED'], 'main_window.ui')

        self.builder = Gtk.Builder()
        self.builder.add_from_file(ui_file)

        #connect signals to python methods
        self.builder.connect_signals(self)

        #connect gtk objects to python variables
        for obj in self.builder.get_objects():
            if issubclass(type(obj), Gtk.Buildable):
                name = Gtk.Buildable.get_name(obj)
                setattr(self, name, obj)

        self.mainwindow.set_title(_('Timekpr Control Panel'))

        #create lists of limit and boundary gtk objects
        self.limitSpins = [self.builder.get_object("limitSpin" + str(x)) for x in range(7)]
        self.fromSpins = [self.builder.get_object("fromSpin" + str(x)) for x in range(7)]
        self.toSpins = [self.builder.get_object("toSpin" + str(x)) for x in range(7)]
        self.boundariesLabels = [self.builder.get_object("boundariesLabel" + str(x)) for x in range(7)]
        self.limitLabels = [self.builder.get_object("limitLabel" + str(x)) for x in range(7)]

        self.statusbarCID = self.statusbar.get_context_id("timekprstatus")

        self.limits = []

        #Using /etc/shadow spwd module
        for userinfo in getspall():
            if is_regular_user(userinfo[0]):
                self.userSelectCombo.append_text(userinfo[0])
                self.userSelectCombo.set_active(0)

        #Ensure we have at least one available normal user
        if self.userSelectCombo.get_active_text() is None:
            dlg = Gtk.MessageDialog(None, 0, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, _("You need to have at least one normal user available to configure timekpr"))
            dlg.set_default_response(Gtk.ResponseType.CLOSE)
            dlg.run()
            dlg.destroy()
            exit("Error: You need to have at least one normal user available to configure timekpr")

        self.on_userSelectCombo_toggled(self)

        return

    def remove_status_message(self, msgid):
        self.statusbar.remove(self.statusbarCID, msgid)

    def set_status_message(self, message):
        msgid = self.statusbar.push(self.statusbarCID, strftime("%Y-%m-%d %H:%M:%S ") + message)
        GObject.timeout_add(6000, self.remove_status_message, msgid)

    def on_window_destroy(self, *args):
        Gtk.main_quit(*args)

    def on_aboutMenuItem_selected(self, widget):
        ui_file = join(VAR['TIMEKPRSHARED'], 'about_dialog.ui')

        builder = Gtk.Builder()
        builder.add_from_file(ui_file)

        dialog = builder.get_object('aboutdialog')
        dialog.set_version(VERSION)
        dialog.set_translator_credits(_("translator-credits"))
        dialog.set_comments(_('Keep control of computer usage'))
        dialog.show_all()
        dialog.run()
        dialog.destroy()

    def on_lockUnlockButton_clicked(self, widget):
        if self.lockUnlockLabel.get_label() == _('Lock account'):
            #lock account
            lockuser(self.user)
            statusmsg = _("Locked account %s") % (self.user)
            self.set_status_message(statusmsg)
            self.read_settings_nolimits()
        else:
            #unlock account
            unlockuser(self.user)
            #remove .lock file
            lockf = join(VAR['TIMEKPRDIR'], self.user + '.lock')
            rm(lockf)
            logoutf = join(VAR['TIMEKPRWORK'], self.user + '.logout')
            rm(logoutf)
            latef = join(VAR['TIMEKPRWORK'], self.user + '.late')
            rm(latef)
            statusmsg = _("Unlocked account %s") % self.user
            self.set_status_message(statusmsg)
            self.read_settings_nolimits()

    def on_clearAllRestrictionsButton_clicked(self, widget):
        #clears all limits and their files
        #FIXME: A yes/no confirmation would be handy here
        timef = join(VAR['TIMEKPRWORK'], self.user + '.time')
        logoutf = join(VAR['TIMEKPRWORK'], self.user + '.logout')
        latef = join(VAR['TIMEKPRWORK'], self.user + '.late')
        allowf = join(VAR['TIMEKPRWORK'], self.user + '.allow')
        #Should remove .allow file? It's not a restriction
        rm(timef)
        rm(logoutf)
        rm(latef)
        rm(allowf)
        #Remove boundaries
        removeuserlimits(self.user)
        #Remove limits
        configf = join(VAR['TIMEKPRDIR'], self.user)
        rm(configf)
        #Unlock user
        unlockuser(self.user)

        statusmsg = _("Removed all restrictions for account %s") % self.user
        self.set_status_message(statusmsg)
        self.on_userSelectCombo_toggled(self)

    def on_resetTimeButton_clicked(self, widget):
        #clear the .time file
        timefile = join(VAR['TIMEKPRWORK'], self.user + '.time')
        rm(timefile)
        statusmsg = _("Cleared used up time for account %s") % self.user
        self.set_status_message(statusmsg)
        self.read_settings_nolimits()

    def on_rewardButton_clicked(self, widget):
        arg = self.rewardSpin.get_value_as_int()
        timefile = join(VAR['TIMEKPRWORK'], self.user + '.time')
        if isfile(timefile):
            f = open(timefile)
            tlast = int(f.read())
            f.close()
        else:
            tlast = 0
        tnew = tlast - arg * 60
        f = open(timefile, 'w')
        f.write(str(tnew))
        f.close()
        statusmsg = _("Applied reward of %(num)s minute(s) to account %(user)s") % {'num': arg, 'user': self.user}
        self.set_status_message(statusmsg)
        self.read_settings_nolimits()

    def on_extendLimitsButton_clicked(self, widget):
        #UPDATE: extend limits button is now "Bypass for today"
        #It now is the same as changing boundaries from 0 to 24 for today's day of the week.
        #.allow can still be useful - active (logged in user accounts) won't be killed
        index = int(strftime("%w"))
        wfrom = self.fromtolimits[0]
        wto = self.fromtolimits[1]
        wfrom[index] = '0'
        wto[index] = '24'
        removeuserlimits(self.user)
        adduserlimits(self.user, wfrom, wto)
        allowfile = join(VAR['TIMEKPRWORK'], self.user + '.allow')
        f = open(allowfile, 'w').close()
        statusmsg = _("Set access hours to 00-24 on %(day)s for account %(user)s") % {'day': strftime("%A"), 'user': self.user}
        self.set_status_message(statusmsg)
        self.on_userSelectCombo_toggled(self)

    def on_refreshButton_clicked(self, widget):
        statusmsg = _("Refreshed setting values from account %s") % self.user
        self.set_status_message(statusmsg)
        self.on_userSelectCombo_toggled(self)

    def on_cancelButton_clicked(self, widget):
        Gtk.main_quit()

    def read_boundaries(self):
        #from-to time limitation (aka boundaries) - time.conf
        if isuserlimited(self.user):
            #Get user time limits (boundaries) as lists from-to
            bfrom = self.fromtolimits[0]
            bto = self.fromtolimits[1]

            for i in range(7):
                self.fromSpins[i].set_value(float(bfrom[i]))
                self.toSpins[i].set_value(float(bto[i]))
            # Use boundaries?
            ub = True
            # Single boundaries? (set per day)
            sb = False
            #Are all boundaries the same?
            #If they're not same, activate single (per day) boundaries
            if [bfrom[0]] * 7 != bfrom or [bto[0]] * 7 != bto:
                sb = True
            #Even if boundaries are Al0000-2400, return False
            if not sb and bfrom[0] == '0' and bto[0] == '24':
                ub = False
            self.boundariesCheck.set_active(ub)
            self.singleBoundariesCheck.set_active(sb)
        else:
            for i in range(7):
                self.fromSpins[i].set_value(7)
                self.toSpins[i].set_value(22)
            self.boundariesCheck.set_active(False)
            self.singleBoundariesCheck.set_active(False)

    def read_limits(self):
        #time length limitation
        configFile = join(VAR['TIMEKPRDIR'], self.user)
        del self.limits[:]
        if isfile(configFile):
            fileHandle = open(configFile)
            self.limits = fileHandle.readline()
            self.limits = self.limits.replace("limit=( ", "")
            self.limits = self.limits.replace(")", "")
            self.limits = self.limits.split(" ")

            for i in range(7):
                self.limitSpins[i].set_value(float(self.limits[i]) / 60)

            # Single limits? (set per day)
            sl = False
            # Use limits?
            ul = True

            for i in range(1, 7):
                if self.limits[i] != self.limits[i-1]:
                    sl = True

            if self.limits[0] == '86400' and not sl:
                ul = False
            self.limitCheck.set_active(ul)
            self.singleLimitsCheck.set_active(sl)
        else:
            for i in range(7):
                self.limitSpins[i].set_value(120)
            self.limitCheck.set_active(False)
            self.singleLimitsCheck.set_active(False)

    def update_status_icons(self, uislocked):
        #Set icons in status gtk-yes or gtk-no
        lockgreen = join(VAR['TIMEKPRSHARED'], 'padlock-green.png')
        lockred = join(VAR['TIMEKPRSHARED'], 'padlock-red.png')
        iconyes = Gtk.STOCK_YES
        iconno = Gtk.STOCK_NO
        iconsize = Gtk.IconSize.BUTTON
        #limitSpins status is already set, so we can use it
        #self.spinlimitvalue = self.builder.get_object("limitSpins" + strftime('%w')).get_value()
        if not isuserlimitedtoday(self.user) and not uislocked:
            self.allDayLoginImage.set_from_stock(iconyes, iconsize)
        else:
            self.allDayLoginImage.set_from_stock(iconno, iconsize)

        if self.limitCheck.get_active():
            self.limitsEnabledImage.set_from_file(lockred)
        else:
            self.limitsEnabledImage.set_from_file(lockgreen)

        if self.boundariesCheck.get_active():
            self.boundariesEnabledImage.set_from_file(lockred)
        else:
            self.boundariesEnabledImage.set_from_file(lockgreen)

        if uislocked:
            self.accountLockedImage.set_from_file(lockred)
        else:
            self.accountLockedImage.set_from_file(lockgreen)

        index = int(strftime("%w"))
        try:
            limit = int(self.limits[index])
        except IndexError:
            limit = 86400 # =60*60*24 (seconds in one day)

        timefile = join(VAR['TIMEKPRWORK'], self.user + '.time')
        used = 0
        if isfile(timefile) and fromtoday(timefile):
            t = open(timefile)
            used = int(t.readline())
            t.close()
        left = limit - used
        m, s = divmod(left, 60)
        self.timeLeftLabel.set_label(ngettext('%(min)s minute', '%(min)s minutes', abs(m)) % {'min': m})

    def set_button_states(self, user_is_locked):
        if user_is_locked:
            self.lockUnlockLabel.set_label(_('Unlock account'))
        else:
            self.lockUnlockLabel.set_label(_('Lock account'))

        if self.limitCheck.get_active():
            timefile = join(VAR['TIMEKPRWORK'], self.user + '.time')
            if isfile(timefile):
                self.resetTimeButton.set_sensitive(True)
            else:
                self.resetTimeButton.set_sensitive(False)
            #Reward button should add time even if .time is not there?
            self.rewardButton.set_sensitive(True)
            self.rewardSpin.set_sensitive(True)
            self.rewardLabel.set_sensitive(True)
        else:
            self.resetTimeButton.set_sensitive(False)
            self.rewardButton.set_sensitive(False)
            self.rewardSpin.set_sensitive(False)
            self.rewardLabel.set_sensitive(False)

        if self.boundariesCheck.get_active():
            index = int(strftime("%w"))
            wfrom = self.fromtolimits[0]
            wto = self.fromtolimits[1]
            if wfrom[index] != '0' or wto[index] != '24':
                self.extendLimitsButton.set_sensitive(True)
            else:
                self.extendLimitsButton.set_sensitive(False)
        else:
            self.extendLimitsButton.set_sensitive(False)

    def on_userSelectCombo_toggled(self, widget):
        self.user = self.userSelectCombo.get_active_text()
        user_is_locked = isuserlocked(self.user)
        self.fromtolimits = getuserlimits(self.user)
        self.read_boundaries()
        self.read_limits()
        self.userStatusLabel.set_label(_('Status for') + ' ' + self.user + '')
        self.update_status_icons(user_is_locked)
        self.set_button_states(user_is_locked)

    def read_settings_nolimits(self):
        user_is_locked = isuserlocked(self.user)
        self.update_status_icons(user_is_locked)
        self.set_button_states(user_is_locked)

    def on_boundariesCheck_toggled(self, widget):
        if self.boundariesCheck.get_active():
            self.fromSpins[0].set_sensitive(True)
            self.toSpins[0].set_sensitive(True)
            self.singleBoundariesCheck.set_sensitive(True)
            self.toLabel.set_sensitive(True)
            self.fromLabel.set_sensitive(True)
            self.boundariesLabels[0].set_sensitive(True)
        else:
            self.fromSpins[0].set_sensitive(False)
            self.toSpins[0].set_sensitive(False)
            self.singleBoundariesCheck.set_sensitive(False)
            self.toLabel.set_sensitive(False)
            self.fromLabel.set_sensitive(False)
            self.boundariesLabels[0].set_sensitive(False)
        self.on_singleBoundariesCheck_toggled(self)

    def on_singleBoundariesCheck_toggled(self, widget):
        if self.singleBoundariesCheck.get_active() and self.boundariesCheck.get_active():
            for i in range(1, 7):
                self.fromSpins[i].set_sensitive(True)
                self.toSpins[i].set_sensitive(True)
                self.boundariesLabels[i].set_sensitive(True)
            self.boundariesLabels[0].set_text(" " + _("Sun") + " ")
        else:
            for i in range(1, 7):
                self.fromSpins[i].set_sensitive(False)
                self.toSpins[i].set_sensitive(False)
                self.boundariesLabels[i].set_sensitive(False)
            self.boundariesLabels[0].set_text(_("Every day"))

    def on_limitCheck_toggled(self, widget):
        if self.limitCheck.get_active():
            self.limitSpins[0].set_sensitive(True)
            self.singleLimitsCheck.set_sensitive(True)
            self.minutesLabel.set_sensitive(True)
            self.limitLabels[0].set_sensitive(True)
        else:
            self.limitSpins[0].set_sensitive(False)
            self.singleLimitsCheck.set_sensitive(False)
            self.minutesLabel.set_sensitive(False)
            self.limitLabels[0].set_sensitive(False)
        self.on_singleLimitsCheck_toggled(self)

    def on_singleLimitsCheck_toggled(self, widget):
        if self.singleLimitsCheck.get_active() and self.limitCheck.get_active():
            for i in range(1, 7):
                self.limitLabels[i].set_sensitive(True)
                self.limitSpins[i].set_sensitive(True)
            self.limitLabels[0].set_text(" " + _("Sun") + " ")
        else:
            for i in range(1, 7):
                self.limitLabels[i].set_sensitive(False)
                self.limitSpins[i].set_sensitive(False)
            self.limitLabels[0].set_text(_("Every day"))

    def on_applyButton_clicked(self, widget):
        space = " "
        limit = "limit=( 86400 86400 86400 86400 86400 86400 86400 )"
        #timekprpam.py adduserlimits() uses lists with numbers as strings
        bFrom = ['0'] * 7
        bTo = ['24'] * 7

        if self.limitCheck.get_active():
            if self.singleLimitsCheck.get_active():
                limit = "limit=("
                for i in range(7):
                    limit = limit + space + str(self.limitSpins[i].get_value_as_int() * 60)
                limit = limit + space + ")"
            else:
                limit = "limit=("
                for i in range(7):
                    limit = limit + space + str(self.limitSpins[0].get_value_as_int() * 60)
                limit = limit + space + ")"
        if self.boundariesCheck.get_active():
            if self.singleBoundariesCheck.get_active():
                bFrom = []
                bTo = []
                for i in range(7):
                    bFrom.append(str(self.fromSpins[i].get_value_as_int()))
                    bTo.append(str(self.toSpins[i].get_value_as_int()))
            else:
                bFrom = []
                bTo = []
                for i in range(7):
                    bFrom.append(str(self.fromSpins[0].get_value_as_int()))
                    bTo.append(str(self.toSpins[0].get_value_as_int()))
        configFile = join(VAR['TIMEKPRDIR'], self.user)
        if self.limitCheck.get_active():
            fH = open(configFile, 'w')
            fH.write(limit + "\n")
            fH.close()
        else:
            rm(configFile)

        #No need to check if boundaries are active or not, apply the default or custom limits
        #Remove old user limits (boundaries)
        rb = removeuserlimits(self.user)
        #Add new limits (boundaries)
        ab = adduserlimits(self.user, bFrom, bTo)

        statusmsg = _("Applied limit changes for account %s") % self.user
        self.set_status_message(statusmsg)
        self.on_userSelectCombo_toggled(self)

if __name__ == "__main__":
    init_locale()
    check_if_admin()
    create_dirs()
    timekprGUI()
    Gtk.main()


import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np

EPS = 0.003

def fanin_init(size, fanin=None):
    fanin = fanin or size[0]
    v = 1. / np.sqrt(fanin)
    return torch.Tensor(size).uniform_(-v, v)

class Critic(nn.Module):

    def __init__(self, state_dim, action_dim):

        super(Critic, self).__init__()
        # self.bn0 = nn.BatchNorm1d(state_dim)
        self.l1 = nn.Linear(state_dim, 400)
        # self.l1.weight.data = fanin_init(self.l1.weight.data.size())
        # self.bn1 = nn.BatchNorm1d(400+action_dim)
        self.l2 = nn.Linear(400 + action_dim, 300)
        # self.l2.weight.data = fanin_init(self.l2.weight.data.size())
        # self.bn2 = nn.BatchNorm1d(300)
        self.l3 = nn.Linear(300, 1)
        # self.l3.weight.data.uniform_(-EPS, EPS)

    def forward(self, state, action):

        q = F.relu(self.l1(state))
        q = F.relu(self.l2(torch.cat([q, action], 1)))
        return self.l3(q)

class Actor(nn.Module):

    def __init__(self, state_dim, action_dim, action_lim):
        super(Actor, self).__init__()

        self.action_lim = action_lim

        # self.bn0 = nn.BatchNorm1d(state_dim)
        self.fc1 = nn.Linear(state_dim, 400)
        # self.fc1.weight.data = fanin_init(self.fc1.weight.data.size())
        # self.bn1 = nn.BatchNorm1d(400)
        self.fc2 = nn.Linear(400, 300)
        # self.fc2.weight.data = fanin_init(self.fc2.weight.data.size())
        # self.bn2 = nn.BatchNorm1d(300)
        self.fc3 = nn.Linear(300, action_dim)
        # self.fc3.weight.data.uniform_(-EPS, EPS)

    def forward(self, state):

        x = F.relu(self.fc1(state))
        x = F.relu(self.fc2(x))
        action = torch.tanh(self.fc3(x))
        action = action * self.action_lim
        return action


# budget program, where I can enter in my expenses and it will store results
# perhaps I can also incorperate some graphs or something

import sqlite3
import random

# might want to change the database name, if I want this to be a budget program
EXPENSE_DATABASE = "expenses.db"
EXPENSE_TYPES = ("FOOD", "RENT", "UTILITIES", "ENTERTAINMENT", "MISC")

# Perhaps I need an income table
def create_database():
    db = sqlite3.connect(EXPENSE_DATABASE)
    cursor = db.cursor()
    cursor.execute('''DROP TABLE expenses''')
    db.commit()
    cursor.execute("CREATE TABLE expenses(uid INTEGER PRIMARY KEY, type TEXT, cost INTEGER)")
    db.commit()
    db.close()

def get_expenses(expense_type):
    db = sqlite3.connect(EXPENSE_DATABASE)
    cursor = db.cursor()
    cursor.execute("SELECT cost, type FROM expenses WHERE type=?", (expense_type,))

    values = [row for row in cursor]

    for row in values:
        print('{0} : {1}'.format(row[0], row[1]))

    print(expense_type + " Total: " + str(sum([row[0] for row in values])))

    db.close()

def add_expense(uid, expense_type, cost):
    db = sqlite3.connect(EXPENSE_DATABASE)
    cursor = db.cursor()

    if expense_type in EXPENSE_TYPES:
        cursor.execute("INSERT INTO expenses(uid, type, cost) \
                            VALUES(?,?,?)", (uid, expense_type, cost))

    db.commit()
    db.close()

#create_database()

#for i in range(100):
    #add_expense(i, random.choice(EXPENSE_TYPES), random.randrange(1000))

get_expenses("FOOD")
print()
get_expenses("RENT")
print()
get_expenses("MISC")


#Boa:Frame:Frame1

import wx

def create(parent):
    return Frame1(parent)

[wxID_FRAME1, wxID_FRAME1BUTTON1, wxID_FRAME1STATICTEXT1,
] = [wx.NewId() for _init_ctrls in range(3)]

class Frame1(wx.Frame):
    def _init_ctrls(self, prnt):
        # generated method, don't edit
        wx.Frame.__init__(self, id=wxID_FRAME1, name='', parent=prnt,
              pos=wx.Point(400, 134), size=wx.Size(578, 475),
              style=wx.DEFAULT_FRAME_STYLE, title=u'my frist app')
        self.SetClientSize(wx.Size(578, 475))
        self.SetMaxSize(wx.Size(578, 475))
        self.SetMinSize(wx.Size(578, 475))

        self.button1 = wx.Button(id=wxID_FRAME1BUTTON1, label=u'Click me',
              name='button1', parent=self, pos=wx.Point(0, 0), size=wx.Size(112,
              48), style=0)
        self.button1.SetMinSize(wx.Size(112, 48))
        self.button1.SetMaxSize(wx.Size(112, 48))
        self.button1.Bind(wx.EVT_BUTTON, self.OnButton1Button,
              id=wxID_FRAME1BUTTON1)

        self.staticText1 = wx.StaticText(id=wxID_FRAME1STATICTEXT1,
              label=u'Welcome to my first app!', name='staticText1',
              parent=self, pos=wx.Point(192, 88), size=wx.Size(168, 32),
              style=0)
        self.staticText1.SetToolTipString(u'RUCULA')

    def __init__(self, parent):
        self._init_ctrls(parent)

    def OnButton1Button(self, event):
        dlg = wx.MessageDialog(self, 'This is a simple modal window', 'first app modal', wx.OK)
        dlg.ShowModal()
        dlg.Destroy()
        event.Skip()


import sys

import pygame
from pygame.locals import *

pygame.init()

fps = 60
fpsClock = pygame.time.Clock()

width, height = 640, 480
screen = pygame.display.set_mode((width, height))

frame = 0
started = False

# Game loop.
while True:
  screen.fill((255, 255, 255))

  for event in pygame.event.get():
    if event.type == QUIT:
      pygame.quit()
      sys.exit()

  if frame >= 60 and frame < 120:
      if frame % 2 == 0:
          screen.fill((0, 0, 0))

  if pygame.key.get_pressed()[pygame.K_SPACE]:
      started = True
  if started:
      frame += 1

  pygame.display.flip()
  fpsClock.tick(fps)


#!/usr/bin/python3

import requests
from pprint import pprint
import json
from datetime import datetime
from sys import stderr

try:
    from ConfigParser import ConfigParser as iniParser
except ModuleNotFoundError:
    from configparser import ConfigParser as iniParser

inifile = iniParser()
inifile.read("/home/mz0/.ssh/aruba/ansible.ini")
myId = inifile.get("acct0", "user")
myPw = inifile.get("acct0", "passwd")
t_json = 'application/json'
h_ct = 'Content-Type'
h_cl = 'Content-Length'

def u(dc, cmd):
    return "https://api.dc{}.computing.cloud.it/WsEndUser/v2.9/WsEndUser.svc/json/{}".format(dc, cmd)

def c0x(cmd, x=''):
    """ x=ServerId
    GetServerDetails, SetEnqueueServerStart (powerOn), SetEnqueueServerPowerOff, SetEnqueueServerDeletion """
    return '{{"ApplicationId":"{}","RequestId":"{}","SessionId":"{}","Password":"{}","Username":"{}"{}' \
           '}}'.format(cmd, cmd, cmd, myPw, myId, x)

def dns(ip, fqdn):
    cmd = "SetEnqueueSetReverseDns"
    return '{{"ApplicationId":"{}","RequestId":"{}","SessionId":"{}","Password":"{}","Username":"{}"' \
           ',"Hosts":["{}"],"IP":"{}"}}'.format(cmd, cmd, cmd, myPw, myId, fqdn, ip)

def list_server(dc):
    server = 0
    cmd = "GetServers"
    data1 = c0x(cmd=cmd)
    headers = {h_ct: t_json, h_cl: str(len(data1))}
    response = requests.post(u(dc, cmd), data=data1, headers=headers)
    if response.status_code == 200 and \
       h_ct in response.headers and \
       response.headers[h_ct] == t_json:
         re = response.content
    try:
        for jr in json.loads(re)['Value']:
            isSmart = jr['HypervisorType']==4
            name    = jr['Name']
            tpl     = jr['OSTemplateId']
            isBusy  = jr['Busy']
            powerOn = jr['ServerStatus']==3
            server  = jr['ServerId']
            cpu = jr['CPUQuantity']
            hdd = jr['HDTotalSize']
            ram = jr['RAMQuantity']
            print("{}cpu-{}GB-ssd{}GB  id {} name '{}' from template {}".format(cpu, ram, hdd, server, name, tpl))
            if isSmart and powerOn and not isBusy:
                print("is running")
    except Exception as e:
        print(e)

    cmd = "GetServerDetails"
    x = ',"ServerId":{}'.format(server)
    data2 = c0x(cmd=cmd, x=x)
    headers = {h_ct: t_json, h_cl: str(len(data2))}
    response = requests.post(u(dc, cmd), data=data2, headers=headers)
    if response.status_code == 200:
        re = response.content
    try:
        jr = json.loads(re)
        print(jr['Value']['EasyCloudIPAddress']['Value'])
        n6s = jr['Value']['NetworkAdapters'][0]['IPAddresses'][0]['StartRangeIPv6']
        n6p = jr['Value']['NetworkAdapters'][0]['IPAddresses'][0]['PrefixIPv6']
        print(n6s)
        if n6p == 64:
            print(n6s[0:20]+":/"+str(n6p))  # ignore SubnetPrefixIPv6 -- Aruba truncates 2 hex digits
        else:
            print("Prefix:" + str(n6p))
        cdt = jr['Value']['CreationDate'][6:16]
        print("Created     @ {}".format(datetime.utcfromtimestamp(int(cdt))))
        rdt = jr['Value']['RenewDateSmart'][6:16]
        print("Next charge @ {}".format(datetime.utcfromtimestamp(int(rdt))))
    except Exception as e:
        print(e)

def templates(dc):
    cmd = 'GetHypervisors'
    data1 = c0x(cmd=cmd)
    headers = {h_ct: t_json, h_cl: str(len(data1))}
    response = requests.post(u(dc, cmd), data=data1, headers=headers)
    if response.status_code == 200 and \
            h_ct in response.headers and \
            response.headers[h_ct] == t_json:
        re = response.content
    for templ in json.loads(re)['Value']:
        if templ['HypervisorType'] == 4:  # is Smart
            print("show|ipv6 |size|sshk|	Id | Name                  |Description")
            #      True|True|-MLX|False|	954|WS08-001_W2K8R2_X64_1_2|Windows 2008 R2 64bit
            for t in templ['Templates']:
                compatl = t['CompatiblePreConfiguredPackages']
                if compatl == [9, 10, 11]:
                    compat = "-MLX"
                elif compatl == [1, 6, 7, 8]:
                    compat = "SMLX"
                else:
                    compat = str(compatl)
                descr  = t['Description']
                showme = t['Enabled']
                id = t['Id']
                id_cod = t['IdentificationCode']
                name   = t['Name']
                ssh_in = t['SshKeyInitializationSupported']
                ipv6_ok= t['Ipv6Compatible']
                print(str(showme)+"|"+str(ipv6_ok)+"|"+compat+"|"+str(ssh_in)+"|\t"+str(id)+"|"+name+"\t|"+descr)

def get_servers(dc):
    servers = []
    cmd = "GetServers"
    data1 = c0x(cmd=cmd)
    headers = {h_ct: t_json, h_cl: str(len(data1))}
    response = requests.post(u(dc, cmd), data=data1, headers=headers)
    if response.status_code == 200 and \
       h_ct in response.headers and \
       response.headers[h_ct] == t_json:
         re = response.content
    try:
        for j in json.loads(re)['Value']:
            # pprint(j)
            name    = j['Name']
            tpl     = j['OSTemplateId']
            powerOn = j['ServerStatus']==3
            server  = j['ServerId']
            busy    = j['Busy']
            servers.append({"DC": dc, "ServerID": server, "ServerName": name,
                 "Busy":busy, "TemplateID": tpl, "isOn": powerOn})
    except Exception as e:
        stderr.write(e)
    return servers

def cmdX(dc, cmd, x=''):
    data = c0x(cmd=cmd, x=x)
    headers = {h_ct: t_json, h_cl: str(len(data))}
    return requests.post(u(dc, cmd), data=data, headers=headers)

def isBusy(s):
    dc  = s["DC"]
    sId = s["ServerID"]
    all = get_servers(dc)
    for x in all:
        if x["ServerID"] == sId:
            return x['Busy']

def report(response, ok="OK"):
    rc = response.content
    if response.status_code == 200 and \
       h_ct in response.headers and \
       response.headers[h_ct] == t_json:
        suc = "Success"
        jr = json.loads(rc)
        if suc in jr and jr[suc]:
            return ok
        elif suc in jr and jr["ResultMessage"]:
            return str(jr["ResultMessage"])
    else:
        return "Error: {}".format(str(rc))

def poweroff(s):
    if not s["isOn"] and not s["Busy"]:
        return "_dn_"
    if isBusy(s):
        return "Busy"
    server = s["ServerID"]
    dc     = s["DC"]
    cmd  = "SetEnqueueServerPowerOff"
    xtra = ',"ServerId":{}'.format(server)
    cmdX(dc, cmd, xtra)

    cmd = "GetJobs"
    response = cmdX(dc, cmd, xtra)
    return report(response, "Qdn_")
# '{"ExceptionInfo":null,"ResultCode":0,"ResultMessage":null,"Success":true,"Value":[
#    {"CreationDate":"\\/Date(1544463013390+0100)\\/","JobId":7958363,"LastUpdateDate":"\\/Date(1544463013390+0100)\\/","LicenseId":null,"OperationName":"StopVirtualMachineSmartVMWare","Progress":0,"ResourceId":null,"ResourceValue":null,"ServerId":292659,"ServerName":"La","Status":1,"UserId":70331,"Username":"AWI-71331"}]}'

def poweron(s):
    if s["isOn"] and not s["Busy"]:
        return "_ON_"
    if isBusy(s):
        return "BUSY"
    server = s["ServerID"]
    dc     = s["DC"]
    cmd = "SetEnqueueServerStart"
    x = ',"ServerId":{}'.format(server)
    data = c0x(cmd=cmd, x=x)
    headers = {h_ct: t_json, h_cl: str(len(data))}
    response = requests.post(u(dc, cmd), data=data, headers=headers)
    return report(response, "Qup^")
# {'Content-Length': '73',
#  'X-AspNet-Version': '4.0.30319', 'Set-Cookie': 'ASP.NET_SessionId=jwe0r42fww1ulyyoam1uwxjx; path=/; HttpOnly',
#  'X-Powered-By': 'ASP.NET',  'Server': 'Microsoft-IIS/7.5', 'Cache-Control': 'private',
#  'Date': 'Mon, 10 Dec 2018 16:14:55 GMT', 'Content-Type': 'application/json'}
#'{"ExceptionInfo":null,"ResultCode":0,"ResultMessage":null,"Success":true}'

def reinit(s):
    server = s["ServerID"]
    dc     = s["DC"]
    if isBusy(s):
        return "Busy"
    if s["isOn"]:
        return "isON"
    cmd = "SetEnqueueReinitializeServer"
    rtpw = 'fsk;dlfk2'  # BUG https://github.com/Arubacloud/pyArubaCloud/issues/55
    ipv6 = 'false'      # BUG https://github.com/Arubacloud/pyArubaCloud/issues/57
    x = ',"AdministratorPassword":"{}","ServerId":{},"ConfigureIPv6":{}'.format(rtpw, server, ipv6)
    data = c0x(cmd=cmd, x=x)
    headers = {h_ct: t_json, h_cl: str(len(data))}
    response = requests.post(u(dc, cmd), data=data, headers=headers)
    return report(response, "QOKW")

for dc in [1, 5, 6]: list_server(dc)
templates(1)

for s in get_servers(1):
    pprint(s)
#   print(poweron(s))
    print(poweroff(s))
    print(reinit(s))


# Write a program that accepts a sentence and calculate the number of upper case letters and lower case letters.

#i/p: Hello world!
# UPPER CASE 1
# LOWER CASE 9

from collections import Counter

UPPER_CASE = LOWER_CASE = 0
raw_input = input("enter the sentence ")
input_dict = Counter(raw_input)
for k,v in input_dict.items():
  if k.islower():
    LOWER_CASE += v
  elif k.isupper():
    UPPER_CASE += v

print(f"UPPER CASE {UPPER_CASE} \n LOWER CASE {LOWER_CASE}")


"""Protocol buffers (Protobuf) format.

A language-agnostic data serialization format developed by Google
https://developers.google.com/protocol-buffers
"""
import ele
import numpy as np
from google.protobuf.text_format import Merge, PrintMessage

from mbuild import Box, Compound
from mbuild.formats import compound_pb2

__all__ = ["write_pb2", "read_pb2"]

def write_pb2(cmpd, filename, binary=True):
    """Convert mb.Compound to Protobuf Message file.

    Parameters
    ----------
    cmpd : mb.Compound
    filename : str
    binary: bool, default True
        If True, will print a binary file
        If False, will print to a text file
    """
    cmpd_to_proto = {}

    root_proto = compound_pb2.Compound()
    root_proto = _mb_to_proto(cmpd, root_proto)
    cmpd_to_proto[cmpd] = root_proto

    for sub_cmpd in cmpd.successors():
        parent_cmpd = sub_cmpd.parent
        sub_proto = cmpd_to_proto[parent_cmpd].children.add()
        sub_proto = _mb_to_proto(sub_cmpd, sub_proto)
        cmpd_to_proto[sub_cmpd] = sub_proto

    _add_proto_bonds(cmpd, root_proto)

    if binary:
        with open(filename, "wb") as f:
            f.write(root_proto.SerializeToString())
    else:
        with open(filename, "w") as f:
            PrintMessage(root_proto, f)

def read_pb2(filename, binary=True):
    """Convert a Protobuf Message file into mb.Compound.

    Parameters
    ----------
    filename : str
    binary: bool, default True
        If True, will print a binary file
        If False, will print to a text file

    Returns
    -------
    root_compound : mb.Compound
    """
    root_proto = compound_pb2.Compound()
    if binary:
        with open(filename, "rb") as f:
            root_proto.ParseFromString(f.read())
    else:
        with open(filename, "r") as f:
            Merge(f.read(), root_proto)

    proto_to_cmpd = {}
    root_compound = _proto_to_mb(root_proto)
    proto_to_cmpd[root_proto.id] = root_compound

    for sub_proto, parent_proto in _proto_successors(root_proto):
        if parent_proto.id not in proto_to_cmpd:
            parent_cmpd = _proto_to_mb(parent_proto)
            proto_to_cmpd[parent_proto.id] = parent_cmpd
        parent_cmpd = proto_to_cmpd[parent_proto.id]

        if sub_proto.id not in proto_to_cmpd:
            sub_cmpd = _proto_to_mb(sub_proto)
            proto_to_cmpd[sub_proto.id] = sub_cmpd
        sub_cmpd = proto_to_cmpd[sub_proto.id]

        parent_cmpd.add(sub_cmpd)

    _add_mb_bonds(root_proto, root_compound, proto_to_cmpd)
    return root_compound

def _mb_to_proto(cmpd, proto):
    """Given mb.Compound, parse propertes into compound_pb2.Compound."""
    proto.name = cmpd.name
    proto.pos.x, proto.pos.y, proto.pos.z = cmpd.pos
    proto.charge = cmpd.charge if cmpd.charge else 0.0
    proto.id = id(cmpd)
    (
        proto.periodicity.x,
        proto.periodicity.y,
        proto.periodicity.z,
    ) = cmpd.periodicity
    if cmpd.element:
        proto.element.name = cmpd.element.name
        proto.element.symbol = cmpd.element.symbol
        proto.element.atomic_number = cmpd.element.atomic_number
        proto.element.mass = cmpd.element.mass

    return proto

def _add_proto_bonds(cmpd, proto):
    """Parse the mb.Compound bonds, add to the proto bonds.

    Parameters
    ----------
    cmpd : mb.Compound
    proto : compound_pb2.Compound
    """
    for b in cmpd.bonds():
        proto_bond = proto.bonds.add()
        proto_bond.id1 = id(b[0])
        proto_bond.id2 = id(b[1])

def _proto_successors(proto):
    """Recurisve method to look for a compound_pb2's children.

    Parameters
    ----------
    proto : compound_pb2

    Notes
    -----
    Base Case: there are no children to the proto, just return
    Recursion: First look at proto's children and return these children
        (sub_proto) Then make the recursive call to look at all the sub_proto's
        successors. This is similar to mb.Compound().successors(). Unlike
        mb.Compound(), we need to also keep track of parents in this recursion
    """
    if len(proto.children) == 0:
        return
    for sub_proto in proto.children:
        yield (sub_proto, proto)
        for sub_sub_proto, parent_proto in _proto_successors(sub_proto):
            yield (sub_sub_proto, parent_proto)

def _proto_to_mb(proto):
    """Given compound_pb2.Compound, create mb.Compound.

    Parameters
    ----------
    proto: compound_pb2.Compound()
    """
    if proto.element.symbol == "":
        elem = None
    else:
        elem = ele.element_from_symbol(proto.element.symbol)
    lengths = [proto.periodicity.x, proto.periodicity.y, proto.periodicity.z]
    if np.all(np.array(lengths) != 0):
        box = Box(lengths)
    else:
        box = None
    return Compound(
        name=proto.name,
        pos=[proto.pos.x, proto.pos.y, proto.pos.z],
        charge=proto.charge,
        box=box,
        element=elem,
    )

def _add_mb_bonds(proto, cmpd, proto_to_cmpd):
    """Parse the compound_pb2.Compound bonds, add to mb.Compound.

    Parameters
    ----------
    proto : compound_pb2.Compound
    cmpd : mb.Compound
    proto_to_cmpd : dict
        keys : compound_pb2.Compound.id
        value : mb.Compound
    """
    for bond in proto.bonds:
        cmpd.add_bond([proto_to_cmpd[bond.id1], proto_to_cmpd[bond.id2]])


from itertools import cycle

with open('input.txt', 'r') as file:
    freqs = [int(i) for i in file.read().split('\n')]

# Part 1

total = sum(freqs)
print(f'Part 1 => {total}')

# Part 2

res, i = 0, 0
dup = set()

for i in cycle(freqs):
    res += i
    if res in dup:
        break
    dup.add(res)

print(f'Part 2 => {res}')