import zipfile
from functools import partial
import re
from collections import OrderedDict, Counter, defaultdict
import shutil
import os
from pprint import pprint
import datetime
import subprocess
import sys
from pathlib import Path

import mistune

from book_section import (CHAPTER, PART, SUBCHAPTER, TOC,
                          _parse_header_line,
                          BookSectionWithNoFiles)
#from citations import create_citation_note, create_bibliography_citation
from references import process_citations

this_module_dir = Path(os.path.dirname(os.path.abspath(__file__)))

EPUBCHECK_JAR = 'epubcheck-4.0.2/epubcheck.jar'
EPUBCHECK_JAR = 'epubcheck-4.2.2/epubcheck.jar'

EPUBCHECK_JAR = this_module_dir / EPUBCHECK_JAR

CONTAINER_XML = '''<?xml version='1.0' encoding='utf-8'?>
<container xmlns="urn:oasis:names:tc:opendocument:xmlns:container" version="1.0">
  <rootfiles>
    <rootfile media-type="application/oebps-package+xml" full-path="EPUB/content.opf"/>
  </rootfiles>
</container>'''

NAV_HEADER_XML = '''<?xml version='1.0' encoding='utf-8'?><!DOCTYPE html>

<title>{title}</title>

'''

NBSP = ' '
NBSP = ' '
EPUB_VERSION = 'old'
ENDNOTES_EPUB_TYPE = {'old': 'rearnotes',
                      '3.1': 'endnotes'}
EPUB_META_DIR = 'META-INF'
EPUB_DIR = 'EPUB'
XHTML_FILES_EXTENSION = 'xhtml'
CONTAINER_XML_FPATH = EPUB_META_DIR + '/container.xml'
ENDNOTE_CHAPTER_ID = 'endnotes'
ENDNOTE_CHAPTER_TITLE = {'es': 'Notas',
                         'en': 'Notes'}
BIBLIOGRAPHY_CHAPTER_ID = 'bibliography'
BIBLIOGRAPHY_CHAPTER_TITLE = {'es': 'Bibliografía',
                              'en': 'Bibliography'}
BACK_MATTER_PART_ID = 'back_matter_part'
BACK_MATTER_PART_FPATH = EPUB_DIR + f'/appendices.{XHTML_FILES_EXTENSION}'
APPENDICES_PART_TITLE = {'es': 'Apéndices',
                         'en': 'Appendices'}
TOC_CHAPTER_TITLE = {'es': 'Índice',
                     'en': 'Table of contents'}
TOC_CHAPTER_ID = 'toc'
NAV_FPATH = EPUB_DIR + f'/nav.{XHTML_FILES_EXTENSION}'

CHAPTER_HEADER_HTML = '''<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html>

<title>{title}</title>
\n'''

CHAPTER_SECTION_LINE = '''

'''

NCX_HEADER_XML = '''<?xml version='1.0' encoding='utf-8'?>
<ncx xmlns="http://www.daisy.org/z3986/2005/ncx/" version="2005-1">'''
NCX_FPATH = os.path.join(EPUB_DIR, 'toc.ncx')

OPF_HEADER_XML = '''<?xml version='1.0' encoding='utf-8'?>
<package unique-identifier="id" version="3.0" xmlns="http://www.idpf.org/2007/opf" prefix="rendition: http://www.idpf.org/vocab/rendition/#">
  <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
'''
OPF_FPATH = os.path.join(EPUB_DIR, 'content.opf')

def _get_epub_fpath_for_endnote_chapter():
    return EPUB_DIR + f'/endnotes.{XHTML_FILES_EXTENSION}'

def _get_epub_fpath_for_bibliography_chapter():
    return EPUB_DIR + f'/bibliography.{XHTML_FILES_EXTENSION}'

def _get_epub_fpath_for_toc_chapter():
    return EPUB_DIR + f'/toc.{XHTML_FILES_EXTENSION}'

def _create_epub_fpath_for_section(section):
    if section.kind == CHAPTER:
        if section.id == ENDNOTE_CHAPTER_ID:
            fpath_in_epub = _get_epub_fpath_for_endnote_chapter()
        elif section.id == BIBLIOGRAPHY_CHAPTER_ID:
            fpath_in_epub = _get_epub_fpath_for_bibliography_chapter()
        elif section.id == TOC_CHAPTER_ID:
            fpath_in_epub =  _get_epub_fpath_for_toc_chapter()
        else:
            fpath_in_epub = EPUB_DIR + f'/chapter_{section.idx}.{XHTML_FILES_EXTENSION}'
    elif section.kind == PART:
        fpath_in_epub = EPUB_DIR + f'/part_{section.idx}.{XHTML_FILES_EXTENSION}'
    elif section.kind == SUBCHAPTER:
        fpath_in_epub = EPUB_DIR + f'/chapter_{section.parent.idx}.{XHTML_FILES_EXTENSION}'
    else:
        raise ValueError(f'No fpath defined for this kind of section: {section.kind}')
    return fpath_in_epub

_NUM_FOOTNOTES_AND_CITATIONS_SEEN = Counter()
_FOOTNOTE_IDS_SEEN = defaultdict(set)
_CITATION_COUNTS = defaultdict(Counter)
_FOOTNOTE_DEFINITION_ID_COUNTS = defaultdict(Counter)

def _create_html_for_numbered_footnote(number):
    html = f'{number}'
    return html

def _footnote_processor(footnote, endnote_chapter_fpath, book):
    footnote_id = footnote['match'].group('id')
    book_id = id(book)

    if footnote_id in _FOOTNOTE_IDS_SEEN[book_id]:
        raise RuntimeError('Repeated footnote ID: ' + footnote_id)
    _FOOTNOTE_IDS_SEEN[book_id].add(footnote_id)
    _NUM_FOOTNOTES_AND_CITATIONS_SEEN[book_id] += 1

    fpath = os.path.join('..', endnote_chapter_fpath)
    href_to_footnote_definition = f'{fpath}#ftd_{footnote_id}'
    a_id = f'ft_{footnote_id}'

    html = _create_html_for_numbered_footnote(_NUM_FOOTNOTES_AND_CITATIONS_SEEN[book_id])

    text = f'{html}'
    return {'processed_text': text,
            'match_location': footnote['match'].span()[0],
            'footnote_id': footnote_id}

def _footnote_definition_processor(footnote_definition,
                                   fpath_for_section_in_epub, book):
    footnote_id = footnote_definition['match'].group('id')
    book_id = id(book)
    _FOOTNOTE_DEFINITION_ID_COUNTS[book_id][footnote_id] += 1
    if _FOOTNOTE_DEFINITION_ID_COUNTS[book_id][footnote_id] > 1:
        raise RuntimeError('More than one footnote definition for footnote ID: ' + footnote_id)

    li_id = f'ftd_{footnote_id}'
    content = footnote_definition['match'].group('content').strip()
    text = f'

{content}

'

    return {'footnote_definition_li': text,
            'footnote_id': footnote_id}

class _CitationProcessor:
    def __init__(self, bibliography_chapter_fpath,
                 endnote_chapter_fpath,
                 bibliography_entries_seen,
                 references_not_found,
                 citation_counts,
                 book):
        self.bibliography_chapter_fpath = bibliography_chapter_fpath
        self.endnote_chapter_fpath = endnote_chapter_fpath
        self.book = book
        self.bibliography_path = book.bibliography_path
        self.bibliography_entries_seen = bibliography_entries_seen
        self.references_not_found = references_not_found
        self.last_citation_id_processed_for_section = {}
        self.last_citation_texts = []
        self._citation_counts = citation_counts

    def __call__(self, citation, fpath_for_section_in_epub):

        debug_notes = ['Mimir2019', 'whatisreal']
        debug_notes = []

        bibliography_path = self.bibliography_path
        if bibliography_path is None:
            msg = 'No bibliography defined in metadata, but citations are used'
            raise ValueError(msg)

        book_id = id(self.book)
        citation_id = citation['match'].group('id')
        citation_counts = self._citation_counts
        citation_counts[citation_id] += 1
        footnote_id = f'{citation_id}_{citation_counts[citation_id]}'
        _NUM_FOOTNOTES_AND_CITATIONS_SEEN[book_id] += 1

        if citation_id in debug_notes:
            print('citation_id', citation_id)

        fpath = os.path.join('..', self.endnote_chapter_fpath)
        href_to_footnote_definition = f'{fpath}#ftd_{footnote_id}'
        a_id = f'ft_{footnote_id}'

        if citation_id != self.last_citation_id_processed_for_section.get(fpath_for_section_in_epub):
            self.last_citation_id_processed_for_section[fpath_for_section_in_epub] = citation_id

        citation_texts_to_process = []
        if self.last_citation_id_processed_for_section.get(fpath_for_section_in_epub):
            citation_texts_to_process = self.last_citation_texts[:]

        citation_text = citation['text'].strip()

        citation_texts_to_process.append(citation_text)
        results = process_citations(citation_texts_to_process,
                                    libray_csl_json_path=bibliography_path)

        if citation_id in debug_notes and False:
            pprint(results)

        strip_citation_id = citation_id.strip().split(' ')[0].split('#')[0].strip('/:')
        if strip_citation_id in results['references_not_found']:
            self.references_not_found.update(results['references_not_found'])
            citation_result = None
            #citation_texts_to_process = citation_texts_to_process[:-1]
        else:
            citation_result = results['citations'][-1]
            if False:
                pprint(results)
                print('citation_id', citation_id)
                print('strip_id', strip_citation_id)
                print('pandoc_key', citation_result['citation_key'])
            assert citation_result['citation_key'].strip('/') == strip_citation_id

        if self.last_citation_id_processed_for_section.get(fpath_for_section_in_epub):
            self.last_citation_texts = citation_texts_to_process
        else:
            self.last_citation_texts = []

        if citation_id in debug_notes:
            print('citation_result')
            pprint(citation_result)

        if citation_result:
            html = _create_html_for_numbered_footnote(_NUM_FOOTNOTES_AND_CITATIONS_SEEN[book_id])
            text = f'{html}'

            li_id = f'ftd_{footnote_id}'
            back_href_to_footnote = f'{fpath_for_section_in_epub}#ft_{footnote_id}'
            footnote_definition_text = f'
{citation_result["footnote_html_text"]}
'

            self.bibliography_entries_seen.update(results['references'])
        else:
            text = citation['text']
            footnote_definition_text = None

        res = {'footnote_definition_li': footnote_definition_text,
                'processed_text': text,
                'match_location': citation['match'].span()[0]}

        if citation_id in debug_notes:
            print('result')
            pprint(res)

        return res

def _internal_link_processor(internal_link, book):
    text = internal_link['match'].group('text')
    link_id = internal_link['match'].group('link_id')
    section = book.get_section_by_id(link_id)
    fname = Path(_create_epub_fpath_for_section(section)).name
    link = fname

    link = _build_link(link, text, id_=section.id, no_id=True)
    return {'processed_text': link}

def _get_citation_location_in_text(footnote_definition, footnote_locations):
    if 'match_location' in footnote_definition:
        return footnote_definition['match_location']
    else:
        return footnote_locations[footnote_definition['footnote_id']]

