import gc
import os
import sys
import dask.dataframe as dd
import time, datetime
import os
from base import *
import numpy as np  # linear algebra
import pandas as pd

ori_col = ['ip', 'device', 'os', 'channel', 'app', 'hour', 'is_attributed', 'day', 'mit', 'sec', 'half_hour',
           'ten_mit_hour', 'in_test_hh', 'cate_v1', 'cate_v2', 'click_time','click_id']

def df_add_counts(df, cols):
    print('create count features ',cols)
    arr_slice = df[cols].values
    unq, unqtags, counts = np.unique(np.ravel_multi_index(arr_slice.T, arr_slice.max(0) + 1),
                                     return_inverse=True, return_counts=True)
    df["_".join(cols) + '_count'] = reduce_series(counts[unqtags])
    del unq, unqtags, counts
    gc.collect()

def df_add_unique(df, cols):
    col = cols[1]
    cols = cols[0]
    print('create unique features ',cols, col)
    df["_".join(cols) + '__' + col + '_unique'] = reduce_series(df[cols + [col]].groupby(cols)[col].nunique().reindex(
        df.set_index(cols).index).values)

def df_add_ratio(df, cols1, cols2):
    print('create ratio features ',cols1,cols2)
    df["_".join(cols1)+"/".join(cols2)] = reduce_series(np.log1p(df["_".join(cols1) + '_count'])-np.log1p(df["_".join(cols2) + '_count']))

def df_add_var(df, cols, col):
    print('create var features ',cols, col)
    df["_".join(cols) + '__' + col + '_var'] = reduce_series(df[cols + [col]].groupby(cols)[col].var().reindex(
        df.set_index(cols).index).values)

def df_add_time(df, cols):
    print('create next_click features ',cols)
    df["_".join(cols) + '_nextClick'] = reduce_series((df.groupby(cols).click_time.shift(-1) - df.click_time).astype(np.float32).values)
    df["_".join(cols) + '_next2Click'] = reduce_series((df.groupby(cols).click_time.shift(-2) - df.click_time).astype(np.float32).values)
    df["_".join(cols) + '_preClick'] = reduce_series((df.groupby(cols).click_time.shift(+1) - df.click_time).astype(np.float32).values)


from django.shortcuts import render, get_object_or_404, redirect
from django.views import View

from courseinfo.forms import InstructorForm, SectionForm, CourseForm, SemesterForm, RegistrationForm, StudentForm
from courseinfo.models import (
    Instructor,
    Section,
    Course,
    Semester,
    Student,
    Registration,
)
from courseinfo.utils import ObjectCreateMixin

class InstructorList(View):

    def get(self, request):
        return render(
            request,
            'courseinfo/instructor_list.html',
            {'instructor_list': Instructor.objects.all()}
        )

class InstructorDetail(View):

    def get(self, request, pk):
        instructor = get_object_or_404(
            Instructor,
            pk=pk
        )
        section_list = instructor.sections.all()
        return render(
            request,
            'courseinfo/instructor_detail.html',
            {'instructor': instructor, 'section_list': section_list}
        )

class InstructorCreate(ObjectCreateMixin, View):
    form_class = InstructorForm
    template_name = 'courseinfo/instructor_form.html'

class InstructorUpdate(View):
    form_class = InstructorForm
    model = Instructor
    template_name = 'courseinfo/instructor_form_update.html'

    def get_object(self, pk):
        return get_object_or_404(
            self.model,
            pk=pk)

    def get(self, request, pk):
        instructor = self.get_object(pk)
        context = {
            'form': self.form_class(
                instance=instructor),
            'instructor': instructor,
        }
        return render(
            request, self.template_name, context)

    def post(self, request, pk):
        instructor = self.get_object(pk)
        bound_form = self.form_class(
            request.POST, instance=instructor)
        if bound_form.is_valid():
            new_instructor = bound_form.save()
            return redirect(new_instructor)
        else:
            context = {
                'form': bound_form,
                'instructor': instructor,
            }
            return render(
                request,
                self.template_name,
                context)

class InstructorDelete(View):

    def get(self, request, pk):
        instructor = self.get_object(pk)
        sections = instructor.sections.all()
        if sections.count() > 0:
            return render(
                request,
                'courseinfo/instructor_refuse_delete.html',
                {'instructor': instructor,
                 'sections': sections,
                 }
            )
        else:
            return render(
                request,
                'courseinfo/instructor_confirm_delete.html',
                {'instructor': instructor}
            )

    def get_object(self, pk):
        return get_object_or_404(
            Instructor,
            pk=pk)

    def post(self, request, pk):
        instructor = self.get_object(pk)
        instructor.delete()
        return redirect('courseinfo_instructor_list_urlpattern')

class SectionList(View):

    def get(self, request):
        return render(
            request,
            'courseinfo/section_list.html',
            {'section_list': Section.objects.all()}
        )

class SectionDetail(View):

    def get(self, request, pk):
        section = get_object_or_404(
            Section,
            pk=pk
        )
        semester = section.semester
        course = section.course
        instructor = section.instructor
        registration_list = section.registrations.all()
        return render(
            request,
            'courseinfo/section_detail.html',
            {'section': section,
             'semester': semester,
             'course': course,
             'instructor': instructor,
             'registration_list': registration_list}
        )

class SectionCreate(ObjectCreateMixin, View):
    form_class = SectionForm
    template_name = 'courseinfo/section_form.html'

class SectionUpdate(View):
    form_class = SectionForm
    model = Section
    template_name = 'courseinfo/section_form_update.html'

    def get_object(self, pk):
        return get_object_or_404(
            self.model,
            pk=pk)

    def get(self, request, pk):
        section = self.get_object(pk)
        context = {
            'form': self.form_class(
                instance=section),
            'section': section,
        }
        return render(
            request, self.template_name, context)

    def post(self, request, pk):
        section = self.get_object(pk)
        bound_form = self.form_class(
            request.POST, instance=section)
        if bound_form.is_valid():
            new_section = bound_form.save()
            return redirect(new_section)
        else:
            context = {
                'form': bound_form,
                'section': section,
            }
            return render(
                request,
                self.template_name,
                context)

class SectionDelete(View):

    def get(self, request, pk):
        section = self.get_object(pk)
        registrations = section.registrations.all()
        if registrations.count() > 0:
            return render(
                request,
                'courseinfo/section_refuse_delete.html',
                {'section': section,
                 'registrations': registrations,
                 }
            )
        else:
            return render(
                request,
                'courseinfo/section_confirm_delete.html',
                {'section': section}
            )

    def get_object(self, pk):
        return get_object_or_404(
            Section,
            pk=pk)

    def post(self, request, pk):
        section = self.get_object(pk)
        section.delete()
        return redirect('courseinfo_section_list_urlpattern')

class SemesterList(View):

    def get(self, request):
        return render(
            request,
            'courseinfo/semester_list.html',
            {'semester_list': Semester.objects.all()}
        )

class SemesterDetail(View):

    def get(self, request, pk):
        semester = get_object_or_404(
            Semester,
            pk=pk
        )
        section_list = semester.sections.all()
        return render(
            request,
            'courseinfo/semester_detail.html',
            {'semester': semester, 'section_list': section_list})

class SemesterCreate(ObjectCreateMixin, View):
    form_class = SemesterForm
    template_name = 'courseinfo/semester_form.html'

class SemesterUpdate(View):
    form_class = SemesterForm
    model = Semester
    template_name = 'courseinfo/semester_form_update.html'

    def get_object(self, pk):
        return get_object_or_404(
            self.model,
            pk=pk)

    def get(self, request, pk):
        semester = self.get_object(pk)
        context = {
            'form': self.form_class(
                instance=semester),
            'semester': semester,
        }
        return render(
            request, self.template_name, context)

    def post(self, request, pk):
        semester = self.get_object(pk)
        bound_form = self.form_class(
            request.POST, instance=semester)
        if bound_form.is_valid():
            new_semester = bound_form.save()
            return redirect(new_semester)
        else:
            context = {
                'form': bound_form,
                'semester': semester,
            }
            return render(
                request,
                self.template_name,
                context)

class SemesterDelete(View):

    def get(self, request, pk):
        semester = self.get_object(pk)
        sections = semester.sections.all()
        if sections.count() > 0:
            return render(
                request,
                'courseinfo/semester_refuse_delete.html',
                {'semester': semester,
                 'sections': sections,
                 }
            )
        else:
            return render(
                request,
                'courseinfo/semester_confirm_delete.html',
                {'semester': semester}
            )

    def get_object(self, pk):
        return get_object_or_404(
            Semester,
            pk=pk)

    def post(self, request, pk):
        semester = self.get_object(pk)
        semester.delete()
        return redirect('courseinfo_semester_list_urlpattern')

class CourseList(View):

    def get(self, request):
        return render(
            request,
            'courseinfo/course_list.html',
            {'course_list': Course.objects.all()}
        )

class CourseDetail(View):

    def get(self, request, pk):
        course = get_object_or_404(
            Course,
            pk=pk
        )
        section_list = course.sections.all()
        return render(
            request,
            'courseinfo/course_detail.html',
            {'course': course, 'section_list': section_list})

class CourseCreate(ObjectCreateMixin, View):
    form_class = CourseForm
    template_name = 'courseinfo/course_form.html'

class CourseUpdate(View):
    form_class = CourseForm
    model = Course
    template_name = 'courseinfo/course_form_update.html'

    def get_object(self, pk):
        return get_object_or_404(
            self.model,
            pk=pk)

    def get(self, request, pk):
        course = self.get_object(pk)
        context = {
            'form': self.form_class(
                instance=course),
            'instructor': course,
        }
        return render(
            request, self.template_name, context)

    def post(self, request, pk):
        course = self.get_object(pk)
        bound_form = self.form_class(
            request.POST, instance=course)
        if bound_form.is_valid():
            new_course = bound_form.save()
            return redirect(new_course)
        else:
            context = {
                'form': bound_form,
                'course': course,
            }
            return render(
                request,
                self.template_name,
                context)

class CourseDelete(View):

    def get(self, request, pk):
        course = self.get_object(pk)
        sections = course.sections.all()
        if sections.count() > 0:
            return render(
                request,
                'courseinfo/course_refuse_delete.html',
                {'course': course,
                 'sections': sections,
                 }
            )
        else:
            return render(
                request,
                'courseinfo/course_confirm_delete.html',
                {'course': course}
            )

    def get_object(self, pk):
        return get_object_or_404(
            Course,
            pk=pk)

    def post(self, request, pk):
        course = self.get_object(pk)
        course.delete()
        return redirect('courseinfo_course_list_urlpattern')

class StudentList(View):

    def get(self, request):
        return render(
            request,
            'courseinfo/student_list.html',
            {'student_list': Student.objects.all()}
        )

class StudentDetail(View):

    def get(self, request, pk):
        student = get_object_or_404(
            Student,
            pk=pk
        )
        registration_list = student.registrations.all()
        return render(
            request,
            'courseinfo/student_detail.html',
            {'student': student, 'registration_list': registration_list}
        )

class StudentCreate(ObjectCreateMixin, View):
    form_class = StudentForm
    template_name = 'courseinfo/student_form.html'

class StudentUpdate(View):
    form_class = StudentForm
    model = Student
    template_name = 'courseinfo/student_form_update.html'

    def get_object(self, pk):
        return get_object_or_404(
            self.model,
            pk=pk)

    def get(self, request, pk):
        student = self.get_object(pk)
        context = {
            'form': self.form_class(
                instance=student),
            'student': student,
        }
        return render(
            request, self.template_name, context)

    def post(self, request, pk):
        student = self.get_object(pk)
        bound_form = self.form_class(
            request.POST, instance=student)
        if bound_form.is_valid():
            new_student = bound_form.save()
            return redirect(new_student)
        else:
            context = {
                'form': bound_form,
                'student': student,
            }
            return render(
                request,
                self.template_name,
                context)

