# coding: utf-8
'''
Created on 27.07.2015

☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦
@author: tr0glo)|(I╠╣
☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦☦
'''
from bs4 import BeautifulSoup
import datetime
import string
import json
import random
from java.sql import Timestamp
from java.util import Date, Calendar
from uuid import uuid4
import base64
import array
import time
import re
from __builtin__ import None

import logging

from ru.curs.celesta.showcase.utils import XMLJSONConverter

from org.apache.poi.xssf.usermodel import XSSFWorkbook
from org.apache.poi.ss.usermodel import Cell

from qt._qt_orm import QtiVariantCursor, ContentCursor, \
    Content_AttributeValueCursor, listAttributeContentCursor, \
    TestingVariantAssignCursor, QtiQuestionCursor, QtiThemeCursor, \
    QtiVariantQuestionCursor, QtiVariantDestructorCursor, QtiDestructorCursor, \
    QtiVariantAnswerCursor, QtiVariantDestructorAnswerCursor, QtiVariantParamsCursor,\
    QtiResourceCursor

from qt._qt_orm import accessToVariantCursor
from java.io import BufferedReader, InputStreamReader
from nci._nci_orm import test_matrix_themesCursor, global_settingsCursor
from fileRepository._fileRepository_orm import fileCursor
from fileRepository.functions import getRelativePathById

# _default_params = {
#     # Произвольное(0)/Последовательное(1) формирование вопросов
#     'NoRandom': 1,
#     # Время прохождения теста, мин
#     'TimeTest': 20,
#     # Количество вопросов
#     'QuantityQuestions': 20,
#     # Процент правильных ответов для оценки 3
#     'Percent3': 60,
#     # Процент правильных ответов для оценки 4
#     'Percent4': 70,
#     # Процент правильных ответов для оценки 5
#     'Percent5': 80,
#     # Формировать(0)/Не формировать(1) протокол
#     'NoProtocol': 0,
#     # Контроль(1)/Самоконтроль(0)
#     'NoSelfTesting': 1,
#     # Отображать оставшееся время выполнения теста
#     'ShowRemainingTime': 1,
#     # Возможность возврата к вопросам после окончания теста (если осталось время)
#     'BackToStart': 1,
#     # Количество повторных возвратов (-1 - без ограничений)
#     'BackToStartCount': -1,
#     # Варианты доступности вопросов при повторном проходе:
#     # 0 - Доступны все вопросы теста
#     # 1 - Доступны только вопросы, пропущенные при первом проходе
#     # 2 - Доступны только вопросы, на которые дан неправильный ответ
#     # 3 - Доступны вопросы,пропущенные при первом проходе и на которые дан неправильный ответ
#     'BackToStartQuestions': 0}

MATCHING_ATTRIBUTE_PACKAGE = {
    'organization': u'Учебная организация',
    'department':u'Кафедра',
    'department_head': u'Зав. кафедрой',
    'faculty': u'Факультет',
    'study_form': u'Форма обучения',
    'discipline': u'Учебная дисциплина',
    'subject': u'Учебный предмет',
    'specialty': u'Специальность',
    'module': u'Модуль',
    'theme': u'Тема',
    'year': u'Учебный год составления',
    'address': u'Адрес (база)',
    'responsible_person': u'Ответственный составитель',
    'email': u'E-mail',
    'phone': u'Моб. телефон',
    'cabinet': u'Кабинет №',
    'source': u'Источник'
} 
MATCHING_NUMBERS_SHORT = {
    0: 'organization',  
    1: 'specialty',    
    2: 'module',    
    3: 'responsible_person',
    4: 'phone',
    5: 'email'
}
MATCHING_NUMBERS = {
    1: 'department',
    2: 'faculty',
    3: 'address',
    4: 'department_head',
    5: 'responsible_person',
    6: 'email',
    7: 'phone',
    8: 'cabinet',
    9: 'discipline',
    10: 'subject',
    11: 'year',
    12: 'specialty',
    13: 'study_form',
    14: 'module',
    15: 'theme',
    19: 'source',
    20: 'organization'
}