def _split_md_text_in_items(md_text):

    # This algorithm has one limitation, it does not allow to have trees
    # it can only yield a stream of items, but not items within an item

    footnote_re = re.compile(r' *\[\^(?P<id>[^\]]+)\]')
    footnote_definition_re = re.compile(r'\[\^(?P<id>[^\]]*)\]:(?P<content>[^\n]+)')
    citation_re = re.compile(r' *\[@(?P<id>[^ \],]+),? *(?P<locator_term>[\w]*):? *(?P<locator_positions>[0-9]*)\]', re.UNICODE)
    internal_link_re = re.compile(r'\[(?P<text>[^\]]+)\]\(#(?P<link_id>[^\)]+)\)')

    item_kinds = OrderedDict([('footnote_definition', {'re': footnote_definition_re}),
                              ('footnote', {'re': footnote_re}),
                              ('citation', {'re': citation_re}),
                              ('internal_link', {'re': internal_link_re}),
                              ])
    re_idx = {item_kind: idx for idx, item_kind in enumerate(item_kinds.keys())}

    debug_text = '@whatisreal'
    debug_text = None

    if debug_text and debug_text in md_text:
        print('md_text')
        pprint(md_text)

    start_pos_to_search = 0
    while True:
        matches = []
        for kind, item_def in item_kinds.items():
            match = item_def['re'].search(md_text, start_pos_to_search)
            if match is None:
                continue
            matches.append({'kind': kind, 'match': match})
        if matches:
            matches.sort(key=lambda match: re_idx[match['kind']])
            matches.sort(key=lambda match: match['match'].start())
            next_match = matches[0]['match']
            kind = matches[0]['kind']
            if debug_text:
                print('matches')
                pprint(matches)
        else:
            yield {'kind': 'std_md',
                   'text': md_text[start_pos_to_search:]}
            break

        if start_pos_to_search < next_match.start():
            res = {'kind': 'std_md',
                   'text': md_text[start_pos_to_search:next_match.start()]}
            if debug_text and debug_text in res['text']:
                print('start_pos_to_search < next_match.start()', start_pos_to_search, next_match.start())
                print('yield result')
                pprint(res)
            yield res
        res = {'kind': kind,
               'text': md_text[next_match.start():next_match.end()],
               'match': next_match}
        if debug_text and debug_text in res['text']:
            print('yielding in last yield')
        yield res
        start_pos_to_search = next_match.end()

        if start_pos_to_search >= len(md_text):
            break

def _process_citations_and_footnotes(md_text,
                                     section,
                                     bibliography_entries_seen,
                                     references_not_found,
                                     endnote_definitions):
    items = _split_md_text_in_items(md_text)

    footnote_definitions = []

    fpath_for_section_in_epub = _create_epub_fpath_for_section(section)
    #split_text_in_items, item kinds: std_markdown, citation, footnote, footnote_definition,
    citation_processor = _CitationProcessor(bibliography_chapter_fpath=_get_epub_fpath_for_bibliography_chapter(),
                                            endnote_chapter_fpath=_get_epub_fpath_for_endnote_chapter(),
                                            bibliography_entries_seen=bibliography_entries_seen,
                                            references_not_found=references_not_found,
                                            citation_counts=_FOOTNOTE_DEFINITION_ID_COUNTS[id(section.book)],
                                            book=section.book)
    item_processors = {'footnote': partial(_footnote_processor,
                                           endnote_chapter_fpath=_get_epub_fpath_for_endnote_chapter(),
                                           book=section.book),
                       'footnote_definition': partial(_footnote_definition_processor,
                                                      fpath_for_section_in_epub=fpath_for_section_in_epub,
                                                      book=section.book),
                       'citation': partial(citation_processor,
                                           fpath_for_section_in_epub=fpath_for_section_in_epub),
                       'internal_link': partial(_internal_link_processor,
                                                book=section.book)
                      }

    debug_item = 'citation'
    debug_text_in_citation = 'what'
    debug_item = None

    processed_text = []
    footnote_locations = {}
    for item in items:
        processor = item_processors.get(item['kind'], None)
        if debug_item and item['kind'] == debug_item:
            if debug_text_in_citation:
                if debug_text_in_citation in item['text']:
                    pprint(item)
            else:
                pprint(item)
        if processor:
            processed_item = processor(item)
            if 'processed_text' in processed_item:
                text = processed_item['processed_text']
            else:
                text = None
        else:
            processed_item = None
            text = item['text']
        if debug_item and item['kind'] == debug_item:
            if debug_text_in_citation:
                if debug_text_in_citation in item['text']:
                    print('text', text)
            else:
                print('text', text)

        if (processed_item and 'footnote_definition_li' in processed_item and
            processed_item['footnote_definition_li']):
            definition = {'footnote_definition_li': processed_item['footnote_definition_li']}
            if 'match_location' in processed_item:
                definition['match_location'] = processed_item['match_location']
            if 'footnote_id' in processed_item:
                definition['footnote_id'] = processed_item['footnote_id']
            footnote_definitions.append(definition)

        if item['kind'] == 'footnote':
            footnote_locations[processed_item['footnote_id']] = processed_item['match_location']

        if text is not None:
            processed_text.append(text)

    get_citation_location_in_text = partial(_get_citation_location_in_text,
                                            footnote_locations=footnote_locations)
    footnote_definitions.sort(key=get_citation_location_in_text)
    endnote_definitions.extend(footnote_definitions)
    return {'rendered_text': ''.join(processed_text)}

def _process_basic_markdown(md_text):
    renderer = mistune.Renderer(use_xhtml=True)
    render_markdown = mistune.Markdown(renderer)
    xhtml_text = render_markdown(md_text)
    xhtml_text = xhtml_text.replace('<', '<').replace('>', '>')
    assert '<h' not in xhtml_text
    return xhtml_text

def _process_md_text(md_text, section, bibliography_entries_seen,
                     references_not_found,
                     endnote_definitions):
    md_text = '\n'.join(md_text)

    result = _process_citations_and_footnotes(md_text=md_text,
                                              section=section,
                                              bibliography_entries_seen=bibliography_entries_seen,
                                              references_not_found=references_not_found,
                                              endnote_definitions=endnote_definitions)
    result['rendered_lines'] = _process_basic_markdown(result['rendered_text'])
    return result

def _split_section_in_fragments(lines):
    fragment_lines = []
    for line in lines:
        if line.startswith('#'):
            if fragment_lines:
                yield {'kind': 'fragment',
                       'lines': fragment_lines}
            fragment_lines = []
            yield {'kind': 'header',
                   'text': line}
        else:
            fragment_lines.append(line)
    if fragment_lines:
        yield {'kind': 'fragment',
               'lines': fragment_lines}

def _create_html_for_md_text_in_section(section, bibliography_entries_seen,
                                        references_not_found):
    md_text = section.md_text

    rendered_lines = []
    footnote_definitions = []

    for fragment in _split_section_in_fragments(md_text):
        if fragment['kind'] == 'header':
            text = fragment['text']
            res = _parse_header_line(text)
            header = f'<h{res["level"]}>{res["text"]}</h{res["level"]}>\n'
            rendered_lines.append(header)
        elif fragment['kind'] == 'fragment':
            result = _process_md_text(fragment['lines'], section=section,
                                      bibliography_entries_seen=bibliography_entries_seen,
                                      references_not_found=references_not_found,
                                      endnote_definitions=footnote_definitions)
            rendered_lines.append(result['rendered_lines'])
    result = {'rendered_lines': rendered_lines,
              'footnote_definitions': footnote_definitions}
    return result

def _write_html_in_zip_file(epub_zip, fpath, html):
    #soup = BeautifulSoup(html, features='lxml')
    #pretty_html = soup.prettify(formatter=None)
    epub_zip.writestr(fpath, html)

def _create_chapter(chapter, epub_zip, bibliography_entries_seen,
                    references_not_found):

    footnote_definitions = []
    res = _create_html_for_md_text_in_section(chapter,
                                              bibliography_entries_seen=bibliography_entries_seen,
                                              references_not_found=references_not_found)
    html = '\n'.join(res['rendered_lines'])
    footnote_definitions.extend(res['footnote_definitions'])

    for subchapter in chapter.subsections:
        html += CHAPTER_SECTION_LINE.format(epub_type='subchapter',
                                            section_id=subchapter.id)
        res = _create_html_for_md_text_in_section(subchapter,
                                                  bibliography_entries_seen=bibliography_entries_seen,
                                                  references_not_found=references_not_found)
        footnote_definitions.extend(res['footnote_definitions'])
        html += '\n'.join(res['rendered_lines'])
        html += '
\n'

    _create_section_xhtml_file(title=chapter.title,
                               id_=chapter.id,
                               section_html=html,
                               fpath=_create_epub_fpath_for_section(chapter),
                               epub_type='chapter',
                               epub_zip=epub_zip)
    return {'footnote_definitions': footnote_definitions}

def _create_section_xhtml_file(title, id_, section_html, fpath, epub_type,
                               epub_zip):
    html = CHAPTER_HEADER_HTML.format(title=title)
    html += '\n'
    html += CHAPTER_SECTION_LINE.format(epub_type=epub_type,
                                        section_id=id_)
    html += section_html
    html += '
\n'
    html += '\n'
    html += '\n'
    _write_html_in_zip_file(epub_zip, fpath, html)

def _create_part(part, epub_zip, bibliography_entries_seen,
                 references_not_found,
                 endnote_definitions):
    title = part.title
    part_id = part.id
    fpath = _create_epub_fpath_for_section(part)

    result = _create_html_for_md_text_in_section(part,
                                                 bibliography_entries_seen=bibliography_entries_seen,
                                                 references_not_found=references_not_found)
    section_html = '\n'.join(result['rendered_lines'])
    endnote_definitions.extend(result['footnote_definitions'])

    _create_section_xhtml_file(title=title,
                               id_=part_id,
                               section_html=section_html,
                               fpath=fpath,
                               epub_type='part',
                               epub_zip=epub_zip)

    footnote_definitions = []
    for chapter in part.subsections:
        if chapter.kind == CHAPTER:
            res = _create_chapter(chapter, epub_zip,
                                  bibliography_entries_seen=bibliography_entries_seen,
                                  references_not_found=references_not_found)
            footnote_definitions.extend(res['footnote_definitions'])
        else:
            raise RuntimeError('A part should only have chapters as subparts.')
    endnote_definitions.extend(footnote_definitions)

def _create_endnotes_section_html(endnote_definitions):

    html = '
\n
\n'
    for endnote_definition in endnote_definitions:
        if endnote_definition['footnote_definition_li']:
            html += endnote_definition['footnote_definition_li']
            html += '\n'
    html += '
\n
\n'
    return html

def _create_endnotes_chapter(chapter, endnote_definitions, header_level,
                             epub_zip):
    html = f'  <h{header_level}>{chapter.title}</h{header_level}>\n'
    html += _create_endnotes_section_html(endnote_definitions)

    _create_section_xhtml_file(title=chapter.title,
                               id_=chapter.id,
                               section_html=html,
                               fpath=_create_epub_fpath_for_section(chapter),
                               epub_type=chapter.kind,
                               epub_zip=epub_zip)

def _create_bibliography_section_html(bibliography_entries, book):
    htmls = list(bibliography_entries.values())
    htmls.sort()

    html = '
\n
\n'
    for html_li in htmls:
        html += f'
{html_li}
\n'
    html += '
\n
\n'
    return html

