from __future__ import unicode_literals

import codecs
import json
import os
import sys
from textwrap import dedent

from compat import unittest

from distlib import __version__
from distlib.compat import StringIO
from distlib.metadata import (LegacyMetadata, Metadata, METADATA_FILENAME,
                              LEGACY_METADATA_FILENAME, PKG_INFO_PREFERRED_VERSION,
                              MetadataMissingError, MetadataUnrecognizedVersionError,
                              MetadataInvalidError, _ATTR2FIELD)

from support import LoggingCatcher, TempdirManager, DistlibTestCase, in_github_workflow

HERE = os.path.abspath(os.path.dirname(__file__))
IN_GITHUB_WORKFLOW = in_github_workflow()

class LegacyMetadataTestCase(LoggingCatcher, TempdirManager,
                             DistlibTestCase):

    maxDiff = None
    restore_environ = ['HOME']

    def setUp(self):
        super(LegacyMetadataTestCase, self).setUp()
        self.argv = sys.argv, sys.argv[:]

    def tearDown(self):
        sys.argv = self.argv[0]
        sys.argv[:] = self.argv[1]
        super(LegacyMetadataTestCase, self).tearDown()

    ####  Test various methods of the LegacyMetadata class

    def get_file_contents(self, name):
        name = os.path.join(HERE, name)
        f = codecs.open(name, 'r', encoding='utf-8')
        try:
            contents = f.read() % sys.platform
        finally:
            f.close()
        return contents

    def test_instantiation(self):
        PKG_INFO = os.path.join(HERE, 'PKG-INFO')
        f = codecs.open(PKG_INFO, 'r', encoding='utf-8')
        try:
            contents = f.read()
        finally:
            f.close()

        fp = StringIO(contents)

        m = LegacyMetadata()
        self.assertRaises(MetadataUnrecognizedVersionError, m.items)

        m = LegacyMetadata(PKG_INFO)
        self.assertEqual(len(m.items()), 22)

        m = LegacyMetadata(fileobj=fp)
        self.assertEqual(len(m.items()), 22)

        m = LegacyMetadata(mapping=dict(name='Test', version='1.0'))
        self.assertEqual(len(m.items()), 17)

        d = dict(m.items())
        self.assertRaises(TypeError, LegacyMetadata,
                          PKG_INFO, fileobj=fp)
        self.assertRaises(TypeError, LegacyMetadata,
                          PKG_INFO, mapping=d)
        self.assertRaises(TypeError, LegacyMetadata,
                          fileobj=fp, mapping=d)
        self.assertRaises(TypeError, LegacyMetadata,
                          PKG_INFO, mapping=m, fileobj=fp)

    def test_mapping_api(self):
        content = self.get_file_contents('PKG-INFO')
        metadata = LegacyMetadata(fileobj=StringIO(content))
        self.assertIn('Version', metadata.keys())
        self.assertIn('0.5', metadata.values())
        self.assertIn(('Version', '0.5'), metadata.items())

        metadata.update({'version': '0.6'})
        self.assertEqual(metadata['Version'], '0.6')
        metadata.update([('version', '0.7')])
        self.assertEqual(metadata['Version'], '0.7')
        # use a kwarg to update
        metadata.update(version='0.6')
        self.assertEqual(metadata['Version'], '0.6')

        # make sure update method checks values like the set method does
        metadata.update({'version': '1--2'})
        self.assertEqual(len(self.get_logs()), 1)

        self.assertEqual(list(metadata), metadata.keys())

    def test_attribute_access(self):
        content = self.get_file_contents('PKG-INFO')
        metadata = LegacyMetadata(fileobj=StringIO(content))
        for attr in _ATTR2FIELD:
            self.assertEqual(getattr(metadata, attr), metadata[attr])

    def test_read_metadata(self):
        fields = {'name': 'project',
                  'version': '1.0',
                  'description': 'desc',
                  'summary': 'xxx',
                  'download_url': 'http://example.com',
                  'keywords': ['one', 'two'],
                  'requires_dist': ['foo']}

        metadata = LegacyMetadata(mapping=fields)
        PKG_INFO = StringIO()
        metadata.write_file(PKG_INFO)
        PKG_INFO.seek(0)

        metadata = LegacyMetadata(fileobj=PKG_INFO)

        self.assertEqual(metadata['name'], 'project')
        self.assertEqual(metadata['version'], '1.0')
        self.assertEqual(metadata['summary'], 'xxx')
        self.assertEqual(metadata['download_url'], 'http://example.com')
        self.assertEqual(metadata['keywords'], ['one', 'two'])
        self.assertEqual(metadata['platform'], [])
        self.assertEqual(metadata['obsoletes'], [])
        self.assertEqual(metadata['requires-dist'], ['foo'])

    def test_write_metadata(self):
        # check support of non-ASCII values
        tmp_dir = self.mkdtemp()
        my_file = os.path.join(tmp_dir, 'f')

        metadata = LegacyMetadata(mapping={
                                     'name': 'my.project',
                                     'author': 'Café Junior',
                                     'summary': 'Café torréfié',
                                     'description': 'Héhéhé',
                                     'keywords': ['café', 'coffee']
                                  })
        metadata.write(my_file)

        # the file should use UTF-8
        metadata2 = LegacyMetadata()
        fp = codecs.open(my_file, encoding='utf-8')
        try:
            metadata2.read_file(fp)
        finally:
            fp.close()

        # XXX when keywords are not defined, metadata will have
        # 'Keywords': [] but metadata2 will have 'Keywords': ['']
        # because of a value.split(',') in LegacyMetadata.get
        self.assertEqual(metadata.items(), metadata2.items())

        # ASCII also works, it's a subset of UTF-8
        metadata = LegacyMetadata(mapping={'author': 'Mister Cafe',
                                     'name': 'my.project',
                                     'author': 'Cafe Junior',
                                     'summary': 'Cafe torrefie',
                                     'description': 'Hehehe'
                                  })
        metadata.write(my_file)

        metadata2 = LegacyMetadata()
        fp = codecs.open(my_file, encoding='utf-8')
        try:
            metadata2.read_file(fp)
        finally:
            fp.close()

    def test_metadata_read_write(self):
        PKG_INFO = os.path.join(HERE, 'PKG-INFO')
        metadata = LegacyMetadata(PKG_INFO)
        out = StringIO()
        metadata.write_file(out)

        out.seek(0)
        res = LegacyMetadata()
        res.read_file(out)
        self.assertEqual(metadata.values(), res.values())

    ####  Test checks

    def test_check_version(self):
        metadata = LegacyMetadata()
        metadata['Name'] = 'vimpdb'
        metadata['Home-page'] = 'http://pypi.org'
        metadata['Author'] = 'Monty Python'
        missing, warnings = metadata.check()
        self.assertEqual(missing, ['Version'])

    def test_check_version_strict(self):
        metadata = LegacyMetadata()
        metadata['Name'] = 'vimpdb'
        metadata['Home-page'] = 'http://pypi.org'
        metadata['Author'] = 'Monty Python'
        self.assertRaises(MetadataMissingError, metadata.check, strict=True)

    def test_check_name(self):
        metadata = LegacyMetadata()
        metadata['Version'] = '1.0'
        metadata['Home-page'] = 'http://pypi.org'
        metadata['Author'] = 'Monty Python'
        missing, warnings = metadata.check()
        self.assertEqual(missing, ['Name'])

    def test_check_name_strict(self):
        metadata = LegacyMetadata()
        metadata['Version'] = '1.0'
        metadata['Home-page'] = 'http://pypi.org'
        metadata['Author'] = 'Monty Python'
        self.assertRaises(MetadataMissingError, metadata.check, strict=True)

    def test_check_author(self):
        metadata = LegacyMetadata()
        metadata['Version'] = '1.0'
        metadata['Name'] = 'vimpdb'
        metadata['Home-page'] = 'http://pypi.org'
        missing, warnings = metadata.check()
        self.assertEqual(missing, ['Author'])

    def test_check_homepage(self):
        metadata = LegacyMetadata()
        metadata['Version'] = '1.0'
        metadata['Name'] = 'vimpdb'
        metadata['Author'] = 'Monty Python'
        missing, warnings = metadata.check()
        self.assertEqual(missing, ['Home-page'])

    def test_check_matchers(self):
        metadata = LegacyMetadata()
        metadata['Version'] = 'rr'
        metadata['Name'] = 'vimpdb'
        metadata['Home-page'] = 'http://pypi.org'
        metadata['Author'] = 'Monty Python'
        metadata['Requires-dist'] = ['Foo (a)']
        metadata['Obsoletes-dist'] = ['Foo (a)']
        metadata['Provides-dist'] = ['Foo (a)']
        missing, warnings = metadata.check()
        self.assertEqual(len(warnings), 4)

    ####  Test fields and metadata versions

    def test_metadata_versions(self):
        metadata = LegacyMetadata(mapping={'name': 'project',
                                           'version': '1.0'})
        self.assertEqual(metadata['Metadata-Version'],
                         PKG_INFO_PREFERRED_VERSION)
        self.assertNotIn('Provides', metadata)
        self.assertNotIn('Requires', metadata)
        self.assertNotIn('Obsoletes', metadata)

        metadata['Classifier'] = ['ok']
        metadata.set_metadata_version()
        self.assertEqual(metadata['Metadata-Version'], '1.1')

        metadata = LegacyMetadata()
        metadata['Download-URL'] = 'ok'
        metadata.set_metadata_version()
        self.assertEqual(metadata['Metadata-Version'], '1.1')

        metadata = LegacyMetadata()
        metadata['Obsoletes'] = 'ok'
        metadata.set_metadata_version()
        self.assertEqual(metadata['Metadata-Version'], '1.1')

        del metadata['Obsoletes']
        metadata['Obsoletes-Dist'] = 'ok'
        metadata.set_metadata_version()
        self.assertEqual(metadata['Metadata-Version'], '1.2')
        metadata.set('Obsoletes', 'ok')
        # See issue #140. Relaxed checking on Obsoletes
        # self.assertRaises(MetadataConflictError,
                          # metadata.set_metadata_version)
        metadata.set_metadata_version()
        self.assertEqual(metadata['Metadata-Version'], '2.2')

        del metadata['Obsoletes']
        del metadata['Obsoletes-Dist']
        metadata.set_metadata_version()
        metadata['Version'] = '1'
        self.assertEqual(metadata['Metadata-Version'], '1.1')

        # make sure the _best_version function works okay with
        # non-conflicting fields from 1.1 and 1.2 (i.e. we want only the
        # requires/requires-dist and co. pairs to cause a conflict, not all
        # fields in _314_MARKERS)
        metadata = LegacyMetadata()
        metadata['Requires-Python'] = '3'
        metadata['Classifier'] = ['Programming language :: Python :: 3']
        metadata.set_metadata_version()
        self.assertEqual(metadata['Metadata-Version'], '1.2')

        PKG_INFO = os.path.join(HERE, 'SETUPTOOLS-PKG-INFO')
        metadata = LegacyMetadata(PKG_INFO)
        self.assertEqual(metadata['Metadata-Version'], '1.0')

        PKG_INFO = os.path.join(HERE, 'SETUPTOOLS-PKG-INFO2')
        metadata = LegacyMetadata(PKG_INFO)
        self.assertEqual(metadata['Metadata-Version'], '1.1')

        # make sure an empty list for Obsoletes and Requires-dist gets ignored
        metadata['Obsoletes'] = []
        metadata['Requires-dist'] = []
        metadata.set_metadata_version()
        self.assertEqual(metadata['Metadata-Version'], '1.1')

        # Update the _fields dict directly to prevent 'Metadata-Version'
        # from being updated by the _set_best_version() method.
        metadata._fields['Metadata-Version'] = '1.618'
        self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys)

        # add a test for 2.1
        metadata = LegacyMetadata()
        metadata['Description-Content-Type'] = 'text/markdown; charset=UTF-8; variant=CommonMark'
        metadata.set_metadata_version()
        self.assertEqual(metadata['Metadata-Version'], '2.1')

    def test_version(self):
        LegacyMetadata(mapping={'author': 'xxx',
                          'name': 'xxx',
                          'version': 'xxx',
                          'home_page': 'xxxx'
                       })
        logs = self.get_logs()
        self.assertEqual(1, len(logs))
        self.assertIn('not a valid version', logs[0])

    @unittest.skipIf(IN_GITHUB_WORKFLOW, 'This test is end-of-line dependent')
    def test_description(self):
        content = self.get_file_contents('PKG-INFO')
        metadata = LegacyMetadata()
        metadata.read_file(StringIO(content))

        # see if we can read the description now
        DESC = os.path.join(HERE, 'LONG_DESC.txt')
        f = open(DESC)
        try:
            wanted = f.read()
        finally:
            f.close()
        self.assertEqual(wanted, metadata['Description'])

        # save the file somewhere and make sure we can read it back
        out = StringIO()
        metadata.write_file(out)
        out.seek(0)

        out.seek(0)
        metadata = LegacyMetadata()
        metadata.read_file(out)
        self.assertEqual(wanted, metadata['Description'])

    def test_description_folding(self):
        # make sure the indentation is preserved
        out = StringIO()
        desc = dedent("""\
        example::
              We start here
            and continue here
          and end here.
        """)

        metadata = LegacyMetadata()
        metadata['description'] = desc
        metadata.write_file(out)

        # folded_desc = desc.replace('\n', '\n' + (7 * ' ') + '|')
        folded_desc = desc.replace('\n', '\n' + (8 * ' '))
        self.assertIn(folded_desc, out.getvalue())

    def test_project_url(self):
        metadata = LegacyMetadata()
        metadata['Project-URL'] = [('one', 'http://ok')]
        self.assertEqual(metadata['Project-URL'], [('one', 'http://ok')])
        metadata.set_metadata_version()
        self.assertEqual(metadata['Metadata-Version'], '1.2')

        # make sure this particular field is handled properly when written
        fp = StringIO()
        metadata.write_file(fp)
        self.assertIn('Project-URL: one,http://ok', fp.getvalue().split('\n'))

        fp.seek(0)
        metadata = LegacyMetadata()
        metadata.read_file(fp)
        self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')])

    # TODO copy tests for v1.1 requires, obsoletes and provides from distutils
    # (they're useless but we support them so we should test them anyway)

    def test_provides_dist(self):
        fields = {'name': 'project',
                  'version': '1.0',
                  'provides_dist': ['project', 'my.project']}
        metadata = LegacyMetadata(mapping=fields)
        self.assertEqual(metadata['Provides-Dist'],
                         ['project', 'my.project'])
        self.assertEqual(metadata['Metadata-Version'], '1.2', metadata)
        self.assertNotIn('Requires', metadata)
        self.assertNotIn('Obsoletes', metadata)

    def test_requires_dist(self):
        fields = {'name': 'project',
                  'version': '1.0',
                  'requires_dist': ['other', 'another (==1.0)']}
        metadata = LegacyMetadata(mapping=fields)
        self.assertEqual(metadata['Requires-Dist'],
                         ['other', 'another (==1.0)'])
        self.assertEqual(metadata['Metadata-Version'], '1.2')
        self.assertNotIn('Provides', metadata)
        self.assertEqual(metadata['Requires-Dist'],
                         ['other', 'another (==1.0)'])
        self.assertNotIn('Obsoletes', metadata)

        # make sure write_file uses one RFC 822 header per item
        fp = StringIO()
        metadata.write_file(fp)
        lines = fp.getvalue().split('\n')
        self.assertIn('Requires-Dist: other', lines)
        self.assertIn('Requires-Dist: another (==1.0)', lines)

        # test warnings for invalid version constraints
        # XXX this would cause no warnings if we used update (or the mapping
        # argument of the constructor), see comment in LegacyMetadata.update
        metadata = LegacyMetadata()
        metadata['Requires-Dist'] = 'Funky (Groovie)'
        metadata['Requires-Python'] = '1a-4'
        self.assertEqual(len(self.get_logs()), 2)

        # test multiple version matches
        metadata = LegacyMetadata()

        # XXX check PEP and see if 3 == 3.0
        metadata['Requires-Python'] = '>=2.6, <3.0'
        metadata['Requires-Dist'] = ['Foo (>=2.6, <3.0)']
        self.assertEqual(self.get_logs(), [])

    def test_obsoletes_dist(self):
        fields = {'name': 'project',
                  'version': '1.0',
                  'obsoletes_dist': ['other', 'another (<1.0)']}
        metadata = LegacyMetadata(mapping=fields)
        self.assertEqual(metadata['Obsoletes-Dist'],
                         ['other', 'another (<1.0)'])
        self.assertEqual(metadata['Metadata-Version'], '1.2')
        self.assertNotIn('Provides', metadata)
        self.assertNotIn('Requires', metadata)
        self.assertEqual(metadata['Obsoletes-Dist'],
                         ['other', 'another (<1.0)'])

    def test_fullname(self):
        md = LegacyMetadata()
        md['Name'] = 'a b c'
        md['Version'] = '1 0 0'
        s = md.get_fullname()
        self.assertEqual(s, 'a b c-1 0 0')
        s = md.get_fullname(True)
        self.assertEqual(s, 'a-b-c-1.0.0')

    def test_fields(self):
        md = LegacyMetadata()
        self.assertTrue(md.is_multi_field('Requires-Dist'))
        self.assertFalse(md.is_multi_field('Name'))
        self.assertTrue(md.is_field('Obsoleted-By'))
        self.assertFalse(md.is_field('Frobozz'))