def completeTest(context, variantId):
    u'''Функция завершения варианта теста и проставления оценки за тестрирование'''
    QtiVariantAnswer = QtiVariantAnswerCursor(context)
    QtiVariantAnswer.setRange('VariantID', variantId)
    QtiVariantAnswer.first()
    
    # Количество правильно отвеченных вопросов
    positiveCount = 0
    # Балл за тестирование
    score = 0    
    # Завершаем тестирование, если оно ещё не было завершено
    if QtiVariantAnswer.TimeFinish is None:
        QtiVariantQuestionCur = QtiVariantQuestionCursor(context)
        QtiVariantQuestionCur.setRange('VariantID', variantId)
        # Общее количество вопросов в тестировании
        questionCount = QtiVariantQuestionCur.count()
        QtiQuestion = QtiQuestionCursor(context)
        QtiDestructor = QtiDestructorCursor(context)
        QtiVariantDestructor = QtiVariantDestructorCursor(context)
        QtiVariantDestructorAnswer = QtiVariantDestructorAnswerCursor(context)
        # Цикл по вопросам тестирования
        for QtiVariantQuestion in QtiVariantQuestionCur.iterate():
            checkFlag = True
            # Очистка курсоров для корректной работы
            QtiDestructor.clear()
            QtiVariantDestructor.clear()
            QtiQuestion.get(QtiVariantQuestion.QuestionID)
            question_type = QtiQuestion.QuestionType
            question_id = QtiQuestion.QuestionID
            # Вопросы с одиночным или множественным выбором
            if question_type == 1 or question_type == 2:
                # Правильные ответы хранятся в QtiDestructor и у правильных ответов поле isTrue равно True
                QtiDestructor.setRange('QuestionID', question_id)
                QtiDestructor.setRange('IsTrue', True)
                answerList = []
                # Формируе список правильных вариантов отвтов
                for QtiDestructor in QtiDestructor.iterate():
                    answerList.append(QtiDestructor.DestructorID)
                
                QtiVariantDestructor.setRange('VariantQuestionID', QtiVariantQuestion.VariantQuestionID)
                # Цикл по вариантам ответов слушателя
                for QtiVariantDestructor in QtiVariantDestructor.iterate():
                    QtiVariantDestructorAnswer.setRange('VariantDestructorID', QtiVariantDestructor.VariantDestructorID)
                    # Если слушатель в ходе  тестирования выбрал вариант текущий вариант, то запись об этом будет в таблице QtiVariantDestructorAnswer
                    if QtiVariantDestructorAnswer.tryFirst():
                        # Если слушатель выбрал вариант, которого нет в списке правильных, то вопрос не засчитывается
                        if QtiVariantDestructor.DestructorID not in answerList:
                            checkFlag = False
                        else:
                            # Если слушатель выбрал правльный вариант, то удаляем этот вариант из списка правальных
                            answerList.remove(QtiVariantDestructor.DestructorID)
                # Список правильных ответов будет непуст, если слушатель не указал какой-либо правильный вариант, следовательно, вопрос не засчитывается 
                if answerList != []:
                    checkFlag = False
            # Вопросы с текстовым ответом
            elif question_type == 3:
                QtiDestructor.setRange('QuestionID', question_id)
                QtiDestructor.first()
                # Правильный ответ хранится в таблице QtiDestructor в полу DestructorHtml. Для вопросов такого типа в QtiDestructor только одна запись соответствует вопросу
                correctAnswer = QtiDestructor.DestructorHtml
                
                QtiVariantDestructor.setRange('VariantQuestionID', QtiVariantQuestion.VariantQuestionID)
                QtiVariantDestructor.first()
                
                QtiVariantDestructorAnswer.setRange('VariantDestructorID', QtiVariantDestructor.VariantDestructorID)
                # Получаем ответ на вопрос, данный слушателем
                if QtiVariantDestructorAnswer.tryFirst():
                    # Если ответв на вопрос не совпадает с верным, то вопрос не засчитывается
                    if QtiVariantDestructorAnswer.DestructorText != correctAnswer:
                        checkFlag = False                   
                else:
                    # Если ответа нет, то вопрос не засчитывается
                    checkFlag = False
            # Вопрос-сопоставление
            elif question_type == 4:
                # Для вопросов-сопоставлений в таблице QtiDestructor в полях DestructorHtml и DestructorHtml2 хранятся значение из левой колонки,
                # в поле AnswerText правильное значений их правой колонки. В QtiDestructorAnswer для каждого варианта в поле DestructorID пишется id,
                # того варианта значение из правой колонки которого было помещено в соответствие текущему варианту.
                QtiVariantDestructor.setRange('VariantQuestionID', QtiVariantQuestion.VariantQuestionID)
                for QtiVariantDestructor in QtiVariantDestructor.iterate():
                    QtiVariantDestructorAnswer.setRange('VariantDestructorID', QtiVariantDestructor.VariantDestructorID)
                    if QtiVariantDestructorAnswer.tryFirst():
                        if QtiVariantDestructorAnswer.DestructorID != QtiVariantDestructor.DestructorID:
                            checkFlag = False
                    else:
                        checkFlag = False
            # Вопрос-сортировка
            elif question_type == 5:
                # В QtiDestructor правильный порядок задаётся полем DestructorOrder. В QtiVariantDestructorAnswer  в поле DestructorText заносится номер выбранной строки
                QtiVariantDestructor.setRange('VariantQuestionID', QtiVariantQuestion.VariantQuestionID)
                for QtiVariantDestructor in QtiVariantDestructor.iterate():
                    QtiDestructor.get(QtiVariantDestructor.DestructorID)
                    QtiVariantDestructorAnswer.setRange('VariantDestructorID', QtiVariantDestructor.VariantDestructorID)
                    if QtiVariantDestructorAnswer.tryFirst():
                        if int(QtiDestructor.DestructorOrder) != int(QtiVariantDestructorAnswer.DestructorText):
                            checkFlag = False
                    else:
                        checkFlag = False
            # Вопрос-классификация
            elif question_type == 6:
                # Ответы хранятся аналогично вопросу-сопоставлению
                QtiVariantDestructor.setRange('VariantQuestionID', QtiVariantQuestion.VariantQuestionID)
                for QtiVariantDestructor in QtiVariantDestructor.iterate():
                    QtiDestructor.get(QtiVariantDestructor.DestructorID)
                    correctAnswer = QtiDestructor.AnswerText
                    QtiVariantDestructorAnswer.setRange('VariantDestructorID', QtiVariantDestructor.VariantDestructorID)
                    if QtiVariantDestructorAnswer.tryFirst():
                        QtiDestructor.get(QtiVariantDestructorAnswer.DestructorID)
                        if QtiDestructor.AnswerText != correctAnswer:
                            checkFlag = False
                    else:
                        checkFlag = False
            # Если ответ правильный, увеличиваем счётчик правильных ответов, в таблицу пишем что на вопрос был дан правильный ответ
            if checkFlag:
                positiveCount += 1
                QtiVariantQuestion.CorrectAnswer = True
                QtiVariantQuestion.update()
                
        QtiVariantDestructorAnswer.close()
        QtiVariantDestructor.close()
        QtiDestructor.close()
        QtiQuestion.close()
        QtiVariantQuestionCur.close()
        # Расчёт баллов за тест
        score = round((positiveCount * 1.0 / questionCount) * 100)
        
        now = Date()
        # Пишет в таблицу время завершения варианта, балл, количество правильных ответов
        QtiVariantAnswer.TimeFinish = now if QtiVariantAnswer.TimeEnd > now else QtiVariantAnswer.TimeEnd
        QtiVariantAnswer.Score = score
        QtiVariantAnswer.PositiveCount = positiveCount
        mark = 2
        # Получаем пороговый уровни для оценок и записываем оценку
#         percent3 = int(getGlobalAttributeValue(Content,Content_Attribute,listAttributeContent,packageId,'Percent3'))
#         percent4 = int(getGlobalAttributeValue(Content,Content_Attribute,listAttributeContent,packageId,'Percent4'))
#         percent5 = int(getGlobalAttributeValue(Content,Content_Attribute,listAttributeContent,packageId,'Percent5'))
        
        QtiVariantParams = QtiVariantParamsCursor(context)
        percent3 = int(getAttributeValue(QtiVariantParams, variantId, 'Percent3'))
        percent4 = int(getAttributeValue(QtiVariantParams, variantId, 'Percent4'))
        percent5 = int(getAttributeValue(QtiVariantParams, variantId, 'Percent5'))
        QtiVariantParams.close()
        
        if score >= percent5:
            mark = 5
        elif score >= percent4:
            mark = 4
        elif score >= percent3:
            mark = 3
        QtiVariantAnswer.TestingMark = mark
        QtiVariantAnswer.update()                    
    
    
def checkParams(QtiVariantParams, variantId):
    u'''Функция проверяет все ли необхомиые атрибуты БТЗ заданы'''
    paramsList = ['NoRandom', 'TimeTest',
                  'QuantityQuestions', 'Percent3',
                  'Percent4', 'Percent5',
                  'NoProtocol', 'NoSelfTesting']    
    QtiVariantParams.setRange('VariantID', variantId)
    
    for param in paramsList:
        QtiVariantParams.setRange('ParamID', param)
        if QtiVariantParams.count() == 0:
            return False    
    return True


def getGlobalAttributeValue(Content, Content_Attribute, listAttributeContent, packageId, attributeName):
    listAttributeContent.setRange('AttributeContentName', attributeName)
    if listAttributeContent.tryFirst():
        attributeId = listAttributeContent.AttributeContentID
        Content.setRange('PackageID', packageId)
        if Content.tryFirst():
            Content_Attribute.setRange('ContentID', Content.ContentID)
            Content_Attribute.setRange('AttributeContentID', attributeId)
            if Content_Attribute.tryFirst():
                return Content_Attribute.AttributeValue
    return None

    
def getAttributeValue(QtiVariantParams, variantId, paramId):
    u'''Функция получения значение атрибута БТЗ'''
    QtiVariantParams.get(variantId, paramId)
    return QtiVariantParams.Value


def setTestTimeByParam(context, variant_id):
    QtiVariantParams = QtiVariantParamsCursor(context)
    timeTest = int(getAttributeValue(QtiVariantParams, variant_id, 'TimeTest'))
    QtiVariantParams.close()
    
    QtiVariantAnswer = QtiVariantAnswerCursor(context)
    QtiVariantAnswer.VariantAnswerID = unicode(uuid4())
    QtiVariantAnswer.VariantID = variant_id
    timeBegin = Date()
    QtiVariantAnswer.TimeBegin = timeBegin
    QtiVariantAnswer.TimeEnd = Timestamp(timeBegin.getTime() + timeTest * 60 * 1000)
    QtiVariantAnswer.insert()
    QtiVariantAnswer.close()
    