def _create_bibliography_chapter(chapter, bibliography_entries, header_level,
                                 epub_zip):

    html = f'  <h{header_level}>{chapter.title}</h{header_level}>\n'
    html += _create_bibliography_section_html(bibliography_entries,
                                              chapter.book)

    _create_section_xhtml_file(title=chapter.title,
                               id_=chapter.id,
                               section_html=html,
                               fpath=_create_epub_fpath_for_section(chapter),
                               epub_type=chapter.kind,
                               epub_zip=epub_zip)

def _build_link(link, text, id_=None, for_nav=False):
    if id_ and not for_nav:
        li = f'{text}'
        #li = f'{text}'
    else:
        li = f'{text}'
    return li

def _build_nav_li_to_section(section, for_nav):
    try:
        has_no_html = section.has_no_html
    except AttributeError:
        has_no_html = False

    if section.kind == PART and has_no_html:
        section_fpath = None
    else:
        section_fpath = os.path.join('..',  _create_epub_fpath_for_section(section))
        #section_fpath = os.path.join(_create_epub_fpath_for_section(section))
        section_fname = os.path.basename(_create_epub_fpath_for_section(section))

    if section_fpath:
        li = _build_link(section_fname,
                         text=section.title,
                         id_=section.id,
                         for_nav=for_nav)
    else:
        li = f'{section.title}'
    return li

def _build_nav_for_chapter(chapter, for_nav):
    li = _build_nav_li_to_section(chapter, for_nav=for_nav)
    subchapters = list(chapter.subsections)

    if subchapters:
        html = f'
{li}\n'
        html += '
\n'
        for subchapter in subchapters:
            li = _build_nav_li_to_section(subchapter, for_nav=False)
            html += f'
{li}
\n'
        html += '

\n'
    else:
        html = f'
{li}
\n'
    return html

def _create_toc_section_html(book, for_nav=False):

    html = '\n'
    html += f'
{TOC_CHAPTER_TITLE[book.lang]}
\n'
    html += '
\n'

    for section in book.subsections:
        if section.kind == PART:
            li = _build_nav_li_to_section(section, for_nav=for_nav)
            html += f'
{li}\n'
            html += '
\n'
            for chapter in section.subsections:
                html += _build_nav_for_chapter(chapter, for_nav=for_nav)
            html += '

\n'
        elif section.kind == CHAPTER:
            html += _build_nav_for_chapter(section, for_nav=for_nav)

    html += '
\n'
    html += '\n'
    return html

def _create_toc_chapter(toc_chapter, header_level, epub_zip):

    _create_section_xhtml_file(title=toc_chapter.title,
                               id_=toc_chapter.id,
                               section_html=_create_toc_section_html(toc_chapter.book),
                               fpath=_get_epub_fpath_for_toc_chapter(),
                               epub_type=toc_chapter.kind,
                               epub_zip=epub_zip)

def _creata_nav(book, epub_zip):
    html = NAV_HEADER_XML.format(title=book.title)
    html += '
\n'

    html += _create_toc_section_html(book, for_nav=True)

    html += '
\n'
    html += '\n'
    html += '\n'
    _write_html_in_zip_file(epub_zip, NAV_FPATH, html)

def _build_nav_point_xml_for_section(section, play_order, level):

    fpath = _create_epub_fpath_for_section(section)
    fname = os.path.basename(fpath)
    span_id = f'nav_span_{section.id}'

    nbsps_for_nested = NBSP * (level - 1)
    if section.kind in [PART, CHAPTER]:
        return f'''<navPoint class="{section.kind}" id="{section.id}" playOrder="{play_order}">
  <navLabel>
    <text>{nbsps_for_nested}{section.title}</text>
  </navLabel>
  <content src="{fname}" />
</navPoint>'''
    else:
        return f'''<navPoint id="{section.id}" playOrder="{play_order}">
<navLabel>
<text>{nbsps_for_nested}{section.title}</text>
</navLabel>
<content src="{fname}#{span_id}" />
</navPoint>'''

def _create_ncx(book, epub_zip):
    html = NCX_HEADER_XML
    html += '\n'
    html += f'<meta content="{book.metadata["uid"]}" name="dtb:uid"/>\n'
    html += '\n'
    html += f'<docTitle> <text>{book.title}</text> </docTitle>\n'
    html += '<navMap>\n'

    # Do not use nested navPoints because some ebook do not support them
    # Use   to simulate nesting

    play_order = 1
    for section in book.subsections:
        if section.kind == PART:
            if not section.has_no_html:
                html += _build_nav_point_xml_for_section(section, play_order, 1)
                play_order += 1
            for chapter in section.subsections:
                html += _build_nav_point_xml_for_section(chapter, play_order, 2)
                play_order += 1
                for subchapter in chapter.subsections:
                    html += _build_nav_point_xml_for_section(subchapter, play_order, 3)
                    play_order += 1
        elif section.kind == CHAPTER:
            html += _build_nav_point_xml_for_section(section, play_order, 1)
            play_order += 1
            for subchapter in section.subsections:
                html += _build_nav_point_xml_for_section(subchapter, play_order, 2)
                play_order += 1

    html += '</navMap>'
    html += '</ncx>'
    _write_html_in_zip_file(epub_zip, NCX_FPATH, html)

def _create_epub_backbone(epub_zip):
    epub_zip.writestr(CONTAINER_XML_FPATH, CONTAINER_XML)

def _create_mimetype_file(epub_zip):
    epub_zip.writestr('mimetype', b'application/epub+zip')

def _create_opf(book, epub_zip):
    xml = OPF_HEADER_XML

    now = datetime.datetime.utcnow().isoformat(timespec='seconds')
    xml += f'<meta property="dcterms:modified">{now}Z</meta>\n'
    xml += f'<dc:identifier id="id">{book.metadata["uid"]}</dc:identifier>\n'
    xml += f'<dc:title>{book.title}</dc:title>\n'
    xml += f'<dc:language>{book.lang}</dc:language>\n'
    for author in book.metadata['author']:
        xml += f'<dc:creator id="creator">{author}</dc:creator>\n'
    xml += '</metadata>\n'

    xml += '<manifest>\n'

    item_xml = '<item href="{fname}" id="{id}" media-type="application/xhtml+xml" />\n'

    spine = [TOC_CHAPTER_ID]
    xml += item_xml.format(fname=os.path.basename(_get_epub_fpath_for_toc_chapter()),
                           id='toc')

    for section in book.subsections:
        if section.kind == PART:
            if not section.has_no_html:
                spine.append(section.id)
                fname = os.path.basename(_create_epub_fpath_for_section(section))
                xml += item_xml.format(fname=fname, id=section.id)
            for chapter in section.subsections:
                spine.append(chapter.id)
                fname = os.path.basename(_create_epub_fpath_for_section(chapter))
                xml += item_xml.format(fname=fname, id=chapter.id)
        elif section.kind == CHAPTER:
            spine.append(section.id)
            fname = os.path.basename(_create_epub_fpath_for_section(section))
            xml += item_xml.format(fname=fname, id=section.id)

    fname = os.path.basename(NCX_FPATH)
    xml += f'<item href="{fname}" id="ncx" media-type="application/x-dtbncx+xml" />\n'
    fname = os.path.basename(NAV_FPATH)
    xml += f'<item href="{fname}" id="nav" media-type="application/xhtml+xml" properties="nav" />\n'

    xml += '</manifest>\n'

    xml += '<spine toc="ncx">\n'
    for chapter_id in spine:
        xml += f'<itemref idref="{chapter_id}"/>\n'
    xml += '</spine>\n'

    xml += '<guide>\n'
    title = TOC_CHAPTER_TITLE[book.lang]
    fname = os.path.basename(_get_epub_fpath_for_toc_chapter())
    xml += f'<reference type="toc" title="{title}" href="{fname}" />\n'
    xml += '</guide>\n'
    xml += '</package>\n'

    _write_html_in_zip_file(epub_zip, OPF_FPATH, xml)

def create_epub(book, epub_path):
    references_not_found = set()

    with zipfile.ZipFile(epub_path, 'w') as epub_zip:
        _create_mimetype_file(epub_zip)
        _create_epub_backbone(epub_zip)

        endnote_definitions = []
        bibliography_entries_seen = OrderedDict()
        for section in book.subsections:
            if section.kind == CHAPTER:
                res = _create_chapter(section, epub_zip,
                                      bibliography_entries_seen=bibliography_entries_seen,
                                      references_not_found=references_not_found)
                endnote_definitions.extend(res['footnote_definitions'])
            elif section.kind == PART:
                _create_part(section, epub_zip,
                             bibliography_entries_seen=bibliography_entries_seen,
                             references_not_found=references_not_found,
                             endnote_definitions=endnote_definitions)
            elif section.kind == BOOK:
                raise ValueError('A book should not include a subsection of kind BOOK')
            elif section.kind == SUBCHAPTER:
                raise ValueError('A book should not include a subsection of kind SUBCHAPTER')
            else:
                assert False

        if (endnote_definitions or bibliography_entries_seen) and book.has_parts():
            back_matter_part = BookSectionWithNoFiles(parent=book,
                                                      id_=BACK_MATTER_PART_ID,
                                                      title=APPENDICES_PART_TITLE[book.lang],
                                                      kind=PART)
            back_matter_part.has_no_html = True
            book.subsections.append(back_matter_part)
            parent = back_matter_part
        else:
            back_matter_part = None
            parent = book

        toc_level_for_appendix_chapters = 2 if book.has_parts() else 1

        if endnote_definitions:
            endnotes_chapter = BookSectionWithNoFiles(parent=parent,
                                                      id_=ENDNOTE_CHAPTER_ID,
                                                      title=ENDNOTE_CHAPTER_TITLE[book.lang],
                                                      kind=CHAPTER)
            parent.subsections.append(endnotes_chapter)
            _create_endnotes_chapter(endnotes_chapter,
                                     endnote_definitions,
                                     header_level=toc_level_for_appendix_chapters,
                                     epub_zip=epub_zip)
        if bibliography_entries_seen:
            chapter = BookSectionWithNoFiles(parent=parent,
                                             id_=BIBLIOGRAPHY_CHAPTER_ID,
                                             title=BIBLIOGRAPHY_CHAPTER_TITLE[book.lang],
                                             kind=CHAPTER)
            parent.subsections.append(chapter)
            _create_bibliography_chapter(chapter,
                                         bibliography_entries_seen,
                                         header_level=toc_level_for_appendix_chapters,
                                         epub_zip=epub_zip)

        toc_chapter = BookSectionWithNoFiles(parent=book,
                                             id_=TOC_CHAPTER_ID,
                                             title=TOC_CHAPTER_TITLE[book.lang],
                                             kind=TOC)
        _create_toc_chapter(toc_chapter,
                            header_level=1,
                            epub_zip=epub_zip)
        book.subsections.insert(0, toc_chapter)

        _creata_nav(book, epub_zip=epub_zip)

        _create_ncx(book, epub_zip=epub_zip)

        _create_opf(book, epub_zip=epub_zip)

    check_epub(epub_path)
    if references_not_found:
        msg = 'Some references were not found in the bibliography database'
        print('References not found:')
        print(','.join(references_not_found))
        raise RuntimeError(msg)