class StudentDelete(View):

    def get(self, request, pk):
        student = self.get_object(pk)
        registrations = student.registrations.all()
        if registrations.count() > 0:
            return render(
                request,
                'courseinfo/student_refuse_delete.html',
                {'student': student,
                 'registrations': registrations,
                 }
            )
        else:
            return render(
                request,
                'courseinfo/student_confirm_delete.html',
                {'student': student}
            )

    def get_object(self, pk):
        return get_object_or_404(
            Student,
            pk=pk)

    def post(self, request, pk):
        student = self.get_object(pk)
        student.delete()
        return redirect('courseinfo_student_list_urlpattern')

class RegistrationList(View):

    def get(self, request):
        return render(
            request,
            'courseinfo/registration_list.html',
            {'registration_list': Registration.objects.all()}
        )

class RegistrationDetail(View):

    def get(self, request, pk):
        registration = get_object_or_404(
            Registration,
            pk=pk
        )
        section = registration.section
        student = registration.student
        return render(
            request,
            'courseinfo/registration_detail.html',
            {'section': section,
             'student': student,
             'registration': registration}
        )

class RegistrationCreate(ObjectCreateMixin, View):
    form_class = RegistrationForm
    template_name = 'courseinfo/registration_form.html'

class RegistrationUpdate(View):
    form_class = RegistrationForm
    model = Registration
    template_name = 'courseinfo/registration_form_update.html'

    def get_object(self, pk):
        return get_object_or_404(
            self.model,
            pk=pk)

    def get(self, request, pk):
        registration = self.get_object(pk)
        context = {
            'form': self.form_class(
                instance=registration),
            'registration': registration,
        }
        return render(
            request, self.template_name, context)

    def post(self, request, pk):
        registration = self.get_object(pk)
        bound_form = self.form_class(
            request.POST, instance=registration)
        if bound_form.is_valid():
            new_registration = bound_form.save()
            return redirect(new_registration)
        else:
            context = {
                'form': bound_form,
                'registration': registration,
            }
            return render(
                request,
                self.template_name,
                context)

class RegistrationDelete(View):

    def get(self, request, pk):
        registration = self.get_object(pk)
        return render(
            request,
            'courseinfo/registration_confirm_delete.html',
            {'registration': registration}
        )

    def get_object(self, pk):
        registration = get_object_or_404(
            Registration,
            pk=pk
        )
        return registration

    def post(self, request, pk):
        registration = self.get_object(pk)
        registration.delete()
        return redirect('courseinfo_registration_list_urlpattern')


import ast
import datetime
import json
import os
import random
import time
import re
from getpass import getpass
import openai

import numpy as np
import streamlit
import torch as torch
from omegaconf import DictConfig, OmegaConf
from transformers import AutoTokenizer, AutoModelForCausalLM
from openelm.codegen.codegen_utilities import set_seed
from openelm.mutation_model import PromptModel

from architext_genotype import ArchitextGenotype
from concurrent.futures import ThreadPoolExecutor
from util import base_folder

max_json = re.compile(r"\{[\d\D]*\}")

class ArchitextPromptMutation(PromptModel):
    """
    Generating hf outputs on the local machine.
    """

    room_labels = ['bedroom1', 'kitchen', 'living_room', 'corridor', 'bathroom1']

    def __init__(self, cfg, prompts: list[str], default_height=2.0):
        """
        Args:
            cfg: the config dict.
            prompts: a list of default prompts
        """
        if isinstance(cfg, str):
            self.cfg = OmegaConf.load(cfg)
        elif isinstance(cfg, (dict, DictConfig)):
            self.cfg = DictConfig(cfg)
        else:
            raise ValueError

        set_seed(self.cfg.seed)
        # Use RNG to rotate random seeds during inference.
        self.rng = np.random.default_rng(seed=self.cfg.seed)
        self.prompts = prompts
        self.default_height = default_height

        self.batch_size = self.cfg.batch_size
        self.device = torch.device("cuda" if torch.cuda.is_available() and self.cfg.cuda else "cpu")

        # Set up the tokenizer
        self.tokenizer = AutoTokenizer.from_pretrained(self.cfg.model)
        self.tokenizer.padding_side = "left"
        self.tokenizer.pad_token = self.cfg.pad_token

        # Set up the model
        # TODO: fix data parallel
        if self.cfg.gpus > 1:
            self.model = torch.nn.DataParallel(
                AutoModelForCausalLM.from_pretrained(self.cfg.model),
                device_ids=list(range(self.cfg.gpus))
            ).to(self.device)
            self.model.generate = self.model.module.generate
        else:
            self.model = AutoModelForCausalLM.from_pretrained(self.cfg.model).to(self.device)

    def mutate_genotypes(self, inputs: list[ArchitextGenotype], **kwargs) -> list[ArchitextGenotype]:
        """
        This class does not use codes as intermediate representation. To fit into the genotype format, we output
        a dict with `program_str` (== `result_obj`) being the string describing a floor plan using coordinates.

        Args:
            inputs: the list of input genotypes.
        Returns:
            a list of mutated prompts.
        """
        prompts = []
        for pd in inputs:
            if pd is None:
                # Random generation
                mutated_prompt = random.choice(self.prompts)
            else:
                # Mutate the design string
                lines = pd.to_design_string().split(', ')
                random_prompt = random.choice(self.prompts)
                cut_off = np.random.randint(1, 3, size=1)[0]
                cut_off = min(cut_off, len(lines) - 1)
                mutated_prompt = random_prompt + ' ' + ', '.join(lines[1:cut_off + 1]) + ", " + random.choice(
                    self.room_labels) + ":"
            prompts.append(mutated_prompt)

        completion = self.model.generate(**self.tokenizer(prompts,
                                                          return_tensors="pt",
                                                          padding=True,
                                                          truncation=True).to(self.device),
                                         num_beams=self.cfg.num_beams,
                                         num_return_sequences=1,
                                         max_length=self.cfg.gen_max_len,
                                         pad_token_id=50256,
                                         do_sample=True,
                                         **kwargs)

        mutated_design_str = self.tokenizer.batch_decode(completion)
        mutated_designs = [
            ArchitextGenotype(design_string=elem,
                              height=self.default_height if parent is None else parent.height,
                              parent=parent) for elem, parent in zip(mutated_design_str, inputs)]
        return mutated_designs

class ArchitextChatGPTMutation(PromptModel):
    """
    This prompt mutation calls GPT-3.5 API to generate designs
    """

    def __init__(self, cfg, prompts: list[str],
                 seed_designs: list[dict] | None = None, default_height=2.0,
                 count_tokens: bool = False, **kwargs):
        """
        Args:
            cfg: the config dict.
            prompts: a list of default prompts
        """
        if isinstance(cfg, str):
            self.cfg = OmegaConf.load(cfg)
        elif isinstance(cfg, (dict, DictConfig)):
            self.cfg = DictConfig(cfg)
        else:
            raise ValueError

        self.prompts = prompts
        self.default_height = default_height
        if seed_designs is None:
            try:
                with open("seed_designs.json", "r") as f:
                    seed_designs = ast.literal_eval(f.read())
            except:
                raise ValueError("Please provide a list of seed designs.")
        self.seed_designs = seed_designs

        if 'OPENAI_API_KEY' in os.environ:
            openai.api_key = os.environ['OPENAI_API_KEY']
        else:
            openai.api_key = getpass('Enter your OpenAI API key:')

        self.count_tokens = count_tokens
        if self.count_tokens:
            import streamlit

    def mutate_genotypes(self, genotypes: list[ArchitextGenotype], num_threads=10, **kwargs) -> list[ArchitextGenotype]:
        args = []
        for x in genotypes:
            example = random.choice(self.seed_designs) if x is None else x.to_dict()
            prompt = random.choice(self.prompts)
            args.append((example, prompt))

        with ThreadPoolExecutor(num_threads) as pool:
            results = list(pool.map(self._get_completion, args))
        mutated_json, usages = [r[0] for r in results], [r[1] for r in results]
        if self.count_tokens:
            for usage in usages:
                streamlit.session_state["prompt_tokens"] = \
                    streamlit.session_state.get("prompt_tokens", 0) + usage.get("prompt_tokens", 0)
                streamlit.session_state["tokens"] = \
                    streamlit.session_state.get("tokens", 0) + usage.get("total_tokens", 0)

        self._log_returns(mutated_json)

        mutated_genotypes = []
        for x, parent in zip(mutated_json, genotypes):
            if x is None:
                mutated_genotypes.append(None)
            else:
                assert isinstance(x, str)
                try:
                    new_dict = max_json.search(x).group(0)
                    new_dict = ast.literal_eval(new_dict)
                    mutated_genotypes.append(
                        ArchitextGenotype.from_dict(new_dict,
                                                    height=self.default_height if parent is None else parent.height,
                                                    parent=parent)
                    )
                except Exception as e:
                    with open("logs.txt", "a") as f:
                        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        f.write(f"[{timestamp} ERROR]: Parsing error in the return of GPT-3.5.\n"
                                f"Returned string: {str(x)}\n"
                                f"Exception: {str(e)}\n"
                                )
                    mutated_genotypes.append(None)

        return mutated_genotypes

    @staticmethod
    def _get_completion(args) -> tuple[str | None, dict]:
        """
        The inner loop of getting new design json from GPT-3.5 API.
        args: a tuple of (example, prompt)
            where example: dict, prompt: str
        """
        example, prompt = args

        requirements = "1. The prompt is all the info you have. The design detailed in `layout` follows the prompt as "\
                       "closely as possible.\n" \
                       "2. Different rooms cannot overlap.\n" \
                       "3. The room names should start with one of 'living_room', 'kitchen', 'bedroom', 'bathroom', " \
                       "'corridor'.\n" \
                       "4. Number of bathroom <= Number of bedroom <= 4.\n" \
                       "5. The return must be a valid JSON document.\n"
        sys_msg = "You are a REST API server receiving prompts describing a floor plan. You only return JSON " \
                  "documents describing your design. The format is the following:\n"\
                  "1. `prompt`: the original input prompt, \n"\
                  "2. `layout`: the room-by-room details of the floor plan in terms of the coordinates.\n"\
                  "An example is the following:\n" \
                  "```\n" + str(example) + "\n```\n" \
                  "In the `layout` field, each room is represented as a list of coordinates defining a polygon.\n" \
                  "The returned JSON document must satisfy " \
                  "the following requirements:\n" + requirements
        for i in range(5):
            try:
                completion = openai.ChatCompletion.create(
                    model="gpt-3.5-turbo",
                    messages=[{"role": "system", "content": sys_msg}, {"role": "user", "content": prompt}]
                )

                return completion["choices"][0]["message"]["content"], completion["usage"]
            except:
                time.sleep(60)
                if i == 4:
                    print("Warning: Calling OpenAI API failed for 5 consecutive times. Returning None. "
                          "See `logs.txt` for details.")
                    with open("logs.txt", "a") as f:
                        timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        f.write(f"[{timestamp} ERROR]: Calling OAI API failed for 5 consecutive times.\n"
                                "Prompt: " + prompt + "\n"
                                                      "Example: " + str(example) + "\n")
                return None, {}

    @staticmethod
    def _log_returns(mutated_json):
        """
        Log the returned jsons to a file.
        """
        with open("returned.txt", "a") as f:
            timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            f.write(f"[{timestamp} INFO]: Returned JSONs: " + str(mutated_json) + "\n")