class MetadataTestCase(LoggingCatcher, TempdirManager,
                       DistlibTestCase):
    def test_init(self):
        "Test initialisation"
        md = Metadata()
        self.assertIsNone(md._legacy)
        self.assertRaises(MetadataMissingError, md.validate)
        md.name = 'dummy'
        self.assertRaises(MetadataMissingError, md.validate)
        md.version = '0.1'
        self.assertRaises(MetadataMissingError, md.validate)
        md.summary = 'Summary'
        md.validate()
        self.assertEqual(md.name, 'dummy')
        self.assertEqual(md.version, '0.1')

        # Initialise from mapping
        md = Metadata(mapping={
                        'metadata_version': '2.0',
                        'name': 'foo',
                        'version': '0.3.4',
                        'summary': 'Summary',
                      })
        md.validate()
        self.assertEqual(md.name, 'foo')
        self.assertEqual(md.version, '0.3.4')
        self.assertEqual(md.run_requires, [])
        self.assertEqual(md.meta_requires, [])
        self.assertEqual(md.provides, ['foo (0.3.4)'])

        # Initialise from legacy metadata
        fn = os.path.join(HERE, 'fake_dists', 'choxie-2.0.0.9.dist-info',
                          LEGACY_METADATA_FILENAME)
        md = Metadata(path=fn)
        md.validate()
        self.assertIsNotNone(md._legacy)
        self.assertEqual(set(md.run_requires), set(['towel-stuff (0.1)', 'nut']))
        self.assertEqual(md.metadata_version, '1.2')
        self.assertEqual(md.version, '2.0.0.9')
        self.assertEqual(md.meta_requires, [])
        self.assertEqual(set(md.provides),
                         set(['choxie (2.0.0.9)', 'truffles (1.0)']))

        # Initialise from new metadata
        fn = os.path.join(HERE, METADATA_FILENAME)
        md = Metadata(path=fn)
        md.validate()
        self.assertIsNone(md._legacy)
        self.assertEqual(md.metadata_version, '2.0')
        self.assertEqual(md.name, 'foobar')
        self.assertEqual(md.version, '0.1')
        self.assertEqual(md.provides, ['foobar (0.1)'])

    def test_add_requirements(self):
        md = Metadata()
        md.name = 'bar'
        md.version = '0.5'
        md.add_requirements(['foo (0.1.2)'])
        self.assertEqual(md.run_requires, [{ 'requires': ['foo (0.1.2)']}])

        fn = os.path.join(HERE, 'fake_dists', 'choxie-2.0.0.9.dist-info',
                          LEGACY_METADATA_FILENAME)
        md = Metadata(path=fn)
        md.add_requirements(['foo (0.1.2)'])
        self.assertEqual(set(md.run_requires),
                         set(['towel-stuff (0.1)', 'nut', 'foo (0.1.2)']))

    def test_requirements(self):
        fn = os.path.join(HERE, METADATA_FILENAME)
        md = Metadata(path=fn)
        self.assertEqual(md.meta_requires, [{'requires': ['bar (1.0)']}])
        r = md.get_requirements(md.run_requires)
        self.assertEqual(r, ['foo'])
        r = md.get_requirements(md.run_requires, extras=['certs'])
        self.assertEqual(r, ['foo', 'certifi (0.0.8)'])
        r = md.get_requirements(md.run_requires, extras=['certs', 'ssl'])
        if sys.platform != 'win32':
            self.assertEqual(r, ['foo', 'certifi (0.0.8)'])
        else:
            self.assertEqual(set(r), set(['foo', 'certifi (0.0.8)',
                                          'wincertstore (0.1)']))
        for ver in ('2.5', '2.4'):
            env = {'python_version': ver}
            r = md.get_requirements(md.run_requires,
                                    extras=['certs', 'ssl'], env=env)
            if sys.platform != 'win32':
                self.assertEqual(set(r), set(['foo', 'certifi (0.0.8)',
                                              'ssl (1.16)']))
            elif ver == '2.4':
                self.assertEqual(set(r), set(['certifi (0.0.8)', 'ssl (1.16)',
                                              'wincertstore (0.1)', 'foo',
                                              'ctypes (1.0.2)']))
            else:
                self.assertEqual(set(r), set(['certifi (0.0.8)', 'ssl (1.16)',
                                              'wincertstore (0.1)', 'foo']))
        env['sys_platform'] = 'win32'
        r = md.get_requirements(md.run_requires,
                                extras=['certs', 'ssl'], env=env)
        self.assertEqual(set(r), set(['foo', 'certifi (0.0.8)', 'ssl (1.16)',
                                      'ctypes (1.0.2)', 'wincertstore (0.1)']))
        env['python_version'] = '2.5'
        r = md.get_requirements(md.run_requires,
                                extras=['certs', 'ssl'], env=env)
        self.assertEqual(set(r), set(['foo', 'certifi (0.0.8)', 'ssl (1.16)',
                                      'wincertstore (0.1)']))
        r = md.get_requirements(md.run_requires, extras=[':test:'])
        self.assertEqual(r, ['foo', 'nose'])
        r = md.get_requirements(md.run_requires, extras=[':test:', 'udp'])
        self.assertEqual(set(r), set(['foo', 'nose', 'nose-udp']))
        self.assertEqual(md.dependencies, {
            'provides': ['foobar (0.1)'],
            'meta_requires': [
                {
                    'requires': ['bar (1.0)']
                }
            ],
            'extras': ['ssl', 'certs'],
            'build_requires': [],
            'test_requires': [
                {
                    'requires': ['nose'],
                },
                {
                    'requires': ['nose-udp'],
                    'extra': 'udp',
                }
            ],
            'run_requires': [
                {
                    'requires': ['foo']
                },
                {
                    'requires': ['certifi (0.0.8)'],
                    'extra': 'certs',
                },
                {
                    'requires': ['wincertstore (0.1)'],
                    'extra': 'ssl',
                    'environment': "sys_platform=='win32'",
                },
                {
                    'requires': ['ctypes (1.0.2)'],
                    'extra': 'ssl',
                    'environment': "sys_platform=='win32' and "
                                   "python_version=='2.4'",
                },
                {
                    'requires': ['ssl (1.16)'],
                    'extra': 'ssl',
                    'environment': "python_version in '2.4, 2.5'",
                }
            ]
        })

    def test_write(self):
        dfn = self.temp_filename()
        # Read legacy, write new
        sfn = os.path.join(HERE, 'fake_dists', 'choxie-2.0.0.9.dist-info',
                           LEGACY_METADATA_FILENAME)
        md = Metadata(path=sfn)
        md.write(path=dfn)
        with codecs.open(dfn, 'r', 'utf-8') as f:
            data = json.load(f)
        self.assertEqual(data, {
            'metadata_version': '2.0',
            'generator': 'distlib (%s)' % __version__,
            'name': 'choxie',
            'version': '2.0.0.9',
            'license': 'BSD',
            'summary': 'Chocolate with a kick!',
            'description': 'Chocolate with a longer kick!',
            'provides': ['truffles (1.0)', 'choxie (2.0.0.9)'],
            'run_requires': [{'requires': ['towel-stuff (0.1)', 'nut']}],
            'keywords': [],
        })
        # Write legacy, compare with original
        md.write(path=dfn, legacy=True)
        nmd = Metadata(path=dfn)
        d1 = md.todict()
        d2 = nmd.todict()
        self.assertEqual(d1, d2)

    def test_valid(self):
        """
        Tests to check that missing and invalid metadata is caught.
        """
        md = Metadata()
        self.assertRaises(MetadataMissingError, md.validate)
        try:
            md.name = 'Foo Bar'
        except MetadataInvalidError:
            pass
        md.name = 'foo_bar'
        # Name now OK, but version and summary to be checked
        self.assertRaises(MetadataMissingError, md.validate)
        try:
            md.version = '1.0a'
        except MetadataInvalidError:
            pass
        md.version = '1.0'
        # Name and version now OK, but summary to be checked
        self.assertRaises(MetadataMissingError, md.validate)
        try:
            md.summary = ''
        except MetadataInvalidError:
            pass
        try:
            md.summary = ' ' * 2048
        except MetadataInvalidError:
            pass
        md.summary = ' ' * 2047
        md.validate()
        md.summary = ' '
        md.validate()