def unzip_epub(ebook_path, out_dir):

    if out_dir.exists():
        shutil.rmtree(out_dir)

    with zipfile.ZipFile(ebook_path) as epubzip:
        epubzip.extractall(path=out_dir)

def check_epub(ebook_path):
    cmd = ['java', '-jar', str(EPUBCHECK_JAR), str(ebook_path)]
    completed_process = subprocess.run(cmd, capture_output=True)

    if completed_process.returncode:
        sys.stdout.write(completed_process.stdout.decode())
        sys.stderr.write(completed_process.stderr.decode())


from PySide2.QtWidgets import QAction, QApplication, QMessageBox, QInputDialog
from PySide2.QtCore import Qt
from PySide2.QtGui import QIcon
from PySide2.QtWidgets import QWidget, QMainWindow, QLabel, QApplication, QFileDialog
from model.Workspace import Workspace
from model.Chapter import Chapter
from model.Book import Book
from model.Page import Page
from model.Text import Text
from model.Picture import Picture
from PySide2.QtGui import QIcon

class NewBookAction(QAction):
    """
    Klasa sadrzi akciju dodavanja book node-a na workspace
    """

    def __init__(self):
        """
        Konstruktor
        """
        super(NewBookAction, self).__init__("New Book")
        self.setIcon(QIcon("src/new.png"))
        self.triggered.connect(self.actionCalled)

    def actionCalled(self):
        """
        Prikazuje dijalog za unos imena nove knjige, kreira novu instancu knjige sa unetim imenom
        """
        parent = QApplication.instance().model
        newName, ok = QInputDialog.getText(None, "New Book name", "Enter desired new name")
        if ok:
            if parent.isValidName(newName):
                newBook = Book(newName)
                book = QApplication.instance().mainWindow.newTab(newBook)
                parent.addChild(book)

            else:
                while not parent.isValidName(newName):
                    dialog = QMessageBox()
                    dialog.setWindowTitle("Error")
                    dialog.setText("That name is not valid")
                    dialog.setWindowIcon(QIcon("src/notification.png"))
                    dialog.setModal(True)
                    dialog.exec_()
                    newName, cancel = QInputDialog.getText(None, "New Book name", "Enter desired new name")
                    if not cancel:
                        break
                    else:
                        if parent.isValidName(newName):
                            newBook = Book(newName)
                            book = QApplication.instance().mainWindow.newTab(newBook)
                            parent.addChild(book)
                            break


import allure
import pytest

from pages.cart_page import CartPage
from pages.home_page import HomePage
from pages.login_page import LoginPage
from utils.locators import LoginPageLocators, CartPageLocators, HomePageLocators
from utils.logger import _step

@pytest.mark.usefixtures('setup', 'website_setup')
class TestHomePage:
    reruns = 2
    reruns_delay = 2

    @pytest.mark.dependency(name="open login page", scope="session")
    @pytest.mark.flaky(reruns=reruns, reruns_delay=reruns_delay)
    @_step
    @allure.title('Open login page test')
    @allure.description('This is test of open login page on home page')
    def test_login_page_opened(self, config):
        home_page = HomePage(self.driver, config)
        home_page.open_page(f"cn/zh/home.html?cid={config['cid']}")
        home_page.go_to_login_page()
        login_page = LoginPage(self.driver, config)
        login_title = '登录您的账户'
        assert login_title in login_page.find_element(*LoginPageLocators.login_title).text

@pytest.mark.usefixtures('setup', 'website_setup')
class TestLoginPage:
    reruns = 2
    reruns_delay = 2

    @pytest.mark.dependency(depends=["open login page"], scope="session")
    @pytest.mark.flaky(reruns=reruns, reruns_delay=reruns_delay)
    @_step
    @allure.title('Login with invalid user test')
    @allure.description('This is test of login with invalid user')
    def test_login_with_invalid_user(self, config):
        home_page = HomePage(self.driver, config)
        home_page.open_page(f"cn/zh/home.html?cid={config['cid']}")
        home_page.go_to_login_page()
        login_page = LoginPage(self.driver, config)
        login_page.login('test', is_valid=False)
        error_msg = '用户名称或密码不正确'
        assert error_msg in login_page.find_element(*LoginPageLocators.login_error_message).text

    @pytest.mark.dependency(name="login", depends=["open login page"], scope="session")
    @pytest.mark.flaky(reruns=reruns, reruns_delay=reruns_delay)
    @_step
    @allure.title('Login with valid user test')
    @allure.description('This is test of login with valid user')
    def test_login_with_valid_user(self, config):
        home_page = HomePage(self.driver, config)
        home_page.open_page(f"cn/zh/home.html?cid={config['cid']}")
        home_page.go_to_login_page()
        login_page = LoginPage(self.driver, config)
        login_page.login('boxing', is_valid=True, save_cookie=True)
        login_page.redirect_to_home()
        profile_msg = '账户'
        assert profile_msg in login_page.find_element(*HomePageLocators.logged_in_menu).text
        login_page.logout()

@pytest.mark.usefixtures('setup', 'website_setup')
class TestCartPage:
    reruns = 2
    reruns_delay = 2

    @pytest.mark.dependency(depends=["login"], scope="session")
    @pytest.mark.flaky(reruns=reruns, reruns_delay=reruns_delay)
    @_step
    @allure.title('Checkout order test')
    @allure.description('This is test of checkout order')
    def test_checkout_order(self, config, product):
        home_page = HomePage(self.driver, config)
        home_page.open_page(f"cn/zh/home.html?cid={config['cid']}")
        if not home_page.load_cookie('boxing'):
            home_page.go_to_login_page()
            login_page = LoginPage(self.driver, config)
            login_page.login('boxing', is_valid=True, save_cookie=True)
        home_page.go_to_cart_page()
        cart_page = CartPage(self.driver, config)
        cart_page.add_forgot_item_to_cart(catalog_number=product['sku'], quantity=product['quantity'])
        cart_page.go_to_order_details_page()
        cart_page.fill_order_entry(ship_to='test', bill_to='test', order_number='NA')
        cart_page.go_to_review_submit_page()
        cart_page.submit_order(is_submit=False)
        assert product['name'] in cart_page.find_element(*CartPageLocators.added_item_name_field).text
        cart_page.empty_cart()


from typing import List
import random
from . import aiplayer, game_config, move, randomai, ship, player

class SearchDestroyAi(aiplayer.AIPlayer):
    def __init__(self, player_num: int, config: game_config.GameConfig, other_players: List["Player"], type):
        super().__init__(player_num, config, other_players, type)
        self.list = []
        self.already_done = []
        self.mode= None

    def destroy_mode(self):
        self.mode="destroy"
        #print("desrtroy activated")
        #coords = self.pick_destroy()
        if len(self.list)==0:
            self.mode="search"
            return self.search_mode()

        coords = self.list[0]
        if coords in self.enemy.possible_hits:
            self.mode="destroy"
            self.add_coordinates(coords)

        row = str(coords[0])
        col = str(coords[1])
        new = (row + "," + col)
        self.list.remove(coords)
        #return new
        while True:
            try:
                firing_location = move.Move.from_str(self, new)
            except ValueError as e:
                print(e)
                continue
            return firing_location

    def search_mode(self):
        coord = random.choice(self.coordinates)
        if coord in self.enemy.possible_hits:
            self.mode = "destroy"
            self.add_coordinates(coord)
            return self.destroy_mode()
        else:
            row = str(coord[0])
            col = str(coord[1])
            self.coordinates.remove(coord)

            coords = (row + "," + col)

        while True:
            try:
                firing_location = move.Move.from_str(self, coords)
                #print(firing_location)
            except ValueError as e:
                print(e)
                continue
            return firing_location

    def add_coordinates(self,coords):
        x=coords[0]
        y=coords[1]
        ctr=(x,y)
        if ctr not in self.list:
            self.list.append(ctr)
            self.coordinates.remove(ctr)
        left=(x , y-1)
        if left in self.coordinates:
            self.coordinates.remove(left)
            if left not in self.list:
                    self.list.append(left)
        up = (x-1 , y)
        if up in self.coordinates:
            self.coordinates.remove(up)
            if up not in self.list:
                self.list.append(up)
        right = (x, y+1)
        if right in self.coordinates:
            self.coordinates.remove(right)
            if right not in self.list:
                self.list.append(right)
        down = (x+1 , y)
        if down in self.coordinates:
            self.coordinates.remove(down)
            if down not in self.list:
                self.list.append(down)

    def pick_destroy(self):
        if len(self.list)==0:
            self.mode="search"
            return self.search_mode()
        next_fire = self.list[0]
        if next_fire in self.enemy.possible_hits:
            self.mode="destroy"
            self.add_coordinates(next_fire)

        row = str(next_fire[0])
        col = str(next_fire[1])
        new = (row + "," + col)
        self.list.remove(next_fire)
        print (self.list)
        return new

    # def __init__(self,coordinates):
    #    self.coordinates= coordinates
    #   self.pick_coordinates()

    def pick_search(self):
        #coordinates = self.board.coordinate_list

        coord = random.choice(self.coordinates)
        if coord in self.enemy.possible_hits:
            self.mode="destroy"
            self.add_coordinates(coord)
            return self.destroy_mode()
        else:
            row = str(coord[0])
            col = str(coord[1])
            self.coordinates.remove(coord)

            #coords = (row + "," + col)

            return (row + "," + col)

    def get_move(self):

        self.enemy = self.opponents[0]
        if self.mode== None:
            self.mode="search"
        if self.mode == "search":
            return self.search_mode()
        if self.mode=="destroy":
            return self.destroy_mode()
       #     if self.scan():
        #        self.mode="destroy"
         #       return destroy_mode()

            #else:

             #   return self.search_mode()

    def scan(self):
        self.enemy=self.opponents[0]
        for x in self.enemy.possible_hits:
            row=x[0]
            col=x[1]
            if self.enemy.board.contents[row][col]=="X":
                mode="destroy"
                self.enemy.possible_hits.remove(x)
                self.add_coordinates(row,col)
                self.already_done.append((row,col))
                return True
            else:
                mode="search"
                return False

        #for x in range(len(self.enemy.board.contents)):
         #   for y in range(len(self.enemy.board.contents[0])):
          #      if str(self.enemy.board.contents[x][y]) == "X":
           #         mode = "destroy"
            #        if (x, y) not in self.already_done:
             #           self.already_done.append((x, y))
              #          self.add_coordinates((x, y))
             #   else:
              #      mode="search"
        #if mode=="destroy":
         #   return True


#!/usr/bin/env python
# encoding: utf-8
import os

"""
@author: wenjiaGuo
@version: ??
@contact: 601152819@qq.com
@software: PyCharm
@file: 8.收集整个网站数据.py
@time: 2017/10/5 20:50
"""

# 如何创建一个爬虫来收集页面标题、正文的第一个段落，
# 以及编辑页面的链接（如果有的话）这些信息。
from urllib.request import urlopen
from bs4 import BeautifulSoup
import re
page = set()
def getLinks(pageUrl):
    global page
    html = urlopen('http://en.wikipedia.org'+pageUrl)
    bsObj = BeautifulSoup(html,'lxml')
    try:
        print(bsObj.h1.get_text())
        print(bsObj.find(id='mw-content-text').findAll('p')[0])
        print(bsObj.find(id="ca-edit").find("span").find("a").attrs['href'])

    except AttributeError:
        print('缺少一些属性，不过不用担心')
    for link in bsObj.findAll('a',href=re.compile('^(/wiki/)')):
        if 'href'in link.attrs:
            if link.attrs['href'] not in page:
                newPage = link.attrs['href']
                print('---------------\n'+newPage)
                page.add(newPage)
                getLinks(newPage)