def build_default_mutation_model(key: str, cfg: DictConfig, count_tokens: bool = True):
    """
    A utility function that builds a default version of each mutation model class.

    Args:
        key (str): A key that defines the mutation model.
        cfg (DictConfig): A configuration object.
        count_tokens (bool): Whether to count the number of tokens in the generated text.
            Note: if True, it will import streamlit and count OpenAI API tokens using
            streamlit.session_state['tokens'].

    Returns:
        MutationModel: A mutation model.
    """
    with open(str(base_folder / 'prompts.txt'), 'r') as f:
        prompts = [p.strip() for p in f.read().split('\n') if p.strip()]
    aug_prompts = ['[prompt] ' + prompt.rstrip() + ' [layout]' for prompt in prompts]

    with open(str(base_folder / "seed_designs.json"), "r") as f:
        seed_designs = json.load(f)

    if key == "gpt-j":
        return ArchitextPromptMutation(cfg,
                                       prompts=aug_prompts,
                                       default_height=cfg.height)

    elif key == "chatgpt":
        return ArchitextChatGPTMutation(cfg,
                                        prompts=prompts,
                                        seed_designs=seed_designs,
                                        default_height=cfg.height,
                                        count_tokens=count_tokens)


from PyQt5 import Qt, QtCore
from PyQt5.QtGui import QColor, QStandardItemModel, QStandardItem
from PyQt5.QtSql import QSqlQueryModel, QSqlQuery, QSql
from PyQt5.QtWidgets import QTableView, QAbstractSlider
from PyQt5.QtCore import QItemSelectionModel, pyqtSignal, QEvent
import time
import math

from PyQt5.QtCore import QCoreApplication as QC

class GenericTableModel(QStandardItemModel):
    rowCountChanged = pyqtSignal()

    db = None
    tableName = ""
    # total row count which must de displayed in the view
    totalRowCount = 0
    #
    lastColumnCount = 0

    # original query string before we modify it
    origQueryStr = QSqlQuery()
    # previous original query string; used to check if the query has changed
    prevQueryStr = ''
    # modified query object
    realQuery = QSqlQuery()

    items = []
    lastItems = []

    def __init__(self, tableName, headerLabels):
        self.tableName = tableName
        self.headerLabels = headerLabels
        self.lastColumnCount = len(self.headerLabels)
        QStandardItemModel.__init__(self, 0, self.lastColumnCount)
        self.setHorizontalHeaderLabels(self.headerLabels)

    #Some QSqlQueryModel methods must be mimiced so that this class can serve as a drop-in replacement
    #mimic QSqlQueryModel.query()
    def query(self):
        return self

    #mimic QSqlQueryModel.query().lastQuery()
    def lastQuery(self):
        return self.origQueryStr

    #mimic QSqlQueryModel.query().lastError()
    def lastError(self):
        return self.realQuery.lastError()

    #mimic QSqlQueryModel.clear()
    def clear(self):
        pass

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if role == QtCore.Qt.DisplayRole:
            items_count = len(self.items)
            if index.isValid() and items_count > 0 and index.row() < items_count:
                return self.items[index.row()][index.column()]
        return QStandardItemModel.data(self, index, role)

    # set columns based on query's fields
    def setModelColumns(self, newColumns):
        self.headerLabels = []
        self.removeColumns(0, self.lastColumnCount)
        self.setHorizontalHeaderLabels(self.headerLabels)
        for col in range(0, newColumns):
            self.headerLabels.append(self.realQuery.record().fieldName(col))
        self.lastColumnCount = newColumns
        self.setHorizontalHeaderLabels(self.headerLabels)
        self.setColumnCount(len(self.headerLabels))

    def setQuery(self, q, db):
        self.origQueryStr = q
        self.db = db
        #print("q:", q)

        if self.prevQueryStr != self.origQueryStr:
            self.realQuery = QSqlQuery(q, db)

        self.realQuery.exec_()
        self.realQuery.last()

        queryRows = max(0, self.realQuery.at()+1)
        self.totalRowCount = queryRows
        self.setRowCount(self.totalRowCount)

        # update view's columns
        queryColumns = self.realQuery.record().count()
        if queryColumns != self.lastColumnCount:
            self.setModelColumns(queryColumns)

        self.prevQueryStr = self.origQueryStr
        self.rowCountChanged.emit()

    def nextRecord(self, offset):
        cur_pos = self.realQuery.at()
        q.seek(max(cur_pos, cur_pos+offset))

    def prevRecord(self, offset):
        cur_pos = self.realQuery.at()
        q.seek(min(cur_pos, cur_pos-offset))

    # refresh the viewport with data from the db.
    def refreshViewport(self, scrollValue, maxRowsInViewport, force=False):
        # set records position to last, in order to get correctly the number of
        # rows.
        self.realQuery.last()
        rowsFound = max(0, self.realQuery.at()+1)
        if scrollValue == 0 or self.realQuery.at() == QSql.BeforeFirstRow:
            self.realQuery.seek(QSql.BeforeFirstRow)
        elif self.realQuery.at() == QSql.AfterLastRow:
            self.realQuery.seek(rowsFound - maxRowsInViewport)
        else:
            self.realQuery.seek(min(scrollValue-1, self.realQuery.at()))

        upperBound = min(maxRowsInViewport, rowsFound)
        self.setRowCount(self.totalRowCount)

        # only visible rows will be filled with data, and only if we're not
        # updating the viewport already.
        if upperBound > 0 or self.realQuery.at() < 0:
            self.fillRows(self.realQuery, upperBound, force)

    def fillRows(self, q, upperBound, force=False):
        rowsLabels = []
        self.setVerticalHeaderLabels(rowsLabels)

        self.items = []
        cols = []
        self.blockSignals(True)
        #don't trigger setItem's signals for each cell, instead emit dataChanged for all cells
        for x in range(0, upperBound):
            q.next()
            if q.at() < 0:
                # if we don't set query to a valid record here, it gets stucked
                # forever at -2/-1.
                q.seek(upperBound)
                break
            rowsLabels.append(str(q.at()+1))
            cols = []
            for col in range(0, len(self.headerLabels)):
                cols.append(str(q.value(col)))

            self.items.append(cols)
        self.blockSignals(False)

        self.setVerticalHeaderLabels(rowsLabels)
        if self.lastItems != self.items or force == True:
            self.dataChanged.emit(self.createIndex(0,0), self.createIndex(upperBound, len(self.headerLabels)))
        self.lastItems = self.items
        del cols

    def dumpRows(self):
        rows = []
        q = QSqlQuery(self.db)
        q.exec(self.origQueryStr)
        q.seek(QSql.BeforeFirstRow)
        while True:
            q.next()
            if q.at() == QSql.AfterLastRow:
                break
            row = []
            for col in range(0, len(self.headerLabels)):
                row.append(q.value(col))
            rows.append(row)
        return rows

class GenericTableView(QTableView):
    # how many rows can potentially be displayed in viewport
    # the actual number of rows currently displayed may be less than this
    maxRowsInViewport = 0
    vScrollBar = None

    def __init__(self, parent):
        QTableView.__init__(self, parent)
        #eventFilter to catch key up/down events and wheel events
        self.installEventFilter(self)
        self.verticalHeader().setVisible(True)
        self.horizontalHeader().setDefaultAlignment(QtCore.Qt.AlignCenter)
        self.horizontalHeader().setStretchLastSection(True)
        #the built-in vertical scrollBar of this view is always off
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

    def setVerticalScrollBar(self, vScrollBar):
        self.vScrollBar = vScrollBar
        self.vScrollBar.valueChanged.connect(self.onValueChanged)
        self.vScrollBar.setVisible(False)

    def setModel(self, model):
        super().setModel(model)
        model.rowCountChanged.connect(self.onRowCountChanged)
        model.rowsInserted.connect(self.onRowsInsertedOrRemoved)
        model.rowsRemoved.connect(self.onRowsInsertedOrRemoved)
        self.horizontalHeader().sortIndicatorChanged.disconnect()
        self.setSortingEnabled(False)

    # model().rowCount() is always <= self.maxRowsInViewport
    # stretch the bottom row; we don't want partial-height rows at the bottom
    # this will only trigger if rowCount value was changed
    def onRowsInsertedOrRemoved(self, parent, start, end):
        if self.model().rowCount() == self.maxRowsInViewport:
            self.verticalHeader().setStretchLastSection(True)
        else:
            self.verticalHeader().setStretchLastSection(False)

    def resizeEvent(self, event):
        super().resizeEvent(event)
        #refresh the viewport data based on new geometry
        self.refresh()

    def refresh(self):
        self.calculateRowsInViewport()
        self.model().setRowCount(min(self.maxRowsInViewport, self.model().totalRowCount))
        self.model().refreshViewport(self.vScrollBar.value(), self.maxRowsInViewport, force=True)

    def calculateRowsInViewport(self):
        rowHeight = self.verticalHeader().defaultSectionSize()
        columnSize = self.horizontalHeader().defaultSectionSize()
        # we don't want partial-height rows in viewport, hence .floor()
        self.maxRowsInViewport = math.floor(self.viewport().height() / rowHeight)+1

    def onValueChanged(self, vSBNewValue):
        self.model().refreshViewport(vSBNewValue, self.maxRowsInViewport, force=True)

    def onRowCountChanged(self):
        scrollBar = self.vScrollBar

        totalCount = self.model().totalRowCount
        scrollBar.setVisible(True if totalCount > self.maxRowsInViewport else False)

        scrollBar.setMinimum(0)
        # we need to substract the displayed rows to the total rows, to scroll
        # down correctly.
        scrollBar.setMaximum(max(0, totalCount - self.maxRowsInViewport+1))

        self.model().refreshViewport(scrollBar.value(), self.maxRowsInViewport)

    def getCurrentIndex(self):
        return self.selectionModel().currentIndex().internalId()

    def selectItem(self, _data, _column):
        """Select a row based on the data displayed on the given column.
        """
        items = self.model().findItems(_data, column=_column)
        if len(items) > 0:
            self.selectionModel().setCurrentIndex(
                items[0].index(),
                QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent
            )

    def _selectLastRow(self):
        internalId = self.getCurrentIndex()
        idx = self.model().createIndex(self.maxRowsInViewport-2, 0, internalId)
        self.selectionModel().setCurrentIndex(
            idx, QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent
        )

    def _selectRow(self, pos):
        internalId = self.getCurrentIndex()
        self.selectionModel().setCurrentIndex(
            self.model().createIndex(pos, 1, internalId), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent
        )

    def onKeyUp(self):
        if self.selectionModel().currentIndex().row() == 0:
            self.vScrollBar.setValue(max(0, self.vScrollBar.value() - 1))

    def onKeyDown(self):
        if self.vScrollBar.isVisible() == False:
            return

        if self.selectionModel().currentIndex().row() >= self.maxRowsInViewport-2:
            self.vScrollBar.setValue(self.vScrollBar.value() + 1)
            self._selectLastRow()

    def onKeyHome(self):
        self.vScrollBar.setValue(0)
        self.selectionModel().setCurrentIndex(self.model().createIndex(0, 0), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent)

    def onKeyEnd(self):
        self.vScrollBar.setValue(self.vScrollBar.maximum())
        self._selectLastRow()

    def onKeyPageUp(self):
        newValue = max(0, self.vScrollBar.value() - self.maxRowsInViewport)
        self.vScrollBar.setValue(newValue)

    def onKeyPageDown(self):
        if self.vScrollBar.isVisible() == False:
            return

        newValue = self.vScrollBar.value() + (self.maxRowsInViewport-2)
        if newValue >= self.model().rowCount():
            self._selectLastRow()
            return

        if newValue < self.model().rowCount():
            self.vScrollBar.setValue(newValue)
            self._selectRow(0)

    def eventFilter(self, obj, event):
        if event.type() == QEvent.KeyPress:
            # FIXME: setValue() does not update the scrollbars correctly in
            # some pyqt versions.
            if event.key() == QtCore.Qt.Key_Up:
                self.onKeyUp()
            elif event.key() == QtCore.Qt.Key_Down:
                self.onKeyDown()
            elif event.key() == QtCore.Qt.Key_Home:
                self.onKeyHome()
            elif event.key() == QtCore.Qt.Key_End:
                self.onKeyEnd()
            elif event.key() == QtCore.Qt.Key_PageUp:
                self.onKeyPageUp()
            elif event.key() == QtCore.Qt.Key_PageDown:
                self.onKeyPageDown()
        elif event.type() == QEvent.Wheel:
            self.vScrollBar.wheelEvent(event)
            return True
        #return False
        return super(GenericTableView, self).eventFilter(obj, event)