if __name__ == '__main__':  # pragma: no cover
    unittest.main()


from django.db import models
from django.forms import CheckboxSelectMultiple
from django.utils import timezone

from wagtail.fields import StreamField, RichTextField
from wagtail.models import Page, Orderable
from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel
from wagtail.search import index

from modelcluster.fields import ParentalKey, ParentalManyToManyField
from modelcluster.tags import ClusterTaggableManager
from taggit.models import TaggedItemBase

from .blocks import ButtonBlock
from .settings import get_grid_item_parent_page_types, get_grid_index_page_subpage_types

class GridItemTag(TaggedItemBase):
    """
    Object to hold existing tags for Grid Items.
    """

    content_object = ParentalKey(
        "GridItem",
        related_name="tagged_items",
        on_delete=models.CASCADE,
    )

class GridCategory(models.Model):
    """
    Categories which a grid item can belong to. A grid item can belong to many
    categories. Categories can be selected from the top of the grid index page.
    """

    name = models.CharField(max_length=255)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "grid categories"

class GridItem(Page):
    """
    The fields needed to properly display a grid item.
    The template will omit any fields not included automagically.
    """

    parent_page_types = get_grid_item_parent_page_types()

    class Meta:
        verbose_name = "Grid Item"

    summary_image = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )
    summary_text = RichTextField(
        "Summary",
        default="",
        help_text='The summary will appear in the item "card" view.',
    )
    description_image = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text="This image will appear in the expanded area when populated.",
    )
    description_text = RichTextField(
        "Full Description",
        null=True,
        blank=True,
        help_text="This description will appear in the expanded area when populated.",
    )
    description_video = models.URLField(
        null=True,
        blank=True,
        help_text="This video will be embedded in the expanded area when populated.",
    )
    landing_page_text = RichTextField(
        "Landing Page Text",
        null=True,
        blank=True,
        help_text="This is the text which will appear on the grid item's landing page.",
    )
    buttons = StreamField(ButtonBlock(), null=True, use_json_field=True)
    tags = ClusterTaggableManager(through=GridItemTag, blank=True)
    categories = ParentalManyToManyField("GridCategory", blank=True)
    modified = models.DateTimeField("Page Modified", null=True)

    search_fields = Page.search_fields + [
        index.SearchField("summary_text"),
        index.SearchField("description_text"),
        index.SearchField("landing_page_text"),
    ]

    CARD_PANELS = [
        FieldPanel("summary_image"),
        FieldPanel("summary_text"),
    ]

    DETAIL_PANELS = [
        FieldPanel("description_image"),
        FieldPanel("description_text"),
        FieldPanel("description_video"),
        FieldPanel("landing_page_text"),
    ]

    META_PANELS = [
        FieldPanel("tags"),
        FieldPanel("categories", widget=CheckboxSelectMultiple),
    ]

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            CARD_PANELS,
            heading="Card Information",
            classname="collapsible",
        ),
        MultiFieldPanel(
            DETAIL_PANELS,
            heading="Expanded Description & Page Information",
            classname="collapsible",
        ),
        FieldPanel(
            "buttons",
        ),
        MultiFieldPanel(
            META_PANELS,
            heading="Metadata",
            classname="collapsible",
        ),
    ]

    def save(self, *args, **kwargs):
        self.modified = timezone.now()
        super().save(*args, **kwargs)

class GridIndexGridItemRelationship(Orderable, models.Model):
    """
    Allows the content creator to associate Grid Items on a
    Grid Index Page.
    """

    grid_relationship = ParentalKey(
        "GridIndexPage",
        related_name="grid_index_grid_item_relationship",
        on_delete=models.CASCADE,
    )
    grid_item = models.ForeignKey(
        "GridItem",
        related_name="+",
        help_text="Add a grid item to the page",
        verbose_name="Grid Items",
        on_delete=models.CASCADE,
    )
    panels = [FieldPanel("grid_item")]

class GridIndexPageAbstract(models.Model):
    """
    Index page for Grid Items.
    This links the grid items to the categories and provides a page to display them on.

    This abstract class exists to allow compositing the index page with other page
    functionality by future users. GridIndexPage below makes this concrete.
    """

    subpage_types = get_grid_index_page_subpage_types()

    hero_background_image = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text="The background image for the hero section. This triggers the "
        "section to be displayed if an image is selected.",
    )

    hero_logo_image = models.ForeignKey(
        "wagtailimages.Image",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text="The logo image to be displayed over the background image.",
    )

    hero_description = RichTextField(
        null=True,
        blank=True,
        help_text="Text to be displayed beneath the logo over the background image.",
    )

    hero_button_text = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text="Text for the call-to-action button beneath the text and logo over "
        "the background image.",
    )

    hero_button_url = models.CharField(
        null=True,
        blank=True,
        max_length=255,
        help_text="URL for the call-to-action button beneath the text and logo over "
        "the background image.",
    )

    featured_description = RichTextField(
        null=True,
        blank=True,
        help_text="Text to be displayed below the hero image next to the featured "
        "items.",
    )

    featured_grid_item_1 = models.ForeignKey(
        GridItem,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text="First featured grid item underneath the hero image.",
        verbose_name="Featured Item One",
    )

    featured_grid_item_2 = models.ForeignKey(
        GridItem,
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
        help_text="Second featured grid item underneath the hero image.",
        verbose_name="Featured Item Two,",
    )

    @property
    def grid_items(self):
        grid_items = [n.grid_item for n in self.grid_index_grid_item_relationship.all()]
        return grid_items

    @property
    def categories(self):
        grid_item_categories = (
            GridIndexGridItemRelationship.objects.values_list(
                "grid_item__categories__name"
            )
            .filter(
                grid_relationship__id=self.id,
            )
            .order_by(
                "grid_item__categories__name",
            )
            .distinct()
        )

        categories = []

        for gic in grid_item_categories:
            if gic[0] is not None:
                categories.append(gic[0])

        return categories

    HERO_PANELS = [
        FieldPanel("hero_background_image"),
        FieldPanel("hero_logo_image"),
        FieldPanel("hero_description"),
        FieldPanel("hero_button_text"),
        FieldPanel("hero_button_url"),
        FieldPanel("featured_description"),
        FieldPanel("featured_grid_item_1"),
        FieldPanel("featured_grid_item_2"),
    ]

    content_panels = Page.content_panels + [
        MultiFieldPanel(
            HERO_PANELS,
            heading="Hero Section (Optional)",
            classname="collapsible collapsed",
        ),
        InlinePanel(
            "grid_index_grid_item_relationship",
            label="grid_items",
            panels=None,
            min_num=1,
        ),
    ]

    search_fields = Page.search_fields + [
        index.SearchField("hero_description"),
    ]

    class Meta:
        abstract = True

    def __str__(self):
        return "{0}".format(
            self.grid_items,
        )

class GridIndexPage(GridIndexPageAbstract, Page):
    """
    Concrete implementation of GridIndexPageAbstract.
    """

    class Meta:
        verbose_name = "Grid Index Page"


"""empty message

Revision ID: da5815fb47ef
Revises: 462e5f0b6353
Create Date: 2020-04-28 12:37:28.619048

"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql

# revision identifiers, used by Alembic.
revision = 'da5815fb47ef'
down_revision = '462e5f0b6353'
branch_labels = None
depends_on = None

def upgrade():
    pass

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('snow_child',
    sa.Column('id', mysql.BIGINT(display_width=20), autoincrement=False, nullable=False, comment='关联的公司用户id'),
    sa.Column('name', mysql.VARCHAR(length=20), nullable=True, comment='测试名字'),
    sa.PrimaryKeyConstraint('id'),
    comment='雪花算法子表',
    mysql_comment='雪花算法子表',
    mysql_default_charset='utf8',
    mysql_engine='InnoDB'
    )
    op.drop_table('Snow_Child')
    # ### end Alembic commands ###


class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        # hash key-value pair
        # any particular sum: how many times it happens
        count = 0
        prefixSum = 0
        prefixSumHash = {0:1} # initialze {0,1}, 来计算那些num[i]==k的情况

        for i in range(0,len(nums)):
            # update sum
            prefixSum += nums[i]
            # check if target in it (思路很像two sum)
            diff = prefixSum - k
            if diff in prefixSumHash:
                count += prefixSumHash[diff]

            if prefixSum in prefixSumHash:
                prefixSumHash[prefixSum] += 1
            else:
                prefixSumHash[prefixSum] = 1

        return count


import yfinance as yf
import sys, os

file = open("stonks.txt",'r')
list_of_stonks = []
ticker_objs = {}
i = 0

for line in file:
    if len(line)>1:
        list_of_stonks.append(line.strip('\n'))

for ticker in list_of_stonks:
    i = i + 1
    try:
        ticker_objs[ticker] = yf.Ticker(ticker)
        print(str(i) + "," + ticker + "," + ticker_objs[ticker].info['sector'] + "," + ticker_objs[ticker].info['industry'])
    except Exception as e:
        print(e.args)

##############################################

## More random code
# import yfinance as yf
#
# list_of_stonks = ['AAPL','MSFT','TSLA','AMD','GME']
# stock_tickers = {}
#
# for ticker in list_of_stonks:
#     print("Getting ", ticker)
#     stock_tickers[ticker] = yf.Ticker(ticker)
#
# print(stock_tickers['AAPL'].balancesheet)   # pandas dataframe


# -*- coding: utf-8 -*-
"""
Created on Sat Oct 20 12:40:07 2018

