# coding: utf-8
'''
Created on 21.10.2016
 
@author: d.gulyakin
'''
 
import json
import string
from java.io import ByteArrayOutputStream,\
    FileNotFoundException, IOException, ByteArrayInputStream, StringBufferInputStream,\
    FileInputStream
from javax.xml.parsers import ParserConfigurationException
from org.xml.sax import SAXException, InputSource
from java.text import SimpleDateFormat
from qt._qt_orm import QtiVariantAnswerCursor, QtiVariantCursor,\
    QtiVariantQuestionCursor, QtiQuestionCursor, QtiThemeCursor,\
    QtiVariantDestructorCursor, QtiVariantDestructorAnswerCursor,\
    QtiDestructorCursor
from common.numbersseries.getNextNo import getSeriesParams
from umk._umk_orm import specialityCursor
from nci._nci_orm import personCursor, test_matrixCursor
try:
    from ru.curs.showcase.core.jython import JythonDTO, JythonDownloadResult, JythonErrorResult
except:
    from ru.curs.celesta.showcase import JythonDTO, JythonDownloadResult, JythonErrorResult
 
#XDocReport
from fr.opensagres.xdocreport.template import TemplateEngineKind
from fr.opensagres.xdocreport.document.registry import XDocReportRegistry
from fr.opensagres.xdocreport.core import XDocReportException
from freemarker.ext.dom import NodeModel
     
from ru.curs.celesta.showcase.utils import XMLJSONConverter
from ru.curs.showcase.core import UserMessageFactory
from ru.curs.showcase.app.api.grid import GridSaveResult
from common.sysfunctions import toHexForXml, getGridHeight
from edu._edu_orm import educational_groupCursor, vw_edu_group_personsCursor,\
    edu_group_personsCursor
# from security.functions import userHasPermission
 
_header = {"id": ["~~id"],
           "name": [u'Группа/Слушатель'],
           "login": [u'Имя пользователя'],
           "test": [u'Тестирование'],
           "time_begin": [u'Время начала'],
           "time_end": [u'Время окончания'],
           "time_finish": [u'Время завершения'],
           "testing_mark": [u'Оценка'],
           "score": [u'Процент правильных ответов'],
           "positive_count": [u'Количество правильных ответов'],
           "create_date": [u'Дата создания группы'],
           "HasChildren": [u'HasChildren'],               
           'properties': [u'properties']}
 
_statuses = {
    0: u'Начать тестирование',
    30: u'Завершить тестирование',
    31: u'Тестирование завершено'}
 
def getTreeData(context, main, add, filterinfo, session, elementId, sortColumnList, firstrecord, pagesize, parentId=None):
    u'''Функция получения данных для tree-грида. '''    
    for column in _header:
        _header[column].append(toHexForXml(_header[column][0]))
         
    data = {"records":{"rec":[]}}    
     
    speciality = main
     
    if parentId is None:
        groupsCur = educational_groupCursor(context)
        groupsCur.setRange('speciality', speciality)
        # Проходят обучение
        groupsCur.orderBy('control_date DESC', 'group_name')
         
        persons = edu_group_personsCursor(context)
                
        for group in groupsCur.iterate():
            row = {}
            row[_header["id"][1]] = group.group_id   