getLinks("")

8.收集整个网站数据.py

8.收集整个网站数据.py


from enum import IntEnum
from .graph import Edge, Graph, Node
from .heap import EdgeHeap

class Color(IntEnum):
    WHITE = 0
    GRAY = 1
    BLACK = 2

class KruskalsAlgorithm:
    # get all the edges of a graph
    # sort them
    # check if they do not make a cycle
    #
    def __init__(self, G: Graph, s: int, t: int) -> None:
        self.G = G
        self.s = s
        self.t = t
        # TODO: move makeset here?
        self.p = [None for i in range(self.G.n)]
        self.h = [None for i in range(self.G.n)]

    def run(self):
        edges = self.G.get_edges()
        # edges = self.heapsort(edges)
        # TODO: replace it with heapsort
        h = EdgeHeap()
        h.insert_all(edges)
        # edges = h.heapsort()
        # edges = sorted(edges, key= lambda x: x[0], reverse=True)

        # TODO: can i put this inside the initialization?
        for i in range(self.G.n):
            self.makeset(i)

        edge_count = 0
        i = 0

        self.result = []
        while(edge_count < self.G.n - 1):
            edge = h.remove()
            i = i+1
            weight, u, v = edge
            r1 = self.find(u)
            r2 = self.find(v)
            if r1 != r2:
                self.result.append(edge)
                edge_count += 1
                self.union(r1, r2)

        return self.output()

    def output(self):
        # print(self.result)
        self.create_mst()
        self.dfs()
        max_bw = -1
        path = []
        if self.reached == True:
            # max_bw = min(self.result, key=lambda x: x[0])
            # print("bandwidth: ", max_bw[0])
            x = self.t
            max_bw = float("inf")
            while(x != self.s):
                max_bw = min(max_bw, self.temp_wt[(x, self.dad[x])])
                # print(x)
                path.append(x)
                x = self.dad[x]
            # print(x)
            path.append(x)
            # print("bandwidth: ", max_bw)
        else:
            print("no s-t path found")
        path.reverse() # just to reverse so that it shows s - t in O(n) time
        return max_bw, path

    def dfs(self):
        self.temp_wt = {}
        self.reached = False
        self.color = [Color.WHITE for i in range(self.G.n)]
        self.dad = [-1 for i in range(self.G.n)]
        self.dfs_recursive(self.s)

    def dfs_recursive(self, v):
        self.color[v] = Color.GRAY
        for edge in self.T[v]:
            w, weight = edge

            if self.color[w] == Color.WHITE:
                self.dad[w] = v
                self.temp_wt[(v, w)] = self.temp_wt[(w, v)] = weight
                if w == self.t:
                    self.reached = True
                    return
                self.dfs_recursive(w)
        self.color[v] = Color.BLACK

    def create_mst(self):
        T = [[] for i in range(self.G.n)]

        for edge in self.result:
            w, a, b = edge
            T[a].append((b, w))
            T[b].append((a, w))

        self.T = T

    def makeset(self, v):
        self.p[v] = -1
        self.h[v] = 0

    def find(self, v):
        w = v
        S = []
        while(self.p[w] != -1):
            S.append(w)
            w = self.p[w]
        while(S):
            u = S.pop()
            self.p[u] = w
        return w

    def union(self, r1, r2):
        if self.h[r1] > self.h[r2]:
            self.p[r2] = r1
        elif self.h[r2] > self.h[r1]:
            self.p[r1] = r2
        else: # h[r2] == h[r1]
            self.p[r2] = r1
            self.h[r1] = self.h[r1] + 1


import collections
import itertools
import time
import random
from typing import Optional

import streamlit as st
import numpy as np

import pyaudio
from pydub import AudioSegment, silence

from audio_io import AudioIO

from audioplots import *
from containers import *
from layout import *

ctx = {
    # names match pyaudio names
    "frames_per_buffer": 1024,  # Record in chunks of 1024 samples
    "format": pyaudio.paInt16,
    "channels": 2,
    "rate": 44100,  # Record at 44100 samples per second
}

def send_balloons_if_lucky():
    if random.randint(0, 100) == 0:
        st.balloons()

# Initialize audio only once per session as it's an expensive operation
def get_audio_handle() -> AudioIO:
    if 'aio' not in st.session_state:
        st.session_state['aio'] = AudioIO(ctx)
    return st.session_state['aio']

def add_sound_to_clips(sound: AudioSegment):
    if not isinstance(sound, AudioSegment):
        st.warning('display_nonsilence called with non-AudioSegment!')
        return

    with st.spinner('Splitting on silence...'):
        start = time.time()
        current_segments = silence.split_on_silence(sound,
                                                    min_silence_len=2000,  # this dude will be modified by the user
                                                    silence_thresh=sound.dBFS-16,  # FIXME: does this work?
                                                    keep_silence=100,
                                                    seek_step=1)
        end = time.time()

    cols = st.columns(3)
    with cols[0]:
        st.caption('Recording time')
        st.markdown(f'{len(sound) / 1000}s')
    with cols[1]:
        st.caption('Processing Time')
        st.markdown(f'{end - start:.3f}s')
    with cols[2]:
        st.caption('New Clips Found')
        st.markdown(len(current_segments))

    all_clips = st.session_state['all_clips']

    with st.spinner('Creating clips...'):
        if len(current_segments) > 1:
            for segment in current_segments[:-1]:
                all_clips.appendleft(AudioClip(audio_segment=segment, selected=False))

        if len(current_segments) > 0:
            all_clips.appendleft(AudioClip(audio_segment=current_segments[-1], selected=True))

def draw_sidebar_with_preferences():
    with st.sidebar:
        st.write('Recording preferences:')
        form = st.form(key='Submit')
        with form:
            min_silence_len_s = st.number_input(
                label="Minimum Silence Length (s)",
                min_value=0,
                value=2,
            )
            silence_thresh_dbfs = st.number_input(
                label="Silence Threshold (dBFS)",
                min_value=-200,
                max_value=3,
                value=-80,
            )
            approximate_bpm = st.number_input(
                label="Approximate Tempo (BPM; for tempo estimation)",
                min_value=0,
                max_value=300,
                value=100,
            )
            submitted = st.form_submit_button('Submit')
            st.write(submitted)
            st.write(min_silence_len_s)
            st.write(silence_thresh_dbfs)

# Set up UI Elements

if 'all_clips' not in st.session_state:
    st.session_state['all_clips'] = collections.deque(maxlen=100)  # TODO: remove maxLen?

st.set_page_config(
    page_title=None,
    page_icon=None,
    layout='wide',
    initial_sidebar_state='auto',
    menu_items=None
)

st.title('Muesli Practice Helper')

#draw_sidebar_with_preferences()

with st.spinner('Initializing Audio...'):
    aio = get_audio_handle()

toggled = st.button('Toggle Recording...')

sound_status = st.markdown('Sample Text')  # TODO: refactor this

# Check to see if we have any output from last run
if toggled:
    if aio.is_recording():
        sound_status.markdown('Checking most recent recording...')
        sound: Optional[AudioSegment] = aio.finish_recording()
        if sound:
            sound_status.markdown('Splitting most recent recording on silence...')
            add_sound_to_clips(sound)
            sound_status.markdown('Recording is ready!')
            send_balloons_if_lucky()
            draw_audio_clips(st.session_state['all_clips'])
        else:
            sound_status.markdown('How are you able to see this?')
    else:
        sound_status.markdown('Recording started...')
        aio.start_recording()

else:
    if aio.is_recording():
        sound_status.markdown('Recording in progress..')
    else:
        sound_status.markdown('Not recording')
        draw_audio_clips(st.session_state['all_clips'])


from binance.client import Client
from yaspin import yaspin
import os.path
from datetime import datetime
import pickle
import pandas as pd
import backtrader as bt

class databutler():

    def __init__(self,directory):
        #binance client without keys for data acquisition
        self.binanceClient = Client("", "")
        #directory in project folder for data storage
        self.directory = directory
        #data source

    def get_data(self,data_params):
        if data_params['data_source'] == 'BINANCE':
            data = self.get_data_BINANCEAPI(data_params = data_params)
        elif data_params['data_source'] == 'YAHOO':
            data = self.get_data_Yahoo(data_params=data_params)
        elif data_params['data_source'] == 'CSV':
            data = self.get_data_CSV(data_params=data_params)
        return data

    def get_data_BINANCEAPI(self,data_params):
        spinner = yaspin()  # loading visualization

        data_details = [data_params['asset'], data_params['interval'],
                        str(data_params['fromdate'].timestamp()), str(data_params['todate'].timestamp())]
        file_ID = data_details[0] + '_' + data_details[1] + '_' + str(datetime.fromtimestamp(float(data_details[2])))[
                                                                  0:10] + '_' + str(
            datetime.fromtimestamp(float(data_details[3])))[0:10]
        print(f'Data: {file_ID}')
        filepath = self.directory + 'Binance_API/' + file_ID + '.dat'
        if os.path.isfile(filepath):
            print('Data already downloaded. Loading it.')
            with open(filepath, 'rb') as f:
                data = pickle.load(f)

        else:
            spinner.text = f'Downloading data from Binance...'
            spinner.start()
            klines = self.binanceClient.get_historical_klines(*data_details)
            klines_df = pd.DataFrame(klines)
            col_names = ['open time', 'open', 'high', 'low', 'close', 'volume', 'close time', 'quote asset volume',
                         'number of trades', 'taker buy base asset volume', 'taker buy quote asset volume',
                         'Can be ignored(see docu)']
            klines_df.columns = col_names
            spinner.stop()

            for col in col_names:
                klines_df[col] = klines_df[col].astype(float)
            klines_df['datetime'] = pd.to_datetime(klines_df['open time'] * 1000000, infer_datetime_format=True)
            klines_df = klines_df.drop(
                ['open time', 'close time', 'quote asset volume', 'number of trades', 'taker buy base asset volume',
                 'taker buy quote asset volume', 'Can be ignored(see docu)'], axis=1, errors='ignore')
            klines_df = klines_df.set_index('datetime')

            # Price multiply
            klines_df['open'] = klines_df['open'] * data_params['price_multiplier']
            klines_df['high'] = klines_df['high'] * data_params['price_multiplier']
            klines_df['low'] = klines_df['low'] * data_params['price_multiplier']
            klines_df['close'] = klines_df['close'] * data_params['price_multiplier']

            # reformat as backtrader datafeed
            data = bt.feeds.PandasData(dataname=klines_df)

            # save data
            print('Saving data.')
            with open(filepath, 'wb') as f:
                pickle.dump(data, f)

        return data

    def get_data_CSV(self,data_params):
        print('not implemented.')
        pass

    def get_data_Yahoo(self,data_params):
        data = bt.feeds.YahooFinanceData(dataname=data_params['asset'], fromdate=data_params['fromdate'], todate=data_params['todate'])
        return data


"""
Player for solitaire.

Created on 19.10.2018

@author: Ruslan Dolovanyuk

"""

import enum

import checker