@author: laksh
"""

## IMPORTS
import numpy as np
import time
import sys
from os import listdir
from os.path import isfile, join
import codecs
import re

if(len(sys.argv) != 4):      # also includes sys.argv[0] i.e. file of name
    sys.exit("Please give the required amount of arguments - \
              <path-to-training-dataset-up-till-folder-name-that-contains-ham-and-spam-folders> \
              <path-to-test-dataset-up-till-folder-name-that-contains-ham-and-spam-folders> \
              <path-to-stop-words-text-file-including-file-name> \n\n \
               Please refer to the Read Me document for further details.")
#if (False):
#    pass
else:
    pathToTrainingDataset = sys.argv[1]
    pathToTestDataset = sys.argv[2]
    pathToStopWordsTextFile = sys.argv[3]
#    pathToTrainingDataset = r'C:\Users\laksh\Downloads\ML\Assignment 2\Submission\hw2_train\train\'
#    pathToTestDataset = r'C:\Users\laksh\Downloads\ML\Assignment 2\Submission\hw2_test\test\'
#    pathToStopWordsTextFile = r'C:\Users\laksh\Downloads\ML\Assignment 2\Submission\stopWords.txt'
#   spyder command line arguments "C:\Users\laksh\Downloads\ML\Assignment 2\Submission\hw2_train\train\\" "C:\Users\laksh\Downloads\ML\Assignment 2\Submission\hw2_test\test\\" "C:\Users\laksh\Downloads\ML\Assignment 2\Submission\stopWords.txt"
# NON-INPUT PARAMETERS
regularizationLambda = 3
learningRateAlpha = 0.001
numberOfIterations = 100

print("VALUES OF PARAMETERS: ")
print("Regularization Lambda: {}".format(str(regularizationLambda)))
print("Learning Rate Alpha: {}".format(str(learningRateAlpha)))
print("Number of Iterations: {}\n".format(str(numberOfIterations)))

# CREATING LIST OF 174 STOPWORDS
with open(pathToStopWordsTextFile, 'r') as stopWordsTextFile:
    listOfStopWords = stopWordsTextFile.read().split('\n')

# DEFINITIONS of ClassificationClass and featureMatrix
class ClassificationClass:
    def __init__(self, className = None, classValue = None, listOfFiles = list(),
                 dictionaryOfWords = dict(), priorProbability = 0,
                 conditionalPriorDictionary = dict(), pathToFiles = None, numberOfWords = 0,
                 countOfWords = 0):
        self.className = className
        self.classValue = classValue
        self.listOfFiles = listOfFiles
        self.dictionaryOfWords = dictionaryOfWords
        self.prior = priorProbability
        self.condPriorDict = conditionalPriorDictionary
        self.pathToFiles = pathToFiles
        self.numberOfWords = numberOfWords

class featureMatrix:
    def __init__(self, matrix = None, numberOfColumns = None, classificationClass = None,
                 wordsList = None, countOfFiles = None, valueOfSigmoidFunction = None, weights = None):
        self.matrix  = matrix                       # fileNames for rows, words for columns
        self.numberOfColumns = numberOfColumns
        self.classificationClass = classificationClass
        self.wordsList= wordsList
        self.countOfFiles= countOfFiles
        self.valueOfSigmoidFunction = valueOfSigmoidFunction
        self.weights = weights

def createFeatureMatrixFrom(pathToTrainingDataset, stopWordsRemoved):
    # Creating listOfClassificationClasses
    listOfClassificationClasses = []
    hamClass = ClassificationClass(className = 'ham', classValue = 0,
                listOfFiles = [file for file in listdir(pathToTrainingDataset + 'ham') if
                                  isfile(join(pathToTrainingDataset + 'ham', file))],
                pathToFiles = pathToTrainingDataset + 'ham\\')
    spamClass = ClassificationClass(className = 'spam', classValue = 1,
                listOfFiles = [file for file in listdir(pathToTrainingDataset + 'spam') if
                                  isfile(join(pathToTrainingDataset + 'spam', file))],
                               pathToFiles = pathToTrainingDataset + 'spam\\')
    listOfClassificationClasses.append(hamClass)
    listOfClassificationClasses.append(spamClass)

    # Populating dictionaryOfWords and numberOfWords for each classificationClass
    # doing this to get overallDictionaryOfWords, in order to get numberOfColumns in matrix
    def wordsDictAndCount(listOfFiles, pathToFile):
        countOfWords=0
        dictionaryOfWords = dict()

        for fileName in listOfFiles:
            fileHandler = codecs.open(pathToFile + fileName,'rU','latin-1') # codecs handles -> UnicodeDecodeError: 'charmap' codec can't decode byte 0x9d in position 1651: character maps to <undefined>
            extractedWords = [word.lower() for word in re.findall('[A-Za-z0-9\']+', fileHandler.read())]
            fileHandler.close()
            countOfWords += len(extractedWords)
            for extractedWord in extractedWords:
                if extractedWord not in dictionaryOfWords:
                    dictionaryOfWords[extractedWord] = 1
                else:
                    dictionaryOfWords[extractedWord] += 1
        return dictionaryOfWords, countOfWords

    for classificationClass in listOfClassificationClasses:
            classificationClass.dictionaryOfWords, classificationClass.numberOfWords = wordsDictAndCount(classificationClass.listOfFiles, classificationClass.pathToFiles)

    # Calculating overallNumberOfFiles: to determine numberOfRows in matrix
    overallNumberOfFiles = 0
    for classificationClass in listOfClassificationClasses:
        overallNumberOfFiles += len(classificationClass.listOfFiles)

    # Populating overall dictionaryOfWords: to determine numberOfColumns in matrix
    overallDictionaryOfWords = dict()
    for classificationClass in listOfClassificationClasses:
        # Combining all dictionaries of classification classes
        for word in classificationClass.dictionaryOfWords:
            if (word not in overallDictionaryOfWords):
                overallDictionaryOfWords[word] = classificationClass.dictionaryOfWords[word]
            else:
                overallDictionaryOfWords[word] += classificationClass.dictionaryOfWords[word]

    numberOfColumns = len(overallDictionaryOfWords)
    numberOfRows = overallNumberOfFiles                           # 463
    matrix = [[0 for columnNumber in range(numberOfColumns)] for rowNumber in range(numberOfRows)]
    targetFunction = [0 for rowNumber in range(numberOfRows)]
    wordsList = []     # for representing each word in numerical form (using indexes)
    countOfFiles = 0   # for populating each row one by one

    # Populating Matrix
    for classificationClass in listOfClassificationClasses:
        for fileName in classificationClass.listOfFiles:
            fileHandler = codecs.open(classificationClass.pathToFiles + fileName,'rU','latin-1')
            extractedWords = [word.lower() for word in re.findall('[A-Za-z0-9\']+', fileHandler.read())]
            fileHandler.close()

            dictionaryOfWords = {}
            for extractedWord in extractedWords:
                if extractedWord not in dictionaryOfWords:
                    dictionaryOfWords[extractedWord] = 1
                else:
                    dictionaryOfWords[extractedWord] += 1

            # WITHOUT STOP WORDS i.e. stopWordsRemoved = True
            if stopWordsRemoved:
                for stopWord in listOfStopWords:
                    if stopWord in dictionaryOfWords:
                        del dictionaryOfWords[stopWord]

            for word in dictionaryOfWords:
                if word in wordsList:
                    index = wordsList.index(word)
                    matrix[countOfFiles][index] = dictionaryOfWords[word]
                elif word not in wordsList:
                    wordsList.append(word)
                    index = wordsList.index(word)
                    matrix[countOfFiles][index] = dictionaryOfWords[word]

            targetFunction[countOfFiles] = classificationClass.classValue
            countOfFiles += 1

    #print("Number of files: {}".format(countOfFiles)) # 463
    return featureMatrix(matrix = matrix, numberOfColumns = numberOfColumns,
                         classificationClass = targetFunction, wordsList = wordsList,
                         countOfFiles = countOfFiles, weights = [0.0 for eachWord in range(len(wordsList))],
                         valueOfSigmoidFunction = [0 for index in range(countOfFiles)])

def populateSigmoid(featureMatrix):
    for fileNumber in range(featureMatrix.countOfFiles):
        summationOfWeightAndFrequency = 1.0
        valueOfSigmoidFunction = 0.0

        #summation of weightOfWord*frequencyOfWord
        for wordNumber in range(len(featureMatrix.wordsList)):
            #print(featureMatrix.weights[wordNumber], end = " ")     # 0.0 for all words in 1st file
            summationOfWeightAndFrequency += featureMatrix.weights[wordNumber]  * featureMatrix.matrix[fileNumber][wordNumber]
        # sigmoid function
        try:
            valueOfSigmoidFunction = ( 1 / (1 + np.exp(-summationOfWeightAndFrequency)))
        except Exception:
            print("Exception while executing sigmoid function. Denominator: {}; Summation: {}".format(str((1 + np.exp(-summationOfWeightAndFrequency))), str(summationOfWeightAndFrequency)))
        featureMatrix.valueOfSigmoidFunction[fileNumber] = valueOfSigmoidFunction
    return featureMatrix

def calculateWeightUpdate(featureMatrix):
    for weightNumber in range(len(featureMatrix.weights)):
        termOne = 0                 # bias term
        for fileNumber in range(featureMatrix.countOfFiles):
            frequencyOfWord = featureMatrix.matrix[fileNumber][weightNumber]
            valueForTargetFunction = featureMatrix.classificationClass[fileNumber]
            valueOfSigmoidFunction = featureMatrix.valueOfSigmoidFunction[fileNumber]

            termOne += frequencyOfWord * (valueForTargetFunction - valueOfSigmoidFunction)
        weightValueBeforeUpdate = featureMatrix.weights[weightNumber]
        # weight update formula as given on slide 26 of 'Logistic Regression.pdf;
        featureMatrix.weights[weightNumber] += ((termOne * learningRateAlpha) - (learningRateAlpha * regularizationLambda * weightValueBeforeUpdate))
    return featureMatrix

def trainingFunction(featureMatrix):
    print("Iterations: ", end = "")
    for iterationNumber in range(1, numberOfIterations + 1):
        print(iterationNumber, end = " ")
        featureMatrix = populateSigmoid(featureMatrix)
        featureMatrix = calculateWeightUpdate(featureMatrix)
    return featureMatrix

def classificationFunction(trainingFeatureMatrix, pathToTestDataset, stopWordsRemoved):
    testFeatureMatrix = createFeatureMatrixFrom(pathToTestDataset, stopWordsRemoved)

    numberOfHamsCorrectlyClassified, numberOfHamsIncorrectlyClassified = 0, 0
    numberOfSpamsCorrectlyClassified, numberOfSpamIncorrectlyClassified = 0, 0

    for fileNumber in range(testFeatureMatrix.countOfFiles):
        summationOfWeightAndFrequency = 1.0
        valueOfSigmoidFunction = 0.0
        # calculate summationOfWeightAndFrequency using weight in training matrix
        # and frequency in test matrix
        for wordIndex in range(len(testFeatureMatrix.wordsList)-1):
            word =  testFeatureMatrix.wordsList[wordIndex]
            if word in trainingFeatureMatrix.wordsList:
                indexOfWord = trainingFeatureMatrix.wordsList.index(word)
                weightInTrainingMatrix = trainingFeatureMatrix.weights[indexOfWord]
                frequencyOfWordInTestMatrix = testFeatureMatrix.matrix[fileNumber][wordIndex]
                summationOfWeightAndFrequency +=  weightInTrainingMatrix * frequencyOfWordInTestMatrix
        # sigmoid function
        try:
            valueOfSigmoidFunction = ( 1 / (1 + np.exp(-summationOfWeightAndFrequency)))
        except Exception:
            print("Exception while executing sigmoid function. Denominator: {}; Summation: {}".format(str((1 + np.exp(-summationOfWeightAndFrequency))), str(summationOfWeightAndFrequency)))
        if(testFeatureMatrix.classificationClass[fileNumber] == 0):
            if valueOfSigmoidFunction < 0.5:
                numberOfHamsCorrectlyClassified += 1.0
            else:
                numberOfHamsIncorrectlyClassified += 1.0
        else:
            if valueOfSigmoidFunction >= 0.5:
                numberOfSpamsCorrectlyClassified += 1.0
            else:
                numberOfSpamIncorrectlyClassified += 1.0

    accuracyOnHamFiles = round((numberOfHamsCorrectlyClassified * 100)/(numberOfHamsCorrectlyClassified + numberOfHamsIncorrectlyClassified) , 2)
    accuracyOnSpamFiles = round((numberOfSpamsCorrectlyClassified * 100) / (numberOfSpamsCorrectlyClassified + numberOfSpamIncorrectlyClassified), 2)
    print("Accuracy over Ham files: {}%".format(str(accuracyOnHamFiles)))
    print("Accuracy over Spam files: {}%".format(str(accuracyOnSpamFiles)))

def main():
    stopWordsRemoved = False
    print("***Executing Logistic Regression with Stop Words intact in files...***")
    timeOne = time.time()
    trainingFeatureMatrix = createFeatureMatrixFrom(pathToTrainingDataset, stopWordsRemoved)
    print("Training Feature Matrix constructed in {} seconds".format(round(time.time() - timeOne, 2)))

    # Training on Training dataset
    timeTwo = time.time()
    print("\nStarting training on training dataset...")
    trainingFeatureMatrix = trainingFunction(trainingFeatureMatrix)
    print("\nTraining completed on training dataset, took {} seconds.".format(round(time.time() - timeTwo, 2)))
    # Classification of test dataset
    timeThree = time.time()
    print("\nStarting classification of test dataset...")
    classificationFunction(trainingFeatureMatrix, pathToTestDataset, stopWordsRemoved)
    print("Classification of test dataset completed, took {} seconds.".format(round(time.time() - timeThree, 2)))
    print("Total time taken for Logistic Regression with Stop Words intact in files: {} seconds.".format(round(time.time() - timeOne, 2)))

    stopWordsRemoved = True
    print("\n\n*** Executing Logistic Regression after removing Stop Words from files...***")
    timeFour = time.time()
    testFeatureMatrix = createFeatureMatrixFrom(pathToTrainingDataset, stopWordsRemoved)
    print("\nTraining Feature Matrix constructed in {} seconds".format(round(time.time() - timeFour, 2)))

    # Training on Training dataset
    timeFive = time.time()
    print("\nStarting training on training dataset...")
    testFeatureMatrix = trainingFunction(testFeatureMatrix)
    print("\nTraining completed on training dataset, took {} seconds.".format(round(time.time() - timeFive, 2)))
    # Classificiation of test dataset
    timeSix = time.time()
    print("\nStarting classification of test dataset...")
    classificationFunction(testFeatureMatrix,pathToTestDataset, stopWordsRemoved)
    print("Classification of test dataset completed, took {} seconds.".format(round(time.time() - timeSix, 2)))
    print("Total time taken for Logistic Regression after removing Stop Words: {} seconds.".format(round(time.time() - timeFour, 2)))

if __name__ == "__main__": main()


from collections import namedtuple
from enum import Enum

MyStruct = namedtuple("MyStruct", ("field1", "field2"))
m = MyStruct("foo", "bar")

class Room(Enum):
    Description = 1
    North = 2
    West = 3
    South = 4
    East = 5
    Up = 6
    Down = 7

def LydiaRoom(op):
    return {
        Room.Description: "You are in Lydia's room. Everything is picked up and looks like except for the candy wrapper on the floor.",
        Room.West: "You walk into the call. There is a scratching post. It is less interesting than the rug."
    }.get(op, "Can't go that way")

commands = {
    "R":"Description",
    "N":"North",
    "S":"South",
    "E":"East",
    "W":"West",
    "U":"Up",
    "D":"Down"
}

print("Allowed commands:")
print(commands)


import pygame as pg
import Button
import Options
import CRUD
import Player as PL
import MapNavigation
import screens

pg.init()
clock = pg.time.Clock()
fps = 60
# background = pg.image.load("market.png")

#game window
bottom_panel = 150
screen_width = 800
screen_height = 400 + bottom_panel
gameSettings = Options.Gameoptions()
# screen = pg.display.set_mode((screen_width, screen_height))
pg.display.set_caption('Battle')

# start_img = pg.image.load('start_btn.png').convert_alpha()

gameSettings = Options.Gameoptions()
gameSettings.loadfile()
WIN = pg.display.set_mode((gameSettings.WIDTH, gameSettings.WIDTH))
pplayer=PL.Player("human",3,200 ,gameSettings)
pplayer.X_cord=6

def mainmenu(screen, player):

    if player.optionSettings.art == True:
        screen.blit(player.optionSettings.BGs[4], (0,0))
    newGameButton = Button.button(600, 200 ,150 ,40,"New game",  "Black", screen,40)
    LoadButton = Button.button(600, 300 ,150 ,40,"Load", "Black", screen,40)
    OptionsButton = Button.button(600, 400 ,150 ,40,"Options",  "Black", screen,40)
    exitButton = Button.button(600, 600 ,150 ,40,"Exit", "Black", screen,40)
    run = True
    while run:
        for event in pg.event.get():
            if event.type == pg.QUIT:
                pg.quit()
            elif newGameButton.draw_button():
                screens.charcreation(screen, player)
                # MapNavigation.main(screen, player)
                if player.optionSettings.art == True:
                        screen.blit(player.optionSettings.BGs[4], (0,0))
            elif LoadButton.draw_button():

                while CRUD.CRUD(screen, player, 1):
                    MapNavigation.main(screen, player)
                if player.optionSettings.art == True:
                    screen.blit(player.optionSettings.BGs[4], (0,0))

            elif OptionsButton.draw_button():
                screens.manage_settings(screen, player.optionSettings)
                if player.optionSettings.art == True:
                        screen.blit(player.optionSettings.BGs[4], (0,0))

            # elif CreditsButton.draw_button():
            #     screens.creditscreen(screen)
            #     screen.fill((0,0,0))
            #     if player.optionSettings.art == True:
            #             screen.blit(player.optionSettings.BGs[4], (0,0))

            elif exitButton.draw_button():
                run = False
        pg.display.update()

mainmenu(WIN, pplayer)


import openai
import discord
from discord import Intents
from discord.commands import slash_command, option
from discord.ext import commands
import re
import os
import asyncio
import logging
import datetime
import base64
import requests
import zipfile
from zipfile import ZipFile
from dotenv import load_dotenv
load_dotenv()
use_images = os.getenv("USE_IMAGES")
cooldown = os.getenv("COOLDOWN")
if use_images != "No": import imagesGeneration
logging.basicConfig(level=logging.INFO)
imageint = ""
if use_images != "No": imageint = "To add an image illustration , use ![bg left:50% 70%](a-long-detailed-description-of-the-image.png) at the beginning of the slide, just after \"---\". Use only .png. It's not possible to add technical images but only illustrations. The images are generated by an ai, the name of the file should be a detailed quite long description of the image wanted. You always need to specify the description of the image."
intstructions = f'''Here is a presentation with marp. It's not possible to make slides longer than 200 characters. to separate slides,
"

---

"
 then go at the line. The presentatio should be for everybody, all technical words and concepts, explained. {imageint} The presentation is minimum 20 slides long. You can use bulletpoints. Use markdown formatting (titles, etc...). The presentation has also a conclusion.'''
bot = discord.Bot()

styles = ["default", "gaia", "uncover", "default-dark", "gaia-dark", "uncover-dark", "olive"]
languages = ["english", "french", "spanish", "german", "italian", "portuguese", "russian", "chinese", "japanese", "korean", "arabic"]
darkstyles = ["default-dark", "gaia-dark", "uncover-dark"]
customstyles = ["olive"]
async def get_style(ctx: discord.AutocompleteContext):
    """Returns a list of colors that begin with the characters entered so far."""
    return [style for style in styles if style.startswith(ctx.value.lower())]
async def get_ln(ctx: discord.AutocompleteContext):
    return [language for language in languages if language.startswith(ctx.value.lower())]

@bot.slash_command(name="private_present", description="Generate a presentation with marp, private command for user 707196665668436019")
@option(name="subject", description="The subject of the presentation", required=True)
@option(name="style", description="The style of the presentation", required=False, autocomplete=get_style)
@option(name="center", description="Center the text", required=False)
@option(name="language", description="The language of the presentation", required=False, autocomplete=get_ln)
@option(name="indications", description="The indications for the presentation", required=False)
#command wprks only in dm and only for user 707196665668436019
@commands.is_owner()
async def private_present(ctx: discord.ApplicationContext, subject: str, style: str = "default", center: bool = True, language: str = "english", indications: str = ""):
    await present(ctx, subject, style, language, indications)

@bot.slash_command(name="present", description="Generate a presentation with marp")
#we create a function that takes the subject of the presentation and the style of the presentation as arguments, and that
@option(name="subject", description="The subject of the presentation", required=True)
@option(name="style", description="The style of the presentation", required=False, autocomplete=get_style)
@option(name="center", description="Center the text", required=False)
@option(name="language", description="The language of the presentation", required=False, autocomplete=get_ln)
@option(name="indications", description="The indications for the presentation", required=False)
# a cooldown of duration cooldown seconds, except if the user is 707196665668436019
#@commands.cooldown(1, int(cooldown), commands.BucketType.user)
@commands.cooldown(1, int(cooldown), commands.BucketType.guild)
async def normal_present(ctx: discord.ApplicationContext, subject: str, style: str = "default", center: bool = True, language: str = "english", indications: str = ""):
    await present(ctx, subject, style, language, indications)
async def present(ctx: discord.ApplicationContext, subject: str, style: str = "default", center: bool = True, language: str = "english", indications: str = ""):
    await ctx.defer()
    date = datetime.datetime.now()
    date = date.strftime("%Y-%m-%d-%H-%M-%S")
    #if the style is dark
    dark = False
    if style in darkstyles:
        style = style.replace("-dark", "")
        dark = True
    marp = f'''---
marp: true
theme: {styles[styles.index(style)]}
class:
'''
    if dark: marp = marp + f"    - invert\n---"
    if center: marp = marp + "    - lead\n---"
    else: marp = marp + "\n---"
#    if style in customstyles:
#        marp = f"/* @theme {style} */\n" + marp
#        print(marp)
    prompt = f"{intstructions} {indications} The subject of the presentation is: {subject} The Language is: {language} <|endofprompt|> \n {marp}"
    subject2 = subject
    forbidden = ["\\", "/", "?", "!", ":", ";", "(", ")", "[", "]", "{", "}", "'", '"', "=", "+", "*", "&", "^", "%", "$", "#", "@", "`", "~", "|", "<", ">", ",", ".", "?", " "]
    for i in forbidden:
        if i in subject: subject = subject.replace(i, "-")
    #we save teh subject in base64 in a variable
    #if dosen't exist, create a directory called "userid" where the userid is the id of the user who called the command
    uid = str(ctx.author.id)
    if not os.path.exists("./data/"+uid):
        os.mkdir("./data/"+uid)
    datenow = datetime.datetime.now()
    datenow = datenow.strftime("%Y-%m-%d-%H-%M-%S")
    response = await openai.Completion.acreate(
        engine="text-davinci-003",
        prompt=prompt,
        temperature=0.6,
        max_tokens=1024,
        top_p=1,
        frequency_penalty=0,
        presence_penalty=0,
        stop=["<|endofprompt|>"]
    )
    #we save the output in a variable
    output = response["choices"][0]["text"]
    present = marp + output
    ##we save the output in a file called "subject.md"
    matches = re.finditer(r'!\[.*?\]\((.*?)\)', present)
    image_filenames = []
    for match in matches:
        image_filenames.append(match.group(1))
    #we create a text file with the image names and a md file for the presentation with utf8 encoding
    if len(subject) > 15: subject = subject[:15]
    b64 = base64.urlsafe_b64encode(subject.encode("utf-8"))
    os.mkdir(f"./data/{uid}/{b64}{datenow}")
    path = f"./data/{uid}/{b64}{datenow}"
    with open(f"{path}/{subject}-images.txt", "w", encoding="utf8") as f:
        for image in image_filenames:
            f.write(image + "\n")
    with open(f"{path}/{subject}.md", "w", encoding="utf8") as f: f.write(present)
    if len(image_filenames) > 0 and  use_images!="no":
        #now we first remove the extension from the image filenames by removing the last 4 characters
        image_filenames = [image[:-4] for image in image_filenames]
        print(image_filenames)
        for images in image_filenames:
            print ("generating image " + images + "with " + str(use_images))
            r = await imagesGeneration.generate(images, f"{os.getcwd()}\\data\\{uid}\\{b64}{datenow}\\", str(use_images), apikey)
            if str(use_images) == "sd": os.rename(f"{os.getcwd()}\\.\\data\\{uid}\\{b64}{datenow}\\{images}_0.png", f"{os.getcwd()}\\data\\{uid}\\{b64}{datenow}\\{images}.png")
            if str(use_images) == "dalle":
                image_url = r['data'][0]['url']
                img_data = requests.get(image_url).content
                with open(f'{path}/{images}.png', 'wb') as handler:
                    handler.write(img_data)
                await asyncio.sleep(15) #wait 15 seconds to avoid rate limiting
    cmd = f"--pdf --allow-local-files {path}/{subject}.md"
    if style in customstyles: cmd = cmd + f" --theme ./themes/{style}.css"
    if os.path.exists("./marp.exe"):
        os.system(f"marp.exe {cmd}")
    else:
        cmd = cmd.replace("'", "\\'")
        os.system(f"./marp {cmd}")
    cmd = f" --image png -o {path}/{subject}.png --allow-local-files {path}/{subject}.md"
    if style in customstyles: cmd = cmd + f" --theme ./themes/{style}.css"
    if os.path.exists("./marp.exe"):
        os.system(f"marp.exe {cmd}")
    else:
        cmd = cmd.replace("'", "\\'")
        os.system(f"./marp {cmd}")
    cmd = f" --html --allow-local-files {path}/{subject}.md"
    if style in customstyles: cmd = cmd + f" --theme ./themes/{style}.css"
    if os.path.exists("./marp.exe"):
        os.system(f"marp.exe {cmd}")
    else:
        cmd = cmd.replace("'", "\\'")
        os.system(f"./marp {cmd}")
    cmd = f" --pptx --allow-local-files {path}/{subject}.md"
    if style in customstyles: cmd = cmd + f" --theme ./themes/{style}.css"
    if os.path.exists("./marp.exe"):
        os.system(f"marp.exe {cmd}")
    else:
        cmd = cmd.replace("'", "\\'")
        os.system(f"./marp {cmd}")
    #now, we create a zip file with all the files
    zipObj = ZipFile(f"{path}/{subject}.zip", 'w')
    zipObj.write(f"{path}/{subject}.md", f"{subject}.md")
    zipObj.write(f"{path}/{subject}.html", f"{subject}.html")
    zipObj.write(f"{path}/{subject}.pdf", f"{subject}.pdf")
    zipObj.write(f"{path}/{subject}.png", f"{subject}.png")
    zipObj.write(f"{path}/{subject}.pptx", f"{subject}.pptx")
    with open(f"{path}/{subject}-images.txt", "r", encoding="utf8") as f:
        for image in f.readlines():
            zipObj.write(f"{path}/{image.strip()}", f"{image.strip()}")
    zipObj.close()
    embed = discord.Embed(title=subject2, description="Thanks for using presentator bot. You will find your presentation in the attached zip file in the following formats: markdown, html, pdf, pptx, and the presentation' images. If you want to modify your presentation you can use the markdown file. More information about how to modify the file [HERE](https://marp.app).", color=discord.Color.brand_red())
    files = [discord.File(f"{path}/{subject}.zip"), discord.File(f"{path}/{subject}.png")]
    embed.set_image(url=f"attachment://{subject}.png")
    await ctx.respond(embed=embed, files=files)

@bot.slash_command(name="list", description="List all the presentations you have created")
async def list(ctx: discord.ApplicationContext):
    embed = discord.Embed(title="Presentations", description="Here is the list of all the presentations you have created. You can download the presentation in different formats (pdf, markdown, html) by doing `/get` \"*presentation id*\". The images are generated by an ai. If you want to modify your presentation you can use the markdown file. More information about how to modify the file [HERE](https://marp.app).", color=0x00ff00)
    liste = await get_presentations(str(ctx.author.id))
    for key in liste:
        embed.add_field(name=f"{liste[key]}", value=f"</get:1063051827010084925> `{key}`", inline=False)
    await ctx.respond(embed=embed, ephemeral=True)

async def get_presentations(uid):
    folders = os.listdir(f"./data/{uid}")
    names = {}
    for folder in folders:
        name = base64.urlsafe_b64decode(folder[2:-20]).decode("utf-8")
        names[folder] = name
    return names

@bot.slash_command(name="get", description="Get a presentation")
@option(name="pid", description="The id of the presentation", required=True)
async def get(ctx: discord.ApplicationContext, pid: str):
    uid = str(ctx.author.id)
    liste = await get_presentations(uid)
    if pid in liste:
        files = [discord.File(f"./data/{uid}/{pid}/{liste[pid]}.pdf"), discord.File(f"./data/{uid}/{pid}/{liste[pid]}.md"), discord.File(f"./data/{uid}/{pid}/{liste[pid]}.html")]
        await ctx.respond(files=files, ephemeral=True)

@bot.event
async def on_ready():
    print("Bot is ready")
    if not os.path.exists("data"):
        os.mkdir("data")
@bot.event
async def on_application_command_error(ctx, error):
    #if there is an error we send a message to the user
    await ctx.respond(f"An error occured: {error}", ephemeral=True)

#get the openai key drom he key.env file
token = os.getenv("TOKEN")
apikey = os.getenv("OPENAI")
openai.api_key = apikey
bot.run(token)


#!/usr/bin/env python
# coding: utf-8

# In[17]:

grove=[]
visib=[]
with open('Day8Input.txt','r') as file:
    for line in file:
        row=[]
        visibrow=[]
        for digit in line.rstrip():
            row.append(int(digit))
            visibrow.append(0)
        grove.append(row)
        visib.append(visibrow)
#print(grove)
#print(visib)

for y in range(len(grove)):
    height=-1
    for x in range(len(grove[y])):
        if grove[y][x]>height: visib[y][x]=1; height=grove[y][x]

    height=-1
    for x in reversed(range(len(grove[y]))):
        if grove[y][x]>height: visib[y][x]=1; height=grove[y][x]

for x in range(len(grove[0])):
    height=-1
    for y in range(len(grove[x])):
        if grove[y][x]>height: visib[y][x]=1; height=grove[y][x]

    height=-1
    for y in reversed(range(len(grove[x]))):
        if grove[y][x]>height: visib[y][x]=1; height=grove[y][x]

#print(visib)
total=0
for y in visib:
    #print(y)
    total+=y.count(1)

print(total)

# In[ ]:


#! /usr/bin/env python3

import networkx as nx
import gurobipy as grb
import numpy as np

import argparse
import itertools

from pcst_fast import pcst_fast

from stp import load_stp

DESCRIPTION = """
 Solves an instance of the PCSTP using Gurobi Python.