#             row[_header["name"][1]] = u'{}. {}'.format(group.group_id, group.group_name)
            row[_header["name"][1]] = string.join(filter(None, [group.group_name, group.kafedra_name, group.stud_spec_name, group.edu_level_name]), ', ')
            row[_header["create_date"][1]] = SimpleDateFormat("dd.MM.yyyy").format(group.create_date)
            status = group.status
            status_name = _statuses[group.status]
            if status==0:
                persons.setRange('group_id', group.group_id)
                if persons.count() > 0:                    
                    row[_header["test"][1]] = {'div':{'ins':{'font':{"@style": "cursor: pointer !important",
                                                        '@color': 'blue',
                                                        '@title': status_name,
                                                        '#text': status_name}}}}
                     
                    row[_header['properties'][1]] = {'event': {
                                    '@name': 'cell_single_click',
                                    '@column': _header['test'][0],
                                    'action': {
                                        '@show_in': 'MODAL_WINDOW',
                                        '#sorted': [
                                            {'main_context': 'current'},
                                            {'modalwindow': {
                                                '@caption': group.group_name,
                                                '@height': '390',
                                                '@width': '640'}},
                                            {'datapanel': {
                                                '@type': 'current',
                                                '@tab': 'current',
                                                'element': {
                                                    '@id': 'startTestCard',
                                                    'add_context': group.group_id}}}]}}}
                else:
                    row[_header["test"][1]] = {'div':{'ins':{'font':{"@style": "cursor: pointer !important",
                                                        '@color': 'blue',
                                                        '@title': u'Добавить слушателей в группу для проведения тестирования',
                                                        '#text': u'Добавить слушателей'}}}}
                     
                    row[_header['properties'][1]] = {'event': {
                                    '@name': 'cell_single_click',
                                    '@column': _header['test'][0],
                                    'action': {
                                        '@show_in': 'MODAL_WINDOW',
                                        '#sorted': [
                                            {'main_context': 'current'},
                                            {'modalwindow': {
                                                '@caption': u'{}. Добавление слушателей'.format(group.group_name),
                                                '@height': '600',
                                                '@width': '980'}},
                                            {'datapanel': {
                                                '@type': 'current',
                                                '@tab': 'current',
                                                'element': {
                                                    '@id': 'addTestGroupPersonsCard',
                                                    'add_context': group.group_id}}}]}}}
            elif status==30:
                # Кнопка для завершения тестирования
                row[_header["test"][1]] = {'div':{'ins':{'font':{"@style": "cursor: pointer !important",
                                                        '@color': 'blue',
                                                        '@title': status_name,
                                                        '#text': status_name}}}}
                 
                row[_header['properties'][1]] = {'event': {
                                    '@name': 'cell_single_click',
                                    '@column': _header['test'][0],
                                    'action': {
                                        '@show_in': 'MODAL_WINDOW',
                                        '#sorted': [
                                            {'main_context': 'current'},
                                            {'modalwindow': {
                                                '@caption': group.group_name,
                                                '@height': '120',
                                                '@width': '600'}},
                                            {'datapanel': {
                                                '@type': 'current',
                                                '@tab': 'current',
                                                'element': {
                                                    '@id': 'endTestCard',
                                                    'add_context': group.group_id}}}]}}}
            elif status == 31:
                row[_header["testing_mark"][1]] = {'span': {'#text': u'Результаты',
                                                           '@style': 'padding: 2px 5px 0 0;'}}
                row[_header["test"][1]] = status_name
            else:
                row[_header["test"][1]] = status_name
                 
            data["records"]["rec"].append(row)
             
        groupsCur.close()
        persons.close()
    else:
        _params = getSeriesParams(context, 'persons')
        prefix = _params['prefix']
        postfix = _params['postfix']
        dateFormat = SimpleDateFormat("dd.MM.yyyy HH:mm")
        group_personsCur = vw_edu_group_personsCursor(context)
        group_personsCur.setRange('group_id', parentId)
        group_personsCur.orderBy('surname', 'name', 'patronymic')
        variantAnsverCur = QtiVariantAnswerCursor(context)
        variantCur = QtiVariantCursor(context)
        for person in group_personsCur.iterate():
            person_id = person.person_id
            row = {}
            row[_header["id"][1]] = "{}.{}".format(person.group_person_id, person_id)       
            row[_header["name"][1]] = u'{} {} {}'.format(person.surname, person.name, person.patronymic)
            row[_header["login"][1]] = person_id.replace(prefix, '').replace(postfix, '')
            variantAnsverCur.setRange('VariantID', person.variant_id)
            if variantAnsverCur.tryFirst():
                if variantCur.tryGet(variantAnsverCur.VariantID):
                    row[_header["test"][1]] = variantCur.VariantName
                time_begin = variantAnsverCur.TimeBegin
                time_end = variantAnsverCur.TimeEnd
                time_finish = variantAnsverCur.TimeFinish
                row[_header["time_begin"][1]] = dateFormat.format(time_begin) if time_begin else None
                row[_header["time_end"][1]] = dateFormat.format(time_end) if time_end else None
                row[_header["time_finish"][1]] = dateFormat.format(time_finish) if time_finish else None
                row[_header["testing_mark"][1]] = {'span': {'#text': variantAnsverCur.TestingMark,
                                                           '@style': 'padding: 2px 5px 0 0;'}}
                row[_header["score"][1]] = variantAnsverCur.Score
                row[_header["positive_count"][1]] = {'span': {'#text': variantAnsverCur.PositiveCount,
                                                           '@style': 'padding: 2px 5px 0 0;'}}
            row[_header["HasChildren"][1]] = False
    #         row[_header['properties'][1]] = {'event': {}}  
            data["records"]["rec"].append(row)
        group_personsCur.close()
        variantAnsverCur.close()
         
    res = XMLJSONConverter.jsonToXml(json.dumps(data))
    return JythonDTO(res, None)
 
 
