import gdb

long_int = gdb.lookup_type('unsigned long')

def to_hex(v: gdb.Value) -> str:
    """Returns v in hex padded with leading zeros"""
    return f"{int(v):#0{8}x}"

def get_register(reg_addr):
    value = int(gdb.parse_and_eval("*(uint32_t *){:#x}".format(reg_addr)).cast(long_int))
    return value

def set_register(addr, value):
    gdb.execute("set *{:#x}={}".format(addr, value))

class SauDump(gdb.Command):
    """Perform a sau dump on ARM Cortex M device."""

    def __init__(self):
        super(SauDump, self).__init__("sau_dump", gdb.COMMAND_USER)
        self.sau_registers = {'SAU_CTRL':0xe000edd0,
                              'SAU_TYPE':0xe000edd4,
                              'SAU_RNR':0xe000edd8,
                              'SAU_RBAR':0xe000eddc,
                              'SAU_RLAR':0xe000ede0,
                              'SFSR':0xe000ede4,
                              'SFAR':0xe000ede8}

    def en_description(self, reg_rlar):
        en_desc = {0: "disabled",
                   1: "enabled"}

        en = (reg_rlar & 0x1)
        return en_desc.get(en)

    def nsc_description(self, reg_rlar):
        nsc_desc = {0: "",
                    1: "Region is Non-secure callable"}

        nsc = (reg_rlar & 0x2) >> 1
        return nsc_desc.get(nsc)

    def get_sau_region(self, indice):
        """Get sau region X"""
        sau_region = []

        set_register(self.sau_registers['SAU_RNR'], indice)

        rbar = get_register(self.sau_registers['SAU_RBAR'])
        rlar = get_register(self.sau_registers['SAU_RLAR'])

        sau_region += [{"indice": indice,
                        "rbar": rbar,
                        "rlar": rlar}]

        return sau_region

    def show_sau_region(self, regions):
        """Show sau region X"""
        gdb.write("show {} sau region(s)\n".format(len(regions)))

        for reg in regions:
            gdb.write("sau{} addr: base:{:#010x} limit:{:#010x} {:8} {}\n".format(
                reg['indice'],
                reg['rbar'] & 0xFFFFFFE0,
                reg['rlar'] | 0x1F,
                self.en_description(reg['rlar']),
                self.nsc_description(reg['rlar'])))

    def invoke(self, arg, from_tty):
        regions = []

        nb_region = get_register(self.sau_registers['SAU_TYPE'])

        for i in range(nb_region):
            regions += self.get_sau_region(i)

        self.show_sau_region(regions)

class MpuDump(gdb.Command):
    """Perform a Mpu dump on ARM Cortex M device.

       mpu_dump [secure|non-secure]
    """

    def __init__(self):
        super(MpuDump, self).__init__("mpu_dump", gdb.COMMAND_USER)
        self.mpu_reg = self.mpu_s_reg = {'MPU_TYPE':0xe000ed90,
                                         'MPU_CTRL':0xe000ed94,
                                         'MPU_RNR':0xe000ed98,
                                         'MPU_RBAR':0xe000ed9c,
                                         'MPU_RLAR':0xe000eda0,
                                         'MPU_MAIR0':0xe000edc0,
                                         'MPU_MAIR1':0xe000edc4}

        self.mpu_ns_reg = {'MPU_TYPE':0xe002ed90,
                           'MPU_CTRL':0xe002ed94,
                           'MPU_RNR':0xe002ed98,
                           'MPU_RBAR':0xe002ed9c,
                           'MPU_RLAR':0xe002eda0,
                           'MPU_MAIR0':0xe002edc0,
                           'MPU_MAIR1':0xe002edc4}

        self.parameters = {'secure': self.mpu_s_reg ,
                           'non-secure': self.mpu_ns_reg}

    def complete(self, text, word):
        args = str(text).split(" ")
        if len(args) > 1:
            return gdb.COMPLETE_NONE

        filt = filter(lambda x: x.startswith(args[0]), self.parameters)

        return filt

    def get_mpu_region(self, indice):
        """Get mpu region X"""
        mpu_region = []

        set_register(self.mpu_reg['MPU_RNR'], indice)

        rbar = get_register(self.mpu_reg['MPU_RBAR'])
        rlar = get_register(self.mpu_reg['MPU_RLAR'])

        mpu_region += [{"indice": indice,
                        "rbar": rbar,
                        "rlar": rlar}]

        return mpu_region

    def sh_description(self, reg_rbar):
        sh_desc = {0: "Non-shareable",
                   1: "reserved",
                   2: "Outer Shareable",
                   3: "Inner Shareable"}

        sh = (reg_rbar & 0x18) >> 3
        return sh_desc.get(sh)

    def ap_description(self, reg_rbar):
        ap_desc = {0: "Read/write by privileged code only",
                   1: "Read/write by any privilege level",
                   2: "Read-only by privileged code only",
                   3: "Read-only by any privilege level"}

        ap = (reg_rbar & 0x6) >> 1
        return ap_desc.get(ap)

    def xn_description(self, reg_rbar):
        xn_desc = {0: "Execution only permitted if read permitted",
                   1: "Execution not permitted"}

        xn = (reg_rbar & 0x1)
        return xn_desc.get(xn)

    def pxn_description(self, reg_rlar):
        pxn_desc = {0: "Execution only permitted if read permitted",
                    1: "Execution from a privileged mode is not permitted"}

        pxn = (reg_rlar & 0x10) >> 4
        return pxn_desc.get(pxn)

    def attr_dev_description(self, attr):
        dev_desc = {0: "Device-nGnRnE",
                    1: "Device-nGnRE",
                    2: "Device-nGRE",
                    3: "Device-GRE"}

        device = (attr & 0xC) >> 2
        return dev_desc.get(device)

    def attr_description(self, attr):
        pol_desc = {0: "write-through transient",
                    1: "write-back transient",
                    2: "write-through non-transient",
                    3: "write-back non-transient"}

        policy = (attr & 0xC) >> 2
        rw = (attr & 0x3)

        if policy == 0x1 and rw == 0x0:
            return "non-cacheable"

        desc = pol_desc.get(policy)

        if rw & 0x2:
            desc += " read"

        if rw & 0x1:
            desc += " write"

        return desc

    def attr_idx(self, reg_rlar):
        idx = (reg_rlar & 0xE) >> 1
        return idx

    def attr_device(self, attr):
        if attr & 0xF0:
            return False
        return True

    def en_description(self, reg_rlar):
        en_desc = {0: "disabled",
                   1: "enabled"}

        en = (reg_rlar & 0x1)
        return en_desc.get(en)

    def hfnmiena_description(self, reg_ctrl):
        hfnmiena_desc = {0: "Disables the MPU for these handlers",
                         1: "Use the MPU for memory accesses by these handlers"}

        hfnmiena = (reg_ctrl & 0x2) >> 1
        return hfnmiena_desc.get(hfnmiena)

    def privdefena_description(self, reg_ctrl):
        privdefena_desc = {0: "Disables the default memory map",
                           1: "Enables the default memory map as a background region for privileged access"}

        privdefena = (reg_ctrl & 0x4) >> 2
        return privdefena_desc.get(privdefena)

    def show_mpu_region(self, regions):
        """Show mpu region X"""

        for reg in regions:
            gdb.write("mpu{} \taddr: base:{:#010x} limit:{:#010x} {}\n".format(
                reg['indice'],
                reg['rbar'] & 0xFFFFFFE0,
                reg['rlar'] | 0x1F,
                self.en_description(reg['rlar'])))

            gdb.write("\t\t-{}\n".format(self.sh_description(reg['rbar'])))
            gdb.write("\t\t-{}\n".format(self.ap_description(reg['rbar'])))
            gdb.write("\t\t-{}\n".format(self.xn_description(reg['rbar'])))
            gdb.write("\t\t-{}\n".format(self.pxn_description(reg['rlar'])))
            gdb.write("\t\t-Attribute index associates to MPU_MAIR0|1: {}\n".format(
                self.attr_idx(reg['rlar'])))

    def invoke(self, arg, from_tty):
        args = str(arg).split(" ")
        regions = []

        filt = filter(lambda x: x == args[0], self.parameters)

        if len(args) > 1 or not list(filt):
            gdb.write("Invalide parameter\n")
            gdb.execute("help mpu_dump")
            return

        gdb.write("mpu: {}\n".format(args[0]))
        self.mpu_reg = self.parameters[args[0]]

        mpu_ctrl = get_register(self.mpu_reg['MPU_CTRL'])
        gdb.write("mpu ctrl: {:#010x}\n".format(mpu_ctrl))
        gdb.write("\t-{}\n".format(self.en_description(mpu_ctrl)))
        gdb.write("\t-{}\n".format(self.hfnmiena_description(mpu_ctrl)))
        gdb.write("\t-{}\n".format(self.privdefena_description(mpu_ctrl)))

        mpu_type = get_register(self.mpu_reg['MPU_TYPE'])
        nb_region = (mpu_type & 0xFF00) >> 8

        for i in range(nb_region):
            regions += self.get_mpu_region(i)

        gdb.write("\n{} mpu region(s)\n".format(len(regions)))
        gdb.write("----------------\n")
        self.show_mpu_region(regions)

        gdb.write("\nmemory attributes\n")
        gdb.write("-----------------\n")

        mair0 = get_register(self.mpu_reg['MPU_MAIR0'])
        mair1 = get_register(self.mpu_reg['MPU_MAIR1'])
        mair = (mair1 << 32) | mair0

        for j in range(8):
            attr = (mair >> (8*j)) & 0xFF
            inner = attr & 0xF
            outer = (attr & 0xF0) >> 4
            gdb.write("\tindex{} attributes:{:#04x}\n".format(j, attr))

            if self.attr_device(attr):
                gdb.write("\t\t-{}\n".format(self.attr_dev_description(attr)))
            else:
                gdb.write("\t\t-Normal memory, Outer {}\n".format(self.attr_description(outer)))
                gdb.write("\t\t-Normal memory, Inner {}\n".format(self.attr_description(inner)))

MpuDump()
SauDump()


while True:
  A, B = map(int, input().split())
  C = int(input())

  if 23 < A or 0 > A: continue
  if 59 < B or 0 > B: continue
  if 1000 < C or 0 > C: continue

  A += C // 60
  B += C % 60

  if B >= 60:
    A += 1
    B -= 60
  if A >= 24:
    A -= 24

  print(A, B)
  break


class Solution:
    ## not optimal
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        word_size = len(words[0])

        s_list = []
        for i in range(len(s)-word_size+1):
            if s[i:i+word_size] in words:
                s_list.append([i, i+word_size, s[i:i+word_size]])
        word_list = {}
        for word in words:
            if word in word_list:
                word_list[word] += 1
            else:
                word_list[word] = 1

        ## print(s_list)

        ans_list = []
        for index in range(len(s_list)-len(words)+1):
            word_list_tmp, length = {}, 1
            word_list_tmp = word_list.copy()
            word_list_tmp[s_list[index][2]] -= 1

            ## print(word_list_tmp)

            curr = index
            for j in range(index+1, len(s_list)):
                if s_list[j][0] > s_list[curr][1]:
                    break
                if s_list[j][0] < s_list[curr][1]:
                    continue
                if word_list_tmp[s_list[j][2]] > 0:
                    word_list_tmp[s_list[j][2]] -= 1
                    length += 1
                    curr = j

            if length == len(words):
                ans_list.append(s_list[index][0])

        return ans_list