# __author__: Mai feng
# __file_name__: profile.py
# __time__: 2019:05:01:20:57

from medicine.api_1_0 import api
from flask import jsonify, session, current_app, request, g
from medicine.utils.response_code import RET
from medicine.utils.common import login_required
from medicine.models import User, Expert
from medicine import db, constants

@api.route('users')
@login_required
def get_user_info():
    '''获取用户信息
        前提是已经登陆
    :return: json
    '''
    user_id = g.user_id
    try:
        user = User.query.filter(User.id == user_id).first()
    except Exception as e:
        current_app.logger.debug(e)
        return jsonify(re_code=RET.DBERR, msg='数据库查询错误')

    if not user:
        return jsonify(re_code=RET.NODATA, msg='用户不存在')

    # 查询用户信息
    user_info = user.to_dict()
    return jsonify(re_code=RET.OK, msg='查询成功', user=user_info)

@api.route('experts')
@login_required
def get_expert_info():
    '''获取专家信息
    :return: json
    '''
    user_id = g.user_id
    try:
        user = Expert.query.filter(Expert.id == user_id).first()
    except Exception as e:
        current_app.logger.debug(e)
        return jsonify(re_code=RET.DBERR, msg='数据库查询错误')

    if not user:
        return jsonify(re_code=RET.NODATA, msg='用户不存在')

    # 查询用户信息
    user_info = user.to_dict()
    return jsonify(re_code=RET.OK, msg='查询成功', expert=user_info)


import numpy as np
import pandas as pd
from scipy.integrate import odeint
from scipy import stats, integrate
import pickle
from dedalus import public as de
from dedalus.extras.plot_tools import plot_bot_2d
from dedalus.extras import *
import matplotlib.pyplot as plt
import seaborn as sns

np.seterr(over='ignore');
#Suppress most Dedalus output
de.logging_setup.rootlogger.setLevel('ERROR')

import matplotlib
matplotlib.rcParams['pdf.fonttype']=42
matplotlib.rcParams['ps.fonttype']=42
matplotlib.rcParams['font.sans-serif']="Arial"
matplotlib.rcParams['font.family']="sans-serif"
# rcParams['font.sans-serif'] = ['Tahoma']
matplotlib.rcParams.update({'font.size': 16})

#need to scale by GTPase concentration scaling factor (200)
AVG_SF = pickle.load(open("PickleFiles/Avg_R2_noscale_128.pickle", "rb"))*200
STD_SF = pickle.load(open("PickleFiles/STDev_R2_noscale_128.pickle", "rb"))*200

#Scale list between 0 and 1
def scale(A):
    return (A-np.min(A))/(np.max(A) - np.min(A))

# steady state ODE
def ssODE(y,t,params):
    u,v,g,G = y
    b,gamma,n,delta,e,c,d,K = params

    du = b*v+v*gamma*u**n/(K**n+u**n)-delta*u-e*G*u
    dv = -du

    dG = c*u*g-d*G
    dg = - dG

    derivs = [du,dv,dg,dG]
    return derivs

#determine homogenous SS using ssODE
def homogenousSS(u,v,g,G,params):

    y0 = (u,v,g,G)
    t_sim = (0,1000)
    odeSoln = integrate.odeint(ssODE,y0,t_sim,args=(params,),mxstep=1000000)

    return(odeSoln[1])

def logistic_decay(x,p_min,p_max,k,x0=1.75):
    #positive k -> decay
    p_amp = p_max - p_min
    return p_min+p_amp/(1+np.exp(k*(x-x0)))

def WPGAP(params,plot_bool=False,title='None',mu_F = 1.75):
    c_s,c_p,c_m,gamma_s,gamma_p,gamma_m,e,d = params

    # Fixed parameters
    b = 0.002
#     c_s = 1/200
#     gamma_s = 0.005
    delta = 0.4

    #Bases:names,modes,intervals,dealiasing
    phi_basis=de.Fourier('p',256,interval=(0,2*np.pi),dealias=3/2)
    r_basis=de.Chebyshev('r',128,interval=(0,4),dealias=3/2)
    #Domain:bases,datatype
    domain=de.Domain([phi_basis,r_basis],float)
    phi, r = domain.grids(scales=1)

    c = domain.new_field(name='c')
    gamma = domain.new_field(name='gamma')

    c['g'] = logistic_decay(r[0],c_s,c_p,c_m,x0=mu_F)
    gamma['g'] = logistic_decay(r[0],gamma_s,gamma_p,gamma_m,x0=mu_F)

    c.meta['p']['constant'] = True
    gamma.meta['p']['constant'] = True

    T = 808
    Tg = 10
    n = 2
    K = 200

    Du = .04
    Dv = 100*Du
    DG = 100*Du #40
    Dg = 100*Du

    params = [b,np.min(gamma['g']),n,delta,e,np.min(c['g']),d,K]
    u0,v0,g0,G0 = homogenousSS(T/2,T/2, Tg/2, Tg/2,params)

    # Specify problem
    problem = de.IVP(domain, variables=['u', 'v','ur','vr','G','g','Gr','gr'])

    problem.parameters['gamma'] = gamma
    problem.parameters['b'] = b
    problem.parameters['n'] = n
    problem.parameters['u0'] = u0
    problem.parameters['v0'] = v0
    problem.parameters['G0'] = G0
    problem.parameters['g0'] = g0
    problem.parameters['c'] = c
    problem.parameters['dd'] = d
    problem.parameters['delta'] = delta
    problem.parameters['e'] = e
    problem.parameters['K'] = K
    problem.parameters['Tg'] = Tg

    problem.parameters['Du'] = Du
    problem.parameters['Dv'] = Dv
    problem.parameters['DG'] = DG
    problem.parameters['Dg'] = Dg

    problem.substitutions['f(u,v,G)'] = 'b*v+v*gamma*u**n/(K**n+u**n)-delta*u-e*G*u'
    problem.substitutions['minf(u,v,G)'] = '-f(u,v,G)'
    problem.substitutions['fg(u,G,g)'] = 'c*u*g-dd*G'
    problem.substitutions['minfg(u,G,g)'] = '-fg(u,G,g)'

    problem.add_equation("r**2*dt(u)-r**2*Du*dr(ur)-r*Du*dr(u)-Du*dp(dp(u))=r**2*f(u,v,G)")
    problem.add_equation("r**2*dt(v)-r**2*Dv*dr(vr)-r*Dv*dr(v)-Dv*dp(dp(v))=r**2*minf(u,v,G)")
    problem.add_equation("r**2*dt(G)-r**2*DG*dr(Gr)-r*DG*dr(G)-DG*dp(dp(G))=r**2*fg(u,G,g)")
    problem.add_equation("r**2*dt(g)-r**2*Dg*dr(gr)-r*Dg*dr(g)-Dg*dp(dp(g))=r**2*minfg(u,G,g)")

    problem.add_equation("ur-dr(u)=0")
    problem.add_equation("vr-dr(v)=0")
    problem.add_equation("Gr-dr(G)=0")
    problem.add_equation("gr-dr(g)=0")

    #Reflective boundary conditions

    problem.add_bc("left (ur) = 0")
    problem.add_bc("right (ur) = 0")
    problem.add_bc("left (vr) = 0")
    problem.add_bc("right (vr) = 0")
    problem.add_bc("left (Gr) = 0")
    problem.add_bc("right (Gr) = 0")
    problem.add_bc("left (gr) = 0")
    problem.add_bc("right (gr) = 0")

    # Pick a timestepper
    ts = de.timesteppers.RK443 #443
    # Build solver
    solver = problem.build_solver(ts)
    # Set integration limits
    solver.stop_wall_time = np.inf
    solver.stop_sim_time = np.inf
    solver.stop_iteration = np.inf
    # Set initial conditions
    u = solver.state ['u']
    v = solver.state['v']
    G = solver.state ['G']
    g = solver.state['g']
    # u_seed = pickle.load( open( "PR_9Dots.pickle", "rb" ) )
    u_seed = pickle.load( open( "PickleFiles/PR_256x128.pickle", "rb" ) )[2]
    u_seed_norm = u_seed/np.max(u_seed)
    urand = 0.1*v0*np.random.rand(*u['g'].shape) + 0.1*v0*u_seed_norm

    u['g'] = u0+urand
    v['g'] = v0-urand
    G['g'] = G0*np.ones(G['g'].shape)
    g['g'] = g0*np.ones(g['g'].shape)

    solver.stop_iteration = 400

    dt =  0.025
    nonan = True
    # Main loop chceking stopping criteria
    while solver.ok and nonan:
        # Step forward
        solver.step(dt)

        if solver.iteration % 50 == 0:
            if np.count_nonzero(np.isnan(u['g'])) > 0 or np.min(u['g']) < 0 :
                return('Numerical Error')
                nonan = False

    usim = u['g'].T

#     phi, r = domain.grids(scales=domain.dealias)
#     phi = np.vstack((phi,2*np.pi))
#     phi,r = np.meshgrid(phi,r)
#     z = np.vstack((u['g'],u['g'][0])).T
#     fig = plt.figure(figsize=(4,4)) #figsize = (18,6))
#     # ax = Axes3D(fig)

#     plt.subplot(projection="polar")

#     plt.pcolormesh(phi,r,z,shading='auto',vmin=0.2*200, vmax = .8*200)

#     plt.plot(phi, r, color='k', ls='none')
# #         plt.legend(['t={}'.format(curr_t)],framealpha=0)
#     plt.xticks([])
#     plt.yticks([])
#     if title != 'None':
#         plt.title(title)
#     cbar = plt.colorbar(fraction=0.046, pad=0.04)