def getTreeSettings(context, main, add, filterinfo, session, elementId):
    u'''Функция получения настроек для tree-грида. '''
    panelWidth = '100%'
    panelHeight = getGridHeight(session, numberOfGrids = 1, delta=65)
 
#     speciality = main
#     groupsCur = educational_groupCursor(context)
#     groupsCur.setRange('speciality', speciality)
    # Проходят обучение
#     totalCount = studentCur.count()
    totalCount = 0
#     groupsCur.close()
 
    # Определяем список полей таблицы для отображения
    settings = {}
    settings["gridsettings"] = {
#         "labels": {'header': u'ЭОР по теме'},
        "columns": {"col":[
                        {"@id":_header["name"][0], "@width":"240px"},
#                         {"@id":_header["login"][0], "@width":"100px", "@horAlign": 'CENTER'},
                        {"@id":_header["test"][0], "@width":"100px", "@horAlign": 'CENTER'},
                        {"@id":_header["testing_mark"][0], "@width":"70px", "@horAlign": 'CENTER', "@type":"DOWNLOAD", "@linkId":"downloadStudentFile"},
                        {"@id":_header["time_begin"][0], "@width":"80px", "@horAlign": 'CENTER'},
                        {"@id":_header["time_end"][0], "@width":"80px", "@horAlign": 'CENTER'},
                        {"@id":_header["time_finish"][0], "@width":"80px", "@horAlign": 'CENTER'},
                        {"@id":_header["score"][0], "@width":"60px", "@horAlign": 'CENTER'},
                        {"@id":_header["positive_count"][0], "@width":"60px", "@horAlign": 'CENTER', "@type":"DOWNLOAD", "@linkId":"downloadStudentAppealFile"},
                        {"@id":_header["create_date"][0], "@width":"80px", "@horAlign": 'CENTER'}
                    ]},
        "properties": {
            "@gridHeight": panelHeight,
            "@gridWidth": panelWidth,
            "@pagesize": "25",
            "@totalCount": totalCount}}
     
    res_set = XMLJSONConverter.jsonToXml(json.dumps(settings))
    return JythonDTO(None, res_set)
 
 
def gridToolBar(context, main=None, add=None, filterinfo=None, session=None, elementId=None): 
    try:
        row = json.loads(session)['sessioncontext']['related']['gridContext']["currentRecordId"]
    except:
        row = None
     
    status_id = -1
    group_name = u''
    if row:
        groupsCur = educational_groupCursor(context)
        if groupsCur.tryGet(row):
            status_id = groupsCur.status
            group_name = groupsCur.group_name
 
    grid_toolbar = {
        'gridtoolbar': {
            '#sorted': [                           
                {'item': {
                    '@text': u'Импорт групп',
                    '@hint': u'Импорт групп тестирования',
                    '@disable': 'false',
                    'action': {
                        '@show_in': 'MODAL_WINDOW',
                        '#sorted': [
                            {'main_context': 'current'},
                            {'modalwindow': {
                                '@caption': u'Импорт групп тестирования',
                                "@height":"200",
                                "@width":"460"}},
                            {'datapanel': {
                                '@type': 'current',
                                '@tab': 'current',
                                'element': {
                                    '@id': 'importTestGroupsCard',
                                    'add_context': row}}}]}}},
                {'item': {
                    '@text': u'Добавить группу',
                    '@hint': u'Добавление группы тестирования',
                    '@disable': 'false',
                    'action': {
                        '@show_in': 'MODAL_WINDOW',
                        '#sorted': [
                            {'main_context': 'current'},
                            {'modalwindow': {
                                '@caption': u'Добавление группы тестирования',
                                "@height":"400",
                                "@width":"460"}},
                            {'datapanel': {
                                '@type': 'current',
                                '@tab': 'current',
                                'element': {
                                    '@id': 'addTestGroupCard',
                                    'add_context': row}}}]}}},
                {'item': {
                    '@text': u'Добавить слушателей',
                    '@hint': u'Добавление слушателей в группу для проведения тестирования',
                    '@disable': 'false' if status_id == 0 else 'true',
                    'action': {
                        '@show_in': 'MODAL_WINDOW',
                        '#sorted': [
                            {'main_context': 'current'},
                            {'modalwindow': {
                                '@caption': u'{}. Добавление слушателей'.format(group_name),
                                "@height":"600",
                                "@width":"980"}},
                            {'datapanel': {
                                '@type': 'current',
                                '@tab': 'current',
                                'element': {
                                    '@id': 'addTestGroupPersonsCard',
                                    'add_context': row}}}]}}}]}}
  
    return XMLJSONConverter.jsonToXml(json.dumps(grid_toolbar))