# -*- coding: utf-8 -*-
import torch.nn as nn
import torch

class Affine(nn.Module):
    """
    实现全连接层, y=x * W_t + b
    """

    def __init__(self, in_features: int, out_features: int):
        super().__init__()
        self.w = nn.Parameter(torch.randn(in_features, out_features))
        self.b = nn.Parameter(torch.randn(out_features))

    def forward(self, x):
        x = x.mm(self.w)
        return x + self.b.expand_as(x)

def test_Affine():
    # 输入特征数量是3， 输出特征数量是4
    affine = Affine(3, 4)
    x = torch.randn(2, 3, requires_grad=True)
    output = affine(x)

    # 直接使用nn.Linear
    linear = nn.Linear(3, 4)
    linear_output = linear(x)
    print("output: {}\nlinear_output: {}".format(output, linear_output))

class Perceptron(nn.Module):
    """
    多层感知机
    """

    def __init__(self, input_features, hidden_features, output_features):
        nn.Module.__init__(self)
        self.layer1 = Affine(input_features, hidden_features)
        self.layer2 = Affine(hidden_features, output_features)

    def forward(self, input):
        x1 = self.layer1(input)
        output = self.layer2(x1)
        return output

    def print_parameters(self):
        for name, patameter in self.named_parameters():
            print("name:{}, type:{}".format(name, patameter.size()))

class NN_Perecptron(nn.Module):

    def __init__(self, input_features, hidden_features, output_features):
        nn.Module.__init__(self)
        self.hidden_layer = nn.Linear(in_features=input_features, out_features=hidden_features, bias=True)
        self.output_layer = nn.Linear(in_features=hidden_features, out_features=output_features, bias=True)

    def forward(self, input: torch.Tensor):
        hidden_output = self.hidden_layer(input)
        output = self.output_layer(hidden_output)
        return output

    def print_parameters(self):
        for name, patameter in self.named_parameters():
            print("name:{}, type:{}".format(name, patameter.shape))

def test_Perceptron():
    input = torch.randn(5, 3)
    perceptron = Perceptron(3, 4, 2)
    perceptron.print_parameters()
    output = perceptron(input)
    print("output:{}".format(output))

    print("==" * 30)

    nn_perceptron = NN_Perecptron(input_features=3, hidden_features=4, output_features=2)
    print("output: {}".format(nn_perceptron(input)))
    nn_perceptron.print_parameters()

    # name:hidden_layer.weight, type:torch.Size([4, 3]) # 这里的[4,3]而不是[3,4]是由于x乘的是W转置的原因
    # name:hidden_layer.bias, type:torch.Size([4])
    # name:output_layer.weight, type:torch.Size([2, 4])
    # name:output_layer.bias, type:torch.Size([2])

def test_batch_norm():
    # 4 Channel
    bn1d = nn.BatchNorm1d(4)
    bn1d.weight.data = torch.ones(4) * 4
    bn1d.bias.data = torch.zeros(4)

    input_x = torch.randn(2, 4)
    bn_out = bn1d(input_x)
    print("bn_out: {}".format(bn_out))
    print("bn_out mean(0): {}\nbn_out.var() :{}".format(bn_out.mean(0), bn_out.var(unbiased=False)))

def test_dropout():
    # 每个元素以0.5的概率舍弃
    dropout = nn.Dropout(0.5)
    input_x = torch.randn(3, 4)
    print("before dropout, input_x:{}".format(input_x))
    o = dropout(input_x)
    print("after dropout, input_x:{}".format(o))

def test_RNN():
    """
     h_t = \text{tanh}(W_{ih} x_t + b_{ih} + W_{hh} h_{(t-1)} + b_{hh})
    :return:
    """
    rnn = nn.RNN(input_size=100, hidden_size=50, num_layers=1, bias=True, batch_first=True, dropout=0.5,
                 nonlinearity="relu")
    # (batch, seq_len input_size)
    input_x = torch.rand((10, 20, 100))

    # num_layers * num_directions, batch, hidden_size
    h_0 = torch.zeros((1 * 1, 10, 50))

    # output size: [seq_len, batch, num_directions * hidden_size]
    # h_n size: [num_layers * num_directions, batch, hidden_size]
    output, h_n = rnn(input_x, h_0)

    print("output size:{}\nh_n size:{}".format(output.size(), h_n.size()))

    output = output.transpose(0, 1)
    print("output shape:{}".format(output.shape))

    id(output[-1, :, :].storage()) == id(h_n.storage())

    print("====" * 4 + "RNN 参数 size" + "====" * 4)
    for name, parameter in rnn.named_parameters():
        print("name:{}, parameter size:{}".format(name, parameter.shape))

def test_lstm():
    torch.manual_seed(1000)
    # input shape: [batch, seq_len, input_size] batch_first=True
    # seq可以理解为时间序列的长度，比如在NLP里面可以代表一句话的长度
    input_x = torch.randn((2, 3, 4))

    lstm = nn.LSTM(input_size=4, batch_first=True, hidden_size=5, num_layers=1)

    # h_0 shape: [num_layers * num_directions, batch, hidden_size]
    h_0 = torch.randn(1, 2, 5)
    # c_0 shape: [num_layers * num_directions, batch, hidden_size]
    c_0 = torch.rand(1, 2, 5)

    # output shape: [batch, seq_len, num_directions * hidden_size]
    # h_n shape: [num_layers * num_directions, batch, hidden_size]
    # c_n shape: [num_layers * num_directions, batch, hidden_size]
    output, (h_n, c_n) = lstm(input_x, (h_0, c_0))

    last_out = output[-1, :, :]

    print("last_out: {}".format(last_out))
    print("tanh(c_n): {}".format(torch.tanh(c_n.transpose(0, 1))))
    print("out_last :{}".format(h_n / torch.tanh(c_n)))

    # print("h_last: {}".format(h_last))
    print("output:{}\nh_n shape:{}\nh_n: {}\nc_n:shape:{}\nc_n:{}".format(output.shape, h_n.shape, h_n, c_n.shape, c_n))

def test_embedding():
    embedding = nn.Embedding(num_embeddings=10, embedding_dim=3, padding_idx=0)
    # 可以用预训练好的词向量初始化embedding
    embedding.weight.data = torch.arange(0, 30).view(10, 3)

    input_x = torch.arange(7, 0, -1).long()
    output = embedding(input_x)
    print("output: {}".format(output))

    embedding = nn.Embedding(5, 3)
    input_ids = torch.LongTensor([[1, 2, 3, 4], [0, 1, 2, 4]])
    print("input_ids shape:{}".format(input_ids.shape))
    # input_ids shape: [2,4]

    out = embedding(torch.LongTensor(input_ids))
    print("out shape:{}".format(out.shape))
    # out shape: [2, 4, 3]

def conv1_test():
    conv1 = nn.Conv1d(in_channels=128, out_channels=100, kernel_size=3, stride=1, padding=3)
    # N, C, L = batch_size, in_channel, sequence_len
    input_x = torch.randn(29, 128, 63)
    # 计算输出的特征大小: (L_in - kernel_size +  2 * padding)/ stride + 1
    #
    conv1_out = conv1(input=input_x)
    print("conv1_out shape: {}".format(conv1_out.shape))
    # [29, 100, 67]

def test_embeddingBag():
    embedding = nn.EmbeddingBag(num_embeddings=10, embedding_dim=3)
    # 三个batch, 分别包含2个,3个,3个indices
    input_x = torch.LongTensor([1,2,4,5,4,3,2,9])
    offsets = torch.LongTensor([0,2,5])
    embedding_mean = embedding(input_x, offsets)
    print("embedding_mean: {}".format(embedding_mean))
    # embedding_mean: tensor([[-0.0401, -0.1045, -0.3961],
    #         [ 0.3339,  2.7423,  0.7119],
    #         [-0.9661,  0.0946,  0.2269]], grad_fn=<EmbeddingBagBackward>)

if __name__ == "__main__":
    # test_Perceptron()
    # test_batch_norm()
    # test_dropout()
    # test_RNN()
    # test_lstm()
    # test_embedding()
    conv1_test()


import logging
import os

import static_babel_content

from utils import customize_babel_locale
from website.yaml_file import YamlFile
from safe_format import safe_format

logger = logging.getLogger(__name__)

COUNTRIES = static_babel_content.COUNTRIES

# Define dictionary for available languages. Fill dynamically later.
ALL_LANGUAGES = {}
ALL_KEYWORD_LANGUAGES = {}

# Todo TB -> We create this list manually, but it would be nice if we find
# a way to automate this as well
NON_LATIN_LANGUAGES = ['ar', 'bg', 'bn', 'el', 'fa', 'hi', 'he', 'pa_PK', 'ru', 'zh_Hans']

# Babel has a different naming convention than Weblate and doesn't support some languages -> fix this manually
CUSTOM_BABEL_LANGUAGES = {'pa_PK': 'pa_Arab_PK',
                          'kmr': 'ku_TR',
                          'tn': 'en',
                          'pap': 'en',
                          'tl': 'en'}

# For the non-existing language manually overwrite the display language to make sure it is displayed correctly
CUSTOM_LANGUAGE_TRANSLATIONS = {'kmr': 'Kurdî (Tirkiye)',
                                'tn': 'Setswana',
                                'pap': 'Papiamentu',
                                'tl': 'ᜆᜄᜎᜓᜄ᜔'}

customize_babel_locale(CUSTOM_BABEL_LANGUAGES)

# This changes the color of the adventure tab to pink
KEYWORDS_ADVENTURES = {'print_command', 'ask_command', 'is_command', 'sleep_command', 'random_command',
                       'add_remove_command', 'quotation_marks', 'if_command', 'in_command', 'maths', 'repeat_command',
                       'repeat_command_2', 'for_command', 'and_or_command', 'while_command', 'elif_command',
                       'clear_command', 'pressit', 'debugging', 'functions'}

