Testing various git hooks
#! /usr/bin/env python3

"""
Kattis problem - cold
https://open.kattis.com/problems/cold
"""

__author__ = "Ram Basnet"
__date__ = "2022/1/1"
__license__ = "MIT"
__version__ = "0.1.0"
__maintainer__ = "Ram Basnet"


from typing import Any, List
import sys
from temperature import Temperature


class Solution():
    """ Solution class to solve the problem"""

    def __init__(self) -> None:
        """ Constructor"""
        self._n: int = 0  # number of temperatures
        self._data: str = ''  # data
        self._temps: List[Temperature] = []  # list of temperatures

    def find_answer(self) -> int:
        """Counts the number of temperatures below zero

        Args:
            temps (List[int]): List of temperatures

        Returns:
            int: number of temperatures below zero
        """
        count = 0
        for temp in self._temps:
            if temp.is_negative():
                count += 1
        return count

    def read_data(self, source: Any) -> None:
        """ Reads data from stdin.

        Args:
            source (Any): stdin typically
        """
        # ignore the first line
        data = source.readlines()
        self._n = int(data[0])
        self._data = data[1].strip()
        self._temps = [Temperature(int(i)) for i in self._data.split()]

    def get_n(self) -> int:
        """Returns the number of temperatures

        Returns:
            int: number of temperatures
        """
        return self._n

    def get_data(self) -> str:
        """ Returns the data

        Returns:
            str: data
        """
        return self._data

    def solve(self, source: Any) -> None:
        """
        Solves the problem
        :return: None
        """
        self.read_data(source)
        print(self.find_answer())
        # sys.stdout.write('1')


if __name__ == "__main__":
    sol = Solution()
    sol.solve(sys.stdin)

cold/OOP/cold_oop.py
rambasnet/course-container
""" Temperature class to represent a temperature"""

__author__ = "Ram Basnet"
__date__ = "2023/1/1"
__license__ = "MIT"
__version__ = "0.1.0"
__maintainer__ = "Ram Basnet"

from typing import Any


class Temperature(object):
    """
    Represents a temperature
    """

    def __init__(self, temp: int, unit: str = 'F') -> None:
        """
        initializes a temperature
        :param temp: temperature
        """
        self._temp: int = temp
        self._unit: str = unit

    @property
    def temp(self) -> int:
        """
        returns the temperature
        :return: _temp
        """
        return self._temp

    @temp.setter
    def temp(self, temp: int) -> None:
        """
        sets the temperature
        :param temp: temperature
        """
        self._temp = temp

    @property
    def unit(self) -> str:
        """
        Property to get/set unit of temperature
        Returns:
                        str: unit
        """
        return self._unit

    @unit.setter
    def unit(self, unit: str) -> None:
        self._unit = unit

    def is_negative(self) -> bool:
        return self._temp < 0

    def __str__(self) -> str:
        return f'{self._temp} {self.unit}'

    def __repr__(self) -> str:
        return f'{self._temp} {self.unit}'

    def __lt__(self, other: 'Temperature') -> bool:
        if not isinstance(other, Temperature):
            return NotImplemented
        return self._temp < other._temp

    def __gt__(self, other: 'Temperature') -> bool:
        if not isinstance(other, Temperature):
            return NotImplemented
        return self._temp > other._temp

    def __eq__(self, other: Any) -> bool:
        if not isinstance(other, Temperature):
            return NotImplemented
        return self._temp == other._temp

    def __le__(self, other: 'Temperature') -> bool:
        if not isinstance(other, Temperature):
            return NotImplemented
        return self._temp <= other._temp

    def __ge__(self, other: 'Temperature') -> bool:
        if not isinstance(other, Temperature):
            return NotImplemented
        return self._temp >= other._temp

cold/OOP/temperature.py
rambasnet/course-container
"""Unittesting Solution class."""