from constants import Colors

import pygame

Actions = enum.Enum('Actions', 'ChangeZoneUp ChangeZoneDown ChangeRowUp ChangeRowDown ChangeCardUp ChangeCardDown Take Drop')

class Player:
    """Player class for solitaire."""

    def __init__(self, board, speech, phrases):
        """Initialize player class."""
        self.board = board
        self.speech = speech
        self.phrases = phrases
        self.color = Colors.BLUE
        self.__actions = [self.__change_zone, self.__change_row, self.__change_card, self.__take, self.__drop]

    def reset(self):
        """Reset current variables."""
        self.current_zone = 0
        self.__took = False
        self.take_card = None

    def draw(self):
        """Draw method for player."""
        zone = self.board.zones[self.current_zone]
        if zone.if_empty():
            left, top = zone.get_coord_zero(zone.current_row)
        else:
            left, top = zone.get_coord_card(zone.current_row, zone.current_card)
        pygame.draw.rect(zone.zone, self.color, (left, top, self.board.card_x, self.board.card_y), 1)

    def speak(self, changed_zone=False):
        """Speak information for moving cell."""
        name = self.phrases[self.board.zones[self.current_zone].NAME]
        row = ''
        if self.board.zones[self.current_zone].if_rows:
            row = self.phrases['column'] + ' ' + str(self.board.zones[self.current_zone].current_row + 1)
        self.__speak_card()
        if changed_zone:
            self.speech.speak(' '.join((name, row)))

    def __speak_card(self, card=None):
        """Speak additionaly information for card."""
        if card is None:
            if not self.board.zones[self.current_zone].if_empty():
                card = self.board.zones[self.current_zone].get_card(self.board.zones[self.current_zone].current_card)
        if self.board.zones[self.current_zone].if_empty() and card is None:
            self.speech.speak(self.phrases['empty'])
        else:
            if card.status:
                if 52 == self.board.deck_count:
                    rate = str(card.rate_index) if 1 < card.rate_index < 11 else self.phrases[card.rate]
                else:
                    rate = str(card.rate_index) if card.rate_index < 11 else self.phrases[card.rate]
                    if 'ace' == card.rate:
                        rate = self.phrases[card.rate]
                self.speech.speak(' '.join((rate, self.phrases[card.suit])))
            else:
                self.speech.speak(self.phrases['close'])

    def actions(self, action):
        """Run actions in zones."""
        zone = self.board.zones[self.current_zone]
        for method_action in self.__actions:
            method_action(action, zone)

    def __change_zone(self, action, zone):
        """Change zone up or down."""
        if Actions.ChangeZoneUp == action:
            if 4 == self.current_zone:
                self.current_zone = 0
            else:
                self.current_zone += 1
            self.speak(True)
        elif Actions.ChangeZoneDown == action:
            if 0 == self.current_zone:
                self.current_zone = 4
            else:
                self.current_zone -= 1
            self.speak(True)

    def __change_row(self, action, zone):
        """Change row in zone up or down."""
        if Actions.ChangeRowUp == action:
            if zone.if_rows:
                if len(zone.rows) == zone.current_row + 1:
                    self.speech.speak(self.phrases['border'])
                    self.__speak_card()
                else:
                    zone.current_row += 1
                    zone.current_card = -1
                    self.speak()
        elif Actions.ChangeRowDown == action:
            if zone.if_rows:
                if 0 == zone.current_row:
                    self.speech.speak(self.phrases['border'])
                    self.__speak_card()
                else:
                    zone.current_row -= 1
                    zone.current_card = -1
                    self.speak()

    def __change_card(self, action, zone):
        """Change card in zone row up or down."""
        if Actions.ChangeCardUp == action and 4 == self.current_zone:
            if not zone.if_empty():
                if zone.get_card(0) == zone.get_card(zone.current_card):
                    self.speech.speak(self.phrases['border'])
                    self.__speak_card()
                elif zone.get_card(zone.current_card - 1).status:
                    zone.current_card -= 1
                    self.speak()
                else:
                    self.speech.speak(self.phrases['close'])
                    self.__speak_card()
        elif Actions.ChangeCardDown == action and 4 == self.current_zone:
            if not zone.if_empty():
                if zone.get_card(-1) == zone.get_card(zone.current_card):
                    self.speech.speak(self.phrases['border'])
                    self.__speak_card()
                else:
                    zone.current_card += 1
                    self.speak()

    def __take(self, action, zone):
        """Took or put card in zone row."""
        if Actions.Take == action:
            result_recall = self.__drop_recall(action, zone)
            result_deck = self.__drop_deck(action, zone)
            if not result_recall and not result_deck:
                if zone.if_empty() and not self.__took:
                    return
                if self.__took:
                    if 'house' == zone.NAME:
                        result = self.board.zones[3].take(self.__take_cards, self.__take_cards_list)
                        if result and len(self.__take_cards_list) > 0:
                            self.__open_card(self.__take_cards_list[-1])
                        self.__took = False
                        self.take_card.take = False
                    elif 'columns' == zone.NAME:
                        result = self.board.zones[4].take(self.__take_cards, self.__take_cards_list)
                        if result and len(self.__take_cards_list) > 0:
                            self.__open_card(self.__take_cards_list[-1])
                        self.__took = False
                        self.take_card.take = False
                    else:
                        self.__took = False
                        self.take_card.take = False
                else:
                    cards = zone.rows[zone.current_row] if zone.if_rows else zone.cards
                    card = zone.get_card(zone.current_card)
                    self.__take_cards = cards[cards.index(card):]
                    if checker.change_suits(self.__take_cards) and checker.rate_down(self.__take_cards):
                        self.__take_cards_list = cards
                        self.__took = True
                        self.take_card = card
                        self.take_card.take = True
                self.board.sounds.play('take')
                self.__speak_card()

    def __drop(self, action, zone):
        """Dropped card from zone."""
        if Actions.Drop == action:
            if zone.if_empty():
                return
            cards = zone.rows[zone.current_row] if zone.if_rows else zone.cards
            card = zone.get_card(zone.current_card)
            rate_index = card.rate_index
            if card == zone.get_card(-1):
                if 'ace' == card.rate:
                    for row in range(len(self.board.zones[3].rows)):
                        if not self.board.zones[3].rows[row]:
                            self.board.zones[3].rows[row].append(cards.pop())
                            if cards:
                                self.__open_card(cards[-1])
                            else:
                                self.__speak_card()
                            return
                for row in range(len(self.board.zones[3].rows)):
                    row_cards = self.board.zones[3].rows[row]
                    if row_cards:
                        if card.suit == row_cards[-1].suit and rate_index - 1 == row_cards[-1].rate_index:
                            row_cards.append(cards.pop())
                            if cards:
                                self.__open_card(cards[-1])
                            else:
                                self.__speak_card()
                            return

    def __drop_recall(self, action, zone):
        """Dropped cards from recall."""
        if 0 == self.current_zone and self.board.zones[1].if_empty():
            if zone.if_empty():
                self.__speak_card()
                return True
            while not self.board.zones[2].if_empty():
                card = self.board.zones[2].cards.pop(0)
                self.board.zones[0].cards.append(card)
                self.__open_card(card, False)
            while not zone.if_empty():
                self.board.zones[1].cards.append(zone.cards.pop())
                self.board.sounds.play('deal')
            self.__speak_card()
            return True
        return False

    def __drop_deck(self, action, zone):
        """Dropped cards from deck."""
        if 1 == self.current_zone:
            if zone.if_empty():
                self.__speak_card()
                return True
            while not self.board.zones[2].if_empty():
                card = self.board.zones[2].cards.pop(0)
                self.board.zones[0].cards.append(card)
                self.__open_card(card, False)
            for _ in range(self.board.delivery):
                if not zone.if_empty():
                    card = zone.cards.pop()
                    self.board.zones[2].cards.append(card)
                    self.__open_card(card)
            self.__speak_card()
            return True
        return False

    def __open_card(self, card, open_flag=True):
        """Open card or close if open_flag = False."""
        card.status = open_flag
        self.board.sounds.play('open')
        if open_flag:
            self.__speak_card(card)


import setuptools

with open("README.md", "r") as fh:
    long_description = fh.read()

setuptools.setup(
     name='BraketLab',
     version='1.8.0',
     author="Audun Skau Hansen",
     author_email="audunsh4@gmail.com",
     description="Educational tool for learning quantum theory with Jupyter Notebooks",
     long_description=long_description,
   long_description_content_type="text/markdown",
     url="https://github.uio.no/audunsh/braketlab",
     packages=setuptools.find_packages(),
     classifiers=[
         "Programming Language :: Python :: 3",
         "License :: OSI Approved :: MIT License",
         "Operating System :: OS Independent",
     ],
     install_requires = ["sympy", "numba", "evince", "bubblebox"],
 )


######################################################################
# Author: IK3D -- Issanou Kamardine
# License: GPL v3
######################################################################

bl_info = {
    "name": "Scripts Management",
    "author": "IK3D",
    "version": (0, 2),
    "location": "Properties panel > Scene",
    "description": "Pin scripts as favourites",
    "category": "Favourites"}

import bpy
from bpy.types import Header, Menu, Panel
from bpy.app.translations import pgettext_iface as iface_
from bpy.app.translations import contexts as i18n_contexts
import fileinput
import sys
import requests

#Operator Add Favourites
class cheminOperator(bpy.types.Operator):
    bl_idname = "wm.get_chemin"
    bl_label = "Gert addons path"
    chemin = bpy.props.StringProperty()

    def execute(self, context):
        if self.chemin == '':
            print("Hello world!")
        else:
            print("Hello world from %s!" % self.chemin)
            dynamic_path = self.chemin

            #var new categories and old
            oldcatReaded = ""
            oldcat =""
            indexe_ligne = 0

            #Open File script
            fichier = open(dynamic_path, "r+")

            #read script
            lingne = fichier.readline()

            if "FAVOURIT" not in lingne:
                #Find actual categorie
                for ligne in fichier:
                    indexe_ligne +=1
                    if "category" in ligne:
                        oldcatReaded = ligne
                        oldcat = oldcatReaded.replace("}", "")
                        oldcat = oldcatReaded.replace('\n', "")

                        #check close or not in line
                        if "}" in ligne:
                            fermer = 1
                        else:
                            fermer = 0
                        break

                #fonction replace categorie
                def replace_cat(file_name, line_num, text):
                    lines = open(file_name, 'r').readlines()
                    lines[line_num] = text
                    out = open(file_name, 'w')
                    out.writelines(lines)
                    out.close()

                #Change categorie
                if fermer == False:
                    replace_cat(dynamic_path, indexe_ligne,'    "category": "Favourites",\n')
                else:
                    replace_cat(dynamic_path, indexe_ligne,'    "category": "Favourites"}\n')

                #save back up, save change
                with open(dynamic_path, "r+") as fichier:
                    first_line = fichier.readline()
                    if first_line != ("FAVOURIT = "+"'" + oldcat + " '" + "\n" + "\n"):
                        lines = fichier.readlines()
                        fichier.seek(0)
                        fichier.write("FAVOURIT = "+"'" + oldcat + " '" + "\n" + "\n" )
                        fichier.write(first_line)
                        fichier.writelines(lines)

            fichier.close()

        return{'FINISHED'}

#var pike-up script path
dynamic_path = ""