ADVENTURE_ORDER_PER_LEVEL = {
    1: [
        'default',
        'print_command',
        'ask_command',
        'parrot',
        'rock',
        'haunted',
        'story',
        'turtle',
        'restaurant',
        'fortune',
        'debugging'
    ],
    2: [
        'default',
        'is_command',
        'rock',
        'ask_command',
        'rock_2',
        'haunted',
        'sleep_command',
        'parrot',
        'story',
        'restaurant',
        'turtle',
        'debugging'
    ],
    3: [
        'default',
        'random_command',
        'dice',
        'rock',
        'fortune',
        'restaurant',
        'add_remove_command',
        'parrot',
        'dishes',
        'story',
        'haunted',
        'turtle',
        'debugging'
    ],
    4: [
        'default',
        'quotation_marks',
        'rock',
        'dice',
        'dishes',
        'parrot',
        'turtle',
        'clear_command',
        'story',
        'haunted',
        'fortune',
        'restaurant',
        'debugging'
    ],
    5: [
        'default',
        'if_command',
        'language',
        'dice',
        'dishes',
        'story',
        'rock',
        'parrot',
        'haunted',
        'in_command',
        'restaurant',
        'fortune',
        'pressit',
        'turtle',
        'debugging'
    ],
    6: [
        'default',
        'maths',
        'is_command',
        'songs',
        'dice',
        'dishes',
        'turtle',
        'calculator',
        'fortune',
        'restaurant',
        'debugging'
    ],
    7: [
        'default',
        'repeat_command',
        'story',
        'songs',
        'dishes',
        'dice',
        'repeat_command_2',
        'fortune',
        'restaurant',
        'pressit',
        'debugging'
    ],
    8: [
        'default',
        'repeat_command',
        'fortune',
        'repeat_command_2',
        'songs',
        'if_command',
        'story',
        'haunted',
        'restaurant',
        'turtle',
        'debugging'
    ],
    9: [
        'default',
        'repeat_command',
        'if_command',
        'rock',
        'story',
        'calculator',
        'restaurant',
        'haunted',
        'pressit',
        'turtle',
        'debugging'
    ],
    10: [
        'default',
        'for_command',
        'dishes',
        'dice',
        'fortune',
        'turtle',
        'harry_potter',
        'songs',
        'story',
        'rock',
        'calculator',
        'restaurant',
        'debugging'
    ],
    11: [
        'default',
        'for_command',
        'years',
        'calculator',
        'songs',
        'restaurant',
        'haunted',
        'debugging'
    ],
    12: [
        'default',
        'maths',
        'functions',
        'quotation_marks',
        'story',
        'fortune',
        'songs',
        'songs_2',
        'restaurant',
        'calculator',
        'turtle',
        'piggybank',
        'secret',
        'debugging'
    ],
    13: [
        'default',
        'and_or_command',
        'secret',
        'functions',
        # 'functions_2',
        # 'functions_3',
        'story',
        'rock',
        'restaurant',
        'calculator',
        'tic',
        'debugging'
    ],
    14: [
        'default',
        'is_command',
        'guess_my_number',
        'haunted',
        'functions',
        'hotel',
        'calculator',
        'calculator_2',
        'piggybank',
        'quizmaster',
        'tic',
        'debugging'
    ],
    15: [
        'default',
        'while_command',
        'restaurant',
        'story',
        'dice',
        'rock',
        'calculator',
        'tic',
        'debugging'
    ],
    16: [
        'default',
        'random_command',
        'haunted',
        'songs',
        'language',
        'debugging'
    ],
    17: [
        'default',
        'for_command',
        'elif_command',
        'tic',
        'blackjack',
        'debugging'
    ],
    18: [
        'default',
        'print_command',
        'ask_command',
        'story',
        'songs',
        'debugging'
    ]
}

HOUR_OF_CODE_ADVENTURES = {
    1: [
        'print_command',
        'parrot',
        'turtle',
        'debugging'
    ],
    2: [
        'default',
        'parrot',
        'turtle',
        'debugging'
    ],
    3: [
        'parrot',
        'dishes',
        'turtle',
        'debugging'
    ],
    4: [
        'dishes',
        'parrot',
        'story',
        'debugging'
    ],
    5: [
        'language',
        'parrot',
        'turtle',
        'debugging'
    ],
    6: [
        'songs',
        'dishes',
        'turtle',
        'debugging'
    ],
    7: [
        'songs',
        'dishes',
        'restaurant',
        'debugging'
    ],
    8: [
        'songs',
        'restaurant',
        'turtle',
        'debugging'
    ],
    9: [
        'rock',
        'restaurant',
        'turtle',
        'debugging'
    ],
    10: [
        'dishes',
        'dice',
        'turtle',
        'songs',
        'debugging'
    ],
    11: [
        'years',
        'songs',
        'restaurant',
        'debugging'
    ],
    12: [
        'maths',
        'functions',
        'story',
        'turtle',
        'debugging'
    ],
    13: [
        'story',
        'rock',
        'restaurant',
        'calculator',
        'debugging'
    ],
    14: [
        'guess_my_number',
        'haunted',
        'hotel',
        'calculator',
        'quizmaster',
        'debugging'
    ],
    15: [
        'restaurant',
        'story',
        'dice',
        'rock',
        'debugging'
    ],
    16: [
        'haunted',
        'songs',
        'language',
        'debugging'
    ],
    17: [
        'blackjack',
        'debugging'
    ],
    18: [
        'story',
        'songs',
        'debugging'
    ]
}

RESEARCH = {}
for paper in sorted(os.listdir('content/research'),
                    key=lambda x: int(x.split("_")[-1][:-4]),
                    reverse=True):
    # An_approach_to_describing_the_semantics_of_Hedy_2022.pdf -> 2022, An
    # approach to describing the semantics of Hedy
    name = paper.replace("_", " ").split(".")[0]
    name = name[-4:] + ". " + name[:-5]
    RESEARCH[name] = paper

# load all available languages in dict
# list_translations of babel does about the same, but without territories.
languages = {}
if not os.path.isdir('translations'):
    # should not be possible, but if it's moved someday, EN would still be working.
    ALL_LANGUAGES['en'] = 'English'
    ALL_KEYWORD_LANGUAGES['en'] = 'EN'

for folder in os.listdir('translations'):
    locale_dir = os.path.join('translations', folder, 'LC_MESSAGES')
    if not os.path.isdir(locale_dir):
        continue
    if filter(lambda x: x.endswith('.mo'), os.listdir(locale_dir)):
        languages[folder] = CUSTOM_LANGUAGE_TRANSLATIONS.get(folder,
                                                             static_babel_content.LANGUAGE_NAMES.get(folder, folder))

for lang in sorted(languages):
    ALL_LANGUAGES[lang] = languages[lang]
    if os.path.exists('./grammars/keywords-' + lang + '.lark'):
        ALL_KEYWORD_LANGUAGES[lang] = lang[0:2].upper()  # first two characters

# Load and cache all keyword yamls
KEYWORDS = {}
for lang in ALL_KEYWORD_LANGUAGES.keys():
    KEYWORDS[lang] = YamlFile.for_file(f'content/keywords/{lang}.yaml').to_dict()
    for k, v in KEYWORDS[lang].items():
        if isinstance(v, str) and "|" in v:
            # when we have several options, pick the first one as default
            # Some keys are ints, turn them into strings
            KEYWORDS[lang][k] = v.split('|')[0]

class StructuredDataFile:
    """Base class for all data files in the content directory."""

    def __init__(self, filename):
        self.filename = filename
        self._file = None

    @property
    def file(self):
        """Lazily load the requested file."""
        if not self._file:
            self._file = YamlFile.for_file(self.filename)
        return self._file

class Commands(StructuredDataFile):
    def __init__(self, language):
        self.language = language
        super().__init__(f'content/cheatsheets/{self.language}.yaml')

    def get_commands_for_level(self, level, keyword_lang):
        return deep_translate_keywords(self.file.get(int(level), {}), keyword_lang)

def deep_translate_keywords(yaml, keyword_language):
    try:
        """Recurse through a data structure and replace keyword placeholders in any strings we encounter."""
        if isinstance(yaml, str):
            # this is used to localize adventures linked in slides (PR 3860)
            yaml = yaml.replace('/raw', f'/raw?keyword_language={keyword_language}')
            return safe_format(yaml, **KEYWORDS.get(keyword_language))
        if isinstance(yaml, list):
            return [deep_translate_keywords(e, keyword_language) for e in yaml]
        if isinstance(yaml, dict):
            return {k: deep_translate_keywords(v, keyword_language) for k, v in yaml.items()}
        return yaml
    except ValueError as E:
        raise ValueError(f'Issue in language {keyword_language}. Offending yaml: {yaml}. Error: {E}')
    except TypeError as E:
        raise TypeError(f'Issue in language {keyword_language}. Offending yaml: {yaml}. Error: {E}')

def get_localized_name(name, keyword_lang):
    return safe_format(name, **KEYWORDS.get(keyword_lang))

# Todo TB -> We don't need these anymore as we guarantee with Weblate that
# each language file is there

class NoSuchCommand:
    def get_commands_for_level(self, level, keyword_lang):
        return {}

class Adventures(StructuredDataFile):
    def __init__(self, language):
        self.language = language
        super().__init__(f'content/adventures/{self.language}.yaml')

    def get_adventure_keyname_name_levels(self):
        return {aid: {adv['name']: list(adv['levels'].keys())} for aid, adv in self.file.get('adventures', {}).items()}

    def get_adventure_names(self):
        return {aid: adv['name'] for aid, adv in self.file.get('adventures', {}).items()}

    def get_adventures(self, keyword_lang="en"):
        return deep_translate_keywords(self.file.get('adventures'), keyword_lang)

    def get_adventures_subset(self, subset=["print_command", "parrot"], keyword_lang="en"):
        adventures = {aid: adv for aid, adv in self.file.get('adventures', {}).items() if aid in subset}
        return deep_translate_keywords(adventures, keyword_lang)

    def has_adventures(self):
        return True if self.file.get('adventures') else False

class NoSuchAdventure:
    def get_adventure(self):
        return {}

class ParsonsProblem(StructuredDataFile):
    def __init__(self, language):
        self.language = language
        super().__init__(f'content/parsons/{self.language}.yaml')

    def get_highest_exercise_level(self, level):
        return max(int(lnum) for lnum in self.file.get('levels', {}).get(level, {}).keys())

    def get_parsons_data_for_level(self, level, keyword_lang="en"):
        return deep_translate_keywords(self.file.get('levels', {}).get(level, None), keyword_lang)

    def get_parsons_data_for_level_exercise(self, level, excercise, keyword_lang="en"):
        return deep_translate_keywords(self.file.get('levels', {}).get(level, {}).get(excercise), keyword_lang)

class Quizzes(StructuredDataFile):
    def __init__(self, language):
        self.language = language
        super().__init__(f'content/quizzes/{self.language}.yaml')

    def get_highest_question_level(self, level):
        return max(int(k) for k in self.file.get('levels', {}).get(level, {}))

    def get_quiz_data_for_level(self, level, keyword_lang="en"):
        return deep_translate_keywords(self.file.get('levels', {}).get(level), keyword_lang)

    def get_quiz_data_for_level_question(self, level, question, keyword_lang="en"):
        return deep_translate_keywords(self.file.get('levels', {}).get(level, {}).get(question), keyword_lang)

class NoSuchQuiz:
    def get_quiz_data_for_level(self, level, keyword_lang):
        return {}

class Tutorials(StructuredDataFile):
    # Want to parse the keywords only once, they can be cached -> perform this
    # action on server start
    def __init__(self, language):
        self.language = language
        super().__init__(f'content/tutorials/{self.language}.yaml')

    def get_tutorial_for_level(self, level, keyword_lang="en"):
        if level not in ["intro", "teacher"]:
            level = int(level)
        return deep_translate_keywords(self.file.get(level, None), keyword_lang)

    def get_tutorial_for_level_step(self, level, step, keyword_lang="en"):
        if level not in ["intro", "teacher"]:
            level = int(level)
        return deep_translate_keywords(self.file.get(level, {}).get('steps', {}).get(step), keyword_lang)

class NoSuchTutorial:
    def get_tutorial_for_level(self, level, keyword_lang):
        return {}

class Slides(StructuredDataFile):
    def __init__(self, language):
        self.language = language
        super().__init__(f'content/slides/{self.language}.yaml')

    def get_slides_for_level(self, level, keyword_lang="en"):
        return deep_translate_keywords(self.file.get('levels', {}).get(level), keyword_lang)

class NoSuchSlides:
    def get_slides_for_level(self, level, keyword_lang):
        return {}

class NoSuchTags:
    def get_tags(self):
        return {}

class Tags(StructuredDataFile):
    def __init__(self, language):
        self.language = language
        super().__init__(f'content/tags/{self.language}.yaml')

    def get_tags_names(self):
        return {tid: tags['items'] for tid, tags in self.file.get('tags', {}).items()}

    def get_tags(self, keyword_lang="en"):
        return deep_translate_keywords(self.file.get('tags'), keyword_lang)

    def has_tags(self):
        return True if self.file.get('tags') else False