__author__ = "Ram Basnet"
__date__ = "2023/1/1"
__license__ = "MIT"
__version__ = "0.1.0"
__maintainer__ = "Ram Basnet"


import unittest
import os
from unittest.mock import patch
from io import StringIO
from cold_oop import Solution


class TestSolution(unittest.TestCase):
    """
    Unittesting Solution class
    """

    def setUp(self) -> None:
        """ Setup method - called before each test"""
        self.sol = Solution()
        dir_name = os.path.dirname(os.path.abspath(__file__))
        file_1 = os.path.join(dir_name, '../data/1.in')
        self.input1 = open(file_1, 'r', encoding='utf-8')
        file_2 = os.path.join(dir_name, '../data/2.in')
        print(f'{file_1=}')
        self.input2 = open(file_2, 'r', encoding='utf-8')

    def tearDown(self) -> None:
        """ Tear down method - called after each test"""
        self.input1.close()
        self.input2.close()
        return super().tearDown()

    def test_read_data1(self) -> None:
        """Tests readData method"""
        self.sol.read_data(self.input1)
        self.assertEqual(self.sol.get_n(), 3)
        self.assertEqual(self.sol.get_data(), '5 -10 15')

    def test_read_data2(self) -> None:
        """
        Tests readData method
        :return: None
        """
        self.sol.read_data(self.input2)
        self.assertEqual(self.sol.get_n(), 5)
        self.assertEqual(self.sol.get_data(), '-14 -5 -39 -5 -7')

    def test_find_answer1(self) -> None:
        """Tests findAnswer method"""
        self.sol.read_data(self.input1)
        expected = self.sol.find_answer()
        self.assertEqual(expected, 1)

    def test_find_answer2(self) -> None:
        """Tests findAnswer method"""
        self.sol.read_data(self.input2)
        expected = self.sol.find_answer()
        self.assertEqual(expected, 5)

    # @patch('sys.stdout', new_callable=StringIO)
    # @patch('sys.stderr', new_callable=StringIO)
    def test_solve1(self) -> None:
        """ Tests solve method - using patch context manager
        - tests 1.in
        """
        # self.sol.solve()
        with patch('sys.stdout', new=StringIO()) as mock_stdout:
            self.sol.solve(self.input1)
            self.assertEqual(mock_stdout.getvalue(), '1\n')

    @patch('sys.stdout', new_callable=StringIO)
    def test_solve2(self, mock_stdout: StringIO) -> None:
        """Tests solve method - using patch decorator
        - tests 2.in
        """
        self.sol.solve(self.input2)
        self.assertEqual(mock_stdout.getvalue(), '5\n')

cold/OOP/test_solution.py
rambasnet/course-container
#! /usr/bin/env python3
"""
Kattis cold problem.

Find the number of temperatures strictly less than zero.

Algorithm:
    1. Read the first line as number of temperatures
    2. Read the second line as space separated temperatures
    3. Split the second line into list of temperatures
    4. Iterate through the list and count the number of -ve temperatures
    5. Print the count
"""
__author__ = "Ram Basnet"
__date__ = "Oct 1, 2021"
__license__ = "MIT"

import sys
from typing import List


def answer(data: List[int]) -> int:
    """Count total number of -ve temeratures in data.

    Args:
        data (List[int]): List of integers as temperature

    Returns:
        int: count of -ve temps
    """
    count = 0
    for temp in data:
        if temp < 0:
            count += 1
    return count


def answer1(line: str) -> int:
    """Count total number of -ve temeratures in data.

    Args:
        line (str): integer temperatures as space separated string

    Returns:
        int: count of -ve temps
    """
    return line.count('-')


def solve1() -> None:
    """Reads input ans uses answer1 function.
    """
    num = input()
    temps = sys.stdin.readline().strip()
    print(f'{num=}; {temps=}', file=sys.stderr)
    print(answer1(temps))