def createTestingVariantAssign(context, variant_id, packageId, sid, personId=None, params=None, cycleId=None):
    u'''Функция добавления записи в таблицу TestingVariantAssign.
        Если не передаютяс параметры, то пишутся глобальные параметры'''
    TVA = TestingVariantAssignCursor(context)
    QtiVariantParams = QtiVariantParamsCursor(context)
    Content = ContentCursor(context)
    Content_Attribute = Content_AttributeValueCursor(context)
    listAttributeContent = listAttributeContentCursor(context)
    TVA.VariantID = variant_id
    TVA.AlgorithmTestingID = 2
    TVA.DateAssign = Date()
    TVA.WhenAdd = Date()
    TVA.UserName = 'admin'
    TVA.ServerName = 'local'
    TVA.PackageID = packageId
    TVA.personUid = personId
    TVA.cycleid = cycleId
    TVA.testtype = 2
    TVA.insert()
    if params is not None:
        # Если есть параметры, пишем их в таблицу
        for param in params:
            QtiVariantParams.VariantID = TVA.VariantID
            QtiVariantParams.ParamID = param
            QtiVariantParams.Value = params[param]
            QtiVariantParams.insert()
    else:
        # Если нет параметров, то получаем глобальные параметры и записываем их
#         params = ['NoRandom', 'TimeTest',
#               'QuantityQuestions', 'Percent3',
#               'Percent4', 'Percent5',
#               'NoProtocol', 'NoSelfTesting']
        global_settingsCur = global_settingsCursor(context)
        for item in global_settingsCur.iterate():
            param = item.id
            value = item.value
            QtiVariantParams.VariantID = TVA.VariantID
            QtiVariantParams.ParamID = param
            QtiVariantParams.Value = getGlobalAttributeValue(Content, Content_Attribute, listAttributeContent, packageId, param)
            if QtiVariantParams.Value is None or QtiVariantParams.Value == '':
                QtiVariantParams.Value = value
            QtiVariantParams.insert()
    
    return TVA.VariantID


def generateVariantFlute(context, params):
    u'''Генерация варианта - функция для флейты. '''
    
    parameters = json.loads(params.params)
    packageId = parameters['packageId']
    variantId = parameters['variantId']
    generateVariant(context, packageId, variantId)
    
    
def getPackageThemes(context, packageId, themesList):
    QtiTheme = QtiThemeCursor(context)
    QtiTheme.setRange('PackageID', packageId)
    QtiTheme.orderBy('ThemeOrder')
    for theme in QtiTheme.iterate():
        themesList.append(theme.ThemeID)
    QtiTheme.close()
    return themesList


def generateRandomCountList(count_1, count_2):
    h = count_1 // count_2
    m = count_1 % count_2
    count_list = list()
    for k in range(count_2):
        l = 1 if m>0 else 0
        count_list.insert(k, h + l)
        if m > 0:
            m -= 1
    random.shuffle(count_list)
    return count_list


def generateRandomQuestionsCountDict(context, theme_list, count):
    td = dict()
    td_corr = dict()
    # Получаем количество вопросов в каждой теме
    QtiQuestion = QtiQuestionCursor(context)
    questions = 0
    for item in theme_list:
        QtiQuestion.setRange('ThemeID', item)
        c = QtiQuestion.count()
        td[item] = c
        questions += c
    QtiQuestion.close()    
    # Уменьшаем количество вопросов в теме для получения требуемого количества
    rc = 0
    for item in theme_list:
        qc = int(round(td[item] * count / float(questions)))
        rc += qc
        td_corr[item] = qc
    # Сумма округленных значений может отличаться    
    if rc > count:
        n = rc - count
        sorted_td_corr = OrderedDict(sorted(td_corr.items(), key=operator.itemgetter(1)))
        for key in reversed(sorted_td_corr):
            value = sorted_td_corr[key]
            if value > 0:
                td_corr[key] = value - 1
                n -= 1
            if n == 0:
                break
    elif rc < count:
        n = count - rc
        for key, value in td_corr.iteritems():
            if (td[key] - value) > 0:
                td_corr[key] = value + 1
                n -= 1
            if n == 0:
                break

    return td_corr

    
def generateVariant(context, packageId, variantId, debugFlag=False):
    u'''Функция генерации варианта'''   
    # Получение режима выбора воропросов: '1' - выбор по порядку, '0' - случайным образом
    QtiVariantParams = QtiVariantParamsCursor(context)
    noRandom = getAttributeValue(QtiVariantParams, variantId, 'NoRandom')
    # Получение количества вопросов в варианте
    quantityQuest = getAttributeValue(QtiVariantParams, variantId, 'QuantityQuestions')
    QtiVariantParams.close()
    
#     themeDict = dict()
#     themeList = list()
#     questionDict = dict()
#     count = 0
#     
#     QtiQuestion = QtiQuestionCursor(context)
#     QtiTheme = QtiThemeCursor(context)
#     QtiTheme.setRange('PackageID', packageId)
#     QtiTheme.orderBy('ThemeOrder')
#     # Формируется список тем и словарь списков вопросов по темам. Темы и вопросы упорядочены по соответствующим табличным полям.
#     for theme in QtiTheme.iterate():
#         QtiQuestion.setRange('ThemeID', theme.ThemeID)
#         themeDict[theme.ThemeID] = QtiQuestion.count()
#         themeList.append(theme.ThemeID)
#         count += QtiQuestion.count()
#         questionDict[theme.ThemeID] = list()
#         QtiQuestion.orderBy('QuestionOrder')
#         for question in QtiQuestion.iterate():
#             questionDict[theme.ThemeID].append(question.QuestionID)
#     
#     QtiQuestion.close()        
#     QtiTheme.close()

    # Формируется список тем и словарь списков вопросов по темам. Темы и вопросы упорядочены по соответствующим табличным полям.
    themeList = list()
    themeDict = dict()
    test_matrix_themesCur = test_matrix_themesCursor(context)
    test_matrix_themesCur.setRange('tm_id', packageId)
    is_btz = True
    for test_item in test_matrix_themesCur.iterate():
        if is_btz:
            is_btz = False
        #Формируем список БТЗ, если для генерации варианта используется Матрица тем
        package_id = test_item.package_id
        theme_id = test_item.theme_id
        if theme_id:
            themeList.append(theme_id)
            themeDict[theme_id] = test_item.question_count
        else:
            tmpList = list()
            getPackageThemes(context, package_id, tmpList)
            tmpDict = generateRandomQuestionsCountDict(context, tmpList, test_item.question_count)

            for k in tmpDict:
                themeDict[k] = tmpDict[k]

            themeList.extend(tmpList)

    if is_btz:
        # Если не из Матрицы тем, то в packageId передан ИД БТЗ
        getPackageThemes(context, packageId, themeList)
    
    test_matrix_themesCur.close()
    