import sys
# caution: path[0] is reserved for script path (or '' in REPL)
sys.path.append('../../')

import pytest

# from lab4 import main
# from lab4.TEACHER_parser import arith_expr
"""Test cases for parser analyzer."""

# -----------------------------------------------------------------------------
@pytest.mark.parametrize(
    "text, expected",
    [
        ("123", 3)
    ]
)
def test_get_parser(text, expected):
    """Get the next token from text starting from pos."""
    # Arrange
    # Act
    result = arith_expr(text)
    print("result")
    print(result)
    # Assert
    # assert result == expected

test_get_parser("123", 1)


__author__ = 'Danyang'

class RandomListNode:
    def __init__(self, x):
        self.label = x
        self.next = None
        self.random = None

class Solution:
    def copyRandomList(self, head):

        dummy = RandomListNode(0)
        dummy.next = head

        pre = dummy
        while pre.next:
            cur = pre.next
            cur_copy = RandomListNode(cur.label)

            temp = cur.next
            cur.next = cur_copy
            cur_copy.next = temp

            pre = pre.next.next

        pre = dummy
        while pre.next:
            cur = pre.next

            if cur.random:
                cur.next.random = cur.random.next

            pre = pre.next.next

        pre = dummy
        head_copy = pre.next.next if pre.next else None
        while pre.next:
            cur = pre.next
            cur_copy = cur.next

            cur.next = cur_copy.next
            if cur_copy.next:
                cur_copy.next = cur_copy.next.next

            pre = pre.next

        return head_copy

data_files/138 Copy List with Random Pointer.py


import time
import cv2
import torch
from ultralytics import YOLO
import supervision as sv
from supervision.draw.color import Color
from datetime import datetime
import os
import shutil
import sys
import logging
import numpy as np

from mqtt_jetson import connect_mqtt, publish, subscribe, get_code_recu_flag

names = {0: 'btl', 1: 'main'}

version = '0.0.3'
'''
    2023-10-27 12:30
    MQTT ok :
    ping-pong + version
    bouteille ok (btl count in)
    bouteille retirée (btl count out)
    bouteille doublée
    bouteille vue
    bouteille disparue (btl disparue après scan)

    Remove camera_status flag
'''

def get_unique_filename(base_filename):
    """
    Retourne un nom de fichier basé sur base_filename qui n'existe pas encore.
    Si base_filename existe, il ajoute (1), (2), etc.
    jusqu'à ce qu'un nom de fichier unique soit trouvé.
    """
    # Séparation du nom de fichier et de l'extension
    filename, extension = os.path.splitext(base_filename)

    counter = 1
    while os.path.exists(base_filename):
        base_filename = f"{filename}({counter}){extension}"
        counter += 1

    return base_filename

def concatenate_videos(video_path1, video_path2, output_path):

    # Ouverture du premier fichier vidéo
    cap1 = cv2.VideoCapture(video_path1)
    width = int(cap1.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap1.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fps = int(cap1.get(cv2.CAP_PROP_FPS))

    # Initialisation de l'écriture de la vidéo de sortie
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))

    # L'écriture de la première vidéo
    while True:
        ret, frame = cap1.read()
        if not ret:
            break
        out.write(frame)

    cap1.release()

    # Ouverture du deuxième fichier vidéo
    cap2 = cv2.VideoCapture(video_path2)

    # Lecture et écriture du deuxième fichier vidéo
    while True:
        ret, frame = cap2.read()
        if not ret:
            break
        out.write(frame)

    cap2.release()
    out.release()

class StreamToLogger(object):
    def __init__(self, logger, log_level=logging.INFO):
        self.logger = logger
        self.log_level = log_level

    def write(self, buf):
        for line in buf.rstrip().splitlines():
            self.logger.log(self.log_level, line.rstrip())

    def flush(self):
        pass

class InfoImg:
    def __init__(self, img_type, timestamp, pos) -> None:
        self.img_type = img_type  # 'img_ini, img_fin, img_in, img_out
        self.timestamp = timestamp  # Timestamp de l'image
        self.pos = pos  # Position de l'objet dans l'image

    def printout(self):
        logging.info("-------- image info presents as follow --------")
        logging.info("obj stats - %s", self.img_type)
        logging.info("image  at - %s", self.timestamp)
        logging.info("obj is at - %s", self.pos)

class Objet:
    def __init__(self, class_id, id, pos_ini):
        global names
        self.class_id = names[class_id]
        self.id = id
        self.img_ini = InfoImg(img_type = "img_ini",
                               timestamp = datetime.now(),
                               pos = pos_ini)
        self.img_fin = InfoImg(img_type = "img_fin",
                               timestamp = datetime.now(),
                               pos = pos_ini)
        self.img_in_out = []

class InfoVideo:
    def __init__(self, tab):
        self.tab = tab
        self.tmp_ini = datetime.now()
        self.tmp_fin = datetime.now()
        self.obj_ids = set()
        self.obj = []

    @property
    def obj_nbs(self):
        return len(self.obj_ids)

    def printout(self):
        logging.info("---------------- video info presents as follow ----------------")
        logging.info("start  at - %s", self.tmp_ini)
        logging.info("finish at - %s", self.tmp_fin)
        logging.info("id of obj - %s", self.obj_ids)
        logging.info("nb of obj - %d", self.obj_nbs)
        for i, item in enumerate(self.obj_ids):
            logging.info("class obj - %s", self.obj[self.tab[item]].class_id)
            logging.info("id of obj - %d", self.obj[self.tab[item]].id)
            self.obj[self.tab[item]].img_ini.printout()
            self.obj[self.tab[item]].img_fin.printout()
            if len(self.obj[self.tab[item]].img_in_out):
                for j, img in enumerate(self.obj[self.tab[item]].img_in_out):
                    img.printout()
            else:
                logging.info("obj stats - Cette %s n'a jamais franchi", self.obj[self.tab[item]].class_id)

        logging.info("---------------------------------------------------------------")