"""

BIG_INT = np.iinfo(np.int64).max
BIG_FLOAT = np.finfo(np.float64).max
EPSILON = 10**(-9)
MAX_CUTS = 1

def build_ilp_model(g):
    model = grb.Model('pcstp')

    x = model.addVars(g.edges, vtype=grb.GRB.BINARY)
    y = model.addVars(g.nodes, vtype=grb.GRB.BINARY)

    # OBJECTIVE
    edge_costs = grb.quicksum(g[i][j]['weight'] * x[i, j] for i, j in g.edges)
    prize_cost = grb.quicksum(g.node[v]['prize'] * (1 - y[v]) for v in g.nodes)
    model.setObjective(edge_costs + prize_cost)

    # one less edges than vertices
    model.addConstr(x.sum() == (y.sum() - 1), name='x.sum == y.sum - 1')

    # Add all |S| = 2 GSECS
    for i, j in g.edges:
        model.addConstr(x[i, j] <= y[i])
        model.addConstr(x[i, j] <= y[j])

    # degree of nonterminals must be at least 2

    # for v in g.node:
    #     if g.node[v]['prize'] > 0:
    #         continue
    #     model.addConstr(x.sum(v, '*') + x.sum('*', v) >= 2 * y[v])

    return model, x, y

def sum_edges(S, x):
    lhs = grb.LinExpr()

    for i, j in itertools.combinations(S, 2):
        if (i, j) in x:
            lhs.add(x[i, j])
        elif (j, i) in x:
            lhs.add(x[j, i])
    return lhs

def separate_gsec_rel(model, x, y, x_bar, y_bar, G):
    F = nx.DiGraph()

    F.add_node(-1)  # source
    F.add_node(-2)  # sink
    for v in G.node:
        F.add_node(v)

    for i, j in G.edges:
        capacity = x_bar[i, j] / 2
        F.add_edge(i, j, capacity=capacity)
        F.add_edge(j, i, capacity=capacity)

    total_source_cap = 0
    for i in G.nodes:
        node = F.node[i]

        capacity = 0
        for j in G.adj[i]:
            capacity += F[i][j]['capacity']

        node['capacity'] = capacity
        source_cap = max(capacity - y_bar[i], 0)
        F.add_edge(-1, i, capacity=source_cap)
        F.add_edge(i, -2, capacity=max(y_bar[i] - capacity, 0))

        total_source_cap += source_cap

    cuts = 0
    # solve max flow problems and collect cuts
    for i in sorted(F.nodes):
        if i < 0:
            continue

        i_capacity = F[-1][i]['capacity']
        F[-1][i]['capacity'] = float('inf')

        cut_val, cut = nx.minimum_cut(F, _s=-1, _t=-2)

        S, T = cut

        constr = -1 * (cut_val - total_source_cap) + y_bar[i]

        S.discard(-1)

        if constr > 0:
            rhs = grb.quicksum(y[v] for v in S if v != i)
            lhs = sum_edges(S, x)

            model.cbCut(lhs <= rhs)
            cuts += 1

            rhs_bar = grb.quicksum(y_bar[v] for v in S if v != i)
            lhs_bar = sum_edges(S, x_bar)
            if lhs_bar.getValue() <= rhs_bar.getValue():
                print('not violated: ', lhs_bar.getValue(), '<=', rhs_bar.getValue())

        else:
            rhs_bar = grb.quicksum(y_bar[v] for v in S if v != i).getValue()
            lhs_bar = sum_edges(S, x_bar).getValue()
            if lhs_bar > rhs_bar:
                print('violated: ', lhs_bar, '>', rhs_bar)

        F[-1][i]['capacity'] = i_capacity
        F[i][-2]['capacity'] = float('inf')

        if cuts >= MAX_CUTS:
            return cuts
    return cuts

def add_gsecs(model, x, y, cycles):
    for cycle in cycles:
        ysum = grb.quicksum(y[v] for v in cycle)

        lhs = sum_edges(cycle, x)

        for k in cycle:
            model.cbLazy(lhs <= (ysum - y[k]))

def callback(G, x, y, model, where):
    if where == grb.GRB.callback.MIPSOL:
        x_val = model.cbGetSolution(x)
        # y_val = model.cbGetSolution(y)

        g = nx.Graph()

        for i, j in x_val.keys():
            if x_val[i, j] > 0.5:
                g.add_edge(i, j)

        cycles = nx.cycle_basis(g)

        add_gsecs(model, x, y, cycles)

    elif where == grb.GRB.callback.MIPNODE:
        x_val = model.cbGetNodeRel(x)
        y_val = model.cbGetNodeRel(y)

        status = model.cbGet(grb.GRB.Callback.MIPNODE_STATUS)
        nodecount = model.cbGet(grb.GRB.Callback.MIP_NODCNT)
        if status == grb.GRB.OPTIMAL:
            cuts = separate_gsec_rel(model, x, y, x_val, y_val, G)
            # if cuts > 0:
            # 0
            # return
        if status == grb.GRB.OPTIMAL:
            model._last_node = nodecount
            for v in G.node:
                node = G.node[v]

                if y_val[v] > 0.5:
                    node['prize'] = BIG_INT
                else:
                    node['prize'] = node['_prize']

            for i, j in G.edges:
                edge = G[i][j]
                edge['weight'] = BIG_FLOAT if x_val[i, j] < 0.1 else edge['_weight']

            gw_nodes, gw_edges = gw(G)

            for v in G.nodes:
                model.cbSetSolution(y[v], 1 if v in gw_nodes else 0)

            for i, j in G.edges:
                model.cbSetSolution(x[i, j], 1 if (i, j) in gw_edges else 0)

def gw(g):
    _e = [(i-1, j-1) for i, j in g.edges]
    # print(_e, len(g.node))
    edges = np.array(_e,
                     dtype='int64')
    costs = np.array([g[i][j]['weight'] for i, j in g.edges],
                     dtype='float64')
    prizes = np.array([g.node[v]['prize'] for v in sorted(g.node)],
                      dtype='int64')

    gw_nodes, gw_edges = pcst_fast(edges,
                                   prizes,
                                   costs,
                                   -1,
                                   1,
                                   'strong',
                                   0)
    # print(gw_edges)
    return set(v+1 for v in gw_nodes), set((i+1, j+1) for i, j in edges[gw_edges])

def main():
    parser = argparse.ArgumentParser(description=DESCRIPTION)

    parser.add_argument('pcstp', help='The PCSTP instance as a stp file')

    args = parser.parse_args()

    g, N, int_only = load_stp(args.pcstp)

    model, x, y = build_ilp_model(g)

    # Get GW initial solution
    gw_nodes, gw_edges = gw(g)

    for v in g.nodes:
        y[v].start = 1 if v in gw_nodes else 0
        y[v].setAttr(grb.GRB.Attr.BranchPriority, 2 if v in N else 0)

    for i, j in g.edges:
        x[i, j].start = 1 if (i, j) in gw_edges else 0

    model.Params.lazyConstraints = 1
    model.Params.preCrush = 1
    model.Params.heuristics = 0

    model._int_only = int_only
    model._last_node = 0
    model.modelSense = grb.GRB.MINIMIZE

    model.optimize(lambda m, w: callback(g, x, y, m, w))

if __name__ == '__main__':
    main()


import cv2
import os

vidcap = cv2.VideoCapture(r'yourpath.avi')
success, image = vidcap.read()
count = 0
success = True
while success:
    cv2.imwrite("frame%d.png" % count, image)  # save frame as JPEG file
    success, image = vidcap.read()
    count += 1

vidcap = cv2.VideoCapture(r'yourpath2')
success, image = vidcap.read()
success = True
while success:
    cv2.imwrite("frame%d.png" % count, image)  # save frame as JPEG file
    success, image = vidcap.read()
    count += 1

image_folder = r'where_is_project'
video_name = 'output.mp4'
frame_rate = float(24) #masterrace
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
images = []
for i in range(500):
    images.append(os.path.join(image_folder, "frame%d.png" % i))

frame = cv2.imread(os.path.join(image_folder, images[0]))
height, width, layers = frame.shape

video = cv2.VideoWriter(video_name, fourcc, frame_rate, (width, height))
for image in images:
    video.write(cv2.imread(os.path.join(image_folder, image)))
video.release()
cv2.destroyAllWindows()


# -----------------------------------------------------------
#
#
#
#
#
# -----------------------------------------------------------

import tkinter as tk
from tkinter import *
from tkinter import filedialog

from PIL import Image, ImageTk
from tkcalendar import DateEntry

import datebase_process
import fee
import numberplate

print("Welcome to the carpark")

# set up windows, label and buttons
window = tk.Tk()
window.geometry("900x506")

window_frame = Frame(window, height=900, width=506)
window_frame.pack(anchor=N, expand=True)

exit_frame = Frame(window)
exit_frame.pack(anchor=SE)

window_font = ('times', 18, 'bold')
window_label = tk.Label(window_frame, text='Welcome to Car Park', fg="#C0C0C0", pady=50, padx=30, font=window_font)
window_label.grid(row=0, column=2)

# This button will call the numberplate_recognition
window_button_1 = tk.Button(window_frame, text='Enter Car Park', fg="#D3D3D3", padx=40, command=lambda: upload_file())
window_button_1.grid(row=2, column=1)

# This button will call the timestamp

cal = DateEntry(window_frame, locale='en_AU', selectmode='day')
cal.grid(row=2, column=2, padx=20, pady=30)

def my_upd():  # triggered on Button Click
    selected_date = str(cal.get_date())
    l1.config(text="Selected Date is: " + selected_date)  # read and display date
    l2.config(text=datebase_process.export_report(selected_date))
    print(selected_date)

l1 = tk.Label(window_frame, text='Please Select Date', bg='yellow')
l1.grid(row=4, column=2)
l2 = tk.Label(window_frame, text='Result', bg='green')
l2.grid(row=5, column=2)
window_button_2 = tk.Button(window_frame, text='Report', fg="#D3D3D3", padx=40,
                            command=lambda: my_upd())  # using Button
window_button_2.grid(row=3, column=2)

# This button will call the timestamp
def exit_carpark():
    # Set global variable to make it displayed
    global displayed_exit_image
    f_types = [('All files', '*')]
    # f_types = [('PNG Files', '*.png')]

    # selected_filename is the actual pic (PNG)
    selected_filename = filedialog.askopenfilename(filetypes=f_types)

    # Load the image
    selected_image = Image.open(selected_filename)
    # resize image
    selected_image = selected_image.resize((300, 205), Image.Resampling.LANCZOS)
    #
    displayed_exit_image = ImageTk.PhotoImage(selected_image)
    # Use button to display the image
    window_button_6 = tk.Button(window_frame, image=displayed_exit_image)  # using Button
    window_button_6.grid(row=3, column=3)
    # Call numberplate_recognition and print the number plate
    numberplate_number = numberplate.numberplate_recognition(selected_filename)
    # numberplate_number = "test3"
    display_timestamp, exit_datatime = datebase_process.save_datetime(numberplate_number, 0)
    test_parking_fee = fee.fee_calculation(datebase_process.export_entry_datetime(numberplate_number), exit_datatime)
    window_label_1 = tk.Label(window_frame,
                              text=numberplate_number + "\n" + display_timestamp + "\nParking Fee: $ " + str(
                                  test_parking_fee)
                              , width=30, font=window_font)
    window_label_1.grid(row=4, column=3)

    # window_label_2 = tk.Label(window_frame, text=numberplate_timestamp,width=30,font=window_font)
    # window_label_2.grid(row=5,column=1)
    print(numberplate_number)
    pass

window_button_3 = tk.Button(window_frame, text='Exit Car Park', fg="#D3D3D3", padx=40,
                            command=lambda: exit_carpark())  # using Button
window_button_3.grid(row=2, column=3)

# Quit Program
window_button_4 = tk.Button(exit_frame, text='Exit Program', fg="#D3D3D3", command=window.quit)  # using Button
window_button_4.grid(row=0, column=0)

# Dark mode and styling
window.configure(bg="#505050")
window_frame.configure(bg="#505050")
exit_frame.configure(bg="#505050")
window_button_1.configure(bg="#696969")
window_button_2.configure(bg="#696969")
window_button_3.configure(bg="#696969")
window_button_4.configure(bg="#696969")
window_label.configure(bg="#505050")

def upload_file():
    # Set global variable to make it displayed
    global displayed_image
    f_types = [('All files', '*')]
    # f_types = [('PNG Files', '*.png')]

    # selected_filename is the actual pic (PNG)
    selected_filename = filedialog.askopenfilename(filetypes=f_types)

    # Load the image
    selected_image = Image.open(selected_filename)
    # resize image
    selected_image = selected_image.resize((300, 205), Image.Resampling.LANCZOS)
    #
    displayed_image = ImageTk.PhotoImage(selected_image)
    # Use button to display the image
    window_button_4 = tk.Button(window_frame, image=displayed_image)  # using Button
    window_button_4.grid(row=3, column=1)
    # Call numberplate_recognition and print the number plate
    numberplate_number = numberplate.numberplate_recognition(selected_filename)
    numberplate_timestamp = datebase_process.save_datetime(numberplate_number, 1)
    window_label_1 = tk.Label(window_frame, text=numberplate_number + "\n" + numberplate_timestamp, width=30,
                              font=window_font)
    window_label_1.grid(row=4, column=1)

    # window_label_2 = tk.Label(window_frame, text=numberplate_timestamp,width=30,font=window_font)
    # window_label_2.grid(row=5,column=1)
    print(numberplate_number)

# Keep the window opens
window.mainloop()

# def save_numberplate(numberplate_number):
# to be continued

# save_numberplate(entry_numberplate)

# if save_numberplate succeed,print something
# else print something


import numpy as np
import scipy.sparse

from sklearn.linear_model import SGDRegressor

from recpack.algorithms.base import ItemSimilarityMatrixAlgorithm
from recpack.matrix import Matrix, to_csr_matrix

class SLIM(ItemSimilarityMatrixAlgorithm):
    """Implementation of the SLIM model.

    SLIM Model described in Ning, Xia, and George Karypis.
    "Slim: Sparse linear methods for top-n recommender systems."
    2011 IEEE 11th International Conference on Data Mining. IEEE, 2011

    Code loosely based on https://github.com/Mendeley/mrec

    :param l1_reg: l1 regularization coefficient, defaults to 0.0005
    :type l1_reg: float, optional
    :param l2_reg: l2 regularization coefficient, defaults to 0.00005
    :type l2_reg: float, optional
    :param fit_intercept: Whether the intercept should be estimated
        or not during gradient descent.
        If False, the data is assumed to be already centered., defaults to True
    :type fit_intercept: bool, optional
    :param ignore_neg_weights: Remove negative weights after training
        to increase speed of predict, defaults to True
    :type ignore_neg_weights: bool, optional
    """

    def __init__(self, l1_reg=0.0005, l2_reg=0.00005, fit_intercept=True, ignore_neg_weights=True):

        super().__init__()

        self.l1_reg = l1_reg
        self.l2_reg = l2_reg
        # Translate regression parameters into the expected sgd parameters
        self.alpha = self.l1_reg + self.l2_reg
        self.l1_ratio = self.l1_reg / self.alpha
        self.fit_intercept = fit_intercept
        self.ignore_neg_weights = ignore_neg_weights

        # Construct internal model
        self.model = SGDRegressor(
            penalty="elasticnet",
            fit_intercept=fit_intercept,
            alpha=self.alpha,
            l1_ratio=self.l1_ratio,
        )

    def _compute_similarities(self, work_matrix, item):
        new_matrix = work_matrix.tocoo()
        target = new_matrix.getcol(item)
        data_indices = np.where(new_matrix.col == item)[0]
        new_matrix.data[data_indices] = 0
        self.model.fit(new_matrix, target.toarray().ravel())

        w = self.model.coef_
        if self.ignore_neg_weights:
            w[w < 0] = 0
        return w

    def _fit(self, X: Matrix):
        """Fit a similarity matrix based on data X.

        X is an m x n binary matrix of user item interactions.
        Where m is the number of users, and n the number of items.
        """
        X = to_csr_matrix(X, binary=True)

        # Prep sparse representation inputs
        data = []
        row = []
        col = []
        # Loop over all items
        for j in range(X.shape[1]):
            # Compute the contribution values of all other items for the item j
            # using linear regression
            w = self._compute_similarities(X, j)
            # Update sparse repr. inputs.
            # w[i,j] = the contribution of item i to predicting item j
            for i in w.nonzero()[0]:
                data.append(w[i])
                row.append(i)
                col.append(j)

        # Construct similarity matrix.
        # Shape is determined by 2nd dimension of the shape of input matrix X
        self.similarity_matrix_ = scipy.sparse.csr_matrix((data, (row, col)), shape=(X.shape[1], X.shape[1]))


# print out pre-order of a tree in a list
def print_pre_order(self, TreeNode):
        if not TreeNode:
                return None

        # print(TreeNode.value)
        # for printing out as a list format instead of a series of number
        # change the simple print sentence to a if-statement to create a list and then return
        if TreeNode.value != None:
                self.L.append(TreeNode.value)

        self.print_pre_order(TreeNode.left)
        self.print_pre_order(TreeNode.right)

        return self.L

# print out in-order of a tree in a list
def print_in_order(self, TreeNode):
        if not TreeNode:
                return None

        self.print_in_order(TreeNode.left)

        if TreeNode.value != None:
                self.L.append(TreeNode.value)

        self.print_in_order(TreeNode.right)

        return self.L

# print out post-order of a tree in a list
def print_post_order(self, TreeNode):
        if not TreeNode:
                return None

        self.print_post_order(TreeNode.left)
        self.print_post_order(TreeNode.right)

        if TreeNode.value != None:
                self.L.append(TreeNode.value)

        return self.L

# print(test.print_pre_order(newTree))
# print(test.print_in_order(newTree))
# print(test.print_post_order(newTree))


# -*- coding: utf-8 -*-
"""
Created on Wed Oct  7 10:17:08 2020