#     print themeList
    questionDict = dict()
    questionTypeDict = dict()
    count = 0    
    QtiQuestion = QtiQuestionCursor(context)
    for theme_id in themeList:
        QtiQuestion.setRange('ThemeID', theme_id)
        count += QtiQuestion.count()
        questionDict[theme_id] = list()
        QtiQuestion.orderBy('QuestionOrder')
        for question in QtiQuestion.iterate():
            questionDict[theme_id].append(question.QuestionID)
            questionTypeDict[question.QuestionID] = question.QuestionType
    QtiQuestion.close()
    
    # Если количество вопросов не определено, то берутся все доступные вопросы.
    if quantityQuest is None:    
        quantityQuest = count
        noRandom = '1'
        
    QtiVariant = QtiVariantCursor(context)    
    QtiVariant.setRange('PackageID', packageId)
    variantName = u'Вариант №%s' % (QtiVariant.count() + 1)
    # Вставляем запись в таблицу сгенерированных вариантов
    QtiVariant.VariantID = variantId
    QtiVariant.PackageID = packageId
    QtiVariant.VariantName = variantName
    QtiVariant.DateCreated = datetime.datetime.now()
    QtiVariant.insert()
    QtiVariant.close()
    
    QtiVariantQuestion = QtiVariantQuestionCursor(context)
    if is_btz:
        # Если режим отладки, то выбираются все вопросы по порядку, иначе берётся количество вопросов из параметров БТЗ
        if debugFlag:
            remainQuest = count
        else:
            remainQuest = min(int(quantityQuest), count)
        # number - определяет номер вопроса в варианте
        number = 1
        
        # Цикл до тех пор, пока не будет набрано необходимое коичество вопросовю
        while remainQuest > 0:
    #         raise Exception(remainQuest,len(themeList),themeList)
            print remainQuest, len(themeList), themeList
            # Пытаемя равномерно распределить вопросы по темам
            questCountInt = remainQuest / len(themeList)
            print questCountInt
            k = len(themeList)
            # Если нужно вопросов меньше, чем тем, то случайным образом выбираем темы.
            if questCountInt == 0:
                k = remainQuest
                questCountInt = 1
            print questCountInt
            deleteThemeList = list()
            # В режиме отладки выбиратся все вопросы по порядку
            if debugFlag:
                remainQuest = 0
                for theme in themeList:
                    for question in questionDict[theme]:
                        QtiVariantQuestion.VariantQuestionID = unicode(uuid4())
                        QtiVariantQuestion.QuestionID = question
                        QtiVariantQuestion.VariantID = variantId
                        QtiVariantQuestion.QuestionNumber = number
                        QtiVariantQuestion.QuestionType = questionTypeDict[question]
                        QtiVariantQuestion.insert()
                        number += 1
            else:
                # Перебираем в случайном порядке темы
                for theme in random.sample(themeList, k):
                    print theme, len(questionDict[theme])
                    # Если в текущй теме осталось вопросов меньше чем нужно взять, то берём все доступные вопросы и помечаем тему для удаления из списка доступных тем
                    if len(questionDict[theme]) < questCountInt:
                        remainQuest -= len(questionDict[theme])
                        deleteThemeList.append(theme)
                        for question in questionDict[theme]:
                            QtiVariantQuestion.VariantQuestionID = unicode(uuid4())
                            QtiVariantQuestion.QuestionID = question
                            QtiVariantQuestion.VariantID = variantId
                            QtiVariantQuestion.QuestionNumber = number
                            QtiVariantQuestion.QuestionType = questionTypeDict[question]
                            QtiVariantQuestion.insert()
                            number += 1
                    else:
                        # Если вопросов в теме ровно столько, скольки и нужно, то помечаем тему для удаления из списка доступных тем
                        if len(questionDict[theme]) == questCountInt:
                            deleteThemeList.append(theme)
                        deleteQuestionList = list()
                        remainQuest -= questCountInt
                        # Выбор вопросов по порядку
                        if noRandom == '1':                  
                            for i in range(questCountInt):
                                qid = questionDict[theme][i]
                                deleteQuestionList.append(qid)
                                QtiVariantQuestion.VariantQuestionID = unicode(uuid4())
                                QtiVariantQuestion.QuestionID = qid
                                QtiVariantQuestion.VariantID = variantId
                                QtiVariantQuestion.QuestionNumber = number
                                QtiVariantQuestion.QuestionType = questionTypeDict[qid]
                                QtiVariantQuestion.insert()
                                number += 1
                        # Выбор случайных вопросов
                        elif noRandom == '0':
                            questions = random.sample(questionDict[theme], questCountInt)
                            for question in questions:
                                deleteQuestionList.append(question)
                                QtiVariantQuestion.VariantQuestionID = unicode(uuid4())
                                QtiVariantQuestion.QuestionID = question
                                QtiVariantQuestion.VariantID = variantId
                                QtiVariantQuestion.QuestionNumber = number
                                QtiVariantQuestion.QuestionType = questionTypeDict[question]
                                QtiVariantQuestion.insert()
                                number += 1
                        # Удаляем выбранные вопросы из списка доступных вопросов по теме.
                        for question in deleteQuestionList:
                            questionDict[theme].remove(question)
                # Удаялем темы, в которых нет вопросов из списка доступных тем.
                for theme in deleteThemeList:
                    themeList.remove(theme)
    else:
        # Вариант "Матрица"
        number = 1
        for theme_id, question_count in themeDict.items():
            questions = random.sample(questionDict[theme_id], question_count)
            for question in questions:
                QtiVariantQuestion.VariantQuestionID = unicode(uuid4())
                QtiVariantQuestion.QuestionID = question
                QtiVariantQuestion.VariantID = variantId
                QtiVariantQuestion.QuestionNumber = number
                QtiVariantQuestion.QuestionType = questionTypeDict[question]
                QtiVariantQuestion.insert()
                number += 1

    # Переносим варианты ответов на вопросы в таблицы, соответствующие сгенерированному варианту.
    QtiVariantQuestion.setRange('VariantID', variantId)
    QtiDestructor = QtiDestructorCursor(context)
    QtiVariantDestructor = QtiVariantDestructorCursor(context)
    for QtiVariantQuestion in QtiVariantQuestion.iterate():
        QtiDestructor.setRange('QuestionID', QtiVariantQuestion.QuestionID)
        for destructor in QtiDestructor.iterate():
            QtiVariantDestructor.VariantDestructorID = unicode(uuid4())
            QtiVariantDestructor.VariantQuestionID = QtiVariantQuestion.VariantQuestionID
            QtiVariantDestructor.DestructorID = destructor.DestructorID
            QtiVariantDestructor.insert()    
    
            
def generateThemesVariant(context, packageId, variantId, themeList):
    u'''Функция генерации варианта'''
    QtiVariant = QtiVariantCursor(context)
#     Content = ContentCursor(context)
#     Content_Attribute = Content_AttributeValueCursor(context)
#     listAttributeContent = listAttributeContentCursor(context)
    QtiVariantDestructor = QtiVariantDestructorCursor(context)
    QtiDestructor = QtiDestructorCursor(context)
    QtiQuestion = QtiQuestionCursor(context)
    QtiVariantQuestion = QtiVariantQuestionCursor(context)
#     QtiVariantParams = QtiVariantParamsCursor(context)
    QtiTheme = QtiThemeCursor(context)
    QtiVariant.setRange('PackageID', packageId)

    variantName = u'Вариант №%s' % (QtiVariant.count() + 1)
    
    QtiVariant.VariantID = variantId
    QtiVariant.PackageID = packageId
    QtiVariant.VariantName = variantName
    QtiVariant.DateCreated = datetime.datetime.now()
    QtiVariant.insert()
    number = 1
    for theme in themeList:
        QtiTheme.get(theme)
        QtiQuestion.setRange('ThemeID',theme)
        QtiQuestion.orderBy('QuestionOrder')
        for question in QtiQuestion.iterate():
            QtiVariantQuestion.VariantQuestionID = unicode(uuid4())
            QtiVariantQuestion.QuestionID = question.QuestionID
            QtiVariantQuestion.VariantID = variantId
            QtiVariantQuestion.QuestionNumber = number
            QtiVariantQuestion.QuestionType = question.QuestionType
            QtiVariantQuestion.insert()
            number += 1   
    # Переносим варианты ответов на вопросы в таблицы, соответствующие сгенерированному варианту.
    QtiVariantQuestion.setRange('VariantID', variantId)
    for QtiVariantQuestion in QtiVariantQuestion.iterate():
        QtiDestructor.setRange('QuestionID', QtiVariantQuestion.QuestionID)
        for destructor in QtiDestructor.iterate():
            QtiVariantDestructor.VariantDestructorID = unicode(uuid4())
            QtiVariantDestructor.VariantQuestionID = QtiVariantQuestion.VariantQuestionID
            QtiVariantDestructor.DestructorID = destructor.DestructorID
            QtiVariantDestructor.insert()    
    
    
def generateQuestionsVariant(context, packageId, variantId, questionList):
    u'''Функция генерации варианта'''
    QtiVariant = QtiVariantCursor(context)