# onction principale
def main(model_path, video_path, device, save_dir, show_flag, mqtt_flag):

    # Accès à la variable globale names
    global names, version

    # -------------------------------- zone definition -------------------------------- #
    # Configuration de la connexion MQTT si le flag mqtt est activé
    if mqtt_flag:
        # Adresse du courtier MQTT (adresse IP de connection vers la bbot)
        broker = '192.168.1.163'
        port = 1883 # Port défaut du courtier MQTT
        # Sujets pour la publication et l'abonnement
        topic_pub = "bbot/ihm"
        topic_sub = "bbot/ia"
        topic_pin = "bbot/ping"
        topic_pon = "bbot/pong"

        client_id = f'Jetson'
        # initialise mqtt client connection
        client = connect_mqtt(broker, port, client_id)
        subscribe(client, topic_sub, topic_pin, topic_pon, version)
        client.loop_start() # Démarrer la boucle MQTT

    # Journalisation des informations sur le modèle, la vidéo et le dispositif
    logging.info("yolov8 info - %s", model_path)
    logging.info("video input - %s", video_path)
    logging.info("device info - %s", device)
    logging.info("demos saved - %s", save_dir)

    # Configuration de l'entrée du modèle (flux vidéo de ffmpeg sur jetson)
    url = video_path
    cap = cv2.VideoCapture(url)

    # Si la caméra est perdue, reboot le jetson automatiquement
    if not cap.isOpened():
        raise ValueError(f"Failed to open video source: {url}, rebooting")

    frame_width  = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    frame_fps = int(cap.get(cv2.CAP_PROP_FPS))
    logging.info("stream  fps - %d", frame_fps)
    cap.release()
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    frame_fps = 30 if frame_fps == 90000 else frame_fps

    # Définition de la ligne séparation, taille de l'image originale (640, 480)
    LINE_START = sv.Point(240, 0)
    LINE_END = sv.Point(240, 480)

    # Définition du compteur et annotator liés à la ligne
    line_counter = sv.LineZone(start = LINE_START, end = LINE_END)
    line_color = Color(r = 224, g = 57, b = 151)
    line_annotator = sv.LineZoneAnnotator(thickness = 2, text_thickness = 2, text_scale = 1,
                                          color = line_color, text_offset = 10)
    box_annotator = sv.BoxAnnotator(thickness = 2, text_thickness = 1, text_scale = 0.5)

    # Construction du modèle YOLO, en important les paramètres
    model = YOLO(model_path, task = 'detect')

    # Initialisation des variables modifiables dans la boucle d'analyse
    in_count  = line_counter.in_count
    out_count = line_counter.out_count

    frame_length = 0
    tab = {}
    frame_avant = []
    frame_apres = []
    fix_length = 10
    is_ending  = fix_length

    dbl_btl_flag = False
    fst_btl_triche = False
    img_in_exit = False
    img_ini_tmp = datetime(1999, 10, 11)
    img_ini_pos = [0, 0, 0, 0]
    img_max_pos = 0
    filename_previous = None
    # -------------------------------- zone definition -------------------------------- #

    # Boucle d'analyse pour traiter chaque résultat de détection sur l'image
    for result in model.track(url, imgsz = 128, conf = 0.4, device = device,
                              half = False, stream = True, persist = True,
                              show = show_flag, verbose = False, tracker = "./bytetrack_stream.yaml"):

        # count frame number from the second image
        # to calculate treatment fps at the end of for lop
        if frame_length == 2:
            start_time = time.time()
        # elif frame_length == 600:
        #     break
        frame_length = frame_length + 1

        # save current pure img to frame0
        # prepare for img lists : 'frame_apres' and 'frame_avant'
        frame0 = result.orig_img

        # check no object detected or no btl(cls==0) detected
        # save these no objet frames {fix_length} as the begining or the end of one video
        # check whether to cut and save the current recording video, once no btl for {fix_length} consecutive images
        if result.boxes.id is None or result.boxes.id[(result.boxes.cls == 0).nonzero()] is None:

            # check if video 'out' is being recorded
            try:
                is_opened = out.isOpened()

            except UnboundLocalError:
                # video 'out' not defined yet
                # video not being recorded
                pass

            # video 'out' is being recorded
            # save {fix_length = 10} images into frame_apres
            # make {fix_length} minus 1, which makes {fix_length} rest images to be added into 'frame_après'
            else:
                if is_opened and is_ending > 0:
                    is_ending -= 1
                    frame_apres.append(frame0)

                # split video once no btl for {fix_length} consecutive images
                if is_opened and is_ending == 0:

                    # write {fix_length = 10} images into 'out' and 'out_org' video
                    for frame_a in frame_apres:
                        out.write(frame_a)
                        out_org.write(frame_a)

                    is_ending = fix_length
                    frame_apres = []

                    # video cut by image vide
                    # logging.info('video is saved as %s', filename)
                    logging.info('video cut by img vide')
                    out.release()
                    out_org.release()

                    # check if this video contains double bouteille triche
                    if dbl_btl_flag == True:

                        if mqtt_flag:
                            publish(client, topic_pub, msg = f"dbl btl-{filename_previous}-{datetime.now()}")

                        logging.info("TRICHE POTENTIELLE 1: %s", filename_previous)
                        logging.info("TRICHE POTENTIELLE 2: %s", filename)

                        video_path1 = filename_previous + '.mp4'
                        video_path2 = filename + '.mp4'
                        output_path = filename[0:len(save_dir)+1] + 'triche/' + filename[len(save_dir)+1:] + '.mp4'
                        concatenate_videos(video_path1, video_path2, output_path)

                    # check if this video contains bouteille disparue triche
                    # print('xy', np.min(detections[detections.class_id == 0].xyxy[:, [0, 2]]))
                    # print('code', get_code_recu_flag())
                    # print('img in', img_in_exit)
                    if get_code_recu_flag() and np.min(detections[detections.class_id == 0].xyxy[:, [0, 2]])<= 240 and img_in_exit == False:
                        get_code_recu_flag(change = True)
                        if mqtt_flag:
                            publish(client, topic_pub, msg = f"btl disparue-{datetime.now()}")
                        logging.info("1ERE BTL DISPARUE   : %s", filename)

                    info_video.tmp_fin = datetime.now()
                    # info_video.printout()

                    filename_previous = filename
                    filename = None
                    tab = {}
                pass

        # Objet(s) de cls==0 détectés par le modèle
        else:

            # Copie de frame originale, pour l'ajout des annotations sur l'image
            frame = frame0.copy()

            detections = sv.Detections.from_yolov8(result)
            detections.tracker_id = result.boxes.id.numpy().astype(int)

            # Ne suivre que les objets de (detections.class_id == 0)
            line_counter.trigger(detections = detections[detections.class_id == 0])

            # Ajout des annotations sur l'image
            labels = [f"#{tracker_id} {names[class_id]} {confidence:0.2f}"
                        for _, _, confidence, class_id, tracker_id in detections]
            frame = box_annotator.annotate(scene = frame, detections = detections, labels = labels)
            line_annotator.annotate(frame = frame, line_counter = line_counter)

            # check if reach the fin de video
            # separte video if new btl id is found in detections.tracker_id
            if len(tab) != 0 and set(tab.keys()).isdisjoint(set(detections.tracker_id)):

                # write {fix_length = 10} images into 'out' and 'out_org' video
                for frame_a in frame_apres:
                    out.write(frame_a)
                    out_org.write(frame_a)

                is_ending = fix_length
                frame_apres = []

                # video cut by new btl introduite
                # logging.info('video is saved as %s', filename)
                logging.info('video cut by new btl id found')
                out.release()
                out_org.release()

                # check if this video contains double bouteille triche
                if dbl_btl_flag == True:

                    if mqtt_flag:
                        publish(client, topic_pub, msg = f"dbl btl-{filename_previous}-{datetime.now()}")

                    logging.info("TRICHE POTENTIELLE 1: %s", filename_previous)
                    logging.info("TRICHE POTENTIELLE 2: %s", filename)

                    video_path1 = filename_previous + '.mp4'
                    video_path2 = filename + '.mp4'
                    output_path = filename[0:len(save_dir)+1] + 'triche/' + filename[len(save_dir)+1:] + '.mp4'
                    concatenate_videos(video_path1, video_path2, output_path)

                info_video.tmp_fin = datetime.now()
                # info_video.printout()

                filename_previous = filename
                filename = None
                tab = {}

            # début de video, car tab n'existe jamais ou tab est initalisé à vide set
            if len(tab) == 0 and len(detections[detections.class_id == 0].xyxy) > 0:
                current_time = datetime.now()
                # Création de fichier vidéo
                filename = (save_dir + '/'
                            + str(current_time.strftime('%Y%m%d%H%M%S')) + str(current_time.microsecond)[:3]
                            + '_btl_' + str(detections.tracker_id[0]))

                if mqtt_flag:
                    publish(client, topic_pub, msg = f"btl vue-{filename}-{current_time}")

                out = cv2.VideoWriter(filename + '.mp4', fourcc = fourcc,
                                      fps = frame_fps, frameSize = (frame_width, frame_height))

                filename_org = (save_dir + '/org/'
                            + str(current_time.strftime('%Y%m%d%H%M%S')) + str(current_time.microsecond)[:3]
                            + '_org_' + str(detections.tracker_id[0]))
                out_org = cv2.VideoWriter(filename_org + '.mp4', fourcc = fourcc,
                                      fps = frame_fps, frameSize = (frame_width, frame_height))

                # write {fix_length = 10} images into 'out' and 'out_org' video
                for frame_a in frame_avant:
                    out.write(frame_a)
                    out_org.write(frame_a)

                # Création des infos à noter concernant la vidéo et les objets détectés
                info_video = InfoVideo(tab)
                info_video.obj_ids.update(detections.tracker_id)
                for index, id in enumerate(detections.tracker_id):
                    info_video.obj.append(Objet(detections.class_id[index], id, detections.xyxy[index]))
                    tab[id] = len(info_video.obj) - 1

                # Vérifie si la vidéo contient une double bouteille triche
                fst_btl_triche = False
                dbl_btl_flag = False
                # print(abs(img_ini_tmp - datetime.now()).total_seconds())
                # print(img_in_exit)
                # print(img_ini_pos)
                if min(img_ini_pos[0], img_ini_pos[2]) <= 240 and img_max_pos >= 100 and abs(img_ini_tmp - datetime.now()).total_seconds() <= 3 and img_in_exit == False:
                    fst_btl_triche = True
                else:
                    fst_btl_triche = False

                img_ini_tmp = datetime.now()
                img_ini_pos = detections[detections.class_id == 0].xyxy[0]
                fst_btl_id  = detections[detections.class_id == 0].tracker_id[0]
                if len(detections[detections.class_id == 0].tracker_id) > 1:
                    for index, id in enumerate(detections[detections.class_id == 0].tracker_id):
                        if min(detections[detections.class_id == 0].xyxy[index][0], detections[detections.class_id == 0].xyxy[index][2]) <= min(img_ini_pos[0], img_ini_pos[2]):
                            img_ini_pos = detections[detections.class_id == 0].xyxy[index]
                            fst_btl_id  = id
                img_max_pos = max(img_ini_pos[0], img_ini_pos[2])
                # print(img_ini_pos)
                img_in_exit = False

            # enregistrement de video en cours / fichier "out" déjà existé
            else:
                is_ending = fix_length
                frame_apres = []
                if_existed = [id in info_video.obj_ids for id in detections.tracker_id]
                info_video.obj_ids.update(detections.tracker_id)

                for index, item in enumerate(if_existed):
                    # pour les ids jamais vus
                    if not item:
                        # info_video.obj_ids.update(detections.tracker_id)
                        info_video.obj.append(Objet(detections.class_id[index], detections.tracker_id[index], detections.xyxy[index]))
                        tab[detections.tracker_id[index]] = len(info_video.obj) - 1
                    # pour les ids déjà existés
                    else:
                        current_id = detections.tracker_id[index]
                        info_video.obj[tab[current_id]].img_fin = InfoImg(img_type = "img_fin",
                                                                          timestamp = datetime.now(),
                                                                          pos = detections.xyxy[index])
                        if current_id == fst_btl_id:
                            pos = detections.xyxy[index]
                            img_max_pos = max(img_max_pos, max(pos[0], pos[2]))

            out.write(frame)
            out_org.write(frame0)

            # Comparer le compteur d'entrées actuel avec le précédent
            if in_count != line_counter.in_count:
                img_in_exit = True

                # Mettre à jour le compte d'entrée
                in_count = line_counter.in_count
                if mqtt_flag:
                    publish(client, topic_pub, msg = f"btl ok-{datetime.now()}")
                logging.info("count numbers in  %d", line_counter.in_count)
                info_video.obj[tab[detections.tracker_id[0]]].img_in_out.append(InfoImg("img_in",
                                                                                datetime.now(),
                                                                                detections.xyxy[0]))
                current_time = datetime.now()
                file_img = (save_dir + '/img/'
                            + str(current_time.strftime('%Y%m%d%H%M%S')) + str(current_time.microsecond)[:3]
                            + '_btl_' + str(detections.tracker_id[0]))
                cv2.imwrite(file_img + '_in.png', frame)

                if fst_btl_triche == True:
                    fst_btl_triche = False
                    dbl_btl_flag = True

            # Comparer le compteur de sorties actuel avec le précédent
            elif out_count != line_counter.out_count:
                img_in_exit = True

                out_count = line_counter.out_count
                if mqtt_flag:
                    publish(client, topic_pub, msg = f"btl retirée-{datetime.now()}")
                logging.info("count numbers out %d", line_counter.out_count)
                info_video.obj[tab[detections.tracker_id[0]]].img_in_out.append(InfoImg("img_out",
                                                                                datetime.now(),
                                                                                detections.xyxy[0]))
                current_time = datetime.now()
                file_img = (save_dir + '/img/'
                            + str(current_time.strftime('%Y%m%d%H%M%S')) + str(current_time.microsecond)[:3]
                            + '_btl_' + str(detections.tracker_id[0]))
                cv2.imwrite(file_img + '_out.png', frame)

        # enregistrement des images avant la video
        frame_avant.append(frame0)
        while len(frame_avant) > fix_length:
            frame_avant.pop(0)

    try:
        is_opened = out.isOpened()
    except UnboundLocalError:
        # print("Video 'out' is not defined yet")
        pass
    else:
        if is_opened:
            for frame_a in frame_apres:
                out.write(frame_a)
                out_org.write(frame_a)

            is_ending = fix_length
            frame_apres = []

            # logging.info('video is saved as %s', filename)
            logging.info('video cut by end of for loop')

            out.release()
            out_org.release()

            # check if this video contains double bouteille triche
            if dbl_btl_flag == True:

                if mqtt_flag:
                    publish(client, topic_pub, msg = f"dbl btl-{filename_previous}-{datetime.now()}")

                logging.info("TRICHE POTENTIELLE 1: %s", filename_previous)
                logging.info("TRICHE POTENTIELLE 2: %s", filename)

                video_path1 = filename_previous + '.mp4'
                video_path2 = filename + '.mp4'
                output_path = filename[0:len(save_dir)+1] + 'triche/' + filename[len(save_dir)+1:] + '.mp4'
                concatenate_videos(video_path1, video_path2, output_path)

            info_video.tmp_fin = datetime.now()
            # info_video.printout()

            filename_previous = filename
            filename = None
            # id_tracking = -1
            tab = {}
        else:
            pass

    end_time = time.time()
    total_time = end_time - start_time
    if mqtt_flag:
        client.loop_stop()
    logging.info('time passed       %.2f', total_time)
    logging.info('time on one frame %.2f', total_time/frame_length)
    logging.info('processing fps    %.2f', frame_length/total_time)
    logging.info('total numbers in  %d', line_counter.in_count)
    logging.info('total numbers out %d', line_counter.out_count)