Re-creating old SQL code in Python to create the complete_patientstayid_list.csv,
which contains any patients with CAM-ICUs, ICDSCs or delirium diagnoses that
had ICU stays of at least 12 hours. This represents all the patients we might
ever use, for prediction or clustering.

@author: Kirby
"""

#%% Import packages and load tables.
import numpy as np
import pandas as pd
import os
#import multiprocessing as mp
import time

pat = pd.read_csv(r"C:\Users\Kirby\OneDrive\JHU\Precision Care Medicine\eicu\patient.csv", usecols=['patientunitstayid', 'unitdischargeoffset'])

#%% Find all CAM-ICUs and delirium diagnoses.

#Just get CAM-ICU/ICDSC data.
nurse_data = pd.read_csv(r"C:\Users\Kirby\OneDrive\JHU\Precision Care Medicine\eicu\nurseCharting.csv",nrows=0)
for chunk in pd.read_csv(r"C:\Users\Kirby\OneDrive\JHU\Precision Care Medicine\eicu\nurseCharting.csv", chunksize=500000):
    temp_rows = chunk[chunk['nursingchartcelltypevalname']=='Delirium Score']
    nurse_data = pd.concat([nurse_data,temp_rows])

#Get delirium diagnosis data.
diagnosis_data = pd.read_csv(r"C:\Users\Kirby\OneDrive\JHU\Precision Care Medicine\eicu\diagnosis.csv")
diagnosis_data = diagnosis_data[diagnosis_data["diagnosisstring"]=='neurologic|altered mental status / pain|delirium']

#%% Get patientstay ID list.
nurse_ids = nurse_data[['patientunitstayid']].drop_duplicates().sort_values('patientunitstayid')
diag_ids = diagnosis_data[['patientunitstayid']].drop_duplicates().sort_values('patientunitstayid')
all_ids = nurse_ids.merge(diag_ids,how='outer',on='patientunitstayid')

#%% Remove those with short ICU stays.
all_ids = all_ids.merge(pat,how='left',on='patientunitstayid')
all_ids = all_ids[all_ids['unitdischargeoffset']>720]

#all_ids.to_csv('complete_patientstayid_list.csv',index=False)

#27,939 patient unit stays.


# Задача 1. Вычислить число с заданной точностью d.
# in -> Enter a real number: 9
# in -> Enter the required accuracy '0.0001': 0.000001
# out -> 9.000000
# in -> Enter a real number: 8.98765
# in -> Enter the required accuracy '0.0001': 0.001
# out -> 8.988

def test_float (value):
    result_out = True
    try:
        f = float(value)
    except Exception as e:
        print(e)
        result_out = False
    return result_out

def test_format_float (value):
    result_out = False
    if test_float(value):
       if float(value) == 1/10**(len(value)-2) or float(value) == 0:
            result_out = True
    return result_out

string_number = input('Enter a real number: ')
while not test_float(string_number):
    string_number = input('Enter a real number: ')

string_accuracy = input('Enter the required accuracy \'0.0001\': ')
while not test_format_float(string_accuracy) :
    print('Error format')
    string_accuracy = input('Enter the required accuracy \'0.0001\': ')

if not float(string_accuracy):
    position_accuracy = 0
else:
    position_accuracy = len(string_accuracy)-2

print(f'result -> {float(string_number):.{position_accuracy}f}')


from collections import defaultdict, deque

def bfs(p, q, n):
    adj = defaultdict(list)
    for i in range(len(p)):
        adj[p[i]].append(q[i])
        adj[q[i]].append(p[i])

    bfslist = []
    vis = [False] * (n)
    for i in range(1, n+1, 1):
        if vis[i-1] == False:
            q = deque()
            q.append(i)
            vis[i-1] = True
            while len(q) > 0:
                node = q.popleft()
                bfslist.append(node)

                for j in adj[node]:
                    if vis[j-1] == False:
                        q.append(j)
                        vis[j-1] = True

    return bfslist

def dfs(p, q, n):
    adj = defaultdict(list)
    for i in range(len(p)):
        adj[p[i]].append(q[i])
        adj[q[i]].append(p[i])

    def dfshelper(x):
        nonlocal dfslist
        dfslist.append(x)
        vis[x-1] = True
        for i in adj[x]:
            if vis[i-1] == False:
                dfshelper(i)

    vis = [False] * n
    dfslist = []

    for i in range(1, n+1, 1):
        if vis[i-1] == False:
            dfshelper(i)

    return dfslist

p = list(map(int, input().strip().split()))
q = list(map(int, input().strip().split()))
n = int(input())
print(bfs(p, q, n))
print(dfs(p, q, n))


"""
Guitar Subset