def solve() -> None:
    """Reads input and parses the string as list of integer.
    """
    num = input()
    # use list comprehension syntax
    temps = [int(temp) for temp in sys.stdin.readline().strip().split()]
    print(f'{num=}; {temps=}', file=sys.stderr)
    print(answer(temps))


if __name__ == "__main__":
    solve()
    # solve1()

cold/cold.py
rambasnet/course-container
#!/ usr/bin/env python3

"""
separate test file for pytest
pytest recursively discovers all the files with test* and runs the
assert statements/function in them
"""

__author__ = "Ram Basnet"
__copyright__ = "Copyright 2021"
__license__ = "MIT"

import sys
from cold import answer, answer1


def test_answer() -> None:
    """Test cold.py answer function.
    """
    assert answer([12, -4, -56, -4544545, 64, 46464]) == 3
    assert answer([0, 453445, -1, -100, -45454, -44445]) == 4
    assert answer1('0 453445 -1 -100 -45454 -44445') == 4
    print('all test casses passed...', file=sys.stderr)


if __name__ == "__main__":
    # Don't need to call test_answer() if we use pytest
    test_answer()

cold/test_cold.py
rambasnet/course-container
#! /usr/bin/env python3

"""unittesting cold.py solution
"""

__author__ = "Ram Basnet"
__copyright__ = "Copyright 2020"
__license__ = "MIT"

import unittest
from typing import List
from hypothesis import given
from hypothesis import strategies as some
from hypothesis import settings, Verbosity
from cold import answer, answer1


class TestCold(unittest.TestCase):
    """ Test cold.py solution
    """

    def test1_answer(self) -> None:
        """Test cold.py answer function"""
        data = [12, -4, -56, -4544545, 64, 46464]
        self.assertEqual(answer(data), 3, 'broken')

    def test2_answer(self) -> None:
        """Test cold.py answer function"""
        self.assertEqual(
            answer([0, 453445, -1, -100, -45454, -44445]), 4, 'borken')

    def test3_answer(self) -> None:
        """ Test cold.py answer1 function"""
        self.assertEqual(
            answer1('0 453445 -1 -100 -45454 -44445'), 4, 'broken')

    def neg_int_count(self, temps: List[int]) -> int:
        """ Count number of negative integers in temps"""
        neg_count = 0
        for temp in temps:
            if temp < 0:
                neg_count += 1
        return neg_count

    @given(some.lists(some.integers()))
    def test4_answer(self, nums: List[int]) -> None:
        """ Test cold.py answer function with random integers"""
        expected = self.neg_int_count(nums)
        ans = answer(nums)
        self.assertEqual(ans, expected)

    # follow input integer ranges provided in the problem statement
    @settings(max_examples=200, verbosity=Verbosity.verbose,
              derandomize=True)
    @given(some.lists(some.integers(min_value=-
                                    1_000_000, max_value=1_000_000),
                      max_size=100))
    def test5_answer(self, nums: List[int]) -> None:
        """ Test cold.py answer function with random integers"""
        expected = self.neg_int_count(nums)
        ans = answer(nums)
        self.assertEqual(ans, expected)

    @settings(max_examples=200, verbosity=Verbosity.verbose,
              derandomize=True)
    @given(some.lists(some.integers(min_value=-
                                    1_000_000, max_value=1_000_000),
                      max_size=100))
    def test6_answer1(self, nums: List[int]) -> None:
        """ Test cold.py answer1 function with random integers"""
        str_nums = ' '.join(map(str, nums))
        expected = self.neg_int_count(nums)
        ans = answer1(str_nums)
        self.assertEqual(ans, expected)

cold/test_unit_cold.py
rambasnet/course-container
"""
Hello World Program using OOP
"""

from __future__ import annotations

__author__ = "Ram Basnet"
__date__ = "2023/1/1"
__license__ = "MIT"
__version__ = "0.1.0"
__maintainer__ = "Ram Basnet"