if __name__ == '__main__':

    # Si CUDA est disponible, on est sur jetson
    if torch.cuda.is_available():
        # Sur jetson le modèle est accéléré par TensorRT engine
        model_path = './weights/bestv8img128all.engine'
        device = torch.device(0)

        video_path = 'rtsp://localhost:8554/entrance'
        # video_path = './video/faux.mp4'
        # video_path = './video/vrai.mp4'

        show_flag = False
        mqtt_flag = True

    # Inférence du modèle sur l'ordi local
    else:
        model_path = './weights/bestv8img128all.pt'
        device = 'cpu'
        # Utiliser le CPU si CUDA n'est pas disponible

        show_flag = True
        mqtt_flag = False

        video_path = './00323-M/run2/db/202309151402_bbot00034_btl_dcp.mp4'
        video_path = './00323-M/run2/db/faux.mp4'

    # Enregistrement des résultats de toutes les exécutions du modèle dans ./demo
    dir = './demo'
    os.makedirs(dir, exist_ok=True)
    dir_list = [d for d in os.listdir(dir) if os.path.isdir(os.path.join(dir, d)) and d.startswith('run')]

    max_number = -1
    if len(dir_list):
        for folder in dir_list:
            try:
                number = int(folder[3:])
                if number > max_number:
                    max_number = number
            except ValueError:
                pass

    # Pour chaque exécution, on crée un nouveau dossier 'run'
    save_dir = dir + '/run' + str(max_number + 1)
    # Dans 'run', on crée les dossiers /org, /img, /triche
    os.makedirs(save_dir)
    # Enregistrement des images au moment de btl passage
    os.makedirs(save_dir + '/img')
    # Enregistrement des vidéos de triche (double bouteilles)
    os.makedirs(save_dir + '/triche')
    # Enregistrement des vidéos pures de triche
    os.makedirs(save_dir + '/org')

    # Configuration de logging système
    logging.basicConfig(level=logging.INFO)
    log_format = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')

    log_file_handler = logging.FileHandler(save_dir + "/log.txt")
    log_file_handler.setFormatter(log_format)
    logging.getLogger().addHandler(log_file_handler)

    stdout_logger = logging.getLogger('STDOUT')
    sl = StreamToLogger(stdout_logger, logging.INFO)
    sys.stdout = sl

    stderr_logger = logging.getLogger('STDERR')
    sl = StreamToLogger(stderr_logger, logging.ERROR)
    sys.stderr = sl

    main(model_path, video_path, device, save_dir, show_flag, mqtt_flag)


#!/usr/bin/env python

"""
Calculates diffusion coefficient using Einstein relation from LAMMPS trajectory files.
"""

import os
import re
import glob

import numpy as np
import pandas as pd
import seaborn as sns
import statsmodels.api as sm
import matplotlib.pyplot as plt

from matplotlib.ticker import ScalarFormatter

from pymatgen.io.lammps.outputs import parse_lammps_log, parse_lammps_dumps

from mdproptools.common import constants
from mdproptools.common.com_mols import calc_com
from mdproptools.utilities.plots import set_axis

__author__ = "Rasha Atwi, Matthew Bliss"
__maintainer__ = "Rasha Atwi"
__email__ = "rasha.atwi@stonybrook.edu"
__status__ = "Development"
__date__ = "Sep 2021"
__version__ = "0.0.4"

class Diffusion:
    def __init__(self, timestep=1, units="real", outputs_dir=None, diff_dir=None):
        self.units = units
        if self.units not in constants.SUPPORTED_UNITS:
            raise KeyError(
                "Unit type not supported. Supported units are: "
                + str(constants.SUPPORTED_UNITS)
            )
        self.outputs_dir = outputs_dir or os.getcwd()
        self.diff_dir = diff_dir or os.getcwd()
        self.timestep = timestep

    @staticmethod
    def _prepare_unwrapped_coords(dump):
        # Ensure access to unwrapped coordinates, may want to add scaled
        # coordinates in future ('sx', 'sxu', etc.)
        if "xu" and "yu" and "zu" not in dump.data.columns:
            assert (
                "x" and "y" and "z" in dump.data.columns
            ), "Missing wrapped and unwrapped coordinates (x y z xu yu zu)"
            assert "ix" and "iy" and "iz" in dump.data.columns, (
                "Missing unwrapped coordinates (xu yu zu) and box location ("
                "ix iy iz) for converting wrapped coordinates (x y z) into "
                "unwrapped coordinates. "
            )
            box_x_length = dump.box.bounds[0][1] - dump.box.bounds[0][0]
            box_y_length = dump.box.bounds[1][1] - dump.box.bounds[1][0]
            box_z_length = dump.box.bounds[2][1] - dump.box.bounds[2][0]
            dump.data["xu"] = dump.data["x"].add(dump.data["ix"].multiply(box_x_length))
            dump.data["yu"] = dump.data["y"].add(dump.data["iy"].multiply(box_y_length))
            dump.data["zu"] = dump.data["z"].add(dump.data["iz"].multiply(box_z_length))
        return dump

    def _calculate_type_com(self, df, ind):
        return df.groupby(ind).apply(
            lambda x: pd.Series(
                x["mass"].values @ x[["xu", "yu", "zu"]].values / x.mass.sum(),
                index=["xu", "yu", "zu"],
            )
        )

    def _modify_dump_coordinates(self, msd_df):
        ref_com = self._calculate_type_com(msd_df.xs(0, 0), ["type"])
        com = self._calculate_type_com(msd_df, ["Time (s)", "type"])
        drift = com - com[[]].join(ref_com)
        msd_df.loc[:, ["xu", "yu", "zu"]] -= drift
        return msd_df

    def detect_linear_region():
        pass

    def get_msd_from_dump(
        self,
        filename,
        tao_coeff=4,
        msd_type="com",
        num_mols=None,
        num_atoms_per_mol=None,
        mass=None,
        com_drift=False,
        avg_interval=False,
    ):
        dumps = parse_lammps_dumps(f"{self.outputs_dir}/{filename}")
        msd_dfs = []
        for dump in dumps:
            assert "id" in dump.data.columns, "Missing atom id's in dump file."
            dump.data = dump.data.sort_values(by=["id"])
            dump.data.reset_index(inplace=True)
            dump = self._prepare_unwrapped_coords(dump)
            if msd_type == "allatom":
                df = dump.data[["id", "xu", "yu", "zu"]]
                msd_dfs.append(df)
                id_cols = ["id"]
                col_1d = ["Time (s)"]
            elif msd_type == "com":
                df = calc_com(
                    dump,
                    num_mols,
                    num_atoms_per_mol,
                    mass,
                    atom_attributes=["xu", "yu", "zu"],
                )
                # convert to SI units
                df["mass"] = df["mass"] * constants.MASS_CONVERSION[self.units]
                df.reset_index(inplace=True)
                msd_dfs.append(df)
                id_cols = ["type", "mol_id"]
                col_1d = ["Time (s)", "type"]
            # convert to SI units
            df.loc[:, "xu"] = df["xu"] * constants.DISTANCE_CONVERSION[self.units]
            df.loc[:, "yu"] = df["yu"] * constants.DISTANCE_CONVERSION[self.units]
            df.loc[:, "zu"] = df["zu"] * constants.DISTANCE_CONVERSION[self.units]
            df["Time (s)"] = (
                dump.timestep * self.timestep * constants.TIME_CONVERSION[self.units]
            )
        msd_df = pd.concat(msd_dfs).set_index(["Time (s)"] + id_cols).sort_index()
        if msd_type == "com" and com_drift:
            msd_df = self._modify_dump_coordinates(msd_df)
        coords = ["xu", "yu", "zu"]
        disps = ["dx2", "dy2", "dz2"]
        msd_all = msd_df.copy()
        ref_df = msd_all.xs(0, 0)
        msd_all[disps] = (msd_all[coords] - msd_all[[]].join(ref_df[coords])) ** 2
        msd_all["msd"] = msd_all[disps].sum(axis=1)
        one_d_cols = disps + ["msd"]
        msd_all = msd_all[one_d_cols]
        msd = msd_all.groupby(col_1d).mean()
        if msd_type == "com":
            msd = msd.reset_index().pivot(columns="type", index="Time (s)")
            msd = msd.sort_index(axis=1, level=1)
            msd.columns = ["".join([str(i) for i in v]) for v in msd.columns.values]
        msd.reset_index(inplace=True)
        msd_all.reset_index(inplace=True)
        if avg_interval:
            times = list(msd_df.index.levels[0])
            times = times[::tao_coeff]
            msd_df = msd_df[msd_df.index.get_level_values(0).isin(times)]
            msd_df[disps] = (
                msd_df[coords]
                .groupby(id_cols)
                .transform(lambda x: (x - x.shift(1)) ** 2)
            )
            msd_df.drop(0, level=0)
            msd_df["msd"] = msd_df[disps].sum(axis=1)
            msd_df = msd_df[one_d_cols]
            msd_int = msd_df.groupby(id_cols).mean().reset_index()
            return msd, msd_all, msd_int
        return msd, msd_all

    def get_msd_from_log(self, log_pattern):
        log_files = glob.glob(f"{self.outputs_dir}/{log_pattern}")
        # based on pymatgen.io.lammps.outputs.parse_lammps_dumps function
        if len(log_files) > 1:
            pattern = r"%s" % log_pattern.replace("*", "([0-9]+)")
            pattern = ".*" + pattern.replace("\\", "\\\\")
            log_files = sorted(log_files, key=lambda f: int(re.match(pattern, f).group(1)))
        list_log_df = []
        for file in log_files:
            log_df = parse_lammps_log(file)
            list_log_df.append(log_df[0])
        for p, l in enumerate(list_log_df[:-1]):
            list_log_df[p] = l[:-1]
        full_log = pd.concat(list_log_df, ignore_index=True)
        msd = full_log.filter(regex="msd")
        for col in msd:
            msd.loc[:, col] = msd[col] * constants.DISTANCE_CONVERSION[self.units] ** 2
        msd["Time (s)"] = (
            full_log["Step"] * self.timestep * constants.TIME_CONVERSION[self.units]
        )
        return msd

    def calc_diff(
        self,
        msd,
        initial_time=None,
        final_time=None,
        dimension=3,
        diff_names=None,
        save=False,
        plot=False,
    ):
        # initial and final time in seconds
        if initial_time is None:
            initial_time = {}
        if final_time is None:
            final_time = {}
        min_t = min(msd["Time (s)"])
        max_t = max(msd["Time (s)"])
        msd_col_names = [col for col in msd.columns if "msd" in col.lower()]
        diff = np.zeros((len(msd_col_names), 3))
        models = []
        counter = 0
        for ind, col in enumerate(msd_col_names):
            msd_df = msd[
                (msd["Time (s)"] >= initial_time.get(counter, min_t))
                & (msd["Time (s)"] <= final_time.get(counter, max_t))
            ]
            counter += 1
            model = sm.OLS(msd_df[col], msd_df["Time (s)"]).fit()
            models.append(model)
            diff[ind] = [
                model.params[0] / (2 * dimension),
                model.bse[0] / (2 * dimension),
                model.rsquared,
            ]
            if save:
                if diff_names:
                    diff_file = f"{self.diff_dir}/diff_{diff_names[ind]}.txt"
                else:
                    diff_file = f"{self.diff_dir}/diff_{ind + 1}.txt"
                with open(diff_file, "w") as f:
                    f.write(str(model.summary()))
        ind = diff_names or [i + 1 for i in range(len(msd_col_names))]
        diffusion = pd.DataFrame(
            diff,
            columns=["diffusion (m2/s)", "std", "R2"],
            index=ind,
        )

        if plot:
            paired = plt.get_cmap("Paired")
            colors = iter(paired(np.linspace(0, 1, 10)))
            ncols = 2
            nrows = int(np.ceil(len(msd_col_names) / ncols))
            fig, axes = plt.subplots(nrows, ncols, figsize=(12, 8))
            fig_log, axes_log = plt.subplots(nrows, ncols, figsize=(12, 8))
            time_data = msd["Time (s)"] * 10**9
            for i, (ax, ax_log, col) in enumerate(
                zip(axes.flatten(), axes_log.flatten(), msd_col_names)
            ):
                color = next(colors)
                y_formatter = ScalarFormatter(useOffset=False)

                # normal plot
                pred = models[i].predict()
                ax.plot(
                    time_data,
                    msd[col],
                    color=color,
                    linewidth=2,
                    label=ind[i],
                )
                ax.plot(time_data, pred, color="k", ls="--", linewidth=2)
                ax.locator_params(axis="y", nbins=6)
                # log plot
                st_line = 10 ** (np.log10(msd[col].max()) - np.log10(time_data.max()))
                ax_log.plot(
                    time_data,
                    msd[col],
                    color=color,
                    linewidth=2,
                    label=ind[i],
                )
                ax_log.plot(
                    time_data, time_data * st_line, color="k", ls="--", linewidth=2
                )
                ax_log.set(xscale="log", yscale="log")

                for axis in [ax, ax_log]:
                    set_axis(axis, axis="both")
                    axis.legend(
                        fontsize=16,
                        frameon=False,
                    )
                    axis.set_xlabel(r"$\mathrm{Time, 10^9 (s)}$", fontsize=18)
                    axis.set_ylabel(r"$\mathrm{MSD\ (m^2)}$", fontsize=18)
                    axis.yaxis.set_major_formatter(y_formatter)
                    axis.yaxis.offsetText.set_fontsize(18)

            for figure, axis, name in zip(
                [fig, fig_log], [axes, axes_log], ["msd.png", "msd_log.png"]
            ):
                if len(msd_col_names) % 2 != 0:
                    figure.delaxes(ax=axis.flatten()[-1])
                figure.tight_layout()
                figure.savefig(
                    f"{self.diff_dir}/{name}",
                    bbox_inches="tight",
                    pad_inches=0.1,
                )

        diffusion.to_csv(f"{self.diff_dir}/diffusion.csv")
        print("Diffusion results written to a .csv file.")
        return diffusion

    def get_diff_dist(
        self, msd_int, dump_freq, dimension=3, tao_coeff=4, plot=False, diff_names=None
    ):
        delta = dump_freq * self.timestep * constants.TIME_CONVERSION[self.units]
        msd_int["diff"] = msd_int["msd"] / (2 * dimension * tao_coeff * delta)
        msd_int.to_csv(f"{self.diff_dir}/msd_int.csv")
        if plot:
            paired = plt.get_cmap("Paired")
            colors = iter(paired(np.linspace(0, 1, 10)))
            if "type" in msd_int.columns:
                # if data is available for the com of each molecule type
                groups = msd_int.groupby("type")
                ind = diff_names or [i + 1 for i in range(len(groups))]
                ncols = 2
                nrows = int(np.ceil(groups.ngroups / ncols))
                fig, axes = plt.subplots(nrows, ncols, figsize=(12, 8))
                for ax, (key, grp) in zip(axes.flatten(), groups):
                    color = next(colors)
                    set_axis(ax, axis="both")
                    sns.histplot(
                        grp["diff"] * 10**9,
                        bins="sqrt",
                        color=color,
                        edgecolor="k",
                        label=ind[key - 1],
                        kde=True,
                        stat="density",
                        ax=ax,
                    )
                    ax.legend(
                        fontsize=16,
                        frameon=False,
                    )
                    ax.set_xlabel(
                        r"$\mathrm{Diffusivity, 10^{-9}\ (m^2/s)}$", fontsize=18
                    )
                    ax.set_ylabel(
                        "Frequency",
                        fontsize=18,
                    )
                    ax.xaxis.get_major_ticks()[1].label1.set_visible(False)
                    ax.xaxis.set_major_formatter(ScalarFormatter())
                    y_formatter = ScalarFormatter(useOffset=False)
                    ax.yaxis.set_major_formatter(y_formatter)
                    ax.yaxis.offsetText.set_fontsize(18)
                    ax.locator_params(axis="y", nbins=6)
                if len(groups) % 2 != 0:
                    fig.delaxes(ax=axes.flatten()[-1])
                fig.tight_layout()
                fig.savefig(
                    f"{self.diff_dir}/diff_dist.png",
                    bbox_inches="tight",
                    pad_inches=0.1,
                )
            else:
                # if data is available for all atoms
                fig, ax = plt.subplots(figsize=(8, 6))
                set_axis(ax, axis="both")
                sns.histplot(
                    msd_int["diff"] * 10**9,
                    bins="sqrt",
                    color=next(colors),
                    edgecolor="k",
                    kde=True,
                    stat="density",
                    ax=ax,
                )
                ax.set_xlabel(r"$\mathrm{Diffusivity, 10^{-9}\ (m^2/s)}$", fontsize=18)
                ax.set_ylabel(
                    "Frequency",
                    fontsize=18,
                )
                ax.xaxis.get_major_ticks()[1].label1.set_visible(False)
                ax.xaxis.set_major_formatter(ScalarFormatter())
                y_formatter = ScalarFormatter(useOffset=False)
                ax.yaxis.set_major_formatter(y_formatter)
                ax.yaxis.offsetText.set_fontsize(18)
                ax.locator_params(axis="y", nbins=6)
                fig.tight_layout()
                fig.savefig(
                    f"{self.diff_dir}/diff_dist.png",
                    bbox_inches="tight",
                    pad_inches=0.1,
                )