#     cbar.ax.get_yaxis().labelpad = 15
#     cbar.ax.set_ylabel('[u]',rotation=0)

    # plt.savefig('../Figures/Sweep_%s.png'%title,bbox_inches='tight',dpi=300)

    avg_curr = []
    q1_std, q2_std , q3_std , q4_std,q5_std, q6_std , q7_std , q8_std = [],[],[],[],[],[],[],[]
    inc = len(usim[0])//8
    for i in range(len(usim)):
        avg_curr.append(np.mean(usim[i]))
        q1_std.append(np.std(usim[i][0:inc]))
        q2_std.append(np.std(usim[i][inc:2*inc]))
        q3_std.append(np.std(usim[i][2*inc:3*inc]))
        q4_std.append(np.std(usim[i][3*inc:4*inc]))
        q5_std.append(np.std(usim[i][4*inc:5*inc]))
        q6_std.append(np.std(usim[i][5*inc:6*inc]))
        q7_std.append(np.std(usim[i][6*inc:7*inc]))
        q8_std.append(np.std(usim[i][7*inc:8*inc]))
    q1_std = np.array(q1_std)
    q2_std = np.array(q2_std)
    q3_std = np.array(q3_std)
    q4_std = np.array(q4_std)
    q5_std = np.array(q5_std)
    q6_std = np.array(q6_std)
    q7_std = np.array(q7_std)
    q8_std = np.array(q8_std)

    avg_curr = np.array(avg_curr)

    def mse(A,B):
        A = A[~np.isnan(A)]
        B = B[~np.isnan(B)]
        return(np.mean(np.subtract(A,B)**2))

    def mae(A,B):
        A = A[~np.isnan(A)]
        B = B[~np.isnan(B)]
        return(np.mean(np.abs(np.subtract(A,B))))

    try:
        total_error = np.sum([mae(AVG_SF[1],avg_curr),
                            mae(STD_SF[1],q1_std),mae(STD_SF[1],q2_std),mae(STD_SF[1],q3_std),mae(STD_SF[1],q4_std),
                            mae(STD_SF[1],q5_std),mae(STD_SF[1],q6_std),mae(STD_SF[1],q7_std),mae(STD_SF[1],q8_std)])
    except:
        print('Error')
        return(avg_curr, q1_std)

    return [np.array(u['g'].T), total_error]

c_s = 1/200
gamma_s = 0.0005

c_p,c_m,gamma_p,gamma_m,e,d = [ 15.8414687/200, 12.89118133,  9.6046689, 2.04555275,  31.3967828, 42.980938]

param_set = [c_s, c_p,c_m,gamma_s,gamma_p,gamma_m,e,d]

## Sweep individual parameters

param_name = 'c_max'
param_i = 1
vals = np.linspace(8/200,26/200,11)

# param_name = 'c_km'
# param_i = 2
# vals = np.linspace(10,18,15)

# param_name = 'gam_max'
# param_i = 4
# vals = np.linspace(8.5,11,11)

#param_name = 'gam_km'
#param_i = 5
#vals = np.linspace(1.7,2.3,11)

#param_name = 'e'
#param_i = 6
#vals = np.linspace(24,36,11)

#param_name = 'd'
#param_i = 7
#vals = np.linspace(36,50,11)

errors = []
for val in vals:
    curr_param_set = np.copy(param_set)
    curr_param_set[param_i] = val

    try:
        u,error =WPGAP(curr_param_set,plot_bool=True,title='%s%.2f'%(param_name,val))
        errors.append(error)
    except:
        print('Error %s = %.2f'%(param_name,val))
        errors.append(100)

# plt.figure(figsize=(4,3))
# plt.scatter(vals,errors)
# plt.ylabel('Score')
# plt.xlabel(param_name)
# plt.ylim([0,60])
# plt.savefig('../Figures/Sweep_Errors%s.png'%(param_name),bbox_inches='tight',dpi=300)

# pickle.dump(np.array([vals,errors]),open('../Figures/%s_errors.pickle'%param_name,'wb'))


from colorama import Fore, init, Style
init(autoreset = True)

# Kelas untuk membuat Node / simpul
class Node:
	def __init__(self, data):
		self.data = data
		self.next = None

# Kelas untuk membuat linked list (6 fungsi)
class linked_list:
	def __init__(self):
		self.head = None
		self.last = self.head

	# Masukkan data baru di awal linked list
	def add_beg(self):
		Error = True
		while Error is True:
			try:
				jumlah = int(input("Jumlah data yang akan dimasukkan : "))
			except ValueError:
				print(f"{Fore.RED}Hanya masukkan bilangan bulat\n")
			else:
				Error = False
				for x in range(jumlah):
					data = input(f"\nMasukkan data ke - {x+1} untuk disisipkan di awal linked list : ")
					if self.head is None:
						self.head = Node(data)
						self.last = self.head
					else:
						new = Node(data)
						new.next = self.head
						self.head = new
					print(f"{Fore.GREEN}Data '{data}' berhasil dimasukkan")
		menu()

	# Masukkan data baru di akhir linked list
	def add_last(self):
		Error = True
		while Error is True:
			try:
				jumlah = int(input("Jumlah data yang akan dimasukkan : "))
			except ValueError:
				print(f"{Fore.RED}Hanya masukkan bilangan bulat\n")
			else:
				Error = False
				for x in range(jumlah):
					data = input(f"\nMasukkan data ke - {x+1} untuk disisipkan di akhir linked list : ")
					if self.head is None:
						self.head = Node(data)
						self.last = self.head
					else:
						new = Node(data)
						self.last.next = new
						# while search.next is not None:
						# 	search = search.next
						# last_node.pointer = new
						self.last = new
					print(f"{Fore.GREEN}Data '{data}' berhasil dimasukkan")
		menu()

	# Tampilkan linked list
	def print_list(self):
		jumlah = 0
		temp = self.head
		if temp is not None:
			while temp is not None:
				if temp.next is not None:
					print(temp.data, end=" -> ")
				else:
					print(temp.data)
				temp = temp.next
				jumlah += 1
			print(f"\nJumlah elemen = {jumlah}")
		else:
			print(f"{Fore.RED}Linked list kosong")
		menu()

	# Cari data tertentu dalam linked list
	def find_node(self, data):
		temp = self.head
		index = 1
		found = False
		while temp is not None:
			# if temp.data.lower().strip() == data.lower().strip():
			if temp.data == data:
				found = True
				break
			else:
				temp = temp.next
				index += 1
		if found is True:
			# print(f"{Fore.GREEN}data '{temp.data.strip()}' ditemukan pada urutan ke - {index}")
			print(f"{Fore.GREEN}data '{temp.data}' ditemukan pada urutan ke - {index}")
		else:
			# print(f"{Fore.RED}data '{data.strip()}' tidak ditemukan")
			print(f"{Fore.RED}data '{data}' tidak ditemukan")
		return(found)

	# Menghapus Node atau data dalam linked list
	def delete_node(self):
		if self.head is None:
			print(f"{Fore.RED}Linked list kosong")
		else:
			data = input("Masukkan data yang ingin dihapus : ")
			if linked_list.find_node(self, data) is True:
				temp = self.head
				Found = False
				while temp is not None:
					# if temp.data.lower().strip() == data.lower().strip():
					if temp.data == data:
						self.head = temp.next
						found = True
						break
					# elif temp.next.data.lower().strip() == data.lower().strip():
					elif temp.next.data == data:
						if temp.next == self.last:
							self.last = temp
						temp.next = temp.next.next
						found = True
						break
					else:
						temp = temp.next
				if found is True:
					print(f"{Fore.GREEN}Data '{data}' berhasil dihapus")
				# else:
				# 	print(f"{Fore.RED}Data '{data}' tidak ditemukan")
		menu()

	def DELETE(self):
		temp = self.head
		while(temp is not None):
			temp = temp.next
			del self.head
			self.head = temp

# Fungsi untuk print menu
def menu():
	print("\n^^^ Pilihan Menu ^^^")
	print("1. Tambahkan data di akhir")
	print("2. Tambahkan data di awal")
	print("3. Print linked list")
	print("4. Cari data / elemen")
	print("5. Hapus data / elemen")
	print("6. Kosongkan linked list")
	print("7. Keluar")


from django.shortcuts import render
from rest_framework import serializers
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework.renderers import JSONRenderer
from.serializers import shopSerilizer
from.models import shopPet
# Create your views here.

@api_view(['GET'])
def apiOverview(request):
    api_urls = {
        'List': '/Shop-list/',
        'Detail View': '/Shop-detail/<int:id>/',
        'Create': '/Shop-create/',
        'Update': '/Shop-update/<int:id>/',
        'Delete': '/Shop-detail/<int:id>/',
    }
    return Response(api_urls)

@api_view(['GET'])
def ShowAll(request):
    products = shopPet.objects.all()
    print(products)
    serializers = shopSerilizer(products,many=True)
    return Response(serializers.data)

@api_view(['GET'])
def ViewProduct(request,pk):
    products = shopPet.objects.get(id=pk)
    serializers = shopSerilizer(products,many=False)
    return Response(serializers.data)

@api_view(['POST'])
def CreateProduct(request):
    serializers = shopSerilizer(data=request.data)

    if serializers.is_valid():
        serializers.save()

    return Response(serializers.data)

@api_view(['POST'])
def updateProduct(request,pk):
    product = shopPet.objects.get(id=pk)
    serializers =shopSerilizer(instance=product, data=request.data)
    if serializers.is_valid():
        serializers.save()
    return Response(serializers.data)

@api_view(['GET'])
def deleteProduct(request,pk):
    product = shopPet.objects.get(id=pk)
    product.delete()

    return Response('Item delete successfully')


import argparse
import jinja2
import ProtoParser

def main():

    arg_parser = argparse.ArgumentParser(description = 'PacketGenerator')
    arg_parser.add_argument('--path', type=str, default='C:/Users/user/Desktop/Education/Portfolio/GameServer/Server/Common/Protobuf/bin/Protocol.proto', help='proto path')
    arg_parser.add_argument('--output', type=str, default='TestPacketHandler', help='output file')
    arg_parser.add_argument('--recv', type=str, default='CS_', help='recv convention')
    arg_parser.add_argument('--send', type=str, default='SC_', help='send convention')
    args = arg_parser.parse_args()

    parser = ProtoParser.ProtoParser(1000, args.recv, args.send)
    parser.parse_proto(args.path)

    #jinja2
    file_loader = jinja2.FileSystemLoader("Template")
    env = jinja2.Environment(loader = file_loader)

    template = env.get_template('PacketHandler.template')
    output = template.render(parser=parser, output=args.output)

    f = open(args.output+'.h', 'w+')
    f.write(output)
    f.close()

    print(output)

    return

if __name__ == '__main__':
    main()


#!/usr/bin/env python

import rospy
from sensor_msgs.msg import LaserScan
from lidar_reader.msg import lidar_data
import math.pi as pi

'''Lidar
This module is unused in the current code. But please add lidar related script to here
if lidar data were to be polished
'''

pub_lidar = rospy.Publisher('/laserSimp', GPS_data, queue_size=1)
lidar_msg = lidar_data()

def laserCallb(msg):
    global pub_lidar, lidar_msg
    inc = msg.angle_increment
    min = msg.angle_min
    i_left = -(pi/2 + min) / inc
    i_right = (pi/2 - min) / inc
    left = msg.ranges[i_left]
    right = msg.ranges[i_right]

    lidar_msg.left = left
    lidar_msg.right = right
    pub_lidar.publish(lidar_msg)

def main():
    rospy.init_node('lidar_reader', anonymous=True)
    rospy.Subscriber("/scan", LaserScan, laserCallb)
    rospy.spin()

if __name__ == '__main__':
    main()


import json

import argparse
from yahoo import Yahoo

from utils import utils, data

def run():
    argparser = argparse.ArgumentParser(description='Yahoo Finance Historical Data')
    argparser.add_argument("-s", "--symbol", help="stock symbol", required=True)
    argparser.add_argument("--save", action='store_true', help="save data")

    args = argparser.parse_args()
    symbol = args.symbol

    yahoo = Yahoo()
    df = yahoo.request_historical_data(symbol, 'max')

    json = df.to_json(orient='index')
    print(json)

    if args.save:
        path = utils.file_path(__file__)
        name = utils.file_name(__file__)

        csv = True
        extension = f'.{"csv" if csv else "pkl"}'

        file = f'{path}/{name}/{symbol}{extension}'
        data.df_write(df, file, verbose=True)