#Operator remove from Favourites
class unchemOperator(bpy.types.Operator):
    bl_idname = "wm.un_chemin"
    bl_label = "Remove from favourit"
    chemin = bpy.props.StringProperty()

    def execute(self, context):
        if self.chemin == '':
            print("Hello world!")
        else:
            print("Hello world from %s!" % self.chemin)
            dynamic_path = self.chemin

            #Read file script
            fichier = open(dynamic_path, "r+")

            #option read
            lingne = fichier.readline()

            #fonction replace remove line
            def replace_efa(file_name, line_num, text):
                lines = open(file_name, 'r').readlines()
                lines[line_num] = text
                out = open(file_name, 'w')
                out.writelines(lines)
                out.close()

            #fonction replace restore categorie
            def replace_cat(file_name, line_num, text):
                lines = open(file_name, 'r').readlines()
                lines[line_num] = text
                out = open(file_name, 'w')
                out.writelines(lines)
                out.close()

            indexe_ligne = 0
            #back up, restor categorie
            if "category" in lingne:
                    #remove bad caractaires
                    FAVOURITrezOld = lingne
                    FAVOURITA= FAVOURITrezOld.replace("FAVOURIT = ", "")
                    FAVOURITB= FAVOURITA.replace("'\n'", "")
                    FAVOURIT= FAVOURITB.replace("'", "")

                    #remove back up line
                    replace_efa(dynamic_path,0,'')

                    #remove empty line
                    if lingne == '\n'or '\n' + '\n':
                            replace_efa(dynamic_path,0,'')

                    #Find categorie line
                    for ligne in fichier:
                            indexe_ligne +=1

                            if "category" in ligne:
                                    #check if line close
                                    if "}" or " }" or " } " or "}" or "} + '\n'" or " }+ '\n'" or " } + '\n'" in ligne:
                                        fermer = 1
                                    else:
                                        fermer = 0
                                    break

                    #Replase, or, and, close line
                    if fermer == 0:
                            replace_cat(dynamic_path, indexe_ligne-2, FAVOURIT+"}")
                    else:
                            replace_cat(dynamic_path, indexe_ligne-2, FAVOURIT)

            fichier.close()

            print ("Vous avez enlevé un Favorit !")

        return{'FINISHED'}

#Display Enabled scripts
class EnadonOperator(bpy.types.Operator):
    bl_idname = "wm.ena_adon"
    bl_label = "View Enabled add-on"

    def execute(self, context):
        bpy.data.window_managers["WinMan"].addon_support = {'OFFICIAL', 'COMMUNITY', 'TESTING'}
        bpy.data.window_managers["WinMan"].addon_filter = 'Enabled'
        return {'FINISHED'}

#Display all scripts
class AlladonOperator(bpy.types.Operator):
    bl_idname = "wm.all_adon"
    bl_label = "View all add-on"

    def execute(self, context):
        bpy.data.window_managers["WinMan"].addon_support = {'OFFICIAL', 'COMMUNITY', 'TESTING'}
        bpy.data.window_managers["WinMan"].addon_filter = 'All'
        return {'FINISHED'}

class CheckeurdOperator(bpy.types.Operator):
    bl_idname = "wm.check_fav"
    bl_label = "check add-on"

    def execute(self, context):
        bpy.data.window_managers["WinMan"].addon_support = {'OFFICIAL', 'COMMUNITY', 'TESTING'}
        bpy.data.window_managers["WinMan"].addon_filter = 'Favourites'
        return {'FINISHED'}

class userOperator(bpy.types.Operator):
    bl_idname = "wm.user_adon"
    bl_label = "View user add-on"

    def execute(self, context):
        bpy.data.window_managers["WinMan"].addon_support = {'OFFICIAL', 'COMMUNITY', 'TESTING'}
        bpy.data.window_managers["WinMan"].addon_filter = 'User'
        return {'FINISHED'}

#UI display
class ScriptsManagementPanel(bpy.types.Panel):
    """Creates a Panel in the scene context of the properties editor"""
    bl_label = "Scripts Management"
    bl_idname = "SCENE_PT_layout"
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = "scene"

    class USERPREF_MT_addons_dev_guides(Menu):
        bl_label = "Development Guides"

    # menu to open web-pages with addons development guides
    def draw(self, context):
        layout = self.layout

    _support_icon_mapping = {
        'OFFICIAL': 'FILE_BLEND',
        'COMMUNITY': 'POSE_DATA',
        'TESTING': 'MOD_EXPLODE',
        }

    @staticmethod
    def is_user_addon(mod, user_addon_paths):
        import os

        if not user_addon_paths:
            for path in (bpy.utils.script_path_user(),
                         bpy.utils.script_path_pref()):
                if path is not None:
                    user_addon_paths.append(os.path.join(path, "addons"))

        for path in user_addon_paths:
            if bpy.path.is_subdir(mod.__file__, path):
                return True
        return False

    #Add-on layout
    def draw(self, context):
        import os
        import addon_utils

        userpref = context.user_preferences
        used_ext = {ext.module for ext in userpref.addons}

        userpref_addons_folder = os.path.join(userpref.filepaths.script_directory, "addons")
        scripts_addons_folder = bpy.utils.user_resource('SCRIPTS', "addons")

        #collect the categories that can be filtered on
        addons = [(mod, addon_utils.module_bl_info(mod)) for mod in addon_utils.modules(refresh=True)]

        #Management header layout
        layout = self.layout
        layout.label("Run Blender as admin for full permission",icon='LAYER_USED')

        split = layout.split(percentage=0.60, align=True,)
        if bpy.data.window_managers["WinMan"].addon_filter == 'Favourites':
            split.operator("wm.check_fav", icon='SPACE2',text="Favourites")
        else:
            split.operator("wm.check_fav", icon='SPACE3',text="Favourites")

        split.operator("wm.user_adon", text="User")
        split.operator("wm.ena_adon", icon='SAVE_AS',text="")
        split.operator("wm.all_adon", icon='BOOKMARKS',text="")

        #searche layout
        layout = self.layout
        layout.prop(context.window_manager, "addon_search", text="", icon='VIEWZOOM')

        row = layout.row()
        split = layout.split()
        col = split.column()

        filter = context.window_manager.addon_filter
        search = context.window_manager.addon_search.lower()
        support = context.window_manager.addon_support

        #initialized on demand
        user_addon_paths = []

        addon_numb = 0
        for mod, info in addons:
            module_name = mod.__name__
            module_realpath = mod.__file__

            is_enabled = module_name in used_ext

            if info["support"] not in support:
                continue

            #serche parmetres
            if search and search not in info["name"].lower():
                if info["author"]:
                    if search not in info["author"].lower():
                        continue
                else:
                    continue

            # check if addon should be visible with current filters
            if ((filter == "All") or
                (filter == info["category"]) or
                (filter == "Enabled" and is_enabled) or
                (filter == "Disabled" and not is_enabled) or
                (filter == "User" and (mod.__file__.startswith((scripts_addons_folder, userpref_addons_folder))))
                ):

                #limit visible addon on 'All' folder
                if bpy.data.window_managers["WinMan"].addon_filter == 'All' and addon_numb < 10:

                    # Addon UI Code
                    col_box = col.column()
                    box = col_box.box()
                    colsub = box.column()
                    row = colsub.row()

                    if info["category"] == "Favourites":
                        row.operator("wm.un_chemin", icon='PINNED',emboss=False,text="").chemin = module_realpath
                        sub = row.row()
                        sub.label(info["name"], icon='SMALL_TRI_RIGHT_VEC')
                    else:
                        row.operator("wm.get_chemin", icon='UNPINNED',emboss=False,text="").chemin = module_realpath
                        sub = row.row()
                        sub.label(info["name"],)

                    sub.operator("wm.addon_remove", text="", icon='PANEL_CLOSE',emboss=False).module = mod.__name__

                    if is_enabled:
                        row.operator("wm.addon_disable", icon='FILE_TICK', text="", emboss=False).module = module_name
                    else:
                        row.operator("wm.addon_enable", icon='CHECKBOX_DEHLT', text="", emboss=False).module = module_name

                    #incrementation for limitation
                    addon_numb +=1

                if bpy.data.window_managers["WinMan"].addon_filter != 'All':
                    # Addon UI Code
                    col_box = col.column()
                    box = col_box.box()
                    colsub = box.column()
                    row = colsub.row()

                    if info["category"] == "Favourites":
                        row.operator("wm.un_chemin", icon='PINNED',emboss=False,text="").chemin = module_realpath
                        sub = row.row()
                        sub.label(info["name"], icon='SMALL_TRI_RIGHT_VEC')
                    else:
                        row.operator("wm.get_chemin", icon='UNPINNED',emboss=False,text="").chemin = module_realpath
                        sub = row.row()
                        sub.label(info["name"],)

                    sub.operator("wm.addon_remove", text="", icon='PANEL_CLOSE',emboss=False).module = mod.__name__

                    if is_enabled:
                        row.operator("wm.addon_disable", icon='FILE_TICK', text="", emboss=False).module = module_name
                    else:
                        row.operator("wm.addon_enable", icon='CHECKBOX_DEHLT', text="", emboss=False).module = module_name

def register():
    bpy.utils.register_class(ScriptsManagementPanel)
    #Save Operator view all script for add
    bpy.utils.register_class(userOperator)
    #Save Operator show favourites
    bpy.utils.register_class(CheckeurdOperator)
    #Save Operator display all scripts
    bpy.utils.register_class(AlladonOperator)
    #Save Operator Add Favourites
    bpy.utils.register_class(unchemOperator)
    #Save Operator Add Favourites
    bpy.utils.register_class(cheminOperator)
    #Save Operator enabled
    bpy.utils.register_class(EnadonOperator)

def unregister():
    bpy.utils.unregister_class(ScriptsManagementPanel)
    bpy.utils.unregister_class(userOperator)
    bpy.utils.unregister_class(CheckeurdOperator)
    bpy.utils.unregister_class(AlladonOperator)
    bpy.utils.unregister_class(unchemOperator)
    bpy.utils.unregister_class(cheminOperator)
    bpy.utils.unregister_class(EnadonOperator)

if __name__ == "__main__":
    register()


"""
Class for computing various metrics on a data set with a BayesNet Node object.
"""
import logging

import numpy as np

from .cpd.ogive import OgiveCPD

EPSILON = 1e-16

MAP_ACCURACY_KEY = 'map_accuracy'
AUC_KEY = 'auc'
LOGLI_KEY = 'logli'
D_PRIME_KEY = 'd_prime'
NAIVE_KEY = 'naive'
METRICS_KEYS = {NAIVE_KEY, LOGLI_KEY, MAP_ACCURACY_KEY, AUC_KEY, D_PRIME_KEY}

LOGGER = logging.getLogger(__name__)