import  threading

g_number=0

def work1(lock):
    global g_number
    for i in range(1000000):
        # 尝试获取并且加锁　如果没有被锁定　就可以被我锁定；
        # 如果已经被锁定　阻塞等待　直到成功获取并且锁定
        lock.acquire()
        g_number += 1

        # 释放锁资源　解锁　　未锁定　－－－－> 未锁定状态
        lock.release()
        # print(g_number)

def work2(lock):
    global g_number
    for i in range(1000000):
        # 尝试获取并且加锁　如果没有被锁定　就可以被我锁定；
        # 如果已经被锁定　阻塞等待　直到成功获取并且锁定
        lock.acquire()
        g_number += 1

        # 释放锁资源　解锁　　未锁定　－－－－> 未锁定状态
        lock.release()
        # print(g_number)

if __name__=='__main__':
    g_lock=threading.Lock()

    wo1_thd=threading.Thread(target=work1,args=(g_lock,))
    wo1_thd.start()

    wo2_thd = threading.Thread(target=work2, args=(g_lock,))
    wo2_thd.start()

    wo1_thd.join()
    wo2_thd.join()
    print("最终数据是：%d"%(g_number))


import logging

from edx_rest_api_client.client import EdxRestApiClient
from edx_rest_api_client.exceptions import HttpClientError

logger = logging.getLogger(__name__)

class CourseStructureApiClient(EdxRestApiClient):
    """
    This class is a sub-class of the edX Rest API Client
    (https://github.com/edx/edx-rest-api-client).

    Details about the API itself can be found at
    https://openedx.atlassian.net/wiki/display/AN/Course+Structure+API.
    """
    DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"

    def __init__(self, url, access_token, timeout):
        super(CourseStructureApiClient, self).__init__(url, oauth_access_token=access_token, timeout=timeout)

    @property
    def all_courses(self):
        courses = []
        page = 1

        while page:
            try:
                logger.debug('Retrieving page %d of course info...', page)
                response = self.courses.get(page=page, page_size=100)
                course_details = response['results']

                courses += course_details

                if response['next']:
                    page += 1
                else:
                    page = None
                    logger.debug('Completed retrieval of course info. Retrieved info for %d courses.', len(courses))
            except HttpClientError as e:
                logger.error("Unable to retrieve course data: %s", e)
                page = None
                break

        return courses


from django.http import HttpResponse
from django.template import loader

from django.views.generic import TemplateView

class MyTemplateView(TemplateView):
    template_name = 'post_page.html'

    def get_context_data(self, **kwargs):
        return {'latest_question_list': [{'id': 1,
                                          'question_text': 'Бути чи не бути?'},
                                         {'id': 2,
                                          'question_text': 'Boxer or wrestler?'},
                                         {'id': 3,
                                          'question_text': 'Russia is parasha?'},
                                         {'id': 4,
                                          'question_text': 'Russia is terrorist?'},
                                         {'id': 5,
                                          'question_text': None}]}

# def index_post(request):
#     latest_question_list =
#     template = loader.get_template('post_page.html')
#     context = {'latest_question_list': latest_question_list}
#     return HttpResponse(template.render(context, request))

def post_page(request, number):
    if number == 1:
        return HttpResponse('Слова з монологу Гамлета, героя трагедії "Гамлет, принц Датський" (1601) великого '
                            'англійського драматурга В. Шекспіра. У переносному вживанні означають: найважливіше '
                            'питання, питання життя і смерті.')
    elif number == 2:
        return HttpResponse('The answer depends on many factors, but its fair to say that wrestlers have better '
                            'chances of beating boxers. Of course, there are hundreds of different scenarios where '
                            'the fight can go both ways. But overall, wrestlers are superior to boxers for one simple '
                            'reason')
    elif number == 3:
        return HttpResponse('Yes, 100%')
    else:
        return HttpResponse('Another question!!!')


import argparse
import inspect
import typing

# `CommonConfigArgs` used for inheritance.
from .configargs import CommonConfigArgs
# `Logger` used for inheritance.
from .logger import Logger
# `CommonLoggingArgs` used for inheritance.
from .loggingargs import CommonLoggingArgs