def downloadStudentAppealFile(context, main=None, add=None, filterinfo=None, session=None, elementId=None, recordId=None):
    u'''процедура получения файла апелляции'''
    grainPath = context.grain.getGrainPath().toString()
    try:
        template = FileInputStream(grainPath + '/template/appeal.docx')
            
        if template:     
            data = getStudentAppealData(context, main, recordId)
            dataXML = XMLJSONConverter.jsonToXml(json.dumps(data)).encode("utf-8")
            
            fileName = u'Апелляция-{}-{}.docx'.format(data['student']['group'], data['student']['fio']).replace(':', '_').replace(',', '_').replace(' ', '_')
             
#             result = open(grainPath + '/template/dataOut.xml', 'rb+')
#             try:
#                 result.write(dataXML)
#             finally:
#                 result.close() 
             
            report = XDocReportRegistry.getRegistry().loadReport(template, TemplateEngineKind.Freemarker)
            reportContext = report.createContext()
             
            projectInputStream = StringBufferInputStream(dataXML)    
            projectInputSource = InputSource(projectInputStream)
            project = NodeModel.parse(projectInputSource)    
            reportContext.put("doc", project) 
                 
            out = ByteArrayOutputStream()
            report.process(reportContext, out)
            bytesOut = out.toByteArray()
            out.close()
                          
            return JythonDownloadResult(ByteArrayInputStream(bytesOut), fileName)
        else:
            return context.error(u"Шаблон отсутствует")  
 
    except FileNotFoundException as err:
        return context.error(u"Ошибка: %s" % format(err))
    except IOException as err:
        return context.error(u"Ошибка: %s" % format(err))
    except XDocReportException as err:
        return context.error(u"Ошибка: %s" % format(err)) 
    except ParserConfigurationException as err:
        return context.error(u"Ошибка: %s" % format(err))
    except SAXException as err:
        return context.error(u"Ошибка: %s" % format(err))
    