class HelloWorld(object):
    """
    Hello World Class
    """

    def __init__(self) -> None:
        """
        Constructor
        """
        self._message = 'Hello World!'

    def print_message(self) -> None:
        """
        Prints the message
        """
        print(self._message)

    def get_message(self) -> str:
        """
        Returns the message - using getter method

        Returns:
                self._message (str): Message stored in the object
        """
        return self._message

    @property
    def message(self) -> str:
        """
        Returns the message - using getter property

        Returns:
                self._message (str): Message stored in the object
        """
        return self._message

hello/OOP/hello_oop.py
rambasnet/course-container
#! /usr/bin/env python3
"""
Main manager class that manages HelloWorld
Uses Single Pattern - https://refactoring.guru/design-patterns/singleton
"""
from __future__ import annotations


__author__ = "Ram Basnet"
__date__ = "2023/1/1"
__license__ = "MIT"
__version__ = "0.1.0"
__maintainer__ = "Ram Basnet"

from hello_oop import HelloWorld


class Main(object):
    """
    Singleton class Main
    """
    _instance = None

    def __init__(self) -> None:
        """ Constructor - uses single pattern"""
        if Main._instance:
            raise NameError(
                "Cannot create multiple instances of a singleton class Main")
        self._solution: 'HelloWorld' = HelloWorld()
        Main._instance = self

    def solve(self) -> None:
        """
        Solves the problem

        :return: None
        """
        self._solution.print_message()

    @classmethod
    def get_instance(cls) -> 'Main':
        """Create or return exsiting instance

        Returns:
                        Main: class instance
        """
        if not cls._instance:
            cls._instance = Main()
        return cls._instance

    @staticmethod
    def main() -> None:
        """main static method
        """
        sol = Main()
        sol.solve()


def main() -> None:
    """main global function
    """
    sol = Main()
    sol.solve()


if __name__ == '__main__':
    Main.main()
    # main = Main()
    # main = Main.get_instance()
    # main.solve()

hello/OOP/main.py
rambasnet/course-container
"""
Unittesting HelloWorld class
"""

__author__ = "Ram Basnet"
__date__ = "2023/1/1"
__license__ = "MIT"
__version__ = "0.1.0"
__maintainer__ = "Ram Basnet"


import unittest
from unittest.mock import patch
from io import StringIO
from hello_oop import HelloWorld


class TestHelloWorld(unittest.TestCase):
    """
    Unittesting HelloWorld class
    """

    def setUp(self) -> None:
        """
        Setup method
        :return: None
        """
        self.hello = HelloWorld()

    @patch('sys.stdout', new_callable=StringIO)
    def test_print_message(self, mock_stdout: StringIO) -> None:
        """
        Tests printMessage method
        :return: None
        """
        self.hello.print_message()
        self.assertEqual(mock_stdout.getvalue(), 'Hello World!\n')

    def test_getMessage(self) -> None:
        """
        Tests getMessage method
        :return: None
        """
        self.assertEqual(self.hello.get_message(), 'Hello World!')

    def test_message(self) -> None:
        self.assertEqual(self.hello.message, 'Hello World!')


if __name__ == '__main__':
    unittest.main(verbosity=2)

hello/OOP/test_hello_oop.py
rambasnet/course-container
"""
Testing Solution class
"""

__author__ = "Ram Basnet"
__date__ = "2023/1/1"
__license__ = "MIT"
__version__ = "0.1.0"
__maintainer__ = "Ram Basnet"


import unittest
from unittest.mock import patch
from io import StringIO
from main import Main


class TestMain(unittest.TestCase):
    """
    Testing Singleton Main class
    """

    def setUp(self) -> None:
        """
        Setup method
        :return: None
        """
        # can't call Main() because it's a singleton class
        self.my_main: 'Main' = Main.get_instance()

    @patch('sys.stdout', new_callable=StringIO)
    def test_solve(self, mock_stdout: StringIO) -> None:
        """
        Tests solve method
        :return: None
        """
        self.my_main.solve()
        self.assertEqual(mock_stdout.getvalue(), 'Hello World!\n')

    def test_get_instance(self) -> None:
        """
        Tests get_instance method
        :return: None
        """
        self.assertIs(self.my_main.get_instance(), self.my_main)