#     Content = ContentCursor(context)
#     Content_Attribute = Content_AttributeValueCursor(context)
#     listAttributeContent = listAttributeContentCursor(context)
    QtiVariantDestructor = QtiVariantDestructorCursor(context)
    QtiDestructor = QtiDestructorCursor(context)
#     QtiQuestion = QtiQuestionCursor(context)
    QtiVariantQuestion = QtiVariantQuestionCursor(context)
#     QtiVariantParams = QtiVariantParamsCursor(context)
#     QtiTheme = QtiThemeCursor(context)
    QtiVariant.setRange('PackageID', packageId)

    variantName = u'Вариант №%s' % (QtiVariant.count() + 1)
    
    QtiVariant.VariantID = variantId
    QtiVariant.PackageID = packageId
    QtiVariant.VariantName = variantName
    QtiVariant.DateCreated = datetime.datetime.now()
    QtiVariant.insert()
    
    QtiQuestion = QtiQuestionCursor(context)
    number = 1
    for question in questionList:
        QtiVariantQuestion.VariantQuestionID = unicode(uuid4())
        QtiVariantQuestion.QuestionID = question
        QtiVariantQuestion.VariantID = variantId
        QtiVariantQuestion.QuestionNumber = number
        if QtiQuestion.tryGet(question):
            QtiVariantQuestion.QuestionType = QtiQuestion.QuestionType
        QtiVariantQuestion.insert()
        number += 1  
    
    QtiQuestion.close()    
     
    # Переносим варианты ответов на вопросы в таблицы, соответствующие сгенерированному варианту.
    QtiVariantQuestion.setRange('VariantID', variantId)
    for QtiVariantQuestion in QtiVariantQuestion.iterate():
        QtiDestructor.setRange('QuestionID', QtiVariantQuestion.QuestionID)
        for destructor in QtiDestructor.iterate():
            QtiVariantDestructor.VariantDestructorID = unicode(uuid4())
            QtiVariantDestructor.VariantQuestionID = QtiVariantQuestion.VariantQuestionID
            QtiVariantDestructor.DestructorID = destructor.DestructorID
            QtiVariantDestructor.insert() 

    
def setAccessPermission(context, sid, mode, variantId):
    u'''Функция, которая даёт разрешение на просмотр тестирования в режиме отладки или режиме тьютора'''
    accessToVariant = accessToVariantCursor(context)
    # Даём доступ на день
    dateTo = Date(Date().getTime() + 24 * 60 * 60 * 1000)
    
    # Если доступ уже есть, то продлеваем его длительность до суток
    if accessToVariant.tryGet(variantId, mode, sid):
        accessToVariant.dateTo = dateTo
        accessToVariant.update()
    else:
        accessToVariant.VariantID = variantId
        accessToVariant.mode = mode
        accessToVariant.sid = sid
        accessToVariant.dateTo = dateTo
        accessToVariant.insert()
        
def getShowCorrectAnswerPermission(context, session, variantId):
    u'''функция, которая проверяет нужно ли отображать правильные ответы в тестировании'''
    accessToVariant = accessToVariantCursor(context)
    sid = session['sessioncontext']['sid']
    now = Date()
    
    # Если в urlparams нет параметров, то правильные ответы не отображаются
    if 'urlparam' not in session['sessioncontext']['urlparams']:
        return False
    
    urlparams = session['sessioncontext']['urlparams']['urlparam']
    
    if not isinstance(urlparams, list):
        urlparams = [urlparams]
        
    mode = ''
    # Находим режим доступа к варианту
    for urlparam in urlparams:
        if urlparam['@name'] == 'mode':
            mode = urlparam['@value'][0]
            
    # Проверяем наличие доступа у данного пользователя к данному варианту в данном режиме
    if accessToVariant.tryGet(variantId, mode, sid):
        if accessToVariant.dateTo < now:
            return False
        return True
    else:
        return False
            
            
def checkTestPermission(context, session, variantId):
    u'''функция, которая проверяет нужно ли отображать правильные ответы в тестировании'''
    accessToVariant = accessToVariantCursor(context)
    sid = session['sessioncontext']['sid']
    now = Date()
    
    # Если в urlparams нет параметров, то правильные ответы не отображаются
    if 'urlparam' not in session['sessioncontext']['urlparams']:
        return False
    
    urlparams = session['sessioncontext']['urlparams']['urlparam']
    
    if not isinstance(urlparams, list):
        urlparams = [urlparams]
        
    mode = ''
    # Находим режим доступа к варианту
    for urlparam in urlparams:
        if urlparam['@name'] == 'mode':
            mode = urlparam['@value'][0]
            
    # Проверяем наличие доступа у данного пользователя к данному варианту в данном режиме
    if accessToVariant.tryGet(variantId, mode, sid):
        if accessToVariant.dateTo < now:
            return False
        return True
    else:
        return False
            
def getPermissionForViewTest(context, session, variantId):
    u'''Проверка разрешения на доступ к варианту теста. Пользователи имеют доступ только к своим тестам'''
    TVA = TestingVariantAssignCursor(context)
    
    sid = session['sessioncontext']['sid']
    
    TVA.get(variantId)
    
    # Если в TestingVariantAssign записан sid пользователя, то проверяем по нему, иначе по personUid
    if TVA.sid is not None:
        if TVA.sid == sid:
            return True
        else:
            return False
    else:
        from journals.functions import personIdFromSID
        personId = personIdFromSID(context, sid)
        if TVA.personUid == personId:
            return True
        else:
            return False
        
    
    
def getBase64Image(imageStream):
    u'''Функция получения строки с данным из потока для вставки изображения'''
    stringout = u''
    byteArray = [-1, -1, -1]
    while True:
        byteArray[0] = imageStream.read()
        byteArray[1] = imageStream.read()
        byteArray[2] = imageStream.read()
        if byteArray[0] == -1:
            break
        elif byteArray[1] == -1:
            stringout += base64.b64encode(array.array('B', byteArray[0:1]).tostring())
            break
        elif byteArray[2] == -1:
            stringout += base64.b64encode(array.array('B', byteArray[0:2]).tostring())
            break
        else:
            stringout += base64.b64encode(array.array('B', byteArray).tostring())
    return stringout        
        
        
def getImageById(QtiResource, resourceId, isImage=True):
    u'''Функция получения изображения по идентификатору'''
    result = ''
    if (resourceId is not None) and (QtiResource.tryGet(resourceId)):
        QtiResource.calcResourceData()
        try:
            imgBlob = QtiResource.ResourceData.getInStream()
            if imgBlob:
                if isImage:
                    result = '''<image src=%s />''' % (u"data:image/png;base64," + getBase64Image(imgBlob))
                else:
                    result = BufferedReader(InputStreamReader(imgBlob, 'utf-8')).readLine()
        finally:
            imgBlob.close()
        
    return result
    
    