def getStudentAppealData(context, speciality_id, recordId):
    speciality_name = u''
    fio = u''
    group_name = u''
    testing_date = ''
    testing_time_start = '00:00'
    testing_time_end = '00:00'
    testing_score = ''
    test_config_id = ''
    test_config_name = u''
    correct_questions = 0
    all_questions = 0
    
    letters = [u'А',u'Б',u'В',u'Г',u'Д',u'Е',u'Ж',u'З',u'И',u'К']
     
    row = recordId.split('.')
    group_person_id = row[0]
    person_id = row[1]
     
    specialityCur = specialityCursor(context)
    if specialityCur.tryGet(speciality_id):
        speciality_name = specialityCur.name
    specialityCur.close()
     
    personCur = personCursor(context)
    if personCur.tryGet(person_id):
        fio = personCur.fullname
    personCur.close()
     
    variant_id = None
    group_id = None
    group_personsCur = edu_group_personsCursor(context)
    if group_personsCur.tryGet(group_person_id):
        variant_id = group_personsCur.variant_id
        group_id = group_personsCur.group_id
    group_personsCur.close()
    
    if group_id:
        groupCur = educational_groupCursor(context)
        if groupCur.tryGet(group_id):
            group_name = groupCur.group_name
            test_config_id = groupCur.package_id
        groupCur.close()
        
        test_matrixCur = test_matrixCursor(context)
        if test_matrixCur.tryGet(test_config_id):
            test_config_name = test_matrixCur.tm_name
        test_matrixCur.close()
     
    questions = {'row': []}
    if variant_id:
        variant_answerCur = QtiVariantAnswerCursor(context)
        variant_answerCur.setRange('VariantID', variant_id)
        if variant_answerCur.tryFirst():
            testing_date = SimpleDateFormat("dd.MM.yyyy HH:mm").format(variant_answerCur.TimeBegin) if variant_answerCur.TimeBegin else ''
            testing_time_start = SimpleDateFormat("HH:mm").format(variant_answerCur.TimeBegin) if variant_answerCur.TimeBegin else '00:00'
            testing_time_end = SimpleDateFormat("HH:mm").format(variant_answerCur.TimeEnd) if variant_answerCur.TimeEnd else'00:00'
            testing_score = variant_answerCur.Score    
        variant_answerCur.close()
         
        variant_questionCur = QtiVariantQuestionCursor(context)
        variant_questionCur.setRange('VariantID', variant_id)
        variant_questionCur.orderBy('QuestionNumber')
        
        questionCur = QtiQuestionCursor(context)
        all_questions = variant_questionCur.count()
        
        QtiVariantDestructor = QtiVariantDestructorCursor(context)
        QtiVariantDestructorAnswer = QtiVariantDestructorAnswerCursor(context)
        QtiDestructor = QtiDestructorCursor(context)
        
        for variant_questionCur in variant_questionCur.iterate():
            allAnswers = []
            question_text = u''
            answer = []            
            allAnswers = []
            if questionCur.tryGet(variant_questionCur.QuestionID):
                question_text = questionCur.QuestionHtml if questionCur.QuestionHtml is not None else ''
            
            if variant_questionCur.QuestionType == 1:
                letter = 0
                QtiVariantDestructor.setRange('VariantQuestionID', variant_questionCur.VariantQuestionID)
                QtiVariantDestructor.orderBy('VariantDestructorID')
                for QtiVariantDestructor in QtiVariantDestructor.iterate():       
                    QtiVariantDestructorAnswer.setRange('VariantDestructorID', QtiVariantDestructor.VariantDestructorID)
                    if QtiDestructor.tryGet(QtiVariantDestructor.DestructorID):
                        allAnswers.append({'answer_letter': letters[letter], 'answer_text': QtiDestructor.DestructorHtml, 'isTrue': QtiDestructor.IsTrue})
                        if QtiVariantDestructorAnswer.tryFirst():
                            answer.append({'answer_letter': letters[letter], 'answer_text':QtiDestructor.DestructorHtml})
                    letter += 1
                
                
                
                       
            if variant_questionCur.CorrectAnswer:
                correct = u'Да' 
                correct_questions += 1
            else:
                correct = u'Нет'        
            questions['row'].append({'number': variant_questionCur.QuestionNumber, 'question_text': question_text, 'answer': answer, 'correct': correct, 'allAnswers': allAnswers})
            
        
        QtiVariantDestructor.close()
        QtiVariantDestructorAnswer.close()
        QtiDestructor.close()
        questionCur.close()
        variant_questionCur.close()
     
    data = {'student': {
                'fio': fio,
                'speciality': speciality_name,
                'group': group_name,
                'testing_date': testing_date,
                'testing_time': '{} - {}'.format(testing_time_start, testing_time_end),
                'testing_score': testing_score,
                'test_config_name': test_config_name,
                'correct_questions': correct_questions,
                'all_questions': all_questions,
                'questions': questions}} 

    return data
    
 
def downloadStudentFile(context, main=None, add=None, filterinfo=None, session=None, elementId=None, recordId=None):
    u'''процедура получения файлов'''
    grainPath = context.grain.getGrainPath().toString()
    try:
        is_group = recordId.find('.')
        if is_group == -1:
            template = FileInputStream(grainPath + '/template/protocolGroup.docx')
            data = getStudentsGroupData(context, main, recordId)
            fileName = u'{}.docx'.format(data['group']['name']).replace(':', '_').replace(',', '_').replace(' ', '_')
        else:
            template = FileInputStream(grainPath + '/template/protocol.docx')
            data = getStudentData(context, main, recordId)
            fileName = u'{}-{}.docx'.format(data['student']['group'], data['student']['fio']).replace(':', '_').replace(',', '_').replace(' ', '_')
            
        if template:             
            dataXML = XMLJSONConverter.jsonToXml(json.dumps(data)).encode("utf-8")
             