run()


import requests
import json
import time
from dataclasses import dataclass
from kafka import KafkaProducer
from datetime import datetime

@dataclass
class BikeSensor5Min:
    ts: str
    id: int
    name: str
    count: int
    time: str
    coordinateType: str
    coordinates: list

# class BikeSensor5Min:

def get_batch_data_5_min():
    # while True:
    resp = requests.get("https://iot.hamburg.de/v1.1/Things?$filter=Datastreams/properties/serviceName%20eq%20%27HH_STA_HamburgerRadzaehlnetz%27%20and%20Datastreams/properties/layerName%20eq%20%27Anzahl_Fahrraeder_Zaehlfeld_5-Min%27&$count=true&$expand=Datastreams($filter=properties/layerName%20eq%20%27Anzahl_Fahrraeder_Zaehlfeld_5-Min%27;$expand=Observations($orderby=phenomenonTime%20desc))")
    # resp = requests.get("https://iot.hamburg.de/v1.1/Things?$filter=Datastreams/properties/serviceName%20eq%20%27HH_STA_HamburgerRadzaehlnetz%27%20and%20Datastreams/properties/layerName%20eq%20%27Anzahl_Fahrraeder_Zaehlstelle_15-Min%27&$count=true&$expand=Datastreams($filter=properties/layerName%20eq%20%27Anzahl_Fahrraeder_Zaehlstelle_15-Min%27;$expand=Observations($top=10;$orderby=phenomenonTime%20desc))")
    response = json.loads(resp.content)
    done = False
    cnt = 0
    sensors = []
    phenomenaTime = ""
    epoch_time = datetime(1970, 1, 1)
    while(not done):
        if resp.status_code == 200:
            for feature in response['value']:
                for stream in feature['Datastreams']:
                    batch_link = 'https://iot.hamburg.de/v1.1/Datastreams(' + str(stream['@iot.id']) + ')/Observations?$skip=0&$orderby=phenomenonTime+desc'

                    f = open("safestate.txt", "r")
                    if str(stream['@iot.id']) in f.read():
                        print("skip")
                        continue

                    batch_resp = requests.get(batch_link, timeout=30)
                    batch_response = json.loads(batch_resp.content)

                    batch_done = False
                    if batch_resp.status_code == 200:
                        while not batch_done:
                            for result_value in batch_response['value']:
                                date = datetime.strptime(result_value['phenomenonTime'].split('/')[0].split('Z')[0], "%Y-%m-%dT%H:%M:%S.%f")
                                bikesensor = BikeSensor5Min((date - epoch_time).total_seconds, feature['@iot.id'], feature['name'], result_value['result'], result_value['phenomenonTime'], stream['observedArea']['type'], stream['observedArea']['coordinates'])
                                sensors.append(bikesensor)
                                date = datetime.strptime(result_value['phenomenonTime'].split('/')[0].split('Z')[0], "%Y-%m-%dT%H:%M:%S.%f")
                                cnt += 1
                            if cnt %  10000 == 0:
                                print(cnt)
                            if '@iot.nextLink' not in batch_response:
                                batch_done = True
                            else:
                                try:
                                    batch_done = True
                                    batch_resp = requests.get(batch_response['@iot.nextLink'], timeout=30)
                                except requests.exceptions.Timeout:
                                    print("TIMEOUT")

                                batch_response = json.loads(batch_resp.content)
                    else:
                        print("BATCHREQUEST NOT OK")

                    data_dict = [sensor.__dict__ for sensor in sensors]
                    sensors = []
                    # print(data_dict)
                    producer = KafkaProducer(bootstrap_servers='localhost:9092')
                    topic = 'bikes_5_min'

                    for bike_data in data_dict:
                        # pass
                        # send every hour data as single message to kafka
                        json_data = json.dumps(bike_data)
                        # print(json_data)
                        future = producer.send(topic, bike_data)

                    f = open("safestate.txt", "a")
                    f.write(str(stream['@iot.id']) + " \n")
                    f.close()
        else:
            print("REQUEST NOT OK")
        if '@iot.nextLink' not in response:
            done = True
        else:
            done = True
            resp = requests.get(response['@iot.nextLink'])
            response = json.loads(resp.content)

    print("cnt ",cnt)
    # data_dict = [sensor.__dict__ for sensor in sensors]
    # # print(data_dict)
    # producer = KafkaProducer(bootstrap_servers='localhost:9092')
    # topic = 'bikes_5_min'

    # for bike_data in data_dict:
    #     # pass
    #     # send every hour data as single message to kafka
    #     json_data = json.dumps(bike_data)
    #     # print(json_data)
    #     future = producer.send(topic, bike_data)

if __name__ == "__main__":
    get_batch_data_5_min()
        # time.sleep(60 * 5)


def find_discounted(v):
    if not v: return ''
    v = sorted(map(int, v.split(' ')))
    used = {}
    r = []
    for i, x in enumerate(v):
        z = int(x * .75)
        for j, y in enumerate(v):
            if i != j and i not in used and j not in used and y == z:
                r.append(y)
                used[i] = 1
                used[j] = 1
                break
    return ' '.join(map(str, r))

codewars/6kyu/6_Find the discounted prices.py


#------------------------------------------------------------------------------
# Title: Graphing Calculator
# Assignment: Final Project
# Date: 16/05/23
# Student: Evan Smart
# Version: 1.0
#------------------------------------------------------------------------------
"""
This is the main code for the use of the different calculator functions
"""

# Importing external files for each operation
import addition
import multiplication
import subtraction
import division
import graphing

# prints a message stating what kind of functions the calculator has
print(""" This graphing calculator can run addition, subtraction, multiplication, division, and graphing!
The calculator does not compute decimals, so please stick to whole numbers.
""")

# main code for when user chooses a function
while True:
    operation = input("What function would you like to use: ").lower()
    if operation == "addition":
        print("""
        To use addition:
        Enter the numbers you'd like to add with a space between each
        """)
        addition.Addition()
    elif operation == "multiplication":
        print("""
        To use multiplication:
        Enter the numbers you'd like to multiply with a space between each
        """)
        multiplication.Multiply()
    elif operation == "subtraction":
        subtraction.Subtraction()
    elif operation == "division":
        division.Division()
    elif operation == "graphing":
        graphing.Graphing()
    elif operation == "quit":
        print("Closing calculator")
        break
    else:
        print(f"Invalid Option: {operation}")


import boto3,time

client = boto3.client('ec2')

response = client.describe_instances()
GroupIds = []
for Dict in response['Reservations']:
    for Instances in Dict['Instances']:
        for SecurityGroups in Instances['SecurityGroups']:
            GroupIds.append(SecurityGroups['GroupId'])

for item in GroupIds:
    print(f"Checking the security group {item} for open port 22")
    time.sleep(2)
    response = client.describe_security_groups(
        GroupIds=[
            item
        ]
    )
    for SGs in response['SecurityGroups']:
        if SGs['IpPermissions']:
            for IpInfo in SGs['IpPermissions']:
                if IpInfo['FromPort'] == 22:
                    for IP in IpInfo['IpRanges']:
                        if IP['CidrIp'] == '0.0.0.0/0':
                            print(f"{item}'s port 22 is open to the internet!")
                            print("Revoking port 22 access")
                            response = client.revoke_security_group_ingress(
                                CidrIp='0.0.0.0/0',
                                FromPort=22,
                                GroupId=item,
                                IpProtocol='tcp',
                                ToPort=22,
                            )
                            print()
                        else:
                            print(f"{item} has a port 22 rule set up, but it is not open to the internet")
                            print()
                else:
                    print(f"{item} does not have any port 22 rules set up")
                    print()
        else:
            print(f"{item} does not have any ingress rules")
            print()


# LabelFrame is the combination of label and frame

from tkinter import *
from tkinter import filedialog # -> filedialog is used here to ask user where to download & store the video
from tkinter.ttk import *
from tkinter.messagebox import * # -> messagebox is used to display info,error messages or warnings
from pytube import YouTube   # -> pytube is the module which helps in downloading Youtube video

class finalDownloader:
    def __init__(self):
        self.root = Tk()
        self.root.geometry('600x600')
        self.root.title('YouTube Video Downloader')
        self.lb = LabelFrame(self.root, text='Choose Video', height=550, width=550)
        self.f = Frame(self.lb)
        self.f.pack(pady=10)
        Label(self.f, text='Enter URL', font=('arial',12)).grid(row=0, column=0, padx=5, pady=20)
        self.searchBar = Entry(self.f, width=45)
        self.searchBar.grid(row=0, column=1, padx=5, pady=20)
        Button(self.f, text='Search', command=self.stepTwo).grid(row=0, column=2, padx=5, pady=20)
        self.lb.pack(pady=20, padx=20, fill=BOTH)
        self.root.mainloop()

    def stepTwo(self):
        self.yt = YouTube(self.searchBar.get(), on_progress_callback=self.makeProgress)
        avail_res = self.get_resolutions()
        self.label = Label(self.lb, text=f"Title :- {self.yt.title}", font=('arial',20))
        self.label.pack(pady=10)
        self.f1 = Frame(self.lb)
        Label(self.f1, text='Select Resolution', font=('arial',12)).grid(row=0, column=0, padx=5, pady=20)
        self.selected_res = Combobox(self.f1, width=42, values=avail_res, state='readonly')
        self.selected_res.grid(row=0, column=1, padx=5, pady=20)
        Button(self.f1, text='Download', command=self.downloadVideo).grid(row=0, column=2, padx=5, pady=20)
        self.f1.pack(pady=10)

    def get_resolutions(self):
        # yt = YouTube(self.searchBar.get())
        all_res = ['144p','240p','360p','480p','720p','1080p','2160p']
        x = []
        for i in all_res:
            a = self.yt.streams.filter(res=i, type='video', progressive=True)
            print(a)
            if len(a)>0:
                x.append(i)
        return x

    def downloadVideo(self):
        location = filedialog.askdirectory()
        self.progressbar = Progressbar(self.lb, orient = HORIZONTAL,length = 300, mode = 'determinate')
        self.progressbar.pack(pady=20)
        # self.label1 = Label()
        self.selected_stream = self.yt.streams.filter(res=self.selected_res.get(), type='video', progressive=True).first()
        self.selected_stream.download(output_path=location)
        showinfo('','Video Downloaded Successfully')
        self.f1.destroy()
        self.label.destroy()
        self.searchBar.delete(0,END)
        self.progressbar.destroy()

    def makeProgress(self, chunk,file_handle, bytes_remaining):
        size = self.selected_stream.filesize

        progress = float(abs(bytes_remaining - size)/size) * float(100)
        print(progress)
        self.progressbar['value'] = int(progress)
        # self.label1.config(text=f'{progress}%')
        self.root.update_idletasks()

finalDownloader()


import base64
import hashlib
import hmac
import struct

import thirdparty.dns.exception
import thirdparty.dns.rdataclass
import thirdparty.dns.name
import thirdparty.dns.rcode

class BadTime(thirdparty.dns.exception.DNSException):

    """The current time is not within the TSIG's validity time."""

class BadSignature(thirdparty.dns.exception.DNSException):

    """The TSIG signature fails to verify."""

class BadKey(thirdparty.dns.exception.DNSException):

    """The TSIG record owner name does not match the key."""

class BadAlgorithm(thirdparty.dns.exception.DNSException):

    """The TSIG algorithm does not match the key."""