def getTestJsonFromFile(data_file):
    u'''Загрузка по формату 2МЕД'''
    begin = time.time()
    html = BeautifulSoup(data_file, 'html5lib')
    print 'HTML loaded', time.time() - begin
    tables = html('table')
    
    options_table = tables[0]
    options_rows = options_table('tr')
    
    count = 0
    
    options = list()
    
    test = dict()
    
    subthemes = list()
    subtheme = dict()

    test['organization'] = u''
    
    for row in options_rows:
        cols = row('td')
        count_cols = 0
        for col in cols:
            strings = col.stripped_strings
            value = [s.replace('\xa0' , ' ') for s in strings]
            value = ''.join(value)
            if count_cols == 0:
                number = int(value)
            elif count_cols == 1:
                name = value
            elif count_cols == 2:
                col_value = value
            count_cols += 1
        options.append([number, name, col_value])
        if number < 16 or number == 19:
            test[MATCHING_NUMBERS[number]] = col_value
    
        if number == 16:
            subtheme['name'] = col_value
        if number == 17:
            subtheme['quest_count'] = int(col_value)
        if number == 18:
            subtheme['quest_type'] = col_value
            subtheme['questions'] = []
            subthemes.append(subtheme)
            subtheme = dict()
    
    
    # print(test)
    
    print 'First table finish', time.time() - begin
    questions_table = tables[1]
    question_rows = questions_table('tr')
    
    subthemeFlag = False
    currentSubtheme = -1
    newQuestionFlag = False
    choiceFlag = False
    
    stopTagSet = {'font-size', 'font', 'line-height'}
    
    for row in question_rows:
        values = ['' for i in range(6)]
        cols = row('td')
        for i in range(6):
            paragraphs = cols[i]('p')
            if len(paragraphs) > 0:
    
                strings = cols[i].stripped_strings
                val = [s for s in strings]
                val = ''.join(val)
                    
                    
                noneFlag = True
                
                for ch in val:
                    if not (ch in string.whitespace or ord(ch) == 160):
                        noneFlag = False
                        break

                if noneFlag:
                    continue

                listTag = [f for f in cols[i].descendants if hasattr(f, 'attrs')]
                for tag in listTag:
                    if 'class' in tag.attrs:
                        del tag['class']
                    if 'style' in tag.attrs:
                        styleList = tag['style'].split(';')
                        newStyleList = []
                        for style in styleList:
                            styleName = style.split(':')[0].lower().strip()
                            if styleName not in stopTagSet:
                                newStyleList.append(style)
                        if newStyleList:
                            tag['style'] = ';'.join(newStyleList)
                        else:
                            del tag['style']
    
                if i == 3 and values[i] is not None:
                    textList = list()
                    for par in paragraphs:
    
                        if 'class' in par.attrs:
                            del par['class']
                        if 'style' in par.attrs:
                            del par['style']
                        textList.append(unicode(par.text))
                    values[i] = ''.join(textList)
                else:
                    values[i] = val
        # print values
        if not any(values):
            newQuestionFlag = True
            choiceFlag = False
            subthemes[currentSubtheme]['questions'].append(question)
        elif values[0].isdigit() and values[1].isdigit() and values[2].isdigit():
            subthemeFlag = True
            currentSubtheme += 1
            newQuestionFlag = True
        elif choiceFlag:
            choice = dict()
            if values[1] == '*':
                choice['correct'] = 'true'
            else:
                choice['correct'] = 'false'
            choice['text'] = values[3]
            question['choices'].append(choice)
        elif newQuestionFlag:
            newQuestionFlag = False
            choiceFlag = True
            question = dict()
            question['text'] = values[3]
            question['choices'] = []    
    
    test['subthemes'] = subthemes
    print 'Second table finish', time.time() - begin
    return (u'', test)
    
    
def cleanString(s):
    text = u''
    ss = s.replace(u' ', '')
    if ss:
        pattern = re.compile(r'[\t\n\r\f\v]+')
        text = re.sub(pattern, '', s)
        text =  (u' '.join(text.split())).strip()
    return text
    
    
def getTestJsonFromFile2(data_file):
    u'''Загрузка по формату Московский врач'''
    html = BeautifulSoup(data_file, 'html5lib')
    tables = html('table')
    
    options_table = tables[0]
    options_rows = options_table('tr')
    
    test = {
        'organization': u'',
        'department': u'',
        'department_head': u'',
        'study_form': u'',
        'faculty': u'',
        'specialty': u'',
        'discipline': u'',
        'subject': u'',
        'module': u'',
        'theme': u'',
        'year': u'',
        'address': u'',
        'responsible_person': u'',
        'email': u'',
        'phone': u'',
        'cabinet': u'',
        'source': u''}
    
    subthemes = list()
    subtheme = dict()
    subtheme['quest_type'] = 'single'
    subtheme['questions'] = []
    subtheme['quest_count'] = 0
    
    qti_name = u''
    
    for i, row in enumerate(options_rows):
        index = i + 1        
        td = row('td')
        param = cleanString(' '.join(td[2].stripped_strings))
        try:
            test[MATCHING_NUMBERS_SHORT[index]] = param
        except:
            pass
        if index == 2:
            qti_name = param
        elif index == 3:
            subtheme['name'] = param
    
    questions_table = tables[1]
    question_rows = questions_table('tr')
    question = dict()
    quest_count = 0
    for ee in question_rows:
        td = ee('td')
        td1 = cleanString(u' '.join(td[0].stripped_strings))
        td2 = cleanString(u' '.join(td[1].stripped_strings))
        td3 = cleanString(u' '.join(td[2].stripped_strings))

        if td1 and td2 and td3:
            if td1 == u'В':
                question = dict()
                question['text'] = td3
                question['choices'] = []
            elif td1 == u'О' or td1 == u'0':
                choice = dict()
                choice['correct'] = 'true' if td[2]('b') else 'false'
                choice['text'] = td3
                question['choices'].append(choice)        
        elif question:
            subtheme['questions'].append(question)
            quest_count += 1
                 
    subtheme['quest_count'] = quest_count
    subthemes.append(subtheme)
    test['subthemes'] = subthemes
    
    return (qti_name, test)


def getTestJsonFromExcel(data_file):
    
#     logging.info(u'Начало обработки Excel-файла')
    
    wb = XSSFWorkbook(data_file)
    
    test = {
        'organization': u'',
        'department': u'',
        'department_head': u'',
        'study_form': u'',
        'faculty': u'',
        'specialty': u'',
        'discipline': u'',
        'subject': u'',
        'module': u'',
        'theme': u'',
        'year': u'',
        'address': u'',
        'responsible_person': u'',
        'email': u'',
        'phone': u'',
        'cabinet': u'',
        'source': u''}
    
    subthemes = dict()
       
    sheetBtz = wb.getSheet(u'БТЗ')
    if sheetBtz is None:
#         logging.error(u'Лист "БТЗ" не найден')
        raise Exception(u'Лист "БТЗ" не найден')
    
    # Наименование БТЗ     
    row = sheetBtz.getRow(0)
    cell = row.getCell(1)
    if cell is None or cell.getCellType() == Cell.CELL_TYPE_BLANK:
#         logging.error(u'Отсутствует наименование БТЗ')
        raise Exception(u'Лист "БТЗ", строка 0. Отсутствует наименование БТЗ.')
        
    qti_name = cleanString(row.getCell(1).toString())
    if qti_name == '':
        raise Exception(u'Лист "БТЗ", строка 0. Отсутствует наименование БТЗ.')
    
    # Получение параметров БТЗ
    for r in range(1, 17, 1):
        if r < 16:
            row = sheetBtz.getRow(r)
        else:
            row = sheetBtz.getRow(r + 3)
            
        cell = row.getCell(1)
        if cell != None:
            test[MATCHING_NUMBERS[r]] = cleanString(row.getCell(1).toString())
    
    # Получение списка тем    
    rows = 100000
    for r in range(21, rows, 1):
        row = sheetBtz.getRow(r)
        if (row != None):
            themeIdCell = row.getCell(0)
            if themeIdCell is None or themeIdCell.getCellType() == Cell.CELL_TYPE_BLANK:
                raise Exception(u'Лист "БТЗ", строка {}. Отсутствует ИД темы.'.format(r + 1))
            
            themeNameCell = row.getCell(1)
            if themeNameCell is None or themeNameCell.getCellType() == Cell.CELL_TYPE_BLANK:
                raise Exception(u'Лист "БТЗ", строка {}. Отсутствует наименование темы.'.format(r + 1))
            
            themeName = cleanString(row.getCell(1).toString())
            if themeName == '':
                raise Exception(u'Лист "БТЗ", строка {}. Отсутствует наименование темы.'.format(r + 1))
                
            subtheme = dict()
            themeId = int(themeIdCell.getNumericCellValue())
            subtheme['id'] = themeId
            subtheme['name'] = themeName
            subtheme['quest_type'] = u'none'
            subtheme['questions'] = []
            subtheme['quest_count'] = 0
            subthemes[themeId] = subtheme

    # Получение вопросов
    sheetQuestions = wb.getSheet(u'Вопросы')
    if sheetQuestions is None:
        raise Exception(u'Лист "Вопросы" не найден')
    
    question_types = {
        1: 'single',  
        2: 'multiple'    
#         3: 'compare',    
#         4: 'fill',
#         5: 'sort',
#         6: 'classify'
    }
    
    CORRECT_CHOISE = '*'
    
    rows = sheetQuestions.getPhysicalNumberOfRows()
    for r in range(1, rows, 1):
        row = sheetQuestions.getRow(r)
        if (row != None):
            themeIdCell = row.getCell(0)
            if themeIdCell is None or themeIdCell.getCellType() == Cell.CELL_TYPE_BLANK:
                raise Exception(u'Лист "Вопросы", строка {}. Отсутствует ИД темы.'.format(r + 1))