if __name__ == '__main__':
    unittest.main(verbosity=2)

hello/OOP/test_main.py
rambasnet/course-container
#!/usr/bin/env python3

"""Kattis hello problem
"""
__author__ = "Ram Basnet"
__date__ = "Oct 1, 2023"
__license__ = "MIT"


def answer() -> str:
    """Returns Hello World!

    Returns:
        str: Hello World!
    """
    return "Hello World!"


def solve() -> None:
    """Solves the problem
    """
    print(answer())


if __name__ == "__main__":
    solve()

hello/hello.py
rambasnet/course-container
#!/ usr/bin/env python3

"""
separate test file for pytest
pytest recursively discovers all the files with test* and runs the
assert statements in them
"""

from hello import answer


def test_answer() -> None:
    assert answer() == "Hello World!"

hello/test_hello.py
rambasnet/course-container
#! /usr/bin/env python3

"""unittest for hello solution"""

__author__ = "Ram Basnet"
__copyright__ = "Copyright 2020"
__license__ = "MIT"

import unittest
from hello import answer


class TestHello(unittest.TestCase):
    """ Test hello.py solution.
    """

    def test1_answer(self) -> None:
        """Test hello.py answer function"""
        self.assertEqual(answer(), 'Hello World!', "Test failed...")


if __name__ == "__main__":
    unittest.main(verbosity=2)

hello/test_unit_hello.py
rambasnet/course-container
#!/usr/bin/env python
from __future__ import print_function
import argparse
import os
import re
import sys
import time

import requests
import requests.exceptions

from lxml.html import fragment_fromstring

# Python 2/3 compatibility
if sys.version_info[0] >= 3:
    import configparser
else:
    # Python 2, import modules with Python 3 names
    import ConfigParser as configparser

# End Python 2/3 compatibility

_DEFAULT_CONFIG = '/usr/local/etc/kattisrc'
_LANGUAGE_GUESS = {
    '.c': 'C',
    '.c++': 'C++',
    '.cc': 'C++',
    '.c#': 'C#',
    '.cpp': 'C++',
    '.cs': 'C#',
    '.cxx': 'C++',
    '.cbl': 'COBOL',
    '.cob': 'COBOL',
    '.cpy': 'COBOL',
    '.fs': 'F#',
    '.go': 'Go',
    '.hs': 'Haskell',
    '.java': 'Java',
    '.js': 'JavaScript (Node.js)',
    '.ts': 'TypeScript',
    '.kt': 'Kotlin',
    '.lisp': 'Common Lisp',
    '.cl': 'Common Lisp',
    '.m': 'Objective-C',
    '.ml': 'OCaml',
    '.pas': 'Pascal',
    '.php': 'PHP',
    '.pl': 'Prolog',
    '.py': 'Python 3',
    '.pyc': 'Python 3',
    '.rb': 'Ruby',
    '.rs': 'Rust',
    '.scala': 'Scala',
    '.f90': 'Fortran',
    '.f': 'Fortran',
    '.for': 'Fortran',
    '.sh': 'Bash',
    '.apl': 'APL',
    '.ss': 'Gerbil',
    '.jl': 'Julia',
    '.vb': 'Visual Basic',
    '.dart': 'Dart',
    '.zig': 'Zig',
    '.swift': 'Swift',
    '.nim': 'Nim',
}

_GUESS_MAINCLASS = {'Java', 'Kotlin', 'Scala'}
_GUESS_MAINFILE = {'APL', 'Bash', 'Dart', 'Gerbil', 'JavaScript (Node.js)', 'Julia', 'Common Lisp', 'Pascal', 'PHP', 'Python 2', 'Python 3', 'Ruby', 'Rust', 'TypeScript', 'Zig'}