class Metrics(object):
    """ Class for computing various performance metrics based on the data and the parameters in a
     Node object. """

    def __init__(self, node):
        """ Initialize this object with a reference to a BayesNet Node object that holds the
        CPD, the data, and the parameters."""
        self.node = node

    def _check_binary_cpd(self, func_name):
        if not isinstance(self.node.cpd, OgiveCPD):
            raise TypeError("{} only defined for OgiveCPDs not %s".format(
                func_name, self.node.cpd.__class__))

    @classmethod
    def _check_finite(cls, prob_true, *args):
        """ Check that all probabilities are finite; if not, remove those elements and corresponding
            elements from other positional args.
        :param np.ndarray prob_true: array to check for finiteness
        :param args: optional arguments to subselect based on isfinite(prob_true)
        :return: np.ndarray|tuple[np.ndarray]
        """
        if not np.all(np.isfinite(prob_true)):
            valid_idx = np.isfinite(prob_true)
            LOGGER.warn("%d non-finite prob corrects found; ignoring these interactions",
                        np.sum(~valid_idx))
            prob_true = prob_true[valid_idx]
            args = tuple([arg[valid_idx] for arg in args])
        if not len(args):
            return prob_true
        else:
            return (prob_true,) + args

    @staticmethod
    def compute_per_student_naive(reg_ids, corrects, is_held_out):
        """ Compute the per-student naive metrics on the training and test sets, based on predicting
        correct if the student had more corrects in the training set.  If no data in the training
        set exist for the student, predict correct.

        :param np.ndarray reg_ids: unique student identifier for each interaction
        :param np.ndarray[bool] corrects: correctness values for each interaction
        :param np.ndarray[bool] is_held_out: indicator whether an interaction is in the test set
        :return: per student naive on the training and test sets
        :rtype: float, float
        """
        if len(corrects) != len(reg_ids) or len(is_held_out) != len(reg_ids):
            raise ValueError("reg_ids (%d), corrects (%d), is_held_out (%d) must have same length",
                             len(reg_ids), len(corrects), len(is_held_out))
        uniq_regids, reg_idxs = np.unique(reg_ids, return_inverse=True)
        num_reg_ids = len(uniq_regids)
        train_reg_idxs = reg_idxs[~is_held_out]
        test_reg_idxs = reg_idxs[is_held_out]
        train_corrects = corrects[~is_held_out]
        test_corrects = corrects[is_held_out]
        per_student_num_correct = np.bincount(train_reg_idxs, weights=train_corrects,
                                              minlength=num_reg_ids)
        per_student_num_responses = np.bincount(train_reg_idxs, minlength=num_reg_ids)
        pred_correct = (2 * per_student_num_correct >= per_student_num_responses)
        train_per_student_naive = np.mean(pred_correct[train_reg_idxs] == train_corrects)
        test_per_student_naive = np.mean(pred_correct[test_reg_idxs] == test_corrects)
        return train_per_student_naive, test_per_student_naive

    def compute_metric(self, metric_key, *args, **kwargs):
        """ Compute metric specified by the supplied key.
        :param str metric_key: key specifying the metric
        :return: the value of the metric
        :rtype: float
        """
        return getattr(self, 'compute_' + metric_key)(*args, **kwargs)

    def compute_naive(self):
        """ Compute the accuracy of predicting always correct or always incorrect,
        whichever is higher. Defined for binary CPDs only.

        :return: a number between 0 and 1 specifying prediction accuracy
        :rtype: float
        """
        self._check_binary_cpd("Naive metric")
        fraction_correct = np.mean(np.array(self.node.data, dtype=float))
        return max(fraction_correct, 1. - fraction_correct)

    def compute_logli(self, avg=False):
        """ Compute the response log-likelihood (the value of the node's CPD given the stored data
        and parameters.

        :param bool avg: whether to normalize the log-likelihood by the size of the node's data
        :return: the sum of the log-likelihoods over the data points.
        :rtype: float
        """
        log_li = self.node.compute_log_prob()
        if avg:
            log_li /= self.node.data.size
        return log_li

    def compute_map_accuracy(self):
        """ Compute the MAP accuracy (fraction of data points predicted correctly at the maximum
        of the binary probability distribution).  Defined for binary CPDs only.

        :return: MAP accuracy
        :rtype: float
        """
        self._check_binary_cpd("MAP accuracy")
        prob_true = self.node.cpd.compute_prob_true(**self.node.param_data)
        prob_true, data = self._check_finite(prob_true, self.node.data)
        return np.mean((prob_true > 0.5) == data)

    def compute_d_prime(self):
        """ Compute the d-prime statistic measuring separation between response probabilities
        conditioned on a true (positive) and false (negative) data points.
        Defined for binary CPDs only.

        :return: the d-prime statistic of distribution separation
        :rtype: float
        """
        self._check_binary_cpd("D prime")
        prob_true = self.node.cpd.compute_prob_true(**self.node.param_data)
        return self.d_prime_helper(self.node.data, prob_true)

    def compute_auc(self):
        """ Compute the area under curve (AUC) for the task of predicting binary labels
        based on the probabilities computed by some model.  The curve is the Receiver Operator
        Characteristic (ROC) curve, which plots the true positive rate vs. the false positive rate
        as one varies the threshold on the probabilities given by the model. AUC is also equal to
        the probability that the model will yield a higher probability for a randomly chosen
        positive data point than for a randomly chosen negative data point.  Defined for binary
        CPDs only.

        NOTE: this assumes at least one positive and one negative data point (otherwise
        the notions of true positive rate and false positive rate do not make
        sense).

        :return: a number between 0 and 1 specifying area under the ROC curve
        :rtype: float
        """
        self._check_binary_cpd("AUC")
        prob_true = self.node.cpd.compute_prob_true(**self.node.param_data)
        return self.auc_helper(self.node.data, prob_true)

    @staticmethod
    def d_prime_helper(data, prob_true):
        """ Compute the d-prime metric (of the separation of probabilities associated with positive
        data labels and negative data labels).

        :param np.ndarray[bool] data: binary data values (positive/negative class labels).
        :param np.ndarray[float] prob_true: probability of positive label
        :return: d-prime metric
        :rtype: float
        """
        if len(prob_true) != len(data):
            raise ValueError('prob_true and data must have the same length')
        prob_true, data = Metrics._check_finite(prob_true, data)
        pc_correct = prob_true[data]
        pc_incorrect = prob_true[np.logical_not(data)]
        mean_sep = np.mean(pc_correct) - np.mean(pc_incorrect)
        norm_const = np.sqrt(0.5 * (np.var(pc_correct) + np.var(pc_incorrect)))
        return mean_sep / norm_const

    @staticmethod
    def auc_helper(data, prob_true):
        """ Compute AUC (area under ROC curve) as a function of binary data values and predicted
        probabilities.  If data includes only positive or only negative labels, returns np.nan.

        :param np.ndarray[bool] data: binary data values (positive/negative class labels).
        :param np.ndarray[float] prob_true: probability of positive label
        :return: area under ROC curve
        :rtype: float
        """
        if len(prob_true) != len(data):
            raise ValueError('prob_true and data must have the same length')

        prob_true, data = Metrics._check_finite(prob_true, data)
        sorted_idx = np.argsort(prob_true)[::-1]
        sorted_prob_true = prob_true[sorted_idx]
        unique_prob_true_idx = np.append(np.flatnonzero(np.diff(sorted_prob_true)),
                                         len(sorted_prob_true) - 1)
        x = data[sorted_idx]
        not_x = np.logical_not(x)

        # Compute cumulative sums of true positives and false positives.
        tp = np.cumsum(x)[unique_prob_true_idx].astype(float)
        fp = np.cumsum(not_x)[unique_prob_true_idx].astype(float)

        # The i'th element of tp (fp) is the number of true (false) positives
        # resulting from using the i'th largest rp as a threshold. That is,
        # we predict correct if a response's rp is >= sorted_prob_true[i].
        # We want the first element to correspond to a threshold sufficiently
        # high to yield no predictions of correct. The highest rp qualifies
        # as this highest threshold if its corresponding response is incorrect.
        # Otherwise, we need to add an artificial "highest threshold" at the
        # beginning that yields 0 true positives and 0 false positives.
        if tp[0] != 0.0:
            tp = np.append(0.0, tp)
            fp = np.append(0.0, fp)

        # Calculate true positive rate and false positive rate.
        # This requires at least 1 correct and 1 incorrect response.
        if not tp[-1]:
            return np.nan
        tpr = tp / tp[-1]

        if not fp[-1]:
            return np.nan
        fpr = fp / fp[-1]

        return np.trapz(tpr, fpr)

    @staticmethod
    def online_perc_correct(correct, student_idx):
        """ For each interaction, compute the percent correct for the student's previous
        interactions.  The returned array will contain NaNs for each student's first interaction.
        :param np.ndarray[bool] correct:
        :param np.ndarray[int] student_idx:
        :return: percent correct on previous interactions for this student
        :rtype: np.ndarray[float]
        """
        student_num_correct = np.zeros(np.max(student_idx) + 1)
        student_num_answered = np.zeros(np.max(student_idx) + 1)
        online_pc = np.nan * np.empty_like(correct, dtype=float)
        for i, c in enumerate(correct):
            j = student_idx[i]
            if student_num_answered[j]:
                online_pc[i] = student_num_correct[j] / float(student_num_answered[j])
            student_num_answered[j] += 1
            student_num_correct[j] += int(c)
        return online_pc


import argparse
import subprocess
import decode
import os
import sys

def full_executable_path(invoked):
     # From https://bugs.python.org/issue8557
     # https://bugs.python.org/issue8557
     # with subprocess.open, c:\windows\system32\curl.exe has precedence on the PATH environment variable for Windows 10
     # which discards the path to the thirdparty directory
    explicit_dir = os.path.dirname(invoked)
    if explicit_dir:
        path = [ explicit_dir ]
    else:
        path = os.environ.get('PATH').split(os.path.pathsep)
    for dir in path:
        full_path = os.path.join(dir, invoked)
        if os.path.exists( full_path ):
            return full_path
    return invoked # Not found; invoking it will likely fail

def main():
    parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description="""Run CURL to fetch data from a Web Server using the environment variable 'CREDENTIALS' or %USERPROFILE%\_netrc
For Windows OS only""")
    parser.add_argument("mediatype", action="store", help="Media type. Typically 'application/json' or 'text/csv'")
    parser.add_argument("url", action="store", help="URL to fetch")
    parser.add_argument("-o", "--output", dest="output", action="store", help="Output file path")
    args = parser.parse_args()

    curl_args = [full_executable_path("curl.exe"), '-c', 'cookies.txt', '-b', 'cookies.txt', '--retry', '5', '--no-buffer', '-f', '-k', '-H', 'Accept: ' + args.mediatype, '-H', 'X-Client: Datamart']
    credentials=os.getenv("CREDENTIALS")
    apikey=os.getenv("APIKEY")
    if credentials:
        curl_args += ['-u', decode.decode(credentials)]
    elif apikey:
        curl_args += ['-H', 'X-API-USER: '  + decode.decode(os.getenv("APIUSER")), '-H', 'X-API-KEY: ' + decode.decode(apikey)]
    else:
        curl_args += ['--netrc-file', os.getenv("USERPROFILE") +  '\_netrc ']
    output=args.output
    if output:
        curl_args += ['-o', output]
    curl_args +=  [args.url]
    # print(curl_args)
    exit_code = subprocess.run(curl_args).returncode
    if exit_code == 22:
        print("on error (470/401): check the credentials", file=sys.stderr)
        print("on error (400): check the REST API version", file=sys.stderr)
        print("on error (404): check the domain name", file=sys.stderr)
        print("Requested URL: "+ args.url, file=sys.stderr)
    sys.exit(exit_code)
if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        sys.exit(1)