#                 break
            
            questionTypeCell = row.getCell(1)
            if questionTypeCell is None or questionTypeCell.getCellType() == Cell.CELL_TYPE_BLANK:
                raise Exception(u'Лист "Вопросы", строка {}. Отсутствует тип вопроса.'.format(r + 1))
#                 continue
            
            questionType = formatNumeric(questionTypeCell.getNumericCellValue())
            if not question_types.has_key(questionType):
                raise Exception(u'Лист "Вопросы", строка {}. Некорректный тип вопроса - {}.'.format(r + 1, questionType))
#                 continue
            
            questionTextCell = row.getCell(2)
            if questionTextCell is None or questionTextCell.getCellType() == Cell.CELL_TYPE_BLANK:
                raise Exception(u'Лист "Вопросы", строка {}. Отсутствует текст вопроса.'.format(r + 1))
#                 continue
            questionText = cleanString(questionTextCell.toString())
            if questionText == '':
                raise Exception(u'Лист "Вопросы", строка {}. Отсутствует текст вопроса.'.format(r + 1))
            
            question = dict()
            question['type'] = question_types[questionType]
            question['text'] = questionText
            question['choices'] = []
            
            # Получение ответов на вопросы
            has_correct_choice = False
            count_correct_choices = 0
            for c in range(3, 13, 1):
                cell = row.getCell(c)
                if cell != None:
                    choice = dict()
                    choice['correct'] = 'false'
                    
                    cellType = cell.getCellType()
                    if cellType == Cell.CELL_TYPE_STRING:
                        cellText = cleanString(cell.toString())
                        if cellText != '':
                            if cellText.startswith(CORRECT_CHOISE):
                                choice['correct'] = 'true'
                                choice['text'] = cellText.strip(CORRECT_CHOISE)
                                has_correct_choice = True
                                count_correct_choices = count_correct_choices + 1
                            else:    
                                choice['text'] = cellText
                                
                            question['choices'].append(choice)
                        
                    elif cellType == Cell.CELL_TYPE_NUMERIC:
                        choice['text'] = str(formatNumeric(cell.getNumericCellValue()))
                    
                        question['choices'].append(choice)
            
            if questionType == 1 and not has_correct_choice:
                raise Exception(u'Лист "Вопросы", строка {}. Отсутствует правильный (отмеченный знаком "*") вариант ответа на вопрос.'.format(r + 1))
            
            elif questionType == 1 and count_correct_choices > 1:
                raise Exception(u'Лист "Вопросы", строка {}. Должен быть только один правильный (отмеченный знаком "*") вариант ответа на вопрос.'.format(r + 1))
            
            elif questionType == 2 and not has_correct_choice:
                raise Exception(u'Лист "Вопросы", строка {}. Отсутствует хотя бы один правильный (отмеченный знаком "*") вариант ответа на вопрос.'.format(r + 1))
            
            elif len(question['choices']) > 0:
                subthemes[int(themeIdCell.getNumericCellValue())]['questions'].append(question)
            else:
                raise Exception(u'Лист "Вопросы", строка {}. Отсутствуют варианты ответа на вопрос.'.format(r + 1))
    
    test['subthemes'] = subthemes.values()

    return (qti_name, test)


def formatNumeric(f):
    trunc = int(f)
    if (f - trunc) != 0 :
        return f
    else:
        return trunc

    
def convertTestHtml(data_file, filename, QtiPackage, QtiPackageAttribute, QtiTheme, QtiQuestion, QtiDestructor, param = "med"):
    
    TEST_JSON_FUNCTIONS = {
        "med": getTestJsonFromFile,
        "mv": getTestJsonFromFile2,
        'excel': getTestJsonFromExcel}
    
    begin = time.time()
#     test_json = getTestJsonFromFile(data_file)

    test = TEST_JSON_FUNCTIONS[param](data_file)

    test_json = test[1]
    
    test_name = filename if param == "med" else test[0]
    
    print 'HTML parsing finish', time.time() - begin
#     print test_json
    package_version = 1
    QtiPackage.setRange('PackageName',test_name)
    if QtiPackage.count() > 0:
        QtiPackage.first()
        packageID = QtiPackage.PackageID
        package_version = QtiPackage.PackageVersion + 1 
        QtiPackage.PackageVersion = package_version
        QtiPackage.update
        
#         QtiTheme.setRange('PackageID',QtiPackage.PackageID)
#         for theme in QtiTheme.iterate():
#             QtiQuestion.setRange('ThemeID',theme.ThemeID)
#             for question in QtiQuestion.iterate():
#                 QtiDestructor.setRange('QuestionID',question.QuestionID)
#                 QtiDestructor.deleteAll()
#             QtiQuestion.deleteAll()
#         QtiTheme.deleteAll()
#     
#         QtiPackageAttribute.setRange('PackageID',QtiPackage.PackageID)
#         QtiPackageAttribute.deleteAll()
#         
#         QtiPackage.delete()
    else:    
        packageID = unicode(uuid4())
        QtiPackage.PackageID = packageID
        QtiPackage.PackageName = test_name
        QtiPackage.ConversionStatus = 0
        QtiPackage.PackageVersion = package_version
        QtiPackage.DateCreated = datetime.datetime.now()
        QtiPackage.Status = u'forming'
        QtiPackage.insert()

  
    for rec in MATCHING_ATTRIBUTE_PACKAGE:
        QtiPackageAttribute.setRange('PackageID', packageID)
        QtiPackageAttribute.setRange('AttributeName', MATCHING_ATTRIBUTE_PACKAGE[rec])
        if QtiPackageAttribute.tryFirst():
            QtiPackageAttribute.AttributeValue = test_json[rec][0:254] if test_json[rec]  else u''
            QtiPackageAttribute.update()
        else:
            QtiPackageAttribute.AttributeID = unicode(uuid4())
            QtiPackageAttribute.AttributeName = MATCHING_ATTRIBUTE_PACKAGE[rec]
            QtiPackageAttribute.AttributeValue = test_json[rec][0:254] if test_json[rec]  else u''
            QtiPackageAttribute.PackageID = packageID
            QtiPackageAttribute.insert()
    
    subthemeCount = 1
    for subtheme in test_json['subthemes']:
        themeName = subtheme['name']
        
        QtiTheme.setRange('PackageID', packageID)
        if QtiTheme.count() > 0:
            subthemeCount = QtiTheme.count() + 1
        
        QtiTheme.setRange('ThemeName', subtheme['name']) 
        if QtiTheme.tryFirst():
            themeName = u"{} copy".format(subtheme['name'])
   
        QtiTheme.clear()        
        QtiTheme.ThemeID = unicode(uuid4())
        QtiTheme.ThemeName = themeName
        QtiTheme.PackageID = packageID
        QtiTheme.ThemeOrder = subthemeCount
        QtiTheme.insert()
        subthemeCount += 1
        questionCount = 1
        for question in subtheme['questions']:
            QtiQuestion.QuestionID = unicode(uuid4())
            QtiQuestion.QuestionHtml = question['text']
            
            if subtheme['quest_type'] == u'none':
                questionType = question['type']
            else:
                questionType = subtheme['quest_type']
            
            if questionType == u'single':
                QtiQuestion.QuestionType = 1
            else:
                QtiQuestion.QuestionType = 2
                
            QtiQuestion.ThemeID = QtiTheme.ThemeID
            QtiQuestion.QuestionOrder = questionCount
            questionCount += 1
            QtiQuestion.insert()
            destructorCount = 1
            for choice in question['choices']:
                QtiDestructor.DestructorID = unicode(uuid4())
                QtiDestructor.DestructorHtml = choice['text']
                QtiDestructor.QuestionID = QtiQuestion.QuestionID
                QtiDestructor.DestructorOrder = destructorCount
                destructorCount += 1
                if choice['correct'] == 'true':
                    QtiDestructor.IsTrue = True
                else:
                    QtiDestructor.IsTrue = False
                QtiDestructor.insert()
    print 'Database updated', time.time() - begin
    return packageID