For a list of integers S and a target number G, a subset of S that adds up to G is called a guitar subset.

For example:
Input:
24
[12, 1, 61, 5, 9, 2]
Output:
[12, 9, 2, 1]
(G=24, S=[12, 1, 61, 5, 9, 2], there is a guitar subset [12, 9, 2, 1] that adds up to 24).

Integers can appear more than once in the list. You may assume all numbers in the list are positive.

Write a program to check if the user input has a guitar subset for the specified number G or not (both the list of integers and the number G are input parameters).
"""
import random, math
from itertools import permutations
print("Enter G number in the first line and S subset in the second line")
g=int(input())
s=list(map(int,input().replace(" ",",").replace(";",",").split(",")))
def guitarsubset(g,s):
    x=False
    for a in range(1,len(s)):
        perm=list(permutations(s,a))
        for b in perm:
            if sum(b)==g:
                print("For G",g,"and S",s,"guitar subset is",list(b))
                x=True
                break
        if x:
            break
    if x==False:
        print("There is no guitar subset for given G:",g," and S:", s)
guitarsubset(g,s)


def solution(n, stations, w):
    answer = 0
    cover = 0
    W = 2*w+1
    for station in stations:
        if station - w > cover+1:
            answer += (station-w-1-cover)//W
            if (station-w-1-cover) % W > 0:
                answer += 1
        cover = station + w
    if cover < n:
        answer += (n-cover)//W
        if (n-cover) % W > 0:
            answer += 1
    return answer

print(solution(11,[4,11],1))
print(solution(16,[9],2))


import pickle
import PIL

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

from layers.other import *
from layers.ModConv2d import ModConv2d, ModConv2d_grouped, torgb
from layers.Block import Block

def StyleGAN(const_shape, resolution, n_dense=2, n_styles=5, middle_input_synth=None, microstyle_layer=6):
    """

    :param const_shape:
    :param middle_input_synth: None - full network; 8, 16, ... - input resolution
    :return:
    """
    def G_mapping(input_, truncation_psi, mapping_layers, dlatent_broadcast, dlatens_size):
        styles_shape = input_.shape[1]
        dlatent_avg = Dlatent_avg(512)(input_)
        x = normalize_2nd_moment(input_)
        x = tf.reshape(x, (-1, 512))
        for layer_idx in range(mapping_layers):
            x = Dense(dlatens_size, lrmul=0.01, name=f'Dense{layer_idx}')(x)
        x = tf.reshape(x, (-1, styles_shape, 512))
        x = tf.tile(x[:, np.newaxis], [1, dlatent_broadcast, 1, 1])
        x = lerp(x, dlatent_avg, truncation_psi)
        return x

    def G_synthesis(W, const_layer, style_strength_maps):
        data_format = 'NHWC'
        def nf(stage): return np.clip(int((16*resolution) / (2.0 ** (stage * 1))), 1, 512)
        # x = ModConv2d(rank=2, filters=512, kernel_size=3, sampling='down', padding='same')([base_res, W])

        resolution_log2 = int(np.log2(resolution))

        # num_layers = resolution_log2 * 2 - 2

        # if middle_input_synth is None:
        # Layers for 4x4 resolution.
        middle_input = None
        style = W[:, 0, :-1]
        y = None
        x = ModConv2d_grouped(rank=2, name="4x4/Conv", kernel_size=3, sampling=None, filters=nf(1), padding='same')([const_layer, style])
        x = tf.reduce_sum(x*style_strength_maps[0], axis=1)
        y = torgb(x, y, style, res_name='4x4', is_grouped=True, style_strength_map=style_strength_maps[0])
        outs = []

        # Layers for >=8x8 resolutions.
        for res in range(3, resolution_log2 + 1):
            print(f'{2 ** res}x{2 ** res}')
            if res <= 3:
                is_grouped = True
                style_strength_map = style_strength_maps[res - 3 + 1]
            else:
                is_grouped = False
                style_strength_map = None
            if res == 4:
                print(W.shape)
            if res == microstyle_layer:
                # style = tf.concat([W[:, 0, :-2], W[:, 0, -2:-1]], axis=1)
                style = W[:, 0, -1][:, np.newaxis]
                # is_grouped = True

                # x = x[:, :12, :, :]
                # y = y[:, :12, :, :]
                # x = tf.concat([x[:, 1:6, :, :], x[:, 12:15, :, :], x[:, 21:, :, :]], axis=1)
                # y = tf.concat([y[:, 1:6, :, :], y[:, 12:15, :, :], y[:, 21:, :, :]], axis=1)
                # x = tf.concat([x[:, 4:10, :, :], x[:, 16:23, :, :], x[:, 27:36, :, :], x[:, 41:, :, :]], axis=1)
                # y = tf.concat([y[:, 4:10, :, :], y[:, 16:23, :, :], y[:, 27:36, :, :], y[:, 41:, :, :]], axis=1)
            x = Block(x, latents=style, fmaps=nf(res-2), res_name=f'{2 ** res}x{2 ** res}', is_grouped=is_grouped, style_strength_map=style_strength_map)
            y = upsample(y, [1, 3, 3, 1], data_format, res_name=f'{2 ** res}x{2 ** res}')
            y = torgb(x, y, style, res_name=f'{2 ** res}x{2 ** res}', is_grouped=is_grouped, style_strength_map=style_strength_map)
            outs.append(x)
            outs.append(y)
        return outs, middle_input
    #
    input_z = tf.keras.layers.Input(shape=[n_styles+1, 512], name='input_z')
    input_const = tf.keras.layers.Input(shape=[const_shape[0], const_shape[1], 512], name='input_const')
    input_truncation_psi = tf.keras.layers.Input(shape=[1,], name='input_truncation_psi')

    n_style_strength_maps = 7  # int(np.log2(resolution))
    style_strength_maps = []
    style_strength_maps_inputs = []
    for style_map_strength in range(n_style_strength_maps - 1):
        shape_modifier = 2**style_map_strength
        input_style_map = tf.keras.layers.Input(shape=[n_styles,
                                                       const_shape[0] * shape_modifier,
                                                       const_shape[1] * shape_modifier],
                                  name='style_map_strength_' + str(4*shape_modifier))
        input_style_newaxis = input_style_map[..., np.newaxis]
        style_strength_maps_inputs.append(input_style_map)
        style_strength_maps.append(input_style_newaxis)
    W = G_mapping(input_z, input_truncation_psi, mapping_layers=n_dense, dlatent_broadcast=14, dlatens_size=512)
    synth_out, middle_input = G_synthesis(W, input_const, style_strength_maps)

    input_ = [input_z, input_const] + style_strength_maps_inputs + [input_truncation_psi]
    return tf.keras.Model(inputs=input_, outputs=synth_out)