import asyncio
import itertools
import json
import math
import os
import tempfile
import time
import uuid
import nanome
import numpy as np
from concurrent.futures import ThreadPoolExecutor
from nanome.api.structure import Complex
from nanome.api.shapes import Label, Shape
from nanome.util import async_callback, Color, enums, Logs, Process, Vector3, ComplexUtils
from typing import List

from .forms import LineSettingsForm
from .menus import ChemInteractionsMenu, SettingsMenu
from .models import InteractionLine, LineManager, LabelManager, InteractionStructure
from .utils import merge_complexes, chunks
from .clean_pdb import clean_pdb

PDBOPTIONS = Complex.io.PDBSaveOptions()
PDBOPTIONS.write_bonds = True

# By default Arpeggio times out after 10 minutes (600 seconds)
ARPEGGIO_TIMEOUT = int(os.environ.get('ARPEGGIO_TIMEOUT', 0) or 600)

class AtomNotFoundException(Exception):
    pass

class ChemicalInteractions(nanome.AsyncPluginInstance):

    def start(self):
        self.temp_dir = tempfile.TemporaryDirectory()
        self.residue = ''
        self.menu = ChemInteractionsMenu(self)
        self.settings_menu = SettingsMenu(self)
        self.show_distance_labels = False
        self.__complex_cache = {}

    def on_stop(self):
        self.temp_dir.cleanup()

    @async_callback
    async def on_run(self):
        self.menu.enabled = True
        complexes = await self.request_complex_list()
        self.menu.render(complexes=complexes, default_values=True)

    @async_callback
    async def on_complex_list_changed(self):
        complexes = await self.request_complex_list()
        await self.menu.render(complexes=complexes, default_values=True)

    def on_advanced_settings(self):
        self.settings_menu.render()

    @property
    def line_manager(self):
        """Maintain a dict of all interaction lines stored in memory."""
        if not hasattr(self, '_line_manager'):
            self._line_manager = LineManager()
        return self._line_manager

    @line_manager.setter
    def line_manager(self, value):
        self._line_manager = value

    @property
    def label_manager(self):
        """Maintain a dict of all labels stored in memory."""
        if not hasattr(self, '_label_manager'):
            self._label_manager = LabelManager()
        return self._label_manager

    @label_manager.setter
    def label_manager(self, value):
        self._label_manager = value

    @async_callback
    async def calculate_interactions(
            self, target_complex: Complex, ligand_residues: list, line_settings: dict,
            selected_atoms_only=False, distance_labels=False):
        """Calculate interactions between complexes, and upload interaction lines to Nanome.

        target_complex: Nanome Complex object
        ligand_residues: List of residues to be used as selection.
        line_settings: Data accepted by LineSettingsForm.
        selected_atoms_only: bool. show interactions only for selected atoms.
        distance_labels: bool. States whether we want distance labels on or off
        """
        ligand_residues = ligand_residues or []
        Logs.message('Starting Interactions Calculation')
        selection_mode = 'Selected Atoms' if selected_atoms_only else 'Specific Structures'
        extra = {"atom_selection_mode": selection_mode}
        Logs.message(f'Selection Mode = {selection_mode}', extra=extra)
        start_time = time.time()

        # Let's make sure we have a deep target complex and ligand complexes
        ligand_complexes = []
        for res in ligand_residues:
            if res.complex:
                ligand_complexes.append(res.complex)
            else:
                raise Exception('No Complex associated with Residue')

        settings = self.settings_menu.get_settings()
        if settings['recalculate_on_update']:
            self.setup_previous_run(
                target_complex, ligand_residues, ligand_complexes, line_settings,
                selected_atoms_only, distance_labels)

        complexes = set([target_complex, *[lig_comp for lig_comp in ligand_complexes if lig_comp.index != target_complex.index]])
        for cmp in complexes:
            self.__complex_cache[cmp.index] = cmp
            cmp.register_complex_updated_callback(self.on_complex_updated)

        # If the ligands are not part of selected complex, merge into one complex
        if len(complexes) > 1:
            full_complex = merge_complexes(complexes, align_reference=target_complex, selected_atoms_only=selected_atoms_only)
        else:
            full_complex = target_complex

        # Clean complex and return as tempfile
        self.menu.set_update_text("Prepping...")
        cleaned_filepath = self.get_clean_pdb_file(full_complex)
        size_in_kb = os.path.getsize(cleaned_filepath) / 1000
        Logs.message(f'Complex File Size (KB): {size_in_kb}')

        # Set up data for request to interactions service
        data = {}
        selection = self.get_interaction_selections(
            target_complex, ligand_residues, selected_atoms_only)
        Logs.debug(f'Selections: {selection}')

        if selection:
            data['selection'] = selection

        # make the request to get interactions
        self.menu.set_update_text("Calculating...")
        contacts_data = await self.run_arpeggio_process(data, cleaned_filepath)

        Logs.debug("Interaction data retrieved!")
        if not contacts_data:
            notification_message = "Arpeggio run failed. Please make sure source files are valid."
            self.send_notification(enums.NotificationTypes.error, notification_message)
            return
        Logs.message(f'Contacts Count: {len(contacts_data)}')

        interacting_entities_to_render = settings['interacting_entities']
        contacts_per_thread = 1000
        thread_count = max(len(contacts_data) // contacts_per_thread, 1)
        futs = []
        self.total_contacts_count = len(contacts_data)
        self.loading_bar_i = 0
        with ThreadPoolExecutor(max_workers=thread_count) as executor:
            for chunk in chunks(contacts_data, len(contacts_data) // thread_count):
                fut = executor.submit(self.parse_contacts_data, chunk, complexes, line_settings, selected_atoms_only, interacting_entities_to_render)
                futs.append(fut)
        new_line_manager = LineManager()
        for fut in futs:
            new_line_manager.update(fut.result())
        all_new_lines = new_line_manager.all_lines()
        Logs.message(f'Adding {len(all_new_lines)} interactions')
        Shape.upload_multiple(all_new_lines)

        # Make sure complexes are locked
        # Skip if user has recalculate on update turned on
        # Causes cycles of continuous recalculation.
        comps_to_lock = [cmp for cmp in complexes if not cmp.locked]
        if any(comps_to_lock) and not settings['recalculate_on_update']:
            for comp in comps_to_lock:
                # Make sure we don't inadvertantly move the complex
                ComplexUtils.reset_transform(comp)
                comp.locked = True
            self.update_structures_shallow(comps_to_lock)

        self.line_manager.update(new_line_manager)
        if distance_labels:
            await self.render_distance_labels(complexes)

        async def log_elapsed_time(start_time):
            """Log the elapsed time since start time.

            Done async to make sure elapsed time accounts for async tasks.
            """
            end_time = time.time()
            elapsed_time = end_time - start_time
            msg = f'Interactions Calculation completed in {round(elapsed_time, 2)} seconds'
            Logs.message(msg, extra={'calculation_time': float(elapsed_time)})

        asyncio.create_task(log_elapsed_time(start_time))

        notification_txt = f"Finished Calculating Interactions!\n{len(all_new_lines)} lines added"
        asyncio.create_task(self.send_async_notification(notification_txt))

    def get_clean_pdb_file(self, complex):
        """Clean complex to prep for arpeggio."""
        Logs.debug("Cleaning complex for arpeggio")
        complex_file = tempfile.NamedTemporaryFile(suffix='.pdb', delete=False, dir=self.temp_dir.name)
        complex.io.to_pdb(complex_file.name, PDBOPTIONS)

        cleaned_filepath = clean_pdb(complex_file.name, self)
        if os.path.getsize(cleaned_filepath) / 1000 == 0:
            message = 'Complex file is empty, unable to clean =(.'
            Logs.error(message)
            raise Exception(message)
        if not os.path.exists(cleaned_filepath):
            # If clean_pdb fails, just try sending the uncleaned
            # complex to arpeggio
            # Not sure how effective that is, but :shrug:
            Logs.warning('Clean Complex failed. Sending uncleaned file to arpeggio.')
            cleaned_filepath = complex_file.name
        else:
            complex_file.close()
        return cleaned_filepath

    @staticmethod
    def clean_chain_name(original_name):
        chain_name = str(original_name)
        if chain_name.startswith('H') and len(chain_name) > 1:
            chain_name = chain_name[1:]
        return chain_name

    @classmethod
    def get_residue_path(cls, residue):
        chain_name = residue.chain.name
        chain_name = cls.clean_chain_name(chain_name)
        path = f'/{chain_name}/{residue.serial}/'
        return path

    @classmethod
    def get_atom_path(cls, atom):
        chain_name = cls.clean_chain_name(atom.chain.name)
        path = f'/{chain_name}/{atom.residue.serial}/{atom.name}'
        return path

    @classmethod
    def get_complex_selection_paths(cls, comp):
        selections = set()
        for res in comp.residues:
            res_selections = cls.get_residue_selection_paths(res)
            if res_selections:
                selections = selections.union(res_selections)
        return selections

    @classmethod
    def get_residue_selection_paths(cls, residue):
        """Return a set of atom paths for the selected atoms in a structure (Complex/Residue)."""
        selections = set()
        unselected_atoms = filter(lambda atom: not atom.selected, residue.atoms)
        if sum(1 for _ in unselected_atoms) == 0:
            selections.add(cls.get_residue_path(residue))
        else:
            selected_atoms = filter(lambda atom: atom.selected, residue.atoms)
            for atom in selected_atoms:
                selections.add(cls.get_atom_path(atom))
        return selections

    @classmethod
    def get_interaction_selections(cls, target_complex, ligand_residues, selected_atoms_only):
        """Generate valid list of selections to send to interactions service.

        target_complex: Nanome Complex object
        ligand_residues: List of Residue objects containing ligands interacting with target complex.
        interactions data: Data accepted by LineSettingsForm.
        selected_atoms_only: bool. show interactions only for selected atoms.

        :rtype: str, comma separated string of atom paths (eg '/C/20/O,/A/60/C2')
        """
        selections = set()
        if selected_atoms_only:
            # Get all selected atoms from both the selected complex and ligand complex
            comp_selections = cls.get_complex_selection_paths(target_complex)
            selections = selections.union(comp_selections)
            for rez in ligand_residues:
                rez_selections = cls.get_residue_selection_paths(rez)
                selections = selections.union(rez_selections)
        else:
            # Add all residues from ligand residues to the selection list.
            # Unless the selected complex is also the ligand, in which case don't add anything.
            for rez in ligand_residues:
                rez_selections = cls.get_residue_path(rez)
                selections.add(rez_selections)
        selection_str = ','.join(selections)
        return selection_str

    @staticmethod
    def get_atom_from_path(complex, atom_path):
        """Return atom corresponding to atom path.

        :arg complex: nanome.api.Complex object
        :arg atom_path: str (e.g C/20/O)

        rtype: nanome.api.Atom object, or None
        """
        chain_name, res_id, atom_name = atom_path.split('/')
        # Use the molecule corresponding to current frame
        complex_molecule = next(
            mol for i, mol in enumerate(complex.molecules)
            if i == complex.current_frame
        )
        # Chain naming seems inconsistent, so we need to check the provided name,
        # as well as heteroatom variation
        atoms = [
            a for a in complex_molecule.atoms
            if all([
                a.name == atom_name,
                str(a.residue.serial) == str(res_id),
                a.chain.name in [chain_name, f'H{chain_name}']
            ])
        ]
        if not atoms:
            return

        if len(atoms) > 1:
            # If multiple atoms found, check exact matches (no heteroatoms)
            atoms = [
                a for a in complex_molecule.atoms
                if all([
                    a.name == atom_name,
                    str(a.residue.serial) == str(res_id),
                    a.chain.name == chain_name
                ])
            ]
            if not atoms:
                msg = f"Error finding atom {atom_path}. Please ensure atoms are uniquely named."
                Logs.warning(msg)
                raise AtomNotFoundException(msg)

            if len(atoms) > 1:
                # Just pick the first one? :grimace:
                Logs.warning(f'Too many Atoms found for {atom_path}')
                atoms = atoms[:1]
        atom = atoms[0]
        return atom

    @classmethod
    def parse_ring_atoms(cls, atom_path, complexes):
        """Parse aromatic ring path into a list of Atoms.

        e.g 'C/100/C1,C2,C3,C4,C5,C6' --> C/100/C1, C/100/C2, C/100/C3, etc
        :rtype: List of Atoms.
        """
        chain_name, res_id, atom_names = atom_path.split('/')
        atom_names = atom_names.split(',')
        atom_paths = [f'{chain_name}/{res_id}/{atomname}' for atomname in atom_names]

        atoms = []
        for atompath in atom_paths:
            atom = None
            for comp in complexes:
                atom = cls.get_atom_from_path(comp, atompath)
                if atom:
                    break
            if atom:
                atoms.append(atom)
        return atoms

    @classmethod
    def parse_atoms_from_atompaths(cls, atom_paths, complexes):
        """Return a list of atoms from the complexes based on the atom_paths.

        :rtype: List of Atoms
        """
        struct_list = []
        for atompath in atom_paths:
            atom = None
            if ',' in atompath:
                # Parse aromatic ring, and add list of atoms to struct_list
                ring_atoms = cls.parse_ring_atoms(atompath, complexes)
                # Get frame and conformer from first atom in ring
                struct = InteractionStructure(ring_atoms)
            else:
                # Parse single atom
                for comp in complexes:
                    atom = cls.get_atom_from_path(comp, atompath)
                    if atom:
                        break
                if not atom:
                    continue
                struct = InteractionStructure(atom)
            struct_list.append(struct)
        return struct_list

    def parse_contacts_data(self, contacts_data, complexes, line_settings, selected_atoms_only=False, interacting_entities=None):
        """Parse .contacts file into list of Lines to be rendered in Nanome.

        contacts_data: Data returned by Chemical Interaction Service.
        complexes: strucutre.Complex objects that can contain atoms in contacts_data.
        line_settings: dict. Data to populate LineSettingsForm.
        interaction_data. LineSettingsForm data describing color and visibility of interactions.

        :rtype: LineManager object containing new lines to be uploaded to Nanome workspace.
        """
        interacting_entities = interacting_entities or ['INTER', 'INTRA_SELECTION', 'SELECTION_WATER']
        form = LineSettingsForm(data=line_settings)
        form.validate()
        if form.errors:
            raise Exception(form.errors)
        # Set variables used to track loading bar progress across threads.
        if not hasattr(self, 'loading_bar_i'):
            self.loading_bar_i = 0
        if not hasattr(self, 'total_contacts_count'):
            self.total_contacts_count = len(contacts_data)

        new_line_manager = LineManager()
        self.menu.set_update_text("Updating Workspace...")
        # Update loading bar every 5% of contacts completed
        update_percentages = list(range(100, 0, -5))
        for row in contacts_data:
            self.loading_bar_i += 1
            current_percentage = math.ceil((self.loading_bar_i / self.total_contacts_count) * 100)
            if update_percentages and current_percentage > update_percentages[-1]:
                Logs.debug(f"{self.loading_bar_i} / {self.total_contacts_count} contacts processed")
                self.menu.update_loading_bar(self.loading_bar_i, self.total_contacts_count)
                update_percentages.pop()

            # Atom paths that current row is describing interactions between
            a1_data = row['bgn']
            a2_data = row['end']
            interaction_types = row['contact']

            # If we dont have line settings for any of the interactions in the row, we can continue
            # Typically this filters out rows with only `proximal` interactions.
            if not set(interaction_types).intersection(set(form.data.keys())):
                continue

            # If structure's relationship is not included, continue
            if row['interacting_entities'] not in interacting_entities:
                continue

            atom1_path = f"{a1_data['auth_asym_id']}/{a1_data['auth_seq_id']}/{a1_data['auth_atom_id']}"
            atom2_path = f"{a2_data['auth_asym_id']}/{a2_data['auth_seq_id']}/{a2_data['auth_atom_id']}"
            atom_paths = [atom1_path, atom2_path]

            # A struct can be either an atom or a list of atoms, indicating an aromatic ring.
            try:
                struct_list = self.parse_atoms_from_atompaths(atom_paths, complexes)
            except AtomNotFoundException:
                message = (
                    f"Failed to parse interactions between {atom1_path} and {atom2_path} "
                    f"skipping {len(interaction_types)} interactions"
                )
                Logs.warning(message)
                continue
            if len(struct_list) != 2:
                Logs.warning("Failed to parse atom paths, skipping")
                continue

            # if selected_atoms_only = True, and neither of the structures contain selected atoms, don't draw line
            all_atoms = []
            for struct in struct_list:
                all_atoms.extend(struct.atoms)

            if selected_atoms_only and not any([a.selected for a in all_atoms]):
                continue

            for struct in struct_list:
                # Set `frame` and `conformer` attribute for InteractionStructure.
                for comp in complexes:
                    atom_indices = [a.index for a in struct.atoms]
                    relevant_atoms = [a.index for a in comp.atoms if a.index in atom_indices]
                    if relevant_atoms:
                        struct.frame = comp.current_frame
                        struct.conformer = list(comp.molecules)[comp.current_frame].current_conformer
            # Create new lines and save them in memory
            struct1, struct2 = struct_list
            structpair_lines = self.create_new_lines(struct1, struct2, interaction_types, form.data)
            new_line_manager.add_lines(structpair_lines)
        return new_line_manager

    def create_new_lines(self, struct1, struct2, interaction_types, line_settings):
        """Parse rows of data from .contacts file into Line objects.

        struct1: InteractionStructure
        struct2: InteractionStructure
        interaction_types: list of interaction types that exist between struct1 and struct2
        line_settings: Color and shape information for each type of Interaction.
        """
        new_lines = []
        for interaction_type in interaction_types:
            form_data = line_settings.get(interaction_type)
            if not form_data:
                continue

            # See if we've already drawn this line
            line_exists = False
            structpair_lines = self.line_manager.get_lines_for_structure_pair(struct1.index, struct2.index)
            for lin in structpair_lines:
                if all([
                    lin.frames.get(struct1.index) == struct1.frame,
                    lin.frames.get(struct2.index) == struct2.frame,
                    lin.conformers.get(struct1.index) == struct1.conformer,
                    lin.conformers.get(struct2.index) == struct2.conformer,
                        lin.interaction_type == interaction_type]):
                    line_exists = True
                    break
            if line_exists:
                continue

            form_data['interaction_type'] = interaction_type
            # Draw line and add data about interaction type and frames.
            line = self.draw_interaction_line(struct1, struct2, form_data)
            new_lines.append(line)

        return new_lines

    @staticmethod
    def draw_interaction_line(struct1, struct2, line_settings):
        """Draw line connecting two structs.

        :arg struct1: struct
        :arg struct2: struct
        :arg line_settings: Dict describing shape and color of line based on interaction_type
        """
        line = InteractionLine(struct1, struct2, **line_settings)

        for struct, anchor in zip([struct1, struct2], line.anchors):
            anchor.anchor_type = nanome.util.enums.ShapeAnchorType.Atom
            anchor.target = struct.line_anchor.index
            # This nudges the line anchor to the center of the structure
            anchor.local_offset = struct.calculate_local_offset()
        return line

    async def update_interaction_lines(self, interactions_data, complexes=None):
        complexes = complexes or []
        stream_type = nanome.api.streams.Stream.Type.shape_color.value

        all_lines = self.line_manager.all_lines()
        line_indices = [line.index for line in all_lines]
        stream, _ = await self.create_writing_stream(line_indices, stream_type)
        if not stream:
            return

        new_colors = []
        in_frame_count = 0
        out_of_frame_count = 0

        for line in all_lines:
            # Make sure that both atoms connected by line are in frame.
            line_in_frame = self.line_in_frame(line, complexes)
            if line_in_frame:
                in_frame_count += 1
            else:
                out_of_frame_count += 1

            # Parse forms, and add line data to stream
            line_type = line.interaction_type
            form_data = interactions_data[line_type]
            hide_interaction = not form_data['visible'] or not line_in_frame
            color = Color(*form_data['color'])

            color.a = 0 if hide_interaction else 255
            new_colors.extend(color.rgba)
            line.color = color
            self.line_manager.update_line(line)

        # Logs.debug(f'in frame: {in_frame_count}')
        # Logs.debug(f'out of frame: {out_of_frame_count}')
        if stream:
            stream.update(new_colors)

        if self.show_distance_labels:
            # Refresh label manager
            self.label_manager.clear()
            await self.render_distance_labels(complexes)

    @classmethod
    def line_in_frame(cls, line, complexes):
        """Return boolean stating whether both structures connected by line are in frame.

        :arg line: Line object. The line in question.
        :arg complexes: List of complexes in workspace that can contain atoms.
        """
        line_atom_indices = [anchor.target for anchor in line.anchors]
        struct1_key, struct2_key = line.structure_indices
        atom_generators = []
        for comp in complexes:
            try:
                current_mol = next(
                    mol for i, mol in enumerate(comp.molecules)
                    if i == comp.current_frame)
            except StopIteration:
                # In case of empty complex, its safe to continue
                if sum(1 for _ in comp.molecules) == 0:
                    continue
                raise
            atom_generators.append(current_mol.atoms)
        current_atoms = itertools.chain(*atom_generators)
        atoms_found = 0
        for atom in current_atoms:
            if atom.index not in line_atom_indices:
                continue
            struct_key = struct1_key if str(atom.index) in struct1_key else struct2_key
            hash_matches = cls.check_struct_key(struct_key, atom)
            if hash_matches:
                atoms_found += 1
            if atoms_found == 2:
                break
        line_in_frame = atoms_found == 2
        return line_in_frame

    @classmethod
    def check_struct_key(cls, struct_key, atom):
        # key_hash = struct_key.split('_')
        # parse key_hash
        atom_pos_strs = struct_key.split(',')
        has_matches = False
        for atom_pos_str in atom_pos_strs:
            try:
                atom_index, x, y, z = atom_pos_str.split('/')
            except ValueError:
                breakpoint()
            if atom_index != str(atom.index):
                continue
            pos = Vector3(float(x), float(y), float(z))
            if np.allclose(atom.position.unpack(), pos.unpack()):
                has_matches = True
                break
        return has_matches

    async def clear_visible_lines(self, complexes, send_notification=True):
        """Clear all interaction lines that are currently visible."""
        lines_to_destroy = []
        labels_to_destroy = []

        # Make sure we have deep complexes.
        shallow_complexes = [comp for comp in complexes if len(list(comp.molecules)) == 0]
        if shallow_complexes:
            deep_complexes = await self.request_complexes([comp.index for comp in shallow_complexes])
            for i, comp in enumerate(deep_complexes):
                if complexes[i].index == comp.index:
                    complexes[i] = comp

        for struct1_key, struct2_key in self.line_manager.get_struct_pairs():
            line_list = self.line_manager.get_lines_for_structure_pair(struct1_key, struct2_key)
            line_count = len(line_list)
            line_removed = False
            for i in range(line_count - 1, -1, -1):
                line = line_list[i]
                if self.line_in_frame(line, complexes):
                    lines_to_destroy.append(line)
                    line_list.remove(line)
                    line_removed = True
            # Remove any labels that have been created corresponding to this structpair
            if line_removed:
                label = self.label_manager.remove_label_for_structpair(struct1_key, struct2_key)
                if label:
                    labels_to_destroy.append(label)

        destroyed_line_count = len(lines_to_destroy)
        Shape.destroy_multiple([*lines_to_destroy, *labels_to_destroy])

        message = f'Deleted {destroyed_line_count} interactions'
        Logs.message(message)
        if send_notification:
            asyncio.create_task(self.send_async_notification(message))

    async def send_async_notification(self, message):
        """Send notification asynchronously."""
        notifcation_type = enums.NotificationTypes.message
        self.send_notification(notifcation_type, message)

    async def render_distance_labels(self, complexes):
        # Make sure we have deep complexes.
        shallow_complexes = [comp for comp in complexes if len(list(comp.molecules)) == 0]
        if shallow_complexes:
            deep_complexes = await self.request_complexes([comp.index for comp in shallow_complexes])
            # Not sure how, but theres errors where a NoneType ends up in deep_complexes
            # This is a quick fix for that.
            deep_complexes = [comp for comp in deep_complexes if comp]
            for i, comp in enumerate(deep_complexes):
                if complexes[i].index == comp.index:
                    complexes[i] = comp

        self.show_distance_labels = True
        for struct1_index, struct2_index in self.line_manager.get_struct_pairs():
            # If theres any visible lines between the two structs in structpair, add a label.
            line_list = self.line_manager.get_lines_for_structure_pair(struct1_index, struct2_index)
            for line in line_list:
                if self.line_in_frame(line, complexes) and line.color.a > 0:
                    label = Label()
                    label.text = str(round(line.length, 2))
                    label.font_size = 0.08
                    label.anchors = line.anchors
                    for anchor in label.anchors:
                        anchor.viewer_offset = Vector3(0, 0, -.01)
                    self.label_manager.add_label(label, struct1_index, struct2_index)
                    break

        label_count = len(self.label_manager.all_labels())
        Shape.upload_multiple(self.label_manager.all_labels())
        Logs.message(f'Uploaded {label_count} distance labels')

    def clear_distance_labels(self):
        self.show_distance_labels = False
        label_count = len(self.label_manager.all_labels())
        Shape.destroy_multiple(self.label_manager.all_labels())
        Logs.message(f'Deleted {label_count} distance labels')

    @staticmethod
    async def run_arpeggio_process(data, input_filepath):
        output_data = {}
        # Set up and run arpeggio command
        exe_path = 'conda'
        arpeggio_path = 'arpeggio'
        args = [
            'run', '-n', 'arpeggio',
            arpeggio_path,
            '--mute',
            input_filepath
        ]
        if 'selection' in data:
            selections = data['selection'].split(',')
            args.append('-s')
            args.extend(selections)

        # Create directory for output
        temp_uuid = uuid.uuid4()
        with tempfile.TemporaryDirectory() as temp_dir:
            output_dir = f'{temp_dir}/{temp_uuid}'
            args.extend(['-o', output_dir])

            p = Process(exe_path, args, True, label="arpeggio", timeout=ARPEGGIO_TIMEOUT)
            p.on_error = Logs.warning
            p.on_output = Logs.message
            exit_code = await p.start()
            Logs.message(f'Arpeggio Exit code: {exit_code}')

            if not os.path.exists(output_dir) or not os.listdir(output_dir):
                Logs.error('Arpeggio run failed.')
                return

            output_filename = next(fname for fname in os.listdir(output_dir))
            output_filepath = f'{output_dir}/{output_filename}'
            with open(output_filepath, 'r') as f:
                output_data = json.load(f)
            return output_data

    def setup_previous_run(
        self, target_complex: Complex, ligand_residues: list, ligand_complexes: list, line_settings: dict,
            selected_atoms_only=False, distance_labels=False):
        self.previous_run = {
            'target_complex': target_complex,
            'ligand_residues': ligand_residues,
            'ligand_complexes': ligand_complexes,
            'line_settings': line_settings,
            'selected_atoms_only': selected_atoms_only,
            'distance_labels': distance_labels
        }

    @async_callback
    async def on_complex_updated(self, updated_comp: Complex):
        """Callback for when a complex is updated."""
        # Get all updated complexes
        cached_complexes = [cmp for cmp in self.__complex_cache.values() if cmp.index != updated_comp.index]
        updated_comp_list = [updated_comp] + cached_complexes

        # Redraw lines
        interactions_data = self.menu.collect_interaction_data()
        await self.update_interaction_lines(interactions_data, complexes=updated_comp_list)

        # Recalculate interactions if that setting is enabled.
        recalculate_enabled = self.settings_menu.get_settings()['recalculate_on_update']
        if recalculate_enabled and hasattr(self, 'previous_run'):
            await self.recalculate_interactions(updated_comp_list)

    async def recalculate_interactions(self, updated_comps: List[Complex]):
        """Recalculate interactions from the previous run."""
        Logs.message("Recalculating previous run with updated structures.")
        await self.send_async_notification('Recalculating interactions...')
        target_complex = self.previous_run['target_complex']
        ligand_residues = self.previous_run['ligand_residues']
        ligand_complexes = self.previous_run['ligand_complexes']
        line_settings = self.previous_run['line_settings']
        selected_atoms_only = self.previous_run['selected_atoms_only']
        distance_labels = self.previous_run['distance_labels']

        updated_target_comp = next(
            cmp for cmp in updated_comps
            if cmp.index == target_complex.index)

        lig_comp_indices = [cmp.index for cmp in ligand_complexes]
        updated_lig_comps = [
            cmp for cmp in updated_comps if cmp.index in lig_comp_indices]

        updated_residues = []
        for comp in updated_lig_comps:
            updated_residues.extend([
                res for res in comp.residues
                if res.index in [r.index for r in ligand_residues]
            ])

        await self.menu.run_calculation(
            updated_target_comp, updated_residues, line_settings,
            selected_atoms_only=selected_atoms_only,
            distance_labels=distance_labels)


# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-

from __future__ import absolute_import
import json
import re
import copy
from urllib.parse import urljoin

from svtplay_dl.service import Service, OpenGraphThumbMixin
from svtplay_dl.fetcher.http import HTTP
from svtplay_dl.error import ServiceError

class Sr(Service, OpenGraphThumbMixin):
    supported_domains = ['sverigesradio.se']

    def get(self):
        data = self.get_urldata()

        match = re.search(r'data-audio-type="publication" data-audio-id="(\d+)">', data)  # Nyheter
        if match:
            dataurl = "https://sverigesradio.se/sida/playerajax/" \
                      "getaudiourl?id={0}&type={1}&quality=high&format=iis".format(match.group(1), "publication")
            data = self.http.request("get", dataurl).text
            playerinfo = json.loads(data)
            yield HTTP(copy.copy(self.config), playerinfo["audioUrl"], 128)
            return
        match = re.search(r'href="(/topsy/ljudfil/\d+-mp3)"', data)  # Ladda ner
        if match:
            yield HTTP(copy.copy(self.config), urljoin("https://sverigesradio.se", match.group(1)), 128)
            return
        else:
            match = re.search(r'data-audio-type="episode" data-audio-id="(\d+)"', data)  # Ladda ner med musik
            match2 = re.search(r'data-audio-type="secondary" data-audio-id="(\d+)"', data)  # Ladda ner utan musik
            if match:
                aid = match.group(1)
                type = "episode"
            elif match2:
                aid = match2.group(1)
                type = "secondary"
            else:
                yield ServiceError("Can't find audio info")
                return

        dataurl = "https://sverigesradio.se/sida/playerajax/" \
                  "getaudiourl?id={0}&type={1}&quality=high&format=iis".format(aid, type)
        data = self.http.request("get", dataurl).text
        playerinfo = json.loads(data)
        yield HTTP(copy.copy(self.config), playerinfo["audioUrl"], 128, output=self.output)


import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler

dataset = pd.read_csv('Position_Salaries.csv')
X = dataset.iloc[:, 1:-1].values
y = dataset.iloc[:, -1].values
# print(X)
# print(y)

y = y.reshape(len(y), 1)
# print(y)

# Feature Scaling

sc_X = StandardScaler()
sc_y = StandardScaler()

X = sc_X.fit_transform(X)
y = sc_y.fit_transform(y)

print(X)
print(y)

y = y.flatten()
print(y)

svr = SVR(kernel='rbf')
svr.fit(X, y)

y_pred = svr.predict(sc_X.transform([[6.5]]))  # We have to transform our 6.5 value as well
print(y_pred)

y_pred = sc_y.inverse_transform(y_pred)
print(y_pred)

# Polynomial Linear Regression

plt.scatter(sc_X.inverse_transform(X), sc_y.inverse_transform(y), color='red')
plt.scatter(sc_X.inverse_transform(X), sc_y.inverse_transform(svr.predict(X)), color='orange')
plt.plot(sc_X.inverse_transform(X), sc_y.inverse_transform(svr.predict(X)), color='blue')
plt.title('Salary vs Position Level (SVR)')
plt.xlabel('Position Level')
plt.ylabel('Salary')
plt.show()

X_grid = np.arange(min(sc_X.inverse_transform(X)), max(sc_X.inverse_transform(X)), 0.1)
X_grid = X_grid.reshape((len(X_grid), 1))
plt.scatter(sc_X.inverse_transform(X), sc_y.inverse_transform(y), color='red')
plt.plot(X_grid, sc_y.inverse_transform(svr.predict(sc_X.transform(X_grid))), color='blue')
plt.title('Salary vs Position Level (SVR)')
plt.xlabel('Position level')
plt.ylabel('Salary')
plt.show()


"""empty message

Revision ID: fca1eb7ab8bf
Revises: b2381d05d435
Create Date: 2021-06-25 11:54:33.884309

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = 'fca1eb7ab8bf'
down_revision = 'b2381d05d435'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.add_column('files', sa.Column('folder', sa.String(length=512), nullable=True))
    op.execute("UPDATE files SET folder = '/h/'")
    op.alter_column('files', 'folder', nullable=False)
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_column('files', 'folder')
    # ### end Alembic commands ###


from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
from datetime import date, timedelta

class GuaranteeLetter(models.Model):
    _name = 'guarantee.letter'
    _description = 'Guarantee Letter'
    _inherit = ['portal.mixin', 'mail.thread', 'mail.activity.mixin']
    _rec_name = 'gl_number'

    helping_id = fields.Char(string='Helping ID')

    @api.onchange('helping_id')
    def _onchange_helping_id(self):
        for rec in self:
            if not rec.helping_id:
                rec.helping_id = rec.id

    claim_number = fields.Char(string='Claim Number', tracking=True)
    gl_number = fields.Char(string='GL Number', default='-', tracking=True)
    claim_status = fields.Selection([
        ('open', 'Open'),
        ('discharge', 'Discharge Completed'),
        ('release', 'Guarantee Release'),
        ('verified', 'Verified'),
        ('completed', 'Completed'),
        ('approved', 'Approved'),
        ('paid', 'Paid'),
        ('decline', 'Declined'),
        ('reject', 'Rejected'),
    ], string='Claim Status', default="open", tracking=True)
    case_status = fields.Selection([
        ('open', 'Open'),
        ('initial', 'Initial Guarantee Release'),
        ('process', 'Case Monitoring Process'),
        ('close', 'Case Monitoring Close'),
        ('submit', 'Final Guarantee Submitted'),
        ('approved', 'Final Guarantee Approved'),
        ('discharge', 'Discharge Completed'),
        ('decline', 'Declined'),
    ], string='Case Status', default="open", tracking=True)
    service_type = fields.Selection([
        ('cashless', 'Cashless'),
        ('reimburse', 'Reimbursement'),
    ], string='Claim Type', default='cashless')
    claim_category = fields.Many2one('benefit.master', string='Claim Category', tracking=True)
    claim_reject_id = fields.Many2one('claim.reject.reason', string='Reason for claim rejection', tracking=True)
    decline_reason_id = fields.Many2one('decline.reason', string='Decline Reason', tracking=True)
    claim_source = fields.Selection([
        ('eclaim', 'E-Claim'),
        ('portal', 'Hospital Portal'),
        ('edc', 'EDC'),
    ], string='Claim Source', default='eclaim')
    created_by = fields.Many2one('res.users', string='Created By', default=lambda self: self.env.user.id, required=True, tracking=True)
    created_date = fields.Datetime(string='Created Date', default=lambda self: fields.datetime.now(), required=True, tracking=True)
    claim_due_date = fields.Integer(compute='_compute_claim_due_date', string='Claim Expired (Int)', store=True)
    claim_due_days = fields.Char(string='Claim Expired')

    @api.depends('client_id')
    def _compute_claim_due_date(self):
        for rec in self:
            if rec.client_id.claim_expiry_length and rec.admission_date:
                due_date = rec.admission_date + timedelta(days=rec.client_id.claim_expiry_length)
                difference = date.today() - due_date
                rec.claim_due_date = difference.days
                rec.claim_due_days = '%s' % rec.claim_due_date
                if rec.claim_due_date > 0:
                    rec.claim_due_days = '+%s' % rec.claim_due_date
            else:
                rec.claim_due_date = False

    name = fields.Char(string='Name', required="1", tracking=True)
    card_number_id = fields.Many2one('card.number', string='Card Number', tracking=True)
    member_id = fields.Many2one('res.partner', string='Member Name', domain=[('member', '=', True)])
    member = fields.Boolean(string='Member', default="True")
    member_number = fields.Char(string='Member Number', tracking=True)
    suffix_id = fields.Many2one('suffix.id', string='Suffix ID', tracking=True)
    dob = fields.Date(string='Date of Birth', tracking=True)
    nik = fields.Char(string='NIK / Parent Name', tracking=True)
    client_id = fields.Many2one('res.partner', string='Client Name', domain=[('client', '=', True)], tracking=True)
    client_branch_id = fields.Many2one('client.branch', string='Branch Name', domain=lambda self: [('client_id', '=', self.client_id)], tracking=True)
    provider_id = fields.Many2one('res.partner', string='Provider Name', domain=[('provider', '=', True), ('provider_type', '=', 'provider')], tracking=True)
    provider_check = fields.Selection([
        ('provider', 'Client\'s Provider'),
        ('not_provider', 'Not Client\'s Provider'),
    ], string='Provider Check', compute='_compute_provider_check')
    program_id = fields.Many2one('client.program', string='Program', tracking=True)
    plan_id = fields.Many2one('client.program.plan', string='Plan', tracking=True)
    policy_number = fields.Char(string='Policy Number')

    @api.depends('provider_id', 'client_id')
    def _compute_provider_check(self):
        for rec in self:
            rec.provider_check = False
            if rec.provider_id and rec.client_id:
                provider_ids = rec.client_id.client_provider_ids
                provider = rec.env['res.partner'].search([('id', '=', rec.provider_id.id)])
                if provider in provider_ids:
                    rec.provider_check = 'provider'
                else:
                    rec.provider_check = 'not_provider'

    join_date = fields.Date(string='Join Date', tracking=True)
    start_date = fields.Date(string='Start Date', tracking=True)
    end_date = fields.Date(string='End Date', tracking=True)
    effective_date_member = fields.Date(string='Effective Date', tracking=True)
    end_policy_date = fields.Date(string='End Policy Date', tracking=True)
    lodgement_status = fields.Selection([
        ('ok', 'Received OK'),
        ('doctor', 'Need contact treating doctor'),
        ('hospital', 'Need contact hospital'),
        ('hrd', 'Need contact HRD'),
        ('facility', 'Need contact facility (apotik, klinik)'),
    ], string='Lodgement Status', tracking=True)
    primary_surgery_id = fields.Many2one('claim.primary.surgery', string='Primary Surgery', tracking=True)
    secondary_surgery_id = fields.Many2one('claim.secondary.surgery', string='Secondary Surgery', tracking=True)
    internal_comment = fields.Char(string='Internal Comment', tracking=True)
    comment_statement = fields.Char(string='Comment Statement', tracking=True)
    number_of_item = fields.Integer(compute='_compute_number_of_item', string='Number of Item')
    checker_flagging = fields.Boolean(string='Checker', tracking=True)
    approval_payment = fields.Selection([
        ('exgratia', 'Ex Gratia'),
        ('exclusion', 'Exclusion'),
        ('hrd', 'Approval by HRD'),
    ], string='Approval Payment', tracking=True)
    approved_by = fields.Many2one('res.users', string='Created By', tracking=True)
    approval_date = fields.Date(string='Approval Date', tracking=True)

    @api.onchange('checker_flagging')
    def _onchange_checker_flagging(self):
        for rec in self:
            if rec.checker_flagging == True:
                rec.approved_by = self.env.user.id
                rec.approval_date = date.today()

                already_exist = False
                user = self.env.user
                for productivity in user.user_productivity_line:
                    if productivity.letter_id.id == rec._origin.id and name == 'Checker':
                        already_exist = True
                if not already_exist:
                    user.user_productivity_line.create({
                        'name' : 'Checker',
                        'user_id' : user.id,
                        'eclaim_category' : rec.benefit_master_id.category,
                        'letter_id' : rec._origin.id,
                        'point' : 1,
                    })

    @api.depends('final_gl_line')
    def _compute_number_of_item(self):
        for rec in self:
            rec.number_of_item = len(rec.final_gl_line)

    doctor_name = fields.Char(string='Doctor Name', tracking=True)
    policy_status = fields.Selection([
        ('A', 'Active'),
        ('N', 'Non Active'),
    ], string='Policy Status', tracking=True)
    marital_status = fields.Selection([
        ('S', 'Single'),
        ('M', 'Married'),
        ('D', 'Divorced'),
    ], string='Marital Status', tracking=True)
    relationship = fields.Selection([
        ('E', 'Employee'),
        ('S', 'Supouse'),
        ('C', 'Child'),
    ], string='Relationship', tracking=True)
    doctor_diagnosis = fields.Char(string='Doctor Diagnosis', tracking=True)
    benefit = fields.Char(string='Package Remarks', tracking=True)
    admission_date = fields.Date(string='Admission Date', tracking=True)
    up = fields.Char(string='UP', tracking=True)
    benefit_master_id = fields.Many2one('benefit.master', string='Claim Category', tracking=True)
    category = fields.Selection([
        ('ip', 'IP'),
        ('op', 'OP'),
        ('de', 'DE'),
        ('eg', 'EG'),
        ('ma', 'MA'),
        ('mcu', 'MCU'),
        ('ot', 'OT'),
    ], string='Category')
    diagnosa1_id = fields.Many2one('diagnosis.diagnosis', string='Diagnosa 1', tracking=True)
    diagnosa2_id = fields.Many2one('diagnosis.diagnosis', string='Diagnosa 2', tracking=True)
    diagnosa3_id = fields.Many2one('diagnosis.diagnosis', string='Diagnosa 3', tracking=True)
    diagnosa4_id = fields.Many2one('diagnosis.diagnosis', string='Diagnosa 4', tracking=True)
    claim_received_currency_id = fields.Many2one('res.currency', 'Currency', default=lambda self: self.env.user.company_id.currency_id.id, required=True, tracking=True)
    claim_paid_currency_id = fields.Many2one('res.currency', 'Currency', default=lambda self: self.env.user.company_id.currency_id.id, required=True, tracking=True)
    claim_received_rate = fields.Float(string='Claim Received Rate', default=1, tracking=True)
    claim_paid_rate = fields.Float(string='Claim Paid Rate', default=1, tracking=True)
    attachment_ids = fields.Many2many('ir.attachment', string='Scan', tracking=True)

    diagnosa1_check = fields.Selection([
        ('exclusion', 'Exclusion')
    ], string='Diagnosa 1 Exclusion Check')

    @api.onchange('diagnosa1_id')
    def _onchange_diagnosa1_id(self):
        if self.diagnosa1_id and self.client_id:
            exclusion_ids = self.client_id.diagnosis_ids
            if self.diagnosa1_id in exclusion_ids:
                self.diagnosa1_check = 'exclusion'
            else:
                self.diagnosa1_check = False

    diagnosa2_check = fields.Selection([
        ('exclusion', 'Exclusion')
    ], string='Diagnosa 2 Exclusion Check')

    @api.onchange('diagnosa2_id')
    def _onchange_diagnosa2_id(self):
        if self.diagnosa2_id and self.client_id:
            exclusion_ids = self.client_id.diagnosis_ids
            if self.diagnosa2_id in exclusion_ids:
                self.diagnosa2_check = 'exclusion'
            else:
                self.diagnosa2_check = False

    diagnosa3_check = fields.Selection([
        ('exclusion', 'Exclusion')
    ], string='Diagnosa 3 Exclusion Check')

    @api.onchange('diagnosa3_id')
    def _onchange_diagnosa3_id(self):
        if self.diagnosa3_id and self.client_id:
            exclusion_ids = self.client_id.diagnosis_ids
            if self.diagnosa3_id in exclusion_ids:
                self.diagnosa3_check = 'exclusion'
            else:
                self.diagnosa3_check = False

    diagnosa4_check = fields.Selection([
        ('exclusion', 'Exclusion')
    ], string='Diagnosa 4 Exclusion Check')

    @api.onchange('diagnosa4_id')
    def _onchange_diagnosa4_id(self):
        if self.diagnosa4_id and self.client_id:
            exclusion_ids = self.client_id.diagnosis_ids
            if self.diagnosa4_id in exclusion_ids:
                self.diagnosa4_check = 'exclusion'
            else:
                self.diagnosa4_check = False

    @api.onchange('diagnosa1_id', 'diagnosa2_id', 'diagnosa3_id', 'diagnosa4_id',)
    def _onchange_diagnosa_id(self):
        for rec in self:
            rec.diagnosis_ids = []
            if rec.diagnosa1_id:
                rec.diagnosis_ids += rec.diagnosa1_id
            if rec.diagnosa2_id:
                rec.diagnosis_ids += rec.diagnosa2_id
            if rec.diagnosa3_id:
                rec.diagnosis_ids += rec.diagnosa3_id
            if rec.diagnosa4_id:
                rec.diagnosis_ids += rec.diagnosa4_id

    @api.onchange('header_id')
    def _onchange_header_id_to_service(self):
        for rec in self:
            if rec.header_id:
                rec.benefit_master_id = rec.header_id.benefit_category_id
            else:
                rec.benefit_master_id = False

    @api.onchange('benefit_master_id')
    def _onchange_benefit_master_id(self):
        for rec in self:
            rec.category = rec.benefit_master_id.category

    header_id = fields.Many2one('program.plan.header', string='Claim Category (Member)')
    get_header_ids = fields.Many2many('program.plan.header', string='Service Domain')
    # preadmission_id = fields.Many2one('pre.admission', string='Preadmision ID')
    call_id = fields.Many2one('call.record', string='Call Record ID')
    preadmission = fields.Boolean(string='Preadmission Check')

    @api.onchange('header_id', 'benefit_master_id')
    def _onchange_header_id(self):
        for rec in self:
            for line in self.final_gl_line:
                line.get_detail_ids = self.env['header.detail'].search([('header_id', '=', rec.header_id.id)])
                line.get_benefit_ids = self.env['benefit.benefit'].search([('master_id', '=', rec.benefit_master_id.id)])

    @api.onchange('discharge_date')
    def _onchange_discharge_date(self):
        for rec in self:
            if rec.discharge_date:
                if rec.discharge_date < rec.admission_date:
                    raise ValidationError(_("Discharge date can not be earlier than admission date"))

    medical_record = fields.Char(string='Medical Record')
    discharge_date = fields.Date(string='Discharge Date')
    gl_date = fields.Date(string='Date of GL', default=lambda self: fields.datetime.now(), required=True, tracking=True)
    pic = fields.Char(string='PIC')
    remarks = fields.Char(string='Remarks')
    diagnosis_ids = fields.Many2many('diagnosis.diagnosis', string='Diagnosis')
    final_gl_line = fields.One2many('final.gl', 'letter_id', string='Final GL', ondelete='cascade')
    final_gl = fields.Boolean(string='Final GL')
    initial_gl = fields.Boolean(string='Initial GL')
    gender = fields.Selection([
        ('M', 'Male'),
        ('F', 'Female'),
    ], string='Gender')
    state = fields.Selection([
        ('draft', 'Draft'),
        ('submit', 'Submitted'),
        ('receive', 'Received'),
    ], string='Status', default="draft")
    company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.user.company_id.id, index=1, tracking=True)

    def action_decline(self):
        return self.write({'claim_status': 'decline'})

    def action_reject(self):
        return self.write({'claim_status': 'decline'})

    def action_approve(self):
        return self.write({'claim_status': 'approved'})

    def action_complete(self):
        return self.write({'claim_status': 'completed'})

    def action_set_verified(self):
        return self.write({'claim_status': 'verified'})

    def action_initial_gl(self):
        for rec in self:
            if not rec.practitioner_id:
                raise ValidationError(_("Required field Practitioner Name is empty"))
            if not rec.admission_date:
                raise ValidationError(_("Required field Admission Date is empty"))
            if rec.gl_number == '-':
                rec.generate_gl_number()
            rec.case_status = 'initial'
        return self.write({'initial_gl': True})

    def action_process_case(self):
        return self.write({'case_status': 'process'})

    def action_close_case(self):
        return self.write({'case_status': 'close'})

    def action_submit_case(self):
        for rec in self:
            if not rec.practitioner_id:
                raise ValidationError(_("Required field Practitioner Name is required"))
            if not rec.admission_date:
                raise ValidationError(_("Required field Admission Date is required"))
            if not rec.discharge_date:
                raise ValidationError(_("Required field Discharge Date is required"))
            if not rec.final_gl_line:
                raise ValidationError(_("Claim Item is empty"))
        return self.write({
            'final_gl': True,
            'case_status': 'submit',
            })

    def action_final_gl(self):
        for rec in self:
            rec.case_status = 'approved'
            if not rec.gl_number:
                rec.generate_gl_number()
        return self.write({'final_gl': True})

    def action_discharge_case(self):
        return self.write({
            'case_status': 'discharge',
            'claim_status': 'release',
            })

    def action_revise_case(self):
        return self.write({'case_status': 'close'})

    def action_print_initial_gl(self):
        action_name = str(self.client_id.id) + ' initial GL'
        name_action = self.env['ir.actions.report'].sudo().search([('name', '=', action_name)])
        return name_action.report_action(self)

    def action_print_final_gl(self):
        for rec in self:
            if not rec.medical_record:
                raise ValidationError(_("Required field Medical Record is empty"))
            if not rec.discharge_date:
                raise ValidationError(_("Required field Discharge Date is empty"))
        action_name = str(self.client_id.id) + ' final GL'
        name_action = self.env['ir.actions.report'].sudo().search([('name', '=', action_name)])
        return name_action.report_action(self)

    @api.onchange('member')
    def _onchange_member(self):
        for rec in self:
            rec.member_id = False
            rec.header_id = False

    @api.onchange('member_id')
    def _onchange_member_id(self):
        if self.member and self.member_id:
            parent = self.env['res.partner'].search([('suffix_id.name', '=', 'A'), ('member_number', '=', self.member_id.member_number)])
            if parent:
                self.nik = self.member_id.nik + " / " + parent.name
            else:
                self.nik = self.member_id.nik
            if self.card_number_id.name != self.member_id.card_number or not self.card_number_id:
                card_number = self.env['card.number'].search([('name', '=', self.member_id.card_number)])
                self.card_number_id = card_number
            self.name = self.member_id.name
            self.client_id = self.member_id.member_client_id
            self.client_branch_id = self.member_id.client_branch_id
            self.program_id = self.member_id.program_id
            self.plan_id = self.member_id.program_plan_id
            self.member_number = self.member_id.member_number
            self.gender = self.member_id.gender
            self.dob = self.member_id.birth_date
            self.relationship = self.member_id.relationship
            self.suffix_id = self.member_id.suffix_id
            self.marital_status = self.member_id.marital_status
            self.policy_number = self.member_id.policy_number
            self.join_date = self.member_id.join_date
            self.start_date = self.member_id.start_date
            self.effective_date_member = self.member_id.effective_date_member
            self.end_date = self.member_id.end_date
            self.end_policy_date = self.member_id.end_policy_date
            self.policy_status = self.member_id.policy_status

    @api.onchange('card_number_id')
    def _onchange_card_number_id(self):
        if self.card_number_id:
            member = self.env['res.partner'].search([('card_number', '=', self.card_number_id.name), ('member', '=', True)])
            self.member_id = member

    @api.model
    def create(self, vals):
        vals['claim_number'] = self.env['ir.sequence'].next_by_code(
            'guarantee.letter.claim.number')
        return super(GuaranteeLetter, self).create(vals)

    def generate_gl_number(self):
        for rec in self:
            if rec.member:
                value = rec.benefit_master_id.category
                rec.gl_number = 'GL/' + dict(self.env['benefit.master']._fields['category'].selection)[value] + self.env['ir.sequence'].next_by_code('guarantee.letter.gl.number')
            if not rec.member:
                rec.gl_number = 'GLNM/' + dict(self.env['benefit.master']._fields['category'].selection)[value] + self.env['ir.sequence'].next_by_code('guarantee.letter.gl.number')

    final_gl_benefit = fields.Char(string='Benefit')
    final_gl_max_per_visit = fields.Float(string='Max Per Visit')
    final_gl_max_day_per_year = fields.Integer(string='Max Day Per Year')

    preadmission_state = fields.Selection([
        ('open', 'Open'),
        ('close', 'Closed'),
    ], string='Status')

    @api.model
    def retrieve_preadmission_dashboard(self):
        self.check_access_rights('read')
        # preadmission_case = self.search_count([('admission_date','=',date.today() - timedelta(days=1)),])
        preadmission_case = self.search_count([('admission_date', '=', fields.Date.today() + timedelta(days=1)), ('preadmission', '=', True)])
        preadmission_open = self.search_count([('preadmission_state', '=', 'open'), ('preadmission', '=', True)])
        preadmission_close = self.search_count([('preadmission_state', '=', 'close'), ('preadmission', '=', True)])
        res = {
            "total_preadmission_case": 0,
            "total_preadmission_open": 0,
            "total_preadmission_close": 0,
        }
        res['total_preadmission_case'] = preadmission_case
        res['total_preadmission_open'] = preadmission_open
        res['total_preadmission_close'] = preadmission_close
        return res

    @api.model
    def retrieve_guarantee_letter_dashboard(self):
        self.check_access_rights('read')
        maternity = self.search_count([('benefit_master_id.category', 'in', ['ip', 'ma'])])
        need_follow_up = self.search_count([('daily_fu', '=', False), ('claim_status', '!=', 'open')])
        followed_up = self.search_count([('daily_fu', '=', True), ('claim_status', '!=', 'open')])
        res = {
            "total_maternity": 0,
            "total_need_follow_up": 0,
            "total_followed_up": 0,
        }
        res['total_maternity'] = maternity
        res['total_need_follow_up'] = need_follow_up
        res['total_followed_up'] = followed_up
        return res

    def action_process_preadmission(self):
        return self.write({'preadmission_state': 'process'})

    def action_close_preadmission(self):
        for rec in self:
            if not rec.benefit_master_id:
                raise ValidationError("Claim Category is required to process preadmission")
        return self.write({'preadmission_state': 'close'})

    def case_history(self):
        for rec in self:
            res = self.env['ir.actions.act_window']._for_xml_id('asb_helpline_guarantee_letter.gl_case_history_smart_button')
            res['domain'] = [('member_id', '=', rec.member_id.id)]
            res['target'] = 'main'
            return res

    def guarantee_letter_relation_smart_button(self):
        action = self.env['ir.actions.act_window']._for_xml_id('asb_membership_member.member_relation_action')
        action['domain'] = [('member', '=', True), ('nik', '=', self.member_id.nik)]
        action['context'] = {'default_member': True}
        action['target'] = 'main'

        return action

    def action_get_attachment_view(self):
        for rec in self:
            if rec.attachment_ids:
                for scan in rec.attachment_ids:
                    scan.res_id = rec._origin.id
        res = self.env['ir.actions.act_window']._for_xml_id('asb_helpline_guarantee_letter.action_attachment_guarantee_letter')
        res['domain'] = [('res_model', '=', 'guarantee.letter'), ('res_id', 'in', self.ids)]
        res['context'] = {'default_res_model': 'guarantee.letter', 'default_res_id': self.id}
        res['target'] = 'main'
        return res

    currency_id = fields.Many2one('res.currency', 'Currency', default=lambda self: self.env.user.company_id.currency_id.id, required=True, tracking=True)
    amount_excess = fields.Float(compute='_compute_amount', string='Total Excess')
    amount_cover = fields.Float(compute='_compute_amount', string='Total Cover')
    amount_deductible = fields.Float(compute='_compute_amount', string='Total Deductible')
    amount_total = fields.Float(compute='_compute_amount', string='Total Amount')
    amount_approved = fields.Float(compute='_compute_amount', string='Total Approved')
    received_claim = fields.Float(string='Received Provider Claim')
    amount_difference = fields.Float(compute='_compute_amount_difference', string='Difference')

    @api.depends('received_claim', 'amount_total')
    def _compute_amount_difference(self):
        for rec in self:
            rec.amount_difference = abs(rec.received_claim - rec.amount_total)

    @api.depends(
        'final_gl_line.real_excess_amount',
        'final_gl_line.real_cover_amount',
        'final_gl_line.deductible_amount',
    )
    def _compute_amount(self):
        for rec in self:
            total_excess = 0
            total_cover = 0
            total_deductible = 0
            total_amount = 0
            total_approved = 0

            for line in rec.final_gl_line:
                total_excess += line.real_excess_amount
                total_cover += line.real_cover_amount
                total_amount += line.total_amount
                total_approved += line.approved_amount
                if line.deductible_amount > 0:
                    total_deductible += line.cover_amount - line.real_cover_amount

            rec.amount_excess = total_excess
            rec.amount_cover = total_cover
            rec.amount_total = total_amount
            rec.amount_approved = total_approved
            rec.amount_deductible = total_deductible

    def confirm_send_email(self):
        return {
            'name': "Send Email",
            'type': 'ir.actions.act_window',
            'res_model': 'confirm.send.email',
            'view_mode': 'form',
            'target': 'new',
            'context': {'default_guarantee_letter_id': self.id}
        }

    daily_fu = fields.Boolean(string='Daily Follow Up')

    def update_daily_fu(self):
        letter = self.env['guarantee.letter'].search([])
        for rec in letter:
            rec.daily_fu = False

    def lmi(self):
        return self.env.ref('asb_helpline_guarantee_letter.action_report_lmi').report_action(self)

    practitioner_id = fields.Many2one('helpline.master.practitioner', string='Practitioner Name', tracking=True)


import threading
import requests
import rumps
import json

class MCUserChecker(rumps.App):
    def __init__(self):
        super(MCUserChecker, self).__init__(name="MCUserChecker")
        self.serverIp = "brimwat.apexmc.co"
        self.serverPort = "25565"
        self.players = []
        self.numPlayers = 0

    @rumps.timer(5)
    def userCheckThread(self, sender):
        thread = threading.Thread(target=self.updateNumUsers)
        thread.start()

    def updateNumUsers(self):
        res = requests.get(f"https://mcapi.us/server/status?ip={self.serverIp}&port={self.serverPort}")
        numPlayers = json.loads(res.text)["players"]["now"]
        for user in json.loads(res.text)["players"]["sample"]:
            if user["name"] not in self.players:
                self.players.append(user["name"])
                self.menu.add(user["name"])
                rumps.MenuItem.update()
            if self.numPlayers != numPlayers:
                self.players = []
                self.numPlayers = numPlayers
                self.menu.clear()
        self.title = f"Players: {numPlayers}"

if __name__ == '__main__':
    MCUserChecker().run()


from lightkurve import search_lightcurve, LightCurveCollection, read
from lightkurve.correctors import CBVCorrector
import numpy as np
from lightkurve import KeplerLightCurveFile
import matplotlib.pyplot as plt

lc_collection = search_lightcurve('KIC 10001167', author='Kepler').download_all()
test = CBVCorrector(lc_collection[5])
period = 120.3903
print(test.cbvs)
plt.plot(np.mod(test.cbvs[0].time.value, period)/period, test.cbvs[0].to_designmatrix().X+np.array([1-x/16 for x in range(0, 16)]))
plt.figure()
plt.plot(np.mod(lc_collection[5].time.value, period)/period, lc_collection[5].normalize().flux, 'k--')
plt.show()
test.cbvs[0].plot()
plt.show()
# for i in range(0, len(lc_collection)):
#     lc = lc_collection[i]
#     cbvCorrector = CBVCorrector(lc, )
#     cbvCorrector.correct(cbv_indices=[np.arange(1, 4)])
    # lc_collection[i] = cbvCorrector.corrected_lc
#     cbvCorrector.corrected_lc.to_fits(f"lc_{i}.fits", overwrite=True)
# plt.figure()
ax = plt.axes()
period = 120.3903
for i in range(0, len(lc_collection)):
    if i==6:
        break
    lc = read(f"lc_{i}.fits")
    phase = np.mod(lc.time.value, period)/period
    plt.plot(phase, lc.normalize().flux, '.', markersize=1)
plt.show()


import torch
from torch.utils import benchmark

from .main_cnn import create_model as ResNet
from .main_transformer import create_model as ViT

for model in (ResNet(), ViT()):

    print(f"#### {model.__class__.__name__} ####")
    x = torch.randn(1, 3, 32, 32)  # CIFAR10

    params = sum(p.numel() for p in model.parameters() if p.requires_grad)
    print(f"Parameters: {params / 1e6 :.3f}M")

    t_cpu = benchmark.Timer(
        stmt="with torch.no_grad(): model(x)", globals={"x": x, "model": model}
    )

    print(t_cpu.timeit(100))

    x = torch.randn(1, 3, 32, 32).cuda()  # CIFAR10
    model.cuda()

    t_gpu = benchmark.Timer(
        stmt="with torch.no_grad(): model(x)", globals={"x": x, "model": model}
    )

    print(t_gpu.timeit(100))

"""
#### ResNet ####
Parameters: 11.174M
<torch.utils.benchmark.utils.common.Measurement object at 0x000001C70AA6D250>
with torch.no_grad(): model(x)
  15.06 ms
  1 measurement, 100 runs , 1 thread
<torch.utils.benchmark.utils.common.Measurement object at 0x000001C733442FD0>
with torch.no_grad(): model(x)
  2.86 ms
  1 measurement, 100 runs , 1 thread
#### VisionTransformer ####
Parameters: 26.355M
<torch.utils.benchmark.utils.common.Measurement object at 0x000001C73345C640>
with torch.no_grad(): model(x)
  11.85 ms
  1 measurement, 100 runs , 1 thread
<torch.utils.benchmark.utils.common.Measurement object at 0x000001C70AE59AF0>
with torch.no_grad(): model(x)
  5.58 ms
  1 measurement, 100 runs , 1 thread
"""


import logging

import pandas as pd
from nltk import tokenize
from nltk.sentiment.vader import SentimentIntensityAnalyzer

from lib.settings import DATA_DIR, LOG_LEVEL
from lib.characters import findAllMentionedCharacters

comments = pd.read_csv(DATA_DIR / 'comments.csv')
comments['Body'] = comments['Body'].fillna('')
analyzer = SentimentIntensityAnalyzer()
logging.basicConfig(level=LOG_LEVEL)
logger = logging.getLogger(__name__)

character_mention_data = {
	'Name': [],
	'Comment ID': [],
	'Sentence': [],
	'Compound Score': []
}

comment_sentiment_data = {
	'Comment ID': [],
	'Compound Score': []
}

for comment in comments.itertuples(name='Comment', index=False):
	id = comment[0]
	body = comment[4]
	logger.info("Processing comment {}: {}".format(id, body))

	sentence_list = tokenize.sent_tokenize(body)
	comment_scores = []
	for sentence in sentence_list:
		score = analyzer.polarity_scores(sentence)
		comment_scores.append(score['compound'])

		mentions = findAllMentionedCharacters(sentence)
		for mention in mentions:
			logger.info("Found mention of {} in comment {}. Sentence: {}".format(mention, id, sentence))
			character_mention_data['Name'].append(mention)
			character_mention_data['Comment ID'].append(id)
			character_mention_data['Sentence'].append(sentence)
			character_mention_data['Compound Score'].append(score['compound'])

	mean_comment_score = pd.np.mean(comment_scores) if len(comment_scores) != 0 else 0.0
	logger.info("Scores for comment {}: {}".format(id, mean_comment_score))
	comment_sentiment_data['Comment ID'].append(id)
	comment_sentiment_data['Compound Score'].append(mean_comment_score)

character_mentions = pd.DataFrame(character_mention_data)
comment_sentiments = pd.DataFrame(comment_sentiment_data)

character_mentions.to_csv(DATA_DIR / 'character-mentions.csv', index=False)
logger.critical("Saved {} character mentions to {}".format(len(character_mentions.index), DATA_DIR / 'character-mentions.csv'))
comment_sentiments.to_csv(DATA_DIR / 'comment-sentiments.csv', index=False)
logger.critical("Saved {} comment sentiments to {}".format(len(comment_sentiments.index), DATA_DIR / 'comment-sentiments.csv'))


from torch.utils.data import Dataset
from PIL import Image
import os
import random

def make_dataset(dir):
    images = []
    for f in sorted(os.listdir(dir)):
        images.append(os.path.join(dir, f))
    return images

class Unaligned_dataset(Dataset):
    def __init__(self, rootA, rootB, transformA=None, transformB=None,
                 randomB=True):
        self.data_pathA = rootA
        self.data_pathB = rootB
        self.transformA = transformA
        self.transformB = transformB
        self.imagesA = make_dataset(self.data_pathA)
        self.imagesB = make_dataset(self.data_pathB)

        self.randomB = randomB

    def __getitem__(self, index):
        indexA = index % len(self.imagesA)
        if self.randomB:
            indexB = random.randint(0, len(self.imagesB) - 1)
        else:
            indexB = index % len(self.imagesB)
        imgA = Image.open(self.imagesA[indexA]).convert('RGB')
        imgB = Image.open(self.imagesB[indexB]).convert('RGB')
        if self.transformA is not None:
            imgA = self.transformA(imgA)
        if self.transformB is not None:
            imgB = self.transformB(imgB)
        return {'A': imgA, 'B': imgB}

    def __len__(self):
        return max(len(self.imagesA), len(self.imagesB))

class ImageFolder(Dataset):
    def __init__(self, root, transform=None):
        self.data_path = root
        self.transform = transform
        self.images = make_dataset(self.data_path)

    def __getitem__(self, index):
        img = Image.open(self.images[index]).convert('RGB')
        if self.transform is not None:
            img = self.transform(img)
        return img

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


# 거짓말
# union-find
# 23.07.15
# https://www.acmicpc.net/problem/1043

from itertools import combinations

def find_parent(idx):
    if parent[idx] != idx:
        parent[idx] = find_parent(parent[idx])
    return parent[idx]

def union(a, b):
    root_a = find_parent(a)
    root_b = find_parent(b)

    if root_a < root_b:
        parent[root_b] = root_a
    else:
        parent[root_a] = root_b

N, M = map(int, input().split())
parent = [i for i in range(N + 1)]
_, *truth_nums = map(int, input().split())

party = []
for _ in range(M):
    _, *party_nums = map(int, input().split())
    party.append(party_nums)

if not truth_nums:
    print(M)
else:
    for a, b in combinations(party_nums, 2):
        union(a, b)

    for nums in party:
        for a, b in combinations(nums, 2):
            union(a, b)

    truth_nums = [find_parent(i) for i in truth_nums]
    truth_nums = [i for i in range(1, N + 1) if find_parent(i) in truth_nums]

    cnt = 0
    for nums in party:
        if len(set(nums) & set(truth_nums)) == 0:
            cnt += 1

    print(cnt)


import pyodbc
import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

#Connecting to SQL Server
server_name = "DESKTOP-5F0PH62\SQLEXPRESS"
db_name = "Youtube_Trend_DW_ALL"

server = "Server="+str(server_name)
db = "Database="+str(db_name)
key = "Driver={SQL Server Native Client 11.0};"+server+";"+db+";"+"Trusted_Connection=yes;"

cnxn = pyodbc.connect(key)

sql= """

    select Top 50 Title as Videos,Likes,Most_Watched,Country,Category,Year_ as Year  from Trends_Fact as T
    inner join Video_DIM as V
    on v.VideoKey=T.VideoKey
    inner join Channel_DIM as C
    on C.ChannelKey=T.ChannelKey
    inner join Date_DIM as D
    on D.DateKey = T.DateKey
    Where  Top_Rated>80000
    order by Top_Rated desc
"""
df=pd.read_sql(sql, cnxn)
df.head(50)

x1=np.array(df['Likes'])
x2=np.array(df['Most_Watched'])

x3=np.array(df['Country'])
x4=np.array(df['Category'])

#Scattered Plot
plt.plot()
plt.title('Youtube Dataset')
plt.scatter(x1,x2)
plt.show()

plt.plot()
plt.title('Most Watched videos by category')
plt.scatter(x3,x4)
plt.xlabel('Country')
plt.ylabel('Category')
plt.show()

#Numpy Zip method
x=np.array(list(zip(x1,x2)))
colors = ['b','g','r']
markers = ['o','v','s']

plt.xlabel('Likes')
plt.ylabel('Most_Watched')

kmeans= KMeans(n_clusters=3).fit(x)

plt.scatter(kmeans.cluster_centers_[:,0], kmeans.cluster_centers_[:,1], s=200, c='Black', label='Trends')

for i,j in enumerate(kmeans.labels_):
    plt.plot(x1[i], x2[i], color=colors[j], marker= markers[j])

plt.legend()
plt.show()


import pygame
from math import *
import matplotlib.pyplot as plt
import time
import numpy as np

class Screen:
    def __init__(self):
        pygame.init()
        self.max_x, self.max_y = 1000, 500
        self.scr = pygame.display.set_mode([self.max_x, self.max_y])

        self.dt = 0.3
        self.N = 5000
        pygame.time.delay(100)

        self.veh = [(0, self.max_y//2)]
        self.v = [Vehicle(0.01, self.veh[-1])]

        self.running = True
        self._handle()

    def _handle(self):
        for i in range(self.N):
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    break

            self._refresh()
            time.sleep(0.001/self.dt)
            pygame.display.flip()
        pygame.quit()

    def _struct(self):
        self.scr.fill((255, 255, 255))
        pygame.draw.rect(self.scr, (100, 0, 255), pygame.Rect(-2, self.max_y//2, self.max_x + 4, 20), 2)

    def _veh(self):
        if self.v[-1].x > 100:
            self.veh.append((0, self.max_y//2))
            self.v.append(Vehicle(0.01, self.veh[-1]))

        for i in range(len(self.veh)):
            self.v[i].update(i, self.v, self.dt)
            pygame.draw.rect(self.scr, (0, 0, 0), self.v[i].get_model())

    def _test(self):
        pygame.draw.circle(self.scr, (0, 0, 255), (250, 250), 75)

    def _refresh(self):
        self._struct()
        self._veh()
        pygame.display.update()

class Vehicle:
    def __init__(self, speed, coord):
        self.speed = speed
        self.x, self.y = coord[0], coord[1]

        self.dist = 50

        self.pos = []; self.err = []

        self.l = 4
        self.s0 = 8
        self.T = 0.8
        self.v_max = 16.6
        self.a_max = 1.44
        self.b_max = 4.61

        self.path = []
        self.current_road_index = 0

        self.x = 0
        self.v = self.v_max
        self.a = 0
        self.stopped = False

        self.sqrt_ab = 2*np.sqrt(self.a_max*self.b_max)
        self._v_max = self.v_max

    def get_model(self):
        return pygame.Rect((self.x, 255, 20, 10))

    def update(self, i, veh, dt):
        if i > 0:
            lead = veh[i-1]

            if self.v + self.a*dt < 0:
                self.x -= 1/2*self.v*self.v/self.a
                self.v = 0
            else:
                self.v += self.a*dt
                self.x += self.v*dt + self.a*dt*dt/2

            # Update acceleration
            alpha = 0
            if lead:
                delta_x = lead.x - self.x - lead.l
                delta_v = self.v - lead.v

                alpha = (self.s0 + max(0, self.T*self.v + delta_v*self.v/self.sqrt_ab)) / delta_x

                self.a = self.a_max * (1-(self.v/self.v_max)**4 - alpha**2)

                if delta_x < 100:
                    self.a = self.a = -self.b_max*self.v/self.v_max

        else:
            if self.x < 500:
                self.speed = self.v_max
            elif self.x > 500 and self.x < 700:
                self.speed = self.v_max*0.3
            else:
                self.v = 0
            self.x += self.speed*dt

if __name__ == "__main__":
    t = Screen()


"""Create products table

Revision ID: 58fc4d3197f6
Revises:
Create Date: 2021-05-21 20:02:18.067173

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '58fc4d3197f6'
down_revision = None
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('products',
    sa.Column('id', sa.BigInteger(), autoincrement=True, nullable=False),
    sa.Column('name', sa.String(), nullable=False),
    sa.Column('price', sa.Numeric(), nullable=False),
    sa.Column('photo', sa.String(), nullable=True),
    sa.Column('description', sa.String(), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('products')
    # ### end Alembic commands ###


import pandas as pd
import json
import re

# add the path to your reconciled csv to produce the compact jsonld.
# to change the context by :
#       - replace the '@context' key below with another context in the url
#       - make changes to the file 'context.jsonld' and make sure to push the
#         changes on the appropriate branch (make sure the link below match where you host the context)

df = pd.read_csv('../reconciled_cantus_09292023.csv')
json_data = df.to_json(orient='records')
parsed_json = json.loads(json_data)

def handle_rec_col(work,key):
    val = work.pop(key)
    wID = work.pop(f'{key}_@id')
    if val is None:
        return None
    if re.match(r"^[Q]\d+", wID ): #if cell was reconciled with wikidata
        # work["@context"].append({key:f"wdt:{name_wID}"}) # overwrite context
        return {"@id":f"wd:{wID}",
                "name":val}

    if val[:8]=="https://" : #if cell was reconciled with another source
        return {"@id": val}
    else: #cell is value
        # work["@context"].append({key:f"wdt:{wID}"}) # overwrite context
        return val

def create_json_compact(js):
    for work in js:

        work["@context"] = "https://raw.githubusercontent.com/malajvan/linkedmusic-datalake/main/cantusdb/jsonld/context.jsonld"
        work["@id"] = f"chant:{work.pop('chant_id')}" #the @id of each document should be the link to the chant in its database
        work["@type"] = "wd:Q23072435" #chant

        work['database'] = 'cantusdb:'
        work["composer"] = handle_rec_col(work, "composer")    #composer anonymous
        # work["P1922"] = work.pop("incipit")

        work["genre"] = handle_rec_col(work,"genre")

        # work["Q731978"] = f'wd:{work.pop("mode_name")}'
        work["mode_name"] = handle_rec_col(work, "mode_name")

        # work["Q4484726"] = work.pop("finalis") #wikidata Final is closest term to finalis

        work["source"] = work.pop("src_link")

        work["cantus_id"] = f"https://cantusindex.org/id/{work.pop('cantus_id')}"
        del work["mode"]

        for key in list(work.keys()): #clean up nulls
            if work[key] is None:
                del work[key]
    # Print the nested list of dictionaries
    return json.dumps(js, indent=4)

with open('compact.jsonld', 'w') as json_file:
    json_file.write(create_json_compact(parsed_json))


import OpenEXR

infile = OpenEXR.InputFile("GoldenGate.exr")
h = infile.header()
channels = h['channels'].keys()
newchannels = dict(zip(channels, infile.channels(channels)))

h['comments'] = "A picture of some delicious pie"

out = OpenEXR.OutputFile("modified.exr", h)
out.writePixels(newchannels)


from sqlalchemy.orm import Session

from app.crud.base import CRUDBase
from app.models.batch import Batch
from app.schemas.batch import BatchCreate

class CRUDBatch(CRUDBase[Batch, BatchCreate]):
    def get_by_company(self, db: Session, *, company_id: str) -> list[Batch]:
        return db.query(Batch).filter(Batch.company_id == company_id).all()

    def create(self, db: Session, *, company_id: int, submitter_id: int) -> Batch:
        batch_creation_obj = {
            "submitter_id": submitter_id,
            "company_id": company_id,
        }
        db_obj = Batch(**batch_creation_obj)
        db.add(db_obj)
        db.commit()
        db.refresh(db_obj)
        return db_obj

batch = CRUDBatch(Batch)


import pygame
from button import Button
from colors import *
from font import *
from network import Network

pygame.font.init()

WIDTH = 700
HEIGHT = 500

window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Client")

def half_width(text):
    return WIDTH / 2 - text.get_width() / 2

def redraw_window(window, game, p):
    window.fill(GREY_DR)

    if not (game.connected()):
        font = create_font(25, bold=True)
        text = font.render("Waiting for Player...", 1, YELLOW_DR, True)
        window.blit(text, (half_width(text), HEIGHT / 2 - text.get_height() / 2))
    else:
        font = create_font(20)
        text = yellow_font(font, "Your Move: ")
        window.blit(text, (20, 50))

        text = yellow_font(font, "Opponent's Move: ")
        window.blit(text, (330, 50))

        move1 = game.get_player_move(0)
        move2 = game.get_player_move(1)

        if game.both_went():
            text1 = white_font(font, move1)
            text2 = white_font(font, move2)
        else:
            if game.p1Went and p == 0:
                text1 = white_font(font, move1)
            elif game.p1Went:
                text1 = white_font(font, "Locked In")
            else:
                text1 = white_font(font, "Waiting...")
            if game.p2Went and p == 1:
                text2 = white_font(font, move2)
            elif game.p2Went:
                text2 = white_font(font, "Locked In")
            else:
                text2 = white_font(font, "Waiting...")

        if p == 1:
            window.blit(text2, (100, 50))
            window.blit(text1, (450, 50))
        else:
            window.blit(text1, (100, 50))
            window.blit(text2, (450, 50))

        for btn in buttons:
            btn.draw(window)

    pygame.display.update()

buttons = [
    Button("Rock", 50, 300, GREY),
    Button("Paper", 250, 300, PURPLE_DR),
    Button("Scissor", 450, 300, BLUE_DR),
    Button("Lizard", 350, 200, ORANGE_DR),
    Button("Spock", 150, 200, GREEN_DR),
]

def main():
    run = True
    clock = pygame.time.Clock()
    n = Network()
    player = int(n.get_p())
    print("You are player", player)

    while run:
        clock.tick(60)
        try:
            game = n.send("get")
        except:
            run = False
            print("Couldn't get game")
            break

        if game.both_went():
            redraw_window(window, game, player)
            pygame.time.delay(500)
            try:
                game = n.send("reset")
            except:
                run = False
                print("Couldn't get game")
                break

            font = create_font(25, bold=True)
            if (game.winner() == 1 and player == 1) or (game.winner() == 0 and player == 0):
                text = green_font(font, "You Won!")
            elif game.winner() == -1:
                text = green_font(font, "Tie Game!")
            else:
                text = green_font(font, "You Lost...")

            window.blit(text, (half_width(text), HEIGHT - 40))
            pygame.display.update()
            pygame.time.delay(2000)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                run = False
                pygame.quit()

            if event.type == pygame.MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
                for btn in buttons:
                    if btn.click(pos) and game.connected():
                        if player == 0:
                            if not game.p1Went:
                                n.send(btn.text)
                        else:
                            if not game.p2Went:
                                n.send(btn.text)

        redraw_window(window, game, player)

def menu_screen():
    run = True
    clock = pygame.time.Clock()

    while run:
        clock.tick(60)
        window.fill(GREY_DR)
        font = create_font(25, bold=True)
        text = yellow_font(font, "Click to Play!")
        window.blit(text, (half_width(text), HEIGHT / 2 - text.get_height() / 2))
        pygame.display.update()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                run = False
            if event.type == pygame.MOUSEBUTTONDOWN:
                run = False

    main()

while True:
    menu_screen()


""" Creation of the database to store new found cars"""

import sqlite3

conn = sqlite3.connect("name_of_the_database.db")
cursor = conn.cursor()

cursor.execute("DROP TABLE IF EXISTS auto")
cursor.execute("""
  CREATE TABLE IF NOT EXISTS auto(
  name char(50) PRIMARY KEY,
  price integer,
  link char(100)
  )
""")


import torch
import torch.nn as nn
import torch.nn.functional as F
from model.layers import GraphAttentionLayer, SpGraphAttentionLayer
import numpy as np
class MGAT(nn.Module):
    def __init__(self):

        super(MGAT, self).__init__()
        self.lstm = nn.LSTM(10,2,2,batch_first=True)
        self.L = nn.Sequential(
            nn.Flatten(),
            nn.Linear(10,1)
        )
    def forward(self,x,h):
        # print(x.shape,h[0].shape)
        out,H_ = self.lstm(x,h)
        out = self.L(out)
        # print(out.shape,H_[0].shape)

        return out,H_
lstm_len = 5
md = MGAT()
datamaxt = np.array([[(i * 10 + xp) for xp in range(10)] for i in range(0,20)])
label = np.array([i for i in range(100)])
# print(datamaxt.shape)
# print(datamaxt[0])
# print(label)
###########每个时刻10个值现在有20个时刻
# h_1 = torch.zeros(2,1,2).float()
# h_2 = torch.zeros(2,1,2).float()
optimizer_G = torch.optim.Adam(md.parameters(), lr=0.0007)

for epochnum in range(1000):
    h_1 = torch.zeros(2, 1, 2).float()
    h_2 = torch.zeros(2, 1, 2).float()
    for i in range(20 - lstm_len):

        print(torch.from_numpy(datamaxt[i:i+5]).unsqueeze(0).shape)
        out,H_ = md(torch.from_numpy(datamaxt[i:i+5]).unsqueeze(0).float(),(h_1,h_2))
        # print(out.shape)
        # print("s",torch.from_numpy(label)[i].unsqueeze(0))
        loss = F.mse_loss(out.squeeze(0).float(),torch.from_numpy(label)[i].unsqueeze(0).float())

        h_1 = H_[0].detach()
        h_2 = H_[1].detach()

        optimizer_G.zero_grad()
        loss.backward()
        optimizer_G.step()
    print(loss)
h_1 = torch.zeros(2,1,2).float()
h_2 = torch.zeros(2,1,2).float()
with torch.no_grad():
    for i in range(20 - lstm_len):
        out, H_ = md(torch.from_numpy(datamaxt[i:i + 5]).unsqueeze(0).float(), (h_1, h_2))
        # print(out.shape)
        # print("s",torch.from_numpy(label)[i].unsqueeze(0))
        print("out",out)
        print("label",label[i])
        loss = F.mse_loss(out.squeeze(0), torch.from_numpy(label)[i].unsqueeze(0))
        h_1 = H_[0].detach()
        h_2 = H_[1].detach()


import discord
from discord.ext import commands
from discord.utils import get

from utils.db_conn import query_select, query_update
#import utils.utils_func
from utils.funcs import check_status
#import utils.errors

import json

# verimmunity
# immunity
# immunitylist

class Immunity(commands.Cog):
    '''
    Sistem de imunitate. Cei imuni de pe server nu vor fi afectați de bot (nu vor mai primi grade etc)
    '''

    def __init__(self, client):
        self.client = client

    @commands.Cog.listener()
    async def on_ready(self):
        print("Immunity cog s-a incarcat cu succes.")

        # salveaza local in json membrii cu imunitate
        infos = query_select('select * from `immunity`')

        immune_users = dict()

        for info in infos:
            if str(info[0]) not in immune_users.keys():
                immune_users[str(info[0])] = list()
            immune_users[str(info[0])].append(info[1])

        with open("json/immunity.json", "w") as file:
            json.dump(immune_users, file, indent=2)

    #
    #
    #
    # =========================================================================
    # Verifica daca un membru are imunitate
    @commands.command()
    @commands.guild_only()
    @commands.has_permissions(administrator=True)
    async def verimmunity(self, ctx, member: discord.Member):
        '''
        Verifici imunitatea unui membru (dacă are sau nu)
        '''
        await ctx.message.delete()

        embed = discord.Embed(
            title=f'👁🗨 Este {member} imun?',
            description=f'👉 {"Da, este" if check_status("immunity", member) else "Nu, nu este"} 👀 ',
            color=discord.Color.from_rgb(117, 114, 218)
        )
        await ctx.send(embed=embed)
    # =========================================================================
    #
    #
    #
    # =========================================================================
    # Adauga sau scoate imunitatea unui membru

    @commands.command()
    @commands.guild_only()
    @commands.has_permissions(administrator=True)
    async def immunity(self, ctx, action, member: discord.Member):
        '''
        Comanda cu care adaugi sau scoți imunitate pentru un membru
        immunity add - Adaugi imunitate unui membru
        immunity remove - Ștergi imunitatea unui membru
        '''
        await ctx.message.delete()

        with open("json/immunity.json", "r") as file:
            result = json.load(file)

        if action == "add":
            if str(member.guild.id) not in result.keys():
                result[str(member.guild.id)] = [member.id]
                await ctx.send(f'{member.mention} a fost adăugat pe lista de imunitate.')
                query_update(
                    f'insert into `immunity`(`guild_id`, `member_id`) values ({ctx.guild.id}, {member.id})')

            elif member.id not in result[str(member.guild.id)]:
                result[str(member.guild.id)].append(member.id)
                await ctx.send(f'{member.mention} a fost adăugat pe lista de imunitate.')
                query_update(
                    f'insert into `immunity`(`guild_id`, `member_id`) values ({ctx.guild.id}, {member.id})')
            else:
                await ctx.send(f'{member.mention} este deja pe lista de imunitate.')

        elif action == "remove":
            if str(member.guild.id) not in result.keys():
                await ctx.send(f'{member.mention} nu este imun.')
            elif member.id in result[str(member.guild.id)]:
                result[str(member.guild.id)].remove(member.id)
                await ctx.send(f'{member.mention} a fost șters de pe lista de imunitate.')
                query_update(
                    f'delete from `immunity` where `guild_id`={ctx.guild.id} and `member_id`={member.id}')
            else:
                await ctx.send(f'{member.mention} nu este imun.')

        else:
            await ctx.send("Folosește 'add' sau 'remove'. Alte valori nu funcționează.")

        with open("json/immunity.json", "w") as file:
            json.dump(result, file, indent=2)
    # =========================================================================
    #
    #
    #
    # =========================================================================
    # Arata o lista cu toti membrii cu imunitate de pe server

    @commands.command()
    @commands.guild_only()
    @commands.has_permissions(administrator=True)
    async def immunitylist(self, ctx):
        '''
        Afișează o listă cu toți cei imuni de pe server.
        '''
        # da load la fisierul json
        with open("json/immunity.json", "r") as file:
            result = json.load(file)

        # daca exista membri incarcati pentru acel server, se pastreaza in memorie doar membri acelui server
        if str(ctx.guild.id) in result.keys():
            result = result[str(ctx.guild.id)]
        else:
            return

        # list comprehension in care se da get la fiecare membru pentru a-i da mention in embed
        members = [(await ctx.guild.fetch_member(id)).mention for id in result]

        if members:
            string = ','.join(members)
        else:
            string = "Nu există!"

        embed = discord.Embed(
            title="🤠 Membrii cu imunitate",
            description=string,
            color=discord.Color.from_rgb(117, 114, 218)
        )

        await ctx.send(embed=embed)
    # =========================================================================

def setup(client):
    client.add_cog(Immunity(client))


import pytest

URL = "https://watch.tonton.com.my/#/"
UNSUPPORTED_CSS = ".ua-barrier"
LOGIN_CSS = ".login-page-container"

# The site can take a little time to load, and this includes
# interstitial ads, so for now we give it 10 seconds.

# Skip Android as the site blocks many Android devices including the emulator
@pytest.mark.skip_platforms("android")
@pytest.mark.asyncio
@pytest.mark.with_interventions
async def test_enabled(client):
    await client.navigate(URL)
    assert client.await_css(LOGIN_CSS, timeout=10)

@pytest.mark.skip_platforms("android")
@pytest.mark.asyncio
@pytest.mark.without_interventions
async def test_disabled(client):
    await client.navigate(URL)
    assert client.await_css(UNSUPPORTED_CSS, timeout=10)


from detectron2.engine import DefaultTrainer
from detectron2.evaluation import COCOEvaluator
from detectron2 import model_zoo

from utils import get_my_cfg
from visdrone import register_one_set

import os

if __name__ == '__main__':
    train_dataset = "VisDrone2019-DET-train"
    val_dataset = "VisDrone2019-DET-val"
    register_one_set(train_dataset)
    register_one_set(val_dataset)

    cfg = get_my_cfg()
    cfg.MODEL.WEIGHTS = model_zoo.get_checkpoint_url("COCO-Detection/faster_rcnn_R_50_FPN_1x.yaml")
    cfg.DATASETS.TRAIN = (train_dataset, )
    cfg.DATASETS.TEST = (val_dataset, )

    os.makedirs(cfg.OUTPUT_DIR, exist_ok=True)
    trainer = DefaultTrainer(cfg)
    trainer.resume_or_load(resume=False)
    trainer.train()

    evaluator = COCOEvaluator(val_dataset, ("bbox",), False, output_dir=os.path.join("output", "evaluate"))
    trainer.test(cfg, trainer.model, evaluator)