_HEADERS = {'User-Agent': 'kattis-cli-submit'}

_RUNNING_STATUS = 5
_COMPILE_ERROR_STATUS = 8
_ACCEPTED_STATUS = 16
_STATUS_MAP = {
    0: 'New', # <invalid value>
    1: 'New',
    2: 'Waiting for compile',
    3: 'Compiling',
    4: 'Waiting for run',
    _RUNNING_STATUS: 'Running',
    6: 'Judge Error',
    7: 'Submission Error',
    _COMPILE_ERROR_STATUS: 'Compile Error',
    9: 'Run Time Error',
    10: 'Memory Limit Exceeded',
    11: 'Output Limit Exceeded',
    12: 'Time Limit Exceeded',
    13: 'Illegal Function',
    14: 'Wrong Answer',
    # 15: '<invalid value>',
    _ACCEPTED_STATUS: 'Accepted',
}


class ConfigError(Exception):
    pass


def get_url(cfg, option, default):
    if cfg.has_option('kattis', option):
        return cfg.get('kattis', option)
    else:
        return 'https://%s/%s' % (cfg.get('kattis', 'hostname'), default)


def get_config():
    """Returns a ConfigParser object for the .kattisrc file(s)
    """
    cfg = configparser.ConfigParser()
    if os.path.exists(_DEFAULT_CONFIG):
        cfg.read(_DEFAULT_CONFIG)

    if not cfg.read([os.path.join(os.path.expanduser("~"), '.kattisrc'),
                     os.path.join(os.path.dirname(sys.argv[0]), '.kattisrc')]):
        raise ConfigError('''\
I failed to read in a config file from your home directory or from the
same directory as this script. To download a .kattisrc file please visit
https://<kattis>/download/kattisrc

The file should look something like this:
[user]
username: yourusername
token: *********

[kattis]
hostname: <kattis>
loginurl: https://<kattis>/login
submissionurl: https://<kattis>/submit
submissionsurl: https://<kattis>/submissions''')
    return cfg


def is_python2(files):
    python2 = re.compile(r'^\s*\bprint\b *[^ \(\),\]]|\braw_input\b')
    for filename in files:
        try:
            with open(filename) as f:
                for index, line in enumerate(f):
                    if index == 0 and line.startswith('#!'):
                        if 'python2' in line:
                            return True
                        if 'python3' in line:
                            return False
                    if python2.search(line.split('#')[0]):
                        return True
        except IOError:
            return False
    return False


def guess_language(ext, files):
    if ext == ".C":
        return "C++"
    ext = ext.lower()
    if ext == ".h":
        if any(f.endswith(".c") for f in files):
            return "C"
        else:
            return "C++"
    if ext == ".py":
        if is_python2(files):
            return "Python 2"
        else:
            return "Python 3"
    return _LANGUAGE_GUESS.get(ext, None)


def guess_mainfile(language, files):
    for filename in files:
        if os.path.splitext(os.path.basename(filename))[0] in ['main', 'Main']:
            return filename
    for filename in files:
        try:
            with open(filename) as f:
                conts = f.read()
                if language in ['Java', 'Rust', 'Scala', 'Kotlin'] and re.search(r' main\s*\(', conts):
                    return filename
                if language == 'Pascal' and re.match(r'^\s*[Pp]rogram\b', conts):
                    return filename
        except IOError:
            pass
    return files[0]


def guess_mainclass(language, files):
    if language in _GUESS_MAINFILE and len(files) > 1:
        return os.path.basename(guess_mainfile(language, files))
    if language in _GUESS_MAINCLASS:
        mainfile = os.path.basename(guess_mainfile(language, files))
        name = os.path.splitext(mainfile)[0]
        if language == 'Kotlin':
            return name[0].upper() + name[1:] + 'Kt'
        return name
    return None