class PeerError(thirdparty.dns.exception.DNSException):

    """Base class for all TSIG errors generated by the remote peer"""

class PeerBadKey(PeerError):

    """The peer didn't know the key we used"""

class PeerBadSignature(PeerError):

    """The peer didn't like the signature we sent"""

class PeerBadTime(PeerError):

    """The peer didn't like the time we sent"""

class PeerBadTruncation(PeerError):

    """The peer didn't like amount of truncation in the TSIG we sent"""

# TSIG Algorithms

HMAC_MD5 = thirdparty.dns.name.from_text("HMAC-MD5.SIG-ALG.REG.INT")
HMAC_SHA1 = thirdparty.dns.name.from_text("hmac-sha1")
HMAC_SHA224 = thirdparty.dns.name.from_text("hmac-sha224")
HMAC_SHA256 = thirdparty.dns.name.from_text("hmac-sha256")
HMAC_SHA384 = thirdparty.dns.name.from_text("hmac-sha384")
HMAC_SHA512 = thirdparty.dns.name.from_text("hmac-sha512")

_hashes = {
    HMAC_SHA224: hashlib.sha224,
    HMAC_SHA256: hashlib.sha256,
    HMAC_SHA384: hashlib.sha384,
    HMAC_SHA512: hashlib.sha512,
    HMAC_SHA1: hashlib.sha1,
    HMAC_MD5: hashlib.md5,
}

default_algorithm = HMAC_SHA256

def _digest(wire, key, rdata, time=None, request_mac=None, ctx=None,
            multi=None):
    """Return a context containing the TSIG rdata for the input parameters
    @rtype: hmac.HMAC object
    @raises ValueError: I{other_data} is too long
    @raises NotImplementedError: I{algorithm} is not supported
    """

    first = not (ctx and multi)
    if first:
        ctx = get_context(key)
        if request_mac:
            ctx.update(struct.pack('!H', len(request_mac)))
            ctx.update(request_mac)
    ctx.update(struct.pack('!H', rdata.original_id))
    ctx.update(wire[2:])
    if first:
        ctx.update(key.name.to_digestable())
        ctx.update(struct.pack('!H', thirdparty.dns.rdataclass.ANY))
        ctx.update(struct.pack('!I', 0))
    if time is None:
        time = rdata.time_signed
    upper_time = (time >> 32) & 0xffff
    lower_time = time & 0xffffffff
    time_encoded = struct.pack('!HIH', upper_time, lower_time, rdata.fudge)
    other_len = len(rdata.other)
    if other_len > 65535:
        raise ValueError('TSIG Other Data is > 65535 bytes')
    if first:
        ctx.update(key.algorithm.to_digestable() + time_encoded)
        ctx.update(struct.pack('!HH', rdata.error, other_len) + rdata.other)
    else:
        ctx.update(time_encoded)
    return ctx

def _maybe_start_digest(key, mac, multi):
    """If this is the first message in a multi-message sequence,
    start a new context.
    @rtype: hmac.HMAC object
    """
    if multi:
        ctx = get_context(key)
        ctx.update(struct.pack('!H', len(mac)))
        ctx.update(mac)
        return ctx
    else:
        return None

def sign(wire, key, rdata, time=None, request_mac=None, ctx=None, multi=False):
    """Return a (tsig_rdata, mac, ctx) tuple containing the HMAC TSIG rdata
    for the input parameters, the HMAC MAC calculated by applying the
    TSIG signature algorithm, and the TSIG digest context.
    @rtype: (string, hmac.HMAC object)
    @raises ValueError: I{other_data} is too long
    @raises NotImplementedError: I{algorithm} is not supported
    """

    ctx = _digest(wire, key, rdata, time, request_mac, ctx, multi)
    mac = ctx.digest()
    tsig = thirdparty.dns.rdtypes.ANY.TSIG.TSIG(thirdparty.dns.rdataclass.ANY, thirdparty.dns.rdatatype.TSIG,
                                     key.algorithm, time, rdata.fudge, mac,
                                     rdata.original_id, rdata.error,
                                     rdata.other)

    return (tsig, _maybe_start_digest(key, mac, multi))

def validate(wire, key, owner, rdata, now, request_mac, tsig_start, ctx=None,
             multi=False):
    """Validate the specified TSIG rdata against the other input parameters.

    @raises FormError: The TSIG is badly formed.
    @raises BadTime: There is too much time skew between the client and the
    server.
    @raises BadSignature: The TSIG signature did not validate
    @rtype: hmac.HMAC object"""

    (adcount,) = struct.unpack("!H", wire[10:12])
    if adcount == 0:
        raise thirdparty.dns.exception.FormError
    adcount -= 1
    new_wire = wire[0:10] + struct.pack("!H", adcount) + wire[12:tsig_start]
    if rdata.error != 0:
        if rdata.error == thirdparty.dns.rcode.BADSIG:
            raise PeerBadSignature
        elif rdata.error == thirdparty.dns.rcode.BADKEY:
            raise PeerBadKey
        elif rdata.error == thirdparty.dns.rcode.BADTIME:
            raise PeerBadTime
        elif rdata.error == thirdparty.dns.rcode.BADTRUNC:
            raise PeerBadTruncation
        else:
            raise PeerError('unknown TSIG error code %d' % rdata.error)
    if abs(rdata.time_signed - now) > rdata.fudge:
        raise BadTime
    if key.name != owner:
        raise BadKey
    if key.algorithm != rdata.algorithm:
        raise BadAlgorithm
    ctx = _digest(new_wire, key, rdata, None, request_mac, ctx, multi)
    mac = ctx.digest()
    if not hmac.compare_digest(mac, rdata.mac):
        raise BadSignature
    return _maybe_start_digest(key, mac, multi)

def get_context(key):
    """Returns an HMAC context foe the specified key.

    @rtype: HMAC context
    @raises NotImplementedError: I{algorithm} is not supported
    """

    try:
        digestmod = _hashes[key.algorithm]
    except KeyError:
        raise NotImplementedError(f"TSIG algorithm {key.algorithm} " +
                                  "is not supported")
    return hmac.new(key.secret, digestmod=digestmod)

class Key:
    def __init__(self, name, secret, algorithm=default_algorithm):
        if isinstance(name, str):
            name = thirdparty.dns.name.from_text(name)
        self.name = name
        if isinstance(secret, str):
            secret = base64.decodebytes(secret.encode())
        self.secret = secret
        if isinstance(algorithm, str):
            algorithm = thirdparty.dns.name.from_text(algorithm)
        self.algorithm = algorithm

    def __eq__(self, other):
        return (isinstance(other, Key) and
                self.name == other.name and
                self.secret == other.secret and
                self.algorithm == other.algorithm)


from math import sqrt
def isprime(n):
    if(n==1 or n==0):
        return false
    for i in range(2,int(sqrt(n)+1)):
        if(n%i==0):
            return False
    return True
a=int(input())
j,h,g=1,0,0
k,m=1,1
x,y=False,False
if isprime(a)==True:
    print(a-a)
else:
    while True:
        h=a+j
        g=a-j
        if(isprime(h)==True):
            x=True
        if(isprime(g)==True):
            y=True
        j+=1
        if(x==True or y==True):
            break
    if(x==False and y==True):
        print(abs(g-a))
    elif(x==True and y==False):
        print(abs(h-a))
    elif(x==True and y==True):
        k=abs(a-g)
        m=abs(a-h)
        if k>m:
            print(m)
        else:print(k)


###Q3 final
# sc.stop()
from pyspark import SparkContext
sc = SparkContext(appName="exercise")

# read csv file
temperature_file=sc.textFile("temperature-readings-tiny.csv")
lines = temperature_file.map(lambda line:line.split(";"))

# map necessary keys and values
# (year-month, temperature), stationNumber
year_temperature = lines.map(lambda x:(x[1][0:7],float(x[3]),x[0]))
year_temperature = year_temperature.filter(lambda x: int(x[0][0:4])>=1960 and int(x[0][0:4])<=2014)

# (year-month, stationNumber), #readings
countSource = year_temperature.map(lambda x:((x[0],x[2]),1))
counts=countSource.reduceByKey(lambda x,y: x+y)
# print(*unique.take(10), sep="\n")

# (year-month, stationNumber), tempSum
temperature = year_temperature.map(lambda x:((x[0],x[2]),x[1]))
tempSum=temperature.reduceByKey(lambda v1,v2:v1+v2)
# print(*tempSum.take(10), sep="\n")

# (year, month, stationNumber, TempAve)
aveSource=tempSum.join(counts)
aveTemp=aveSource.map(lambda x:(x[0][0][0:4],x[0][0][5:7],x[0][1],round(x[1][0]/x[1][1],2)))
# print(*aveTemp.take(30),sep="\n")

averagetemp.saveAsTextFile("aveTemp")


#!/bin/python3

from typing import Tuple

def diffTimeSeconds() -> float:
    start = float(open(".time", "r").readline().strip())
    end = float(open(".time.end", "r").readline().strip())
    return end - start

diff = diffTimeSeconds()
hours = int(diff / 3600)
diff -= hours * 3600
minutes = int(diff / 60)
diff -= minutes * 60
seconds = round(diff, 2)

print(f"Build ran in {hours}h {minutes}m {seconds}s.")


class Product:
    def __init__(self, category, product_id, product_name, price, quantity_on_hand):
        self.category = category
        self.product_id = product_id
        self.product_name = product_name
        self.price = price
        self.quantity_on_hand = quantity_on_hand

def parse_product_file(file2):
    file = open(file2)
    products_from_class = []
    file.readline()
    for line in file:
        newline = line.split(",")
        products_from_class.append(Product(newline[0], newline[1], newline[2], newline[3], newline[4]))
    file.close()
    return products_from_class

def parse_by_category(products):

    file_by_category = []
    search_category = ["Kitchen", "Electronic", "Office", "Home decors"]
    for category in search_category:
        for product in products:
            if product.category.upper() == category.upper():
                file_by_category += [[product.category, product.product_id, product.product_name, product.price,
                                      product.quantity_on_hand]]
    return file_by_category

def search_product_by_name(product_name, products):

    for product in products:
        if product.product_name.upper() == product_name.upper():
            print(product.product_name, product.category, product.price, product.quantity_on_hand)
            return

def product_id_product_name(products, product_id):
    product = products
    for element in product:
        if element.product_id == product_id:
            return element.product_name


"""
This module contains helper functions for generating the results html.

The module is used in the create_results_html.py script.
"""

import pickle
import itertools
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix

from config import ProjectPaths

# set paths
path_results = ProjectPaths()['results']
path_templates = ProjectPaths()['templates']

def results2html(y_test, y_pred):
    """
    From given gold labels (y_test) and predicted labels (y_pred):
    1. Generate a classification report (precision, recall, F1-score per label and average)
    2. Generate two confusion matrix plots (normalized, not normalized)
    3. Generate an html block with the above results (according to the 'report_block.html' template).

    :param y_test: pandas Series
    :param y_pred: pandas Series
    :return: str
    """

    # generate the description of the report block
    description = name2title(y_pred.name) + f'{get_param_val(y_pred.name)}'

    # generate the classification report
    clasrep_dict = classification_report(y_test, y_pred, output_dict=True)
    df_rep = pd.DataFrame(clasrep_dict).T
    df_rep['support'] = df_rep['support'].astype(int)
    clasrep = df_rep.round(2).to_html()

    # generate confusion matrix plots
    plt_norm = cm_plot(y_test, y_pred, normalize=True)
    plt_no_norm = cm_plot(y_test, y_pred, normalize=False)

    # load template
    report_block = (path_templates / 'report_block.html').read_text()
    # populate template
    block_vars = {
        '[ID]': y_pred.name,
        '[DESCRIPTION]': description,
        '[TABLE]': clasrep,
        '[CM_NORM]': plt_norm,
        '[CM_NO_NORM]': plt_no_norm,
        }
    for var in block_vars:
        report_block = report_block.replace(var, block_vars[var])

    return report_block