class Args(Logger, CommonConfigArgs, CommonLoggingArgs):
    """
    Common program arguments management.

    Handles:

    - ``--help`` option,
    - Configuration file options,
    - Logging options.
    """

    #: Main instance of :class:`Args`.
    _instance = None  # type: typing.Optional[Args]

    @staticmethod
    def setinstance(
            instance,  # type: Args
            warn_reset=True,  # type: bool
    ):  # type: (...) -> None
        """
        Sets the main instance of :class:`Args`.

        :param instance: :class:`Args` instance.
        :param warn_reset: Set to ``False`` in order to avoid the warning to be logged.

        When consecutive calls occur, the latest overwrites the previous,
        and a warning is displayed unless ``warn_reset`` is set to ``False``.
        """
        from .loggermain import MAIN_LOGGER

        if Args._instance and (instance is not Args._instance) and warn_reset:
            MAIN_LOGGER.warning(f"Multiple instances of argument parser: {instance!r} takes place of {Args._instance!r}")
        Args._instance = instance

    @classmethod
    def getinstance(
            cls,  # type: typing.Type[VarArgsType]
    ):  # type: (...) -> VarArgsType
        """
        Singleton.

        :return: Main :class:`Args` instance.

        .. warning:: The main :class:`Args` instance is not created automatically by this method,
                     and should be set with :meth:`setinstance()` prior to any :meth:`getinstance()` call.
        """
        from .reflex import qualname

        assert Args._instance is not None, f"No {qualname(cls)} instance available"
        assert isinstance(Args._instance, cls), f"Wrong type {qualname(type(Args._instance))}, {qualname(cls)} expected"
        return Args._instance

    @classmethod
    def isset(
            cls,  # type: typing.Type[VarArgsType]
    ):  # type: (...) -> bool
        """
        Checks whether the single instance of :class:`Args` is set and of the ``cls`` type.

        :param cls: Expected type.
        :return: ``True`` if the single :class:`Args` instance is of the given type, ``False`` otherwise.
        """
        return isinstance(Args._instance, cls)

    def __init__(
            self,
            class_debugging,  # type: bool
    ):  # type: (...) -> None
        """
        Defines common program arguments.

        :param class_debugging: See :class:`.loggingargs.CommonLoggingArgs`.
        """
        from .debugclasses import DebugClass
        from .errcodes import ErrorCode

        Logger.__init__(self, log_class=DebugClass.ARGS)

        # Initialize parsing members.

        #: ``argparse`` parser object.
        self.__arg_parser = argparse.ArgumentParser(add_help=False)  # type: argparse.ArgumentParser

        #: Arguments information.
        self.__arg_infos = {}  # type: typing.Dict[str, ArgInfo]

        #: Parsed flag.
        #: Tells whether arguments have been successfully parsed yet or not.
        self.parsed = False

        #: Argument parsing error code.
        self.error_code = ErrorCode.ARGUMENTS_ERROR  # type: ErrorCode

        # Common command line arguments.
        self.__arg_parser.add_argument(
            "-h", "--help",
            action="help",
            help="Show this help message and exit.",
        )
        CommonConfigArgs.__init__(self)
        CommonLoggingArgs.__init__(self, class_debugging=class_debugging)

    def setprog(
            self,
            name,  # type: str
    ):  # type: (...) -> None
        """
        Overwrites program name.

        :param name: Program name to be displayed with usage info.
        """
        self.__arg_parser.prog = name

    def setdescription(
            self,
            description,  # type: str
    ):  # type: (...) -> None
        """
        Overwrites program description.

        :param description: Program description to be displayed with usage info.
        """
        self.__arg_parser.description = description

    def addarg(
            self,
            member_desc,  # type: str
            member_name,  # type: str
            member_type,  # type: typing.Union[type, typing.Tuple[typing.Type[str], type], typing.Callable[[str], typing.Any]]
    ):  # type: (...) -> ArgInfo
        """
        Adds a program argument.

        :param member_desc: Textual description of the program argument(s).
        :param member_name: Corresponding member name in the owner :class:`Args` instance.
        :param member_type:
            Type of the program argument, or base type of the program arguments list, or conversion handler.
            When defined as a 2 items tuple, the argument feeds a dictionary:
            the first item of the tuple shall be ``str`` (for the dictionary keys),
            and the second item gives the type of the dictionary values.
        :return: :class:`Args.ArgInfo` instance whose :meth:`ArgInfo.define()` should be called onto.

        :meth:`ArgInfo.define()` should be called on the :class:`ArgInfo` object returned:

        .. code-block:: python

           self.addarg("Configuration files", "config_paths", Path).define(
               "--config-file", metavar="CONFIG_PATH",
               action="append", type=str, default=[],
               help="Input configuration file path. "
                    "This option may be called several times.",
           )
        """
        self.__arg_infos[member_name] = ArgInfo(self.__arg_parser, member_desc, member_name, member_type)
        return self.__arg_infos[member_name]

    def parse(
            self,
            args,  # type: typing.List[str]
    ):  # type: (...) -> bool
        """
        Parses program arguments.

        :param args: Argument list, without the program name.
        :return: ``True`` for success, ``False`` otherwise.
        """
        from .configdb import CONFIG_DB
        from .errcodes import ErrorCode
        from .loggermain import MAIN_LOGGER
        from .path import Path

        # Parse command line arguments.
        _parsed_args = self.__arg_parser.parse_args(args)  # type: typing.Any

        # Copy arguments from the untyped `_parsed_args` object to `self`.
        for _member_name in self.__arg_infos:  # type: str
            if not self.__arg_infos[_member_name].process(self, _parsed_args):
                return False

        # Load configurations:
        # - 1) load the single configuration values so that they are taken in account immediately,
        for _key in self.config_values:  # type: str
            try:
                CONFIG_DB.set(_key, data=Args.getinstance().config_values[_key], origin="<args>")
            except Exception as _err:
                MAIN_LOGGER.error(str(_err))
                return False
        # - 2) load configuration files,
        for _config_path in self.config_paths:  # type: Path
            try:
                CONFIG_DB.loadfile(_config_path)
            except EnvironmentError as _env_err:
                self.error_code = ErrorCode.ENVIRONMENT_ERROR
                MAIN_LOGGER.error(str(_env_err))
                return False
            except Exception as _err:
                MAIN_LOGGER.error(str(_err))
                return False
        # - 3) reload the single configuration values, so that they prevail on configuration files.
        for _key in self.config_values:  # Type already declared above.
            try:
                CONFIG_DB.set(_key, data=Args.getinstance().config_values[_key], origin="<args>")
            except Exception as _err:
                MAIN_LOGGER.error(str(_err))
                return False

        # Configure unclassed debugging.
        MAIN_LOGGER.enabledebug(self.debug_main)

        # Post-check arguments.
        if not self._checkargs(_parsed_args):
            self.__arg_parser.print_usage()
            return False

        self.error_code = ErrorCode.SUCCESS
        self.parsed = True
        return True

    def _checkargs(
            self,
            args,  # type: typing.Any
    ):  # type: (...) -> bool
        """
        Handler for special verifications on program arguments.

        :param args: The untyped object returned by :meth:`argparse.ArgumentParser.parse_args()`.
        :return: ``True`` for success, ``False`` otherwise.

        Shall be overridden in subclasses.
        """
        return True

class ArgInfo:
    """
    Class that describes a single program argument (single value, list or dictionary).

    Whether the program argument is a single value, or a list of value, depends on the ``argparse`` definition made
    through :meth:`ArgInfo.define()`.
    """

    def __init__(
            self,
            arg_parser,  # type: argparse.ArgumentParser
            member_desc,  # type: str
            member_name,  # type: str
            member_type,  # type: typing.Union[type, typing.Tuple[typing.Type[str], type], typing.Callable[[str], typing.Any]]
    ):  # type: (...) -> None
        """
        :param arg_parser: Related :class:`argparse.ArgumentParser` instance.
        :param member_desc: Textual description of the program argument(s).
        :param member_name: Corresponding member name in the owner :class:`Args` instance.
        :param member_type:
            Base type of the program argument(s).
            See :meth:`Args.addarg()` for a detailed description of this parameter.

        :meth:`Args.ArgInfo.define()` should be called onto each :class:`Args.ArgInfo` instance newly created.

        .. seealso:: :meth:`Args.addarg()`, :meth:`Args.ArgInfo.define()`
        """
        #: Related :class:`argparse.ArgumentParser` instance.
        self.arg_parser = arg_parser  # type: argparse.ArgumentParser
        #: Textual description of the program argument(s).
        self.member_desc = member_desc  # type: str
        #: Corresponding member name in the owner :class:`Args` instance.
        self.member_name = member_name  # type: str
        #: Key type, when the argument feeds a dictionary.
        self.key_type = None  # type: typing.Optional[typing.Type[str]]
        if isinstance(member_type, tuple):
            assert len(member_type) == 2
            self.key_type = member_type[0]
        #: Base type of the program argument(s).
        self.value_type = str  # type: typing.Union[type, typing.Callable[[str], typing.Any]]
        if isinstance(member_type, tuple):
            assert len(member_type) == 2
            self.value_type = member_type[1]
        else:
            self.value_type = member_type
        #: :class:`argparse.Action` instance defined by the :meth:`Args.ArgInfo.define()` method.
        self.parser_arg = None  # type: typing.Optional[argparse.Action]

    def define(
            self,
            *args,  # type: typing.Any
            **kwargs  # type: typing.Any
    ):  # type: (...) -> None
        """
        Defines the ``argparse`` command line argument.

        :param args: List of positional arguments.
        :param kwargs: Dictionary of named arguments.

        Refer to the regular ``argparse`` documentation, except for the :attr:`dest` parameter which should not be set.
        The :attr:`Args.ArgInfo.member_name` member will be used for the purpose.

        Should be called on the :class:`Args.ArgInfo` returned the :meth:`Args.addarg()` method.

        .. seealso:: :meth:`Args.addarg()`
        """
        assert "dest" not in kwargs
        if len(args) > 0:
            self.arg_parser.add_argument(*args, dest=self.member_name, **kwargs)
        else:
            self.arg_parser.add_argument(self.member_name, **kwargs)

    def process(
            self,
            args_instance,  # type: Args
            parsed_args,  # type: typing.Any
    ):  # type: (...) -> bool
        """
        Process the argument value once parsed by ``argparse`` and feed the :class:`Args` instance.

        :param args_instance: :class:`Args` instance to feed.
        :param parsed_args: Opaque parsed object returned by the ``argparse`` library.
        :return: ``True`` when the operation succeeded, ``False`` otherwise.
        """
        from .loggermain import MAIN_LOGGER
        from .path import Path
        from .reflex import qualname

        # Retrieve and check members from both: the :class:`Args` instance on the one hand, and the opaque parsed object on the other hand.
        if self.member_name not in vars(args_instance):
            MAIN_LOGGER.error(f"No such attribute '{self.member_name}' in {qualname(type(args_instance))}")
            return False
        _args_member = getattr(args_instance, self.member_name)  # type: typing.Any
        _parsed_member = getattr(parsed_args, self.member_name)  # type: typing.Any
        args_instance.debug("ArgInfo['%s'].process(): _parsed_member = %r", self.member_name, _parsed_member)
        if _parsed_member is None:
            # No value => nothing to do.
            # Things will be checked later on with the :meth:`Args._checkargs()` handler.
            return True
        if self.key_type is not None:
            if not isinstance(_args_member, dict):
                MAIN_LOGGER.error(f"Attribute '{self.member_name}' in {qualname(type(args_instance))} should be a dictionary")
                return False

        # Build the list of parsed values to process.
        _parsed_values = []  # type: typing.List[typing.Any]
        if isinstance(_parsed_member, list):
            _parsed_values.extend(_parsed_member)
        else:
            _parsed_values.append(_parsed_member)

        # Process each parsed value.
        for _parsed_value in _parsed_values:  # type: typing.Any
            # Parse the 'key=value' pattern for dictionary arguments.
            _parsed_key = ""  # type: str
            if self.key_type is not None:
                if (not isinstance(_parsed_value, list)) or (len(_parsed_value) != 2) or (not isinstance(_parsed_value[0], str)):
                    MAIN_LOGGER.error(f"{_parsed_value!r} should be a [str, ANY] list")
                    return False
                _parsed_key = _parsed_value[0]
                _parsed_value = _parsed_value[1]

            # Check and convert parsed item values.
            if (self.value_type is Path) and isinstance(_parsed_value, str):
                _parsed_value = Path(_parsed_value)
            elif inspect.isfunction(self.value_type) and isinstance(_parsed_value, str):
                _parsed_value = self.value_type(_parsed_value)
            if _parsed_value is not None:
                if (not inspect.isfunction(self.value_type)) and (not isinstance(_parsed_value, typing.cast(type, self.value_type))):
                    MAIN_LOGGER.error(f"Wrong type {_parsed_value!r}, {qualname(self.value_type)} expected")
                    return False

            # Save the value in the :class:`Args` instance.
            if self.key_type is not None:
                args_instance.debug("%s: %s: %r", self.member_desc, _parsed_key, _parsed_value)
                _args_member[_parsed_key] = _parsed_value
            elif isinstance(_args_member, list):
                args_instance.debug("%s: %d: %r", self.member_desc, len(_args_member), _parsed_value)
                _args_member.append(_parsed_value)
            else:
                args_instance.debug("%s: %r", self.member_desc, _parsed_value)
                setattr(args_instance, self.member_name, _parsed_value)

        return True

if typing.TYPE_CHECKING:
    VarArgsType = typing.TypeVar("VarArgsType", bound=Args)