def login(login_url, username, password=None, token=None):
    """Log in to Kattis.

    At least one of password or token needs to be provided.

    Returns a requests.Response with cookies needed to be able to submit
    """
    login_args = {'user': username, 'script': 'true'}
    if password:
        login_args['password'] = password
    if token:
        login_args['token'] = token

    return requests.post(login_url, data=login_args, headers=_HEADERS)


def login_from_config(cfg):
    """Log in to Kattis using the access information in a kattisrc file

    Returns a requests.Response with cookies needed to be able to submit
    """
    username = cfg.get('user', 'username')
    password = token = None
    try:
        password = cfg.get('user', 'password')
    except configparser.NoOptionError:
        pass
    try:
        token = cfg.get('user', 'token')
    except configparser.NoOptionError:
        pass
    if password is None and token is None:
        raise ConfigError('''\
Your .kattisrc file appears corrupted. It must provide a token (or a
KATTIS password).

Please download a new .kattisrc file''')

    loginurl = get_url(cfg, 'loginurl', 'login')
    return login(loginurl, username, password, token)


def submit(submit_url, cookies, problem, language, files, mainclass='', tag=''):
    """Make a submission.

    The url_opener argument is an OpenerDirector object to use (as
    returned by the login() function)

    Returns the requests.Result from the submission
    """

    data = {'submit': 'true',
            'submit_ctr': 2,
            'language': language,
            'mainclass': mainclass,
            'problem': problem,
            'tag': tag,
            'script': 'true'}

    sub_files = []
    for f in files:
        with open(f) as sub_file:
            sub_files.append(('sub_file[]',
                              (os.path.basename(f),
                               sub_file.read(),
                               'application/octet-stream')))

    return requests.post(submit_url, data=data, files=sub_files, cookies=cookies, headers=_HEADERS)


def confirm_or_die(problem, language, files, mainclass, tag):
    print('Problem:', problem)
    print('Language:', language)
    print('Files:', ', '.join(files))
    if mainclass:
        if language in _GUESS_MAINFILE:
            print('Main file:', mainclass)
        else:
            print('Mainclass:', mainclass)
    if tag:
        print('Tag:', tag)
    print('Submit (y/N)?')
    if sys.stdin.readline().upper()[:-1] != 'Y':
        print('Cancelling')
        sys.exit(1)


def get_submission_url(submit_response, cfg):
    m = re.search(r'Submission ID: (\d+)', submit_response)
    if m:
        submissions_url = get_url(cfg, 'submissionsurl', 'submissions')
        submission_id = m.group(1)
        return '%s/%s' % (submissions_url, submission_id)


def get_submission_status(submission_url, cookies):
    reply = requests.get(submission_url + '?json', cookies=cookies, headers=_HEADERS)
    return reply.json()


_RED_COLOR = 31
_GREEN_COLOR = 32
def color(s, c):
    return '\x1b[%sm%s\x1b[0m' % (c, s)


def show_judgement(submission_url, cfg):
    print()
    login_reply = login_from_config(cfg)
    while True:
        status = get_submission_status(submission_url, login_reply.cookies)
        status_id = status['status_id']
        testcases_done = status['testcase_index']
        testcases_total = status['row_html'].count('<i') - 1

        status_text = _STATUS_MAP.get(status_id, 'Unknown status %s' % status_id)


        if status_id < _RUNNING_STATUS:
            print('\r%s...' % (status_text), end='')
        else:
            print('\rTest cases: ', end='')

        if status_id == _COMPILE_ERROR_STATUS:
            print('\r%s' % color(status_text, _RED_COLOR), end='')
            try:
                root = fragment_fromstring(status['feedback_html'], create_parent=True)
                error = root.find('.//pre').text
                print(color(':', _RED_COLOR))
                print(error, end='')
            except:
                pass
        elif status_id < _RUNNING_STATUS:
            print('\r%s...' % (status_text), end='')
        else:
            print('\rTest cases: ', end='')

            if testcases_total == 0:
                print('???', end='')
            else:
                s = '.' * (testcases_done - 1)
                if status_id == _RUNNING_STATUS:
                    s += '?'
                elif status_id == _ACCEPTED_STATUS:
                    s += '.'
                else:
                    s += 'x'

                print('[%-*s]  %d / %d' % (testcases_total, s, testcases_done, testcases_total), end='')

        sys.stdout.flush()

        if status_id > _RUNNING_STATUS:
            # Done
            print()
            success = status_id == _ACCEPTED_STATUS
            try:
                root = fragment_fromstring(status['row_html'], create_parent=True)
                cpu_time = root.find('.//*[@data-type="cpu"]').text
                status_text += " (" + cpu_time + ")"
            except:
                pass
            if status_id != _COMPILE_ERROR_STATUS:
                print(color(status_text, _GREEN_COLOR if success else _RED_COLOR))
            return success

        time.sleep(0.25)