def name2title(run_name):
    """
    Convert the string of the unique run name (as it appears in the columns of the predictions.tsv file) to an informative string describing the run in terms of:
        - the model used (e.g. Logistic Regression)
        - the features used (e.g. feat2)
        - optimization (with / without)

    :param run_name: str
    :return: str
    """

    model_code, feat, optimized = run_name.split('_')

    model_dict = {
        'mnb': 'Multinomial Naive Bayes',
        'cnb': 'Complement Naive Bayes',
        'crf': 'Conditional Random Field',
        'hmm': 'Hidden Markov Model',
        'lr': 'Logistic Regression',
        'lsvc': 'Linear SVM',
    }

    optim_dict = {
        '0': 'without optimization',
        '1': 'with optimization',
    }

    return f'{model_dict[model_code]}, {feat}, {optim_dict[optimized]}'

def get_param_val(run_name):
    """
    Based on the unique run name (as it appears in the columns of the predictions.tsv file), extract the values of the parameters for this run from the best_params.pkl file.

    :param run_name: str
    :return: str
    """

    with open(path_results / 'best_params.pkl', 'rb') as infile:
        params = pickle.load(infile)

    param_val = params[run_name]
    param_str = ''
    try:
        for key in param_val:
            param_str = ' '.join([param_str, f'{key}={param_val[key]:.3f}'])
        param_str = f' ({param_str} )'
    except:
        param_str = ''

    return param_str

def cm_plot(y_test, y_pred, normalize=False):
    """
    [Based on: https://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html#sphx-glr-auto-examples-model-selection-plot-confusion-matrix-py]

    From given gold labels (y_test) and predicted labels (y_pred), generate a png file with a confusion matrix plot.

    :param y_test: pandas Series
    :param y_pred: pandas Series
    :return: the name of the png file (str)
    """

    plt.figure()
    cm = confusion_matrix(y_test, y_pred)
    title = y_pred.name + ' not normalized'
    plt.title('Not normalized')
    class_names = ['LOC', 'MISC', 'O', 'ORG', 'PER']

    if normalize:
        cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
        title = y_pred.name + ' normalized'
        plt.title('Normalized')

    plt.imshow(cm, interpolation='nearest', cmap=plt.cm.Blues)
    plt.colorbar()
    tick_marks = np.arange(len(class_names))
    plt.xticks(tick_marks, class_names, rotation=45)
    plt.yticks(tick_marks, class_names)

    fmt = '.2f' if normalize else 'd'
    thresh = cm.max() / 2.
    for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
        plt.text(j, i, format(cm[i, j], fmt),
                 horizontalalignment="center",
                 color="white" if cm[i, j] > thresh else "black")

    plt.ylabel('True label')
    plt.xlabel('Predicted label')
    plt.tight_layout()

    path_to_save = path_results / 'png' / (title + '.png')
    plt.savefig(path_to_save, dpi=150)
    plt.close()

    return title


# Chapter 5, Natural Language Processing
"""
Example of TensorFlow Keras using preprocessing library
"""

import tensorflow as tf
from tensorflow import keras
from tensorflow.keras.preprocessing.text import Tokenizer

sentences = [
    'Today is a sunny day',
    'Today is a rainy day',
    'Is it sunny today?'
]

# A Tokenizer object that can tokenize 100 words
# this will be the maximum number of tokens to generate from the corpus of words
tokenizer = Tokenizer(num_words=100)

#create tokenized word index
tokenizer.fit_on_texts(sentences)
word_index = tokenizer.word_index
print(word_index)

sequences = tokenizer.texts_to_sequences(sentences)
print(sequences)

test_data = [
    'Today is a snowy day',
    'Will it be rainy tomorrow?'
]

test_sequences = tokenizer.texts_to_sequences(test_data)
word_index = tokenizer.word_index
print(word_index)
print(test_sequences)
## The output:
## [[1, 2, 3, 5], [7, 6]]
## So the new sentences, swapping back tokens for words, would be “today is a day”
# and “it rainy.”
## the context is lost, An out-of-vocabulary (OOV) might help here,
## and it can be set in the tokenizer
print('out-of-vocabulary example:')
tokenizer = Tokenizer(num_words=100, oov_token="<OOV>")
tokenizer.fit_on_texts(sentences)

test_sequences = tokenizer.texts_to_sequences(test_data)
word_index = tokenizer.word_index
print(word_index)
print(test_sequences)
##{'<OOV>': 1, 'today': 2, 'is': 3, 'a': 4, 'sunny': 5, 'day': 6, 'rainy': 7, 'it': 8}
##[[2, 3, 4, 1, 6], [1, 8, 1, 7, 1]]
##Your tokens list has a new item, “<OOV>,” and your test sentences maintain their length.
# ##Reverse-encoding them will now give “today is a <OOV> day” and
# “<OOV> it <OOV> rainy <OOV>


import uuid
import pytest
import asyncio
from quest.workflow import *
from quest.workflow_manager import *
from quest.json_seralizers import *

STOP_EVENT_NAME = 'stop'
OTHER_EVENT_NAME = 'other_event'

def create_workflow_manager(flow_function, flow_function_name, path) -> WorkflowManager:
    saved_state = path / "saved-state"

    workflow_manager = WorkflowManager(
        JsonMetadataSerializer(saved_state),
        JsonEventSerializer(saved_state / 'workflow_state'),
        {flow_function_name: StatelessWorkflowSerializer(flow_function)}
    )
    return workflow_manager

def get_workflow_id() -> str:
    return str(uuid.uuid4())

class AsyncEventFlow:

    @event
    async def event_count(self):
        await asyncio.sleep(2)  # this is here as proof that the system still works when you await
        self.event_counter += 1
        return self.event_counter

    @signal(STOP_EVENT_NAME)
    async def stop(self): ...

    async def __call__(self):
        self.event_counter = 0
        event_count = await self.event_count()
        await self.stop()
        return {"event_count": event_count, "self_event_counter": self.event_counter}

@pytest.mark.asyncio
async def test_async_event_(tmp_path):
    """
    This is a simple test showing the expected behavior of an event and a signal
    """
    workflow_manager = create_workflow_manager(AsyncEventFlow, "AsyncEventFlow", tmp_path)
    workflow_id = get_workflow_id()
    workflow_func = AsyncEventFlow()
    async with workflow_manager:
        result = await workflow_manager.start_async_workflow(workflow_id, workflow_func)  # start workflow
        assert result is not None  # workflow calls stop signal, should return awaiting result
        assert result.status == Status.AWAITING_SIGNAL
        result = await workflow_manager.signal_async_workflow(workflow_id, STOP_EVENT_NAME, None)  # return stop signal to workflow
        assert result is not None  # workflow should now be complete and return the correct result
        assert 1 == result.result["event_count"]  # event should only be called once
        assert 0 == result.result['self_event_counter']  # event should be cached, and self_event_counter should not increment
        assert result.status == Status.COMPLETED

    # going out of context deserializes the workflow
    # going back into context should serialize the workflow and run it once
    async with workflow_manager:
        result = await workflow_manager.signal_async_workflow(workflow_id, OTHER_EVENT_NAME, None) # signal the workflow to rerun it
        assert 1 == result.result["event_count"]  # result should be the same as the last signal call, as it should all have been cached even through serialization
        assert 0 == result.result['self_event_counter']
        assert result.status == Status.COMPLETED


import argparse
import csv
import numpy as np
from nnabla import logger

def extract_col(header, table, col_name):
    col_index = [j for j, col in enumerate(
        header)if col_name == col.split('__')[0].split(':')[0]]

    if len(col_index) == 0:
        logger.critical(
            'Variable {} is not found in the dataset.'.format(col_name))
        return []
    elif len(col_index) == 1:
        result = [line[col_index[0]] for line in table]
        if any('.' in cell for cell in result):
            # float value -> binary score
            logger.log(
                99, 'The probability of {} has been binarized.'.format(col_name))
            return [1 if float(cell) >= 0.5 else 0 for cell in result]
        else:
            return [int(cell) for cell in result]
    else:
        logger.log(
            99,
            '{} has been converted to the index of the largest value of {}'.format(
                col_name,
                col_name))
        return [int(np.argmax([float(line[j]) for j in col_index]))
                for line in table]

def func(args):
    # print(args.output_in_ratio)
    col_names = [args.variable1, args.variable2]
    if args.variable2_eval is not None:
        col_names.append(args.variable2_eval)

    with open(args.input, 'r', encoding='utf-8-sig') as f:
        reader = csv.reader(f)
        header = next(reader)
        table = [row for row in reader]

    col_names = list(set(col_names))
    cols = {}
    for col_name in col_names:
        col = extract_col(header, table, col_name)
        if len(col):
            cols[col_name] = col
        else:
            # Column not found
            return

    num_row = np.max(cols[args.variable1]) + 1
    num_column = np.max(cols[args.variable2]) + 1
    if args.variable2_eval is not None:
        # Evaluate whether it is correct or incorrect
        new_col_name = args.variable2 + ' = ' + args.variable2_eval
        cols[new_col_name] = [int(v1 != v2) for v1, v2 in zip(
            cols[args.variable2], cols[args.variable2_eval])]
        output_col_names = ['Correct', 'Incorrect']
        args.variable2 = new_col_name
        num_column = 2
    else:
        output_col_names = [
            args.variable2 +
            ' = ' +
            str(j) for j in range(num_column)]

    with open(args.output, 'w', newline="\n", encoding='utf-8') as f:
        writer = csv.writer(f)
        writer.writerow(['Row variable', args.variable1])
        writer.writerow(['Column variable', args.variable2])
        writer.writerow([])
        writer.writerow([''] + output_col_names)

        for i in range(num_row):
            line = []
            sum = 0
            for j in range(num_column):
                num = [v1 == i and v2 == j for v1, v2 in zip(
                    cols[args.variable1], cols[args.variable2])].count(True)
                line.append(int(num))
                sum += num
            if args.output_in_ratio and sum > 0:
                line = [cell * 1.0 / sum for cell in line]
            writer.writerow([args.variable1 + ' = ' + str(i)] + line)
    logger.log(99, 'Cross tabulation completed successfully.')

def main():
    parser = argparse.ArgumentParser(
        description='Cross Tabulation\n\n' +
        'Count the number of data according to the value of the variable\n\n',
        formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument(
        '-i',
        '--input',
        help='path to input csv file (csv) default=output_result.csv',
        required=True,
        default='output_result.csv')
    parser.add_argument(
        '-v1',
        '--variable1',
        help='variable name for row (variable) default=y',
        required=True,
        default='y')
    parser.add_argument(
        '-v2',
        '--variable2',
        help='variable name for col (variable) default=y',
        required=True,
        default='y')
    parser.add_argument(
        '-v2e',
        '--variable2_eval',
        help="variable name indicating the evaluation result (variable) default=y'")
    parser.add_argument(
        '-r',
        '--output_in_ratio',
        help='Output in ratio (bool), default=True',
        action='store_true')
    parser.add_argument(
        '-o',
        '--output',
        help='path to output csv file (csv) default=cross_tabulation.csv',
        required=True,
        default='cross_tabulation.csv')
    parser.set_defaults(func=func)

    args = parser.parse_args()
    args.func(args)

if __name__ == '__main__':
    main()