#             result = open(grainPath + '/template/dataOut.xml', 'rb+')
#             try:
#                 result.write(dataXML)
#             finally:
#                 result.close() 
             
            report = XDocReportRegistry.getRegistry().loadReport(template, TemplateEngineKind.Freemarker)
            reportContext = report.createContext()
             
            projectInputStream = StringBufferInputStream(dataXML)    
            projectInputSource = InputSource(projectInputStream)
            project = NodeModel.parse(projectInputSource)    
            reportContext.put("doc", project) 
                 
            out = ByteArrayOutputStream()
            report.process(reportContext, out)
            bytesOut = out.toByteArray()
            out.close()
                          
            return JythonDownloadResult(ByteArrayInputStream(bytesOut), fileName)
        else:
            return context.error(u"Шаблон отсутствует")  
 
    except FileNotFoundException as err:
        return context.error(u"Ошибка: %s" % format(err))
    except IOException as err:
        return context.error(u"Ошибка: %s" % format(err))
    except XDocReportException as err:
        return context.error(u"Ошибка: %s" % format(err)) 
    except ParserConfigurationException as err:
        return context.error(u"Ошибка: %s" % format(err))
    except SAXException as err:
        return context.error(u"Ошибка: %s" % format(err))        


def getStudentsGroupData(context, speciality_id, recordId):
    group_id = recordId
    group_name = u''
    groupCur = educational_groupCursor(context)
    if groupCur.tryGet(group_id):
        group_name = groupCur.group_name
    groupCur.close()
    
    group_personsCur = vw_edu_group_personsCursor(context)
    group_personsCur.setRange('group_id', group_id)
    group_personsCur.orderBy('surname', 'name', 'patronymic')
    
    group_data = {'group': {
                    'name': group_name,
                    'scount': 0,
                    'students': []}}
    i = 0
    for person in group_personsCur.iterate():
        i += 1
        row_id = "{}.{}".format(person.group_person_id, person.person_id)
        student_data = getStudentData(context, speciality_id, row_id)
        student_data['student']['index'] = i
        group_data['group']['students'].append(student_data['student'])
    group_data['group']['scount'] = i
    
    return group_data

        