def getPrevNextQuestionId(context, variant_questionCur, variant_question_id):
    u'''Функция поиска предыдущего и следующего вопроса '''
    variant_questionCur.setRange("QuestionNumber",1)
    variant_questionCur.first()
    
    first_question_id = variant_questionCur.VariantQuestionID
    
    variant_questionCur.setRange("QuestionNumber")
    variant_questionCur.get(variant_question_id)
    
    next_question_id = 'ID_FINISH'
    if variant_questionCur.navigate('>'):
        next_question_id = variant_questionCur.VariantQuestionID
    
    prev_question_id = first_question_id
    variant_questionCur.get(variant_question_id)
    if variant_questionCur.navigate('<'):
        prev_question_id = variant_questionCur.VariantQuestionID
    
    return prev_question_id, next_question_id


def getNotAsweredQuestions(context, main=None, add=None, filterinfo=None, session=None, data=None):
    u'''Получение количества неотвеченных вопросов'''
    data_json = json.loads(data)['schema']
    
    variant_id = None
    if data_json.get('description'):
        variant_id = data_json['description']['variantId']
    elif data_json.get('testDescription'):
        variant_id = data_json['testDescription']['variantId']
    
    count = 0
    QtiVariantQuestionCur = QtiVariantQuestionCursor(context)
    QtiVariantQuestionCur.setRange('VariantID', variant_id)
    QtiVariantQuestionCur.setRange('HasAnswer', False)
    count = QtiVariantQuestionCur.count()
    QtiVariantQuestionCur.setRange('HasAnswer', None)
    count += QtiVariantQuestionCur.count()
    QtiVariantQuestionCur.close()
    message = {"schema": {"@xmlns":"", "count": count}}
    return XMLJSONConverter.jsonToXml(json.dumps(message))


def getQuestionImg(context, img_id):
    question_img = ''
    if img_id:
        QtiResource = QtiResourceCursor(context)
        images_json = json.loads(getImageById(QtiResource, img_id, isImage=False))
        QtiResource.close()
        if images_json:
            image_files = {'image_file': []}
            file_cursor = fileCursor(context)
            image_items = images_json['question_img']
            if isinstance(image_items, dict):
                image_items = [image_items]
            for item in image_items:
                image_id = item['@id']
                if file_cursor.tryGet(image_id):                        
                    image_files['image_file'].append({'#text': '/'.join(getRelativePathById(context, str(image_id), file_version_id=None))})                    
                else:
                    image_files['image_file'].append({'#text': ''})
            file_cursor.close()
            question_img = image_files
    return question_img


def getTimeJs(time_start, time_end):
    calNow = Calendar.getInstance()
    time_start = Date()
    calNow.setTime(time_start)    
    calEnd = Calendar.getInstance()
    calEnd.setTime(time_end)
    
    #Скрипт, который вставляет счётчик оставшегося времени
    js = '''javascript:injectCounter(new Date(%s,%s,%s,%s,%s,%s),new Date(%s,%s,%s,%s,%s,%s))'''%\
    (calEnd.get(Calendar.YEAR),calEnd.get(Calendar.MONTH),calEnd.get(Calendar.DAY_OF_MONTH),\
     calEnd.get(Calendar.HOUR_OF_DAY),calEnd.get(Calendar.MINUTE),calEnd.get(Calendar.SECOND),\
     calNow.get(Calendar.YEAR),calNow.get(Calendar.MONTH),calNow.get(Calendar.DAY_OF_MONTH),\
     calNow.get(Calendar.HOUR_OF_DAY),calNow.get(Calendar.MINUTE),calNow.get(Calendar.SECOND))
    return js

def getVariantParams(context, variant_question_id):
    params = {}
    QtiVariantQuestion = QtiVariantQuestionCursor(context)
    QtiVariantQuestion.get(variant_question_id)
    params['variant_id'] = QtiVariantQuestion.VariantID
    params['question_id'] = QtiVariantQuestion.QuestionID
    params['has_answer'] = 1 if QtiVariantQuestion.HasAnswer else 0
    QtiVariantQuestion.setRange('VariantID', params['variant_id'])
    QtiVariantQuestion.orderBy('QuestionNumber')
    params['question_number'] = QtiVariantQuestion.QuestionNumber
    params['question_count'] = QtiVariantQuestion.count()
    
    params['prev_variant_question_id'], params['next_variant_question_id'] = getPrevNextQuestionId(context, QtiVariantQuestion, variant_question_id)
    QtiVariantQuestion.close()
    return params


def getQuestionFields(context, question_id):
    fields = {}
    QtiQuestion = QtiQuestionCursor(context)
    QtiQuestion.get(question_id)
    fields['question_html'] = QtiQuestion.QuestionHtml if QtiQuestion.QuestionHtml is not None else ''
    fields['question_html2'] = QtiQuestion.QuestionHtml2 if QtiQuestion.QuestionHtml2 is not None else ''
    fields['img_id'] = QtiQuestion.QuestionImg
    QtiQuestion.close()
    return fields

def getTestingAlgorithm(context, variant_id):
    testing_algorithm = u'Тестирование'
    TVA = TestingVariantAssignCursor(context)
    TVA.get(variant_id)
    if TVA.AlgorithmTestingID == 1:
        testing_algorithm = u'Тестирование для самоконтроля'
    elif TVA.AlgorithmTestingID == 2:
        testing_algorithm = u'Аудиторное тестирование'
    TVA.close()
    return testing_algorithm

def getVariantAnswerParams(context, variant_id):
    params = {}
    QtiVariantAnswer = QtiVariantAnswerCursor(context)    
    QtiVariantAnswer.setRange('VariantID', variant_id)
    QtiVariantAnswer.first()    
    #Тестирование завершилось или нет
    params['timeEnd'] = QtiVariantAnswer.TimeEnd
    params['timeout'] = '1' if QtiVariantAnswer.TimeEnd < Date() else '0'
    if QtiVariantAnswer.TimeFinish is None and QtiVariantAnswer.TimeEnd < Date():
        params['testingStatus'] = '2'
    elif QtiVariantAnswer.TimeFinish is not None:
        params['testingStatus'] = '1'
    else:
        params['testingStatus'] = '0'
    QtiVariantAnswer.close()
    return params