def main():
    parser = argparse.ArgumentParser(prog='kattis', description='Submit a solution to Kattis')
    parser.add_argument('-p', '--problem',
                        help=''''Which problem to submit to.
Overrides default guess (first part of first filename)''')
    parser.add_argument('-m', '--mainclass',
                        help='''Sets mainclass.
Overrides default guess (first part of first filename)''')
    parser.add_argument('-l', '--language',
                        help='''Sets language.
Overrides default guess (based on suffix of first filename)''')
    parser.add_argument('-t', '--tag',
                        help=argparse.SUPPRESS)
    parser.add_argument('-f', '--force',
                        help='Force, no confirmation prompt before submission',
                        action='store_true')
    parser.add_argument('files', nargs='+')

    args = parser.parse_args()
    files = args.files

    try:
        cfg = get_config()
    except ConfigError as exc:
        print(exc)
        sys.exit(1)

    problem, ext = os.path.splitext(os.path.basename(files[0]))
    language = guess_language(ext, files)
    mainclass = guess_mainclass(language, files)
    tag = args.tag

    problem = problem.lower()

    if args.problem:
        problem = args.problem

    if args.mainclass is not None:
        mainclass = args.mainclass

    if args.language:
        language = args.language

    if language is None:
        print('''\
No language specified, and I failed to guess language from filename
extension "%s"''' % (ext,))
        sys.exit(1)

    files = sorted(list(set(args.files)))

    try:
        login_reply = login_from_config(cfg)
    except ConfigError as exc:
        print(exc)
        sys.exit(1)
    except requests.exceptions.RequestException as err:
        print('Login connection failed:', err)
        sys.exit(1)

    if not login_reply.status_code == 200:
        print('Login failed.')
        if login_reply.status_code == 403:
            print('Incorrect username or password/token (403)')
        elif login_reply.status_code == 404:
            print('Incorrect login URL (404)')
        else:
            print('Status code:', login_reply.status_code)
        sys.exit(1)

    submit_url = get_url(cfg, 'submissionurl', 'submit')

    if not args.force:
        confirm_or_die(problem, language, files, mainclass, tag)

    try:
        result = submit(submit_url,
                        login_reply.cookies,
                        problem,
                        language,
                        files,
                        mainclass,
                        tag)
    except requests.exceptions.RequestException as err:
        print('Submit connection failed:', err)
        sys.exit(1)

    if result.status_code != 200:
        print('Submission failed.')
        if result.status_code == 403:
            print('Access denied (403)')
        elif result.status_code == 404:
            print('Incorrect submit URL (404)')
        else:
            print('Status code:', result.status_code)
        sys.exit(1)

    plain_result = result.content.decode('utf-8').replace('<br />', '\n')
    print(plain_result)

    submission_url = None
    try:
        submission_url = get_submission_url(plain_result, cfg)
    except configparser.NoOptionError:
        pass

    if submission_url:
        print(submission_url)
        if not show_judgement(submission_url, cfg):
            sys.exit(1)


if __name__ == '__main__':
    main()

kattis-cli/submit.py
rambasnet/course-container