def getStudentData(context, speciality_id, recordId):
    speciality_name = u''
    fio = u''
    group_name = u''
    testing_date = ''
    testing_date_by_group = ''
    testing_time_start = '00:00'
    testing_time_end = '00:00'
    testing_score = '0'
    edu_level_name = ''
    stud_spec_name = ''
    kafedra_name = ''
    positive_count = '0'
    mark = 'неудовлетворительно'
    params = None
    
     
    row = recordId.split('.')
    group_person_id = row[0]
    person_id = row[1]
     
    specialityCur = specialityCursor(context)
    if specialityCur.tryGet(speciality_id):
        speciality_name = specialityCur.name
    specialityCur.close()
     
    personCur = personCursor(context)
    if personCur.tryGet(person_id):
        fio = personCur.fullname
    personCur.close()
     
    variant_id = None
    group_id = None
    group_personsCur = edu_group_personsCursor(context)
    if group_personsCur.tryGet(group_person_id):
        variant_id = group_personsCur.variant_id
        group_id = group_personsCur.group_id
    group_personsCur.close()
    
    if group_id:
        groupCur = educational_groupCursor(context)
        if groupCur.tryGet(group_id):
            group_name = groupCur.group_name
            kafedra_name = groupCur.kafedra_name
            stud_spec_name = groupCur.stud_spec_name
            edu_level_name = groupCur.edu_level_name
            params = json.loads(groupCur.params)
            testing_date_by_group = SimpleDateFormat("dd.MM.yyyy").format(groupCur.control_date)
        groupCur.close()
     
    questions = {'row': []}
    if variant_id:
        variant_answerCur = QtiVariantAnswerCursor(context)
        variant_answerCur.setRange('VariantID', variant_id)
        if variant_answerCur.tryFirst():
            testing_date = SimpleDateFormat("dd.MM.yyyy").format(variant_answerCur.TimeBegin) if variant_answerCur.TimeBegin else ''
            testing_time_start = SimpleDateFormat("HH:mm").format(variant_answerCur.TimeBegin) if variant_answerCur.TimeBegin else '00:00'
            testing_time_end = SimpleDateFormat("HH:mm").format(variant_answerCur.TimeEnd) if variant_answerCur.TimeEnd else'00:00'
            testing_score = variant_answerCur.Score    
            positive_count = variant_answerCur.PositiveCount    
        variant_answerCur.close()
         
        variant_questionCur = QtiVariantQuestionCursor(context)
        variant_questionCur.setRange('VariantID', variant_id)
        variant_questionCur.orderBy('QuestionNumber')
        
        questionCur = QtiQuestionCursor(context)
        themeCur = QtiThemeCursor(context)
        
        row = {'question': []}
        num = 0
        for variant_questionCur in variant_questionCur.iterate():
            theme_num = ''
            question_num = ''
            
            if questionCur.tryGet(variant_questionCur.QuestionID):
                question_num = str(questionCur.QuestionOrder).zfill(4)
                theme_id = questionCur.ThemeID
                if themeCur.tryGet(theme_id):
                    theme_num = str(themeCur.ThemeOrder).zfill(2)
            
            answer = '+' if variant_questionCur.CorrectAnswer else '-'
            if theme_num and question_num:
                answer = '{}.{} {}'.format(theme_num, question_num, answer)
            
            number = variant_questionCur.QuestionNumber
            row_num = (number - 1) // 4
            if row_num <> num:
                num = row_num
                questions['row'].append(row)
                row = {'question': []}
            row['question'].append({'number': '{}.'.format(number), 'answer': answer})
            
        if row['question']:
            questions['row'].append(row)
        
        themeCur.close()
        questionCur.close()
        variant_questionCur.close()
     
    if params != None:
        mark3 = params['Percent3']
        mark4 = params['Percent4']
        mark5 = params['Percent5']
        if testing_score >= mark5:
            mark = 'отлично'
        elif testing_score >= mark4:
            mark = 'хорошо'
        elif testing_score >= mark3:
            mark = 'удовлетворительно'
        else:
            mark = 'неудовлетворительно'
    if testing_score != None:
        if testing_score>=90:
            mark = 'отлично'
        elif testing_score >= 80:
            mark = 'хорошо'
        elif testing_score >= 70:
            mark = 'удовлетворительно'
        else:
            mark = 'неудовлетворительно'
    if (testing_date == None or testing_date == ''):
        testing_date = testing_date_by_group
    data = {'student': {
                'fio': fio,
                'speciality': speciality_name,
                'group': group_name,
                'kafedra_name': kafedra_name,
                'stud_spec_name': stud_spec_name,
                'edu_level_name': edu_level_name,
                'testing_date': testing_date,
                'testing_time': '{} - {}'.format(testing_time_start, testing_time_end),
                'testing_score': testing_score,
                'positive_count': positive_count,
                'mark': mark,
                'total_count': len(questions['row']),
                'questions': questions}} 
     
    return data
 
# def gridPartialUpdate(context, main, add, filterinfo, session, elementId, sortColumnList, firstrecord, pagesize, parentId=None):
#     u"""Частичное обновление грида"""    
#     data = {"records":{"rec":[]}}
#     
#     if add:
#         row = add.split('.')
#         row_type = row[0]
#         row_id = row[1]
#         if row_type == 'task':
#             eor_tasksCursor = student_eor_tasksCursor(context)
#             if eor_tasksCursor.tryGet(row_id):
#                 type = eor_tasksCursor.eor_type
#                 id = "task.{}".format(eor_tasksCursor.task_id)
#                 row = {}
#                 row[_header["id"][1]] = id
#                 row[_header["name"][1]] = eor_tasksCursor.package_name
#                 row[_header["eor_type"][1]] = EOR_TYPE[type][0]
#                 row[_header["score"][1]] = eor_tasksCursor.score
#                 if type == TEST:
#                     row[_header["attempts_count"][1]] = eor_tasksCursor.attempts_count
#                     row[_header["HasChildren"][1]] = 0
#                     row[_header['properties'][1]] = {'event': {
#                                                 '@name': 'row_save_data',
#                                                 'action': {
#                                                     '#sorted': [
#                                                         {'main_context': 'current'},
#                                                         {'datapanel': {
#                                                             '@type': 'current',
#                                                             '@tab': 'current',
#                                                             "element": [ 
#                                                                 {"@id": "studentsTreeGrid",
#                                                                  "@keep_user_settings": "true",
#                                                                  "@partial_update": "true",
#                                                                  "add_context": id}]}}]}}}
#                 data["records"]["rec"].append(row)
#                 
#             eor_tasksCursor.close()
#     
#     res = XMLJSONConverter.jsonToXml(json.dumps(data))
#     return JythonDTO(res, None)