import numpy as np
import subprocess
import tempfile
import sys, os
from ParamFunhouse import ParamFunhouse
import GA_Tuner as GT
import Repository as REPO

problemCard = sys.argv[1]  #e.g. test1.wcard
timeout = sys.argv[2]   #e.g. 2 (seconds)
timeoutIt = eval(timeout)/20

pf = ParamFunhouse()

def main():
    repo = REPO.Repository()
    repo.initConnection()
    repo.insert([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,1,2,3])
    repo.insert([4,5,6,7,8,9,17,18,19,110,111,112,113,114,115,116,117,118,119,120,4,5,6])
    tables = repo.showTables()
    thing = repo.getStatesRanked([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,7,8,9])
    # for x in thing:
    #     print(x)
   # tuner = GT.GA_Tuner()
    #result = tuner.geneticAlgorithm()

    # tuner.report(result)

def mainTest():
    print("entered testing")
    if testCall == 'carlSAT' or testCall == 'obj' or testCall == 'cost':
        pf = ParamFunhouse()
        x=[1,2,1,4,6,10,14]
        print("running test...")
        saveFile = runCarlSAT(x)
        print("finished!!!")

        if testCall == 'obj' or testCall == 'cost':
            print(saveFile.name)
            objectives = extractObjectives(saveFile)
            print(str(objectives) + " are the objectives")

            if testCall == 'cost':
                cost = calculateCosts(objectives)
                print(str(cost) + " is the associated cost")

#this takes in pymoo parameters, runs the solver with those parameters
#extracts the objectives and returns the calculated cost(s) associated with that run
def getCost(pymooParams): #repo writes
    #convert pymoo parameters into carlsat parameters
    carlSATparams = pf.getParameters(pymooParams)
    resultFile = runCarlSAT(carlSATparams)
    objectives = extractObjectives(resultFile)

    return calculateCosts(objectives)

def runCarlSAT(p, entry):

  # inc(outputNum, entry) #repo's autonumber or something

    sParamString = '-a {} -b {} -c {} -e {} -f {} -r {} -x {}'.format( p[0], p[1], p[2], p[3], p[4], p[5], p[6]) #TV
    sArguments = '-m {} -v 2 -z {} -i {} -w {}'.format(timeoutIt, problemCard, p[7])
    sRunLine = './CarlSAT ' + sParamString + ' ' + sArguments

    tempf = tempfile.NamedTemporaryFile(delete=False)
    with open(tempf.name, 'w') as tf:

        proc = subprocess.Popen(list(sRunLine.split(" ")), stdout=tf)
        proc.wait()
        tf.seek(0)

    return [tempf,OutputfileName]

def extractObjectives(tempf):

    #extract A = end score of previous run (intermediate score, no longer final score)
    #starting score B (what run started with)
    # time needed to achieve starting score (cumulative) (dependent on A)
    # timeout given.

    with open(tempf.name, 'rb') as tf:
        tf.seek(-27,2)
        cost = eval(str(tf.readline(), 'utf-8').split()[1])
        time = eval(str(tf.readline(), 'utf-8').split()[3])
    tempf.close()
    os.unlink(tempf.name)

    timeTakenMs = time * 1000
    maxTimeMs = eval(timeout) * 1000

    return [A, B, C, D] #potentially be used by callback as printing.

# this function can be expanded to P2 and P3 where what/how we calculate cost(s) will change
def calculateCosts(objectives):

    #An - Bn + Cn - Dn

    finalCost = (objectives[0] - objectives[1]) + (objectives[2] - objectives[3])
    return finalCost

def output(results):
        print('Time taken:', results[0])
        #these are just the indices, not param vals #update
        print("Best solution found with: \nParameters = %s,\nGiving Cost = %s" % (pf.getParameters(results[1]), results[2]))

if len(sys.argv) > 3:

    testCall = sys.argv[3] #e.g. 'carlSAT'
    mainTest()

else:
    if __name__ == "__main__":

        main()


from django.conf.urls import url
from django.urls import path
from . import views

urlpatterns = [
    url(r"^nosotros/$", views.nosotros, name = "nosotros"),
    url(r"^contacto/$", views.contacto, name = "contacto"),
    url(r"^login/$", views.ingresar, name = "login"),
    url(r"^registro/$", views.registrar, name = "registros"),
    url(r"^salir/$", views.salir, name = "salir"),
    url(r"^gestionClientes/$", views.gestionClientes, name = "gestionClientes"),
    url(r"^gestionClientes/actualizar/(?P<pk>[0-9_]+)$", views.actualizarCliente, name = "actualizarCliente"),
    url(r"^gestionClientes/eliminar/(?P<pk>[0-9_]+)$", views.eliminarCliente, name = "eliminarCliente"),
    url(r"^gestionInstrumentos/$", views.gestionInstrumentos, name = "gestionInstrumentos"),
    url(r"^gestionInstrumentos/actualizar/(?P<pk>[0-9_]+)$", views.actualizarInstrumento, name = "actualizarInstrumento"),
    url(r"^gestionInstrumentos/eliminar/(?P<pk>[0-9_]+)$", views.eliminarInstrumento, name = "eliminarInstrumento"),
    url(r"", views.index, name="index"),
]


import datetime
import json
import random
from unittest import mock
from unittest.mock import patch

from django.contrib import messages
from django.contrib.auth.models import User
from django.contrib.messages import get_messages
from django.core.cache import cache
from django.core.files.uploadedfile import SimpleUploadedFile
from django.db import models
from django.forms import model_to_dict
from django.test import TestCase
from django.urls import reverse
from django.utils.translation import gettext_lazy as _, gettext
from rest_framework import status

from base.models.person import Person
from base.tests.factories.academic_year import create_current_academic_year, AcademicYearFactory
from base.tests.factories.education_group import EducationGroupFactory
from base.tests.factories.education_group_year import EducationGroupYearFactory
from base.tests.factories.entity_version import EntityVersionFactory
from base.tests.factories.person import PersonWithPermissionsFactory
from continuing_education.business.enums.rejected_reason import DONT_MEET_ADMISSION_REQUIREMENTS
from continuing_education.models.admission import Admission
from continuing_education.models.continuing_education_person import ContinuingEducationPerson
from continuing_education.models.enums import file_category_choices, admission_state_choices
from continuing_education.models.enums.admission_state_choices import NEW_ADMIN_STATE, SUBMITTED, DRAFT, REJECTED, \
    ACCEPTED, ACCEPTED_NO_REGISTRATION_REQUIRED
from continuing_education.models.enums.groups import STUDENT_WORKERS_GROUP
from continuing_education.tests.factories.admission import AdmissionFactory
from continuing_education.tests.factories.continuing_education_training import ContinuingEducationTrainingFactory
from continuing_education.tests.factories.file import AdmissionFileFactory
from continuing_education.tests.factories.person import ContinuingEducationPersonFactory
from continuing_education.tests.factories.roles.continuing_education_manager import ContinuingEducationManagerFactory
from continuing_education.tests.factories.roles.continuing_education_training_manager import \
    ContinuingEducationTrainingManagerFactory
from continuing_education.views.admission import admission_detail
from continuing_education.views.common import get_versions, save_and_create_revision, VERSION_MESSAGES, \
    get_revision_messages
from reference.tests.factories.country import CountryFactory

FILE_CONTENT = "test-content"

class ViewAdmissionTestCase(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.academic_year = create_current_academic_year()
        cls.education_group = EducationGroupFactory()
        EducationGroupYearFactory(
            education_group=cls.education_group,
            academic_year=cls.academic_year
        )
        cls.formation = ContinuingEducationTrainingFactory(
            education_group=cls.education_group,
            additional_information_label='additional_information',
            registration_required=True
        )
        cls.education_group_no_registration_required = EducationGroupFactory()
        EducationGroupYearFactory(
            education_group=cls.education_group_no_registration_required,
            academic_year=cls.academic_year
        )
        cls.formation_no_registration_required = ContinuingEducationTrainingFactory(
            education_group=cls.education_group_no_registration_required,
            registration_required=False
        )
        cls.manager = ContinuingEducationManagerFactory()
        cls.training_manager = ContinuingEducationTrainingManagerFactory(training=cls.formation)
        EntityVersionFactory(
            entity=cls.formation.management_entity
        )
        cls.admission = AdmissionFactory(
            formation=cls.formation,
            state=SUBMITTED,
            person_information__person__gender='H',
            academic_year=cls.academic_year,
        )
        a_person_information = ContinuingEducationPersonFactory(person__gender='H')
        cls.admission_no_admission_required = AdmissionFactory(
            formation=cls.formation_no_registration_required,
            state=ACCEPTED_NO_REGISTRATION_REQUIRED,
            person_information=a_person_information,
            academic_year=cls.academic_year,
        )

        cls.file = SimpleUploadedFile(
            name='upload_test.pdf',
            content=str.encode(FILE_CONTENT),
            content_type="application/pdf"
        )
        cls.country = CountryFactory()
        cls.person_data = {
            'last_name': cls.admission.person_information.person.last_name,
            'first_name': cls.admission.person_information.person.first_name,
            'gender': cls.admission.person_information.person.gender,
            'email': cls.admission.person_information.person.email,
        }
        cls.continuing_education_person_data = {
            'birth_date': cls.admission.person_information.birth_date.strftime('%Y-%m-%d'),
            'birth_location': cls.admission.person_information.birth_location,
            'birth_country': cls.admission.person_information.birth_country.id,
        }

    def setUp(self):
        self.client.force_login(self.manager.person.user)

    def test_list_admissions(self):
        url = reverse('admission')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTemplateUsed(response, 'continuing_education/admissions.html')
        self.assertEqual(len(response.context['admissions'].object_list), 2)

    def test_list_admissions_filtered_by_training_manager_with_no_admission(self):
        other_training_manager = ContinuingEducationTrainingManagerFactory()
        self.client.force_login(other_training_manager.person.user)
        url = reverse('admission')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.context['admissions'].object_list), 0)
        self.assertTemplateUsed(response, 'continuing_education/admissions.html')

    def test_list_admissions_filtered_by_training_manager_with_admission(self):
        training_manager = ContinuingEducationTrainingManagerFactory(training=self.formation)
        self.client.force_login(training_manager.person.user)
        url = reverse('admission')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.context['admissions'].object_list), 1)
        self.assertTemplateUsed(response, 'continuing_education/admissions.html')

    def test_list_admissions_pagination_empty_page(self):
        url = reverse('admission')
        response = self.client.get(url, {'page': 0})
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTemplateUsed(response, 'continuing_education/admissions.html')

    def test_admission_detail(self):
        url = reverse('admission_detail', args=[self.admission.id])
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTemplateUsed(response, 'continuing_education/admission_detail.html')

    def test_admission_detail_not_found(self):
        response = self.client.get(reverse('admission_detail', kwargs={
            'admission_id': 0,
        }))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_admission_detail_access_denied(self):
        other_training_manager = ContinuingEducationTrainingManagerFactory()
        self.client.force_login(other_training_manager.person.user)
        url = reverse('admission_detail', args=[self.admission.id])
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertTemplateUsed(response, 'access_denied.html')

    def test_admission_new(self):
        url = reverse('admission_new')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTemplateUsed(response, 'continuing_education/admission_form.html')

    def test_admission_new_save(self):
        admission = model_to_dict(self.admission)
        admission.update(self.person_data)
        admission.update(self.continuing_education_person_data)
        admissions = Admission.objects.all()
        qs_to_find_new_admissions = Admission.objects.all()
        for a in admissions:
            qs_to_find_new_admissions = qs_to_find_new_admissions.exclude(pk=a.id)
        response = self.client.post(reverse('admission_new'), data=admission)
        created_admission = qs_to_find_new_admissions.first()
        self.assertEqual(response.status_code, status.HTTP_302_FOUND)
        self.assertRedirects(response, reverse('admission_detail', args=[created_admission.pk]))

    def test_admission_new_save_email(self):
        admission = model_to_dict(self.admission)
        admission.update(self.person_data)
        admission.update(self.continuing_education_person_data)
        admission.pop("person_information")
        new_admission_email = 'toto@uclouvain.be'
        admission['email'] = new_admission_email
        admissions = Admission.objects.all()
        qs_to_find_new_admissions = Admission.objects.all()
        for a in admissions:
            qs_to_find_new_admissions = qs_to_find_new_admissions.exclude(pk=a.id)
        self.client.post(reverse('admission_new'), data=admission)
        created_admission = qs_to_find_new_admissions.first()
        self.assertEqual(created_admission.person_information.person.email, new_admission_email)

    def test_admission_new_save_gender_required_if_new_person(self):
        admission = model_to_dict(self.admission)
        admission.update(self.person_data)
        admission.update(self.continuing_education_person_data)
        admission.pop("person_information")
        admission['gender'] = ""
        response = self.client.post(reverse('admission_new'), data=admission)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTemplateUsed(response, 'continuing_education/admission_form.html')

    def test_admission_new_save_gender_not_required_if_existing_person(self):
        admission = model_to_dict(self.admission)
        admission.update(self.person_data)
        admission.update(self.continuing_education_person_data)
        admission['gender'] = ""
        admissions = Admission.objects.all()
        qs_to_find_new_admissions = Admission.objects.all()
        for a in admissions:
            qs_to_find_new_admissions = qs_to_find_new_admissions.exclude(pk=a.id)
        response = self.client.post(reverse('admission_new'), data=admission)
        created_admission = qs_to_find_new_admissions.first()
        self.assertEqual(response.status_code, status.HTTP_302_FOUND)
        self.assertRedirects(response, reverse('admission_detail', args=[created_admission.pk]))

    def test_admission_save_with_error(self):
        admission = model_to_dict(self.admission)
        admission['person_information'] = "no valid pk"
        response = self.client.post(reverse('admission_new'), data=admission)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTemplateUsed(response, 'continuing_education/admission_form.html')

    def test_admission_edit_not_found(self):
        response = self.client.get(reverse('admission_edit', kwargs={
            'admission_id': 0,
        }))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_edit_get_admission_found(self):
        url = reverse('admission_edit', args=[self.admission.id])
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTemplateUsed(response, 'continuing_education/admission_form.html')

    def test_edit_post_admission_found(self):
        admission = {
            'person_information': self.admission.person_information.pk,
            'motivation': 'abcd',
            'professional_personal_interests': 'abcd',
            'formation': self.formation.pk,
            'awareness_ucl_website': True,
            'academic_year': self.admission.academic_year.id,
        }
        data = admission.copy()
        # Data to update
        data_person_updated = self.person_data.copy()
        data_person_updated.update({'gender': 'F'})
        data.update(data_person_updated.copy())
        # Data to update
        data_person_information_updated = self.continuing_education_person_data
        data_person_information_updated.update({'birth_location': 'namur'})
        data.update(data_person_information_updated.copy())

        new_email = 'new_email@uclouvain.be'
        data.update({'email': new_email})

        person_objects_quantity = Person.objects.all().count()
        continuing_education_person_objects_quantity = ContinuingEducationPerson.objects.all().count()

        url = reverse('admission_edit', args=[self.admission.pk])
        response = self.client.post(url, data=data)
        self.assertRedirects(response, reverse('admission_detail', args=[self.admission.id]))
        self.admission.refresh_from_db()
        self.admission.person_information.refresh_from_db()
        self.admission.person_information.person.refresh_from_db()

        # verifying that fields are correctly updated
        self._check_update_correct(admission, self.admission)
        self._check_update_correct(data_person_updated, self.admission.person_information.person)
        self._check_update_correct(self.continuing_education_person_data, self.admission.person_information)

        # Email must be updated in admission object but not in existing person object
        self.assertEqual(self.admission.email, new_email)
        self.assertNotEqual(self.admission.person_information.person.email, new_email)

        # Assert that we did not create new person and continuing_education_person
        self.assertEqual(person_objects_quantity, Person.objects.all().count())
        self.assertEqual(continuing_education_person_objects_quantity, ContinuingEducationPerson.objects.all().count())

    def _check_update_correct(self, data, db_obj):
        for key in data:
            field_value = db_obj.__getattribute__(key)
            if isinstance(field_value, datetime.date):
                field_value = field_value.strftime('%Y-%m-%d')
            if isinstance(field_value, models.Model):
                field_value = field_value.pk
            self.assertEqual(field_value, data[key], key)

    def test_admission_list_unauthorized(self):
        unauthorized_user = User.objects.create_user('unauthorized', 'unauth@demo.org', 'passtest')
        self.client.force_login(unauthorized_user)
        url = reverse('admission')
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_admission_detail_unauthorized(self):
        unauthorized_user = User.objects.create_user('unauthorized', 'unauth@demo.org', 'passtest')
        self.client.force_login(unauthorized_user)
        url = reverse('admission_detail', kwargs={'admission_id': self.admission.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_admission_edit_unauthorized(self):
        unauthorized_user = User.objects.create_user('unauthorized', 'unauth@demo.org', 'passtest')
        self.client.force_login(unauthorized_user)
        url = reverse('admission_edit', kwargs={'admission_id': self.admission.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_admission_download_file(self):
        uploaded_file = SimpleUploadedFile(
            name='upload_test.pdf',
            content=str.encode('content'),
            content_type="application/pdf"
        )
        admission_file = AdmissionFileFactory(
            admission=self.admission,
            path=uploaded_file,
            uploaded_by=self.admission.person_information.person
        )
        url = reverse('download_file', kwargs={'admission_id': self.admission.pk, 'file_id': admission_file.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_list(self):
        response = self.client.post(reverse('admission'))
        self.assertEqual(response.status_code, 200)
        self.assertCountEqual(response.context['admissions'].object_list,
                              [self.admission, self.admission_no_admission_required])

    def test_get_versions(self):
        version_list = get_versions(self.admission)

        self.assertEqual(len(version_list), 0)
        i = 1
        for msg in VERSION_MESSAGES:
            save_and_create_revision(
                get_revision_messages({'icon': '', 'text': msg}),
                self.admission,
                self.training_manager.person.user
            )
            version_list = get_versions(self.admission)
            self.assertEqual(len(version_list), i)
            i += 1

    def test_ajax_get_formation_information(self):
        response = self.client.get(reverse('get_formation_information'), data={
            'formation_id': self.formation.pk
        }, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(json.loads(response.content.decode('utf-8')),
                         {'additional_information_label': 'additional_information'}
                         )

    def test_error_message_no_draft_admission_selected(self):
        response = self.client.post(
            reverse('admission_delete_draft'),
            data={},
            follow=True,
            HTTP_REFERER=reverse('admission', args=[])
        )
        self.assertEqual(response.status_code, 200)

        msg = [m.message for m in get_messages(response.wsgi_request)]
        msg_level = [m.level for m in get_messages(response.wsgi_request)]
        self.assertEqual(len(msg), 1)
        self.assertIn(messages.ERROR, msg_level)
        self.assertEqual(msg[0], _("Please select at least one admission in 'draft' status"))

    def test_draft_deleted(self):
        draf_admission = AdmissionFactory(
            state=DRAFT,
            archived=False
        )
        self.assertTrue(Admission.objects.filter(id=draf_admission.id).exists())
        response = self.client.post(
            reverse('admission_delete_draft'),
            data={'selected_draft_action': draf_admission.id},
        )
        self.assertFalse(Admission.objects.filter(id=draf_admission.id).exists())

class InvoiceNotificationEmailTestCase(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.academic_year = create_current_academic_year()
        cls.education_group = EducationGroupFactory()
        EducationGroupYearFactory(
            education_group=cls.education_group,
            academic_year=cls.academic_year
        )
        cls.formation = ContinuingEducationTrainingFactory(
            education_group=cls.education_group
        )

        cls.manager = ContinuingEducationManagerFactory()

        cls.admission = AdmissionFactory(
            formation=cls.formation,
            state=ACCEPTED
        )
        cls.admission_file = AdmissionFileFactory(
            admission=cls.admission
        )

        cls.url = reverse('send_invoice_notification_mail', args=[cls.admission.pk])

    def setUp(self):
        self.client.force_login(self.manager.person.user)

    @patch('continuing_education.business.admission.send_email')
    def test_send_mail_with_invoice(self, mock_send_mail):
        self.admission_file.file_category = file_category_choices.INVOICE
        self.admission_file.save()

        response = self.client.get(self.url)

        self.assertRedirects(response, reverse('admission_detail', args=[self.admission.id]) + '#documents')
        messages_list = [str(msg) for msg in list(messages.get_messages(response.wsgi_request))]
        self.assertEqual(response.status_code, 302)
        self.assertIn(
            gettext(_("A notification email has been sent to the participant")),
            messages_list
        )
        self.assertTrue(mock_send_mail.called)

    @patch('continuing_education.business.admission.send_email')
    def test_send_mail_without_invoice(self, mock_send_mail):
        self.admission_file.file_category = file_category_choices.DOCUMENT
        self.admission_file.save()

        response = self.client.get(self.url)

        self.assertRedirects(response, reverse('admission_detail', args=[self.admission.id]) + '#documents')
        messages_list = [str(msg) for msg in list(messages.get_messages(response.wsgi_request))]
        self.assertEqual(response.status_code, 302)
        self.assertIn(
            gettext(_("There is no invoice for this admission, notification email not sent")),
            messages_list
        )
        self.assertNotIn(
            gettext(_("A notification email has been sent to the participant")),
            messages_list
        )
        self.assertFalse(mock_send_mail.called)

class AdmissionStateChangedTestCase(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.academic_year = create_current_academic_year()
        cls.education_group = EducationGroupFactory()
        education_group_year = EducationGroupYearFactory(
            education_group=cls.education_group,
            academic_year=cls.academic_year
        )
        cls.formation = ContinuingEducationTrainingFactory(
            education_group=cls.education_group
        )
        cls.manager = ContinuingEducationManagerFactory()
        EntityVersionFactory(
            entity=education_group_year.management_entity
        )
        cls.admission = AdmissionFactory(
            academic_year=cls.academic_year,
            formation=cls.formation,
            state=random.choice(admission_state_choices.STATE_CHOICES)[0]
        )
        cls.admission_submitted = AdmissionFactory(
            academic_year=cls.academic_year,
            formation=cls.formation,
            state=SUBMITTED
        )

    def setUp(self):
        self.client.force_login(self.manager.person.user)

    @patch('continuing_education.views.admission.send_admission_to_queue')
    @patch('continuing_education.business.admission._get_continuing_education_managers')
    @patch('osis_common.messaging.send_message.send_messages')
    def test_admission_detail_edit_state(self, mock_send, mock_managers, mock_queue):
        states = NEW_ADMIN_STATE[self.admission.state]['states'].copy()
        if self.admission.state == admission_state_choices.SUBMITTED:
            states.remove(DRAFT)
        if self.admission.state in states:
            states.remove(self.admission.state)
        new_state = random.choice(states)
        admission = {
            'state': new_state,
            'formation': self.formation.pk,
            'person_information': self.admission.person_information.pk,
            'email': 'test@gmail.com'
        }
        data = admission
        if new_state == REJECTED:
            data['rejected_reason'] = DONT_MEET_ADMISSION_REQUIREMENTS
        url = reverse('admission_detail', args=[self.admission.pk])
        response = self.client.post(url, data=data)
        self.assertRedirects(response, reverse(admission_detail, args=[self.admission.pk]))
        self.admission.refresh_from_db()
        self.assertEqual(self.admission.state, admission['state'], 'state')
        if self.admission.state == admission_state_choices.VALIDATED:
            self.assertTrue(mock_queue.called)

    @mock.patch('continuing_education.business.admission.send_email')
    def test_admission_detail_edit_state_to_draft(self, mock_send_email):
        admission_draft = {
            'formation': self.formation.pk,
            'state': DRAFT,
            'person_information': self.admission.person_information.pk,
            'email': 'test@gmail.com'
        }
        url = reverse('admission_detail', args=[self.admission_submitted.pk])
        response = self.client.post(url, data=admission_draft)
        self.assertRedirects(response, url)
        self.admission_submitted.refresh_from_db()
        self.assertEqual(self.admission_submitted.state, DRAFT, 'state')

        self.assertTrue(mock_send_email.called)
        mock_call_args = mock_send_email.call_args[1]
        self.assertEqual(
            mock_call_args.get('template_references').get('html'),
            'iufc_participant_state_changed_other_html'
        )
        receivers = mock_call_args.get('receivers')
        self.assertCountEqual(
            [receiver.get('receiver_email') for receiver in receivers],
            [self.admission_submitted.email, self.admission_submitted.person_information.person.email]
        )

class ViewAdmissionCacheTestCase(TestCase):
    @classmethod
    def setUpTestData(cls):

        cls.manager = ContinuingEducationManagerFactory()
        cls.academic_year = AcademicYearFactory(current=True)

    def setUp(self):
        self.client.force_login(self.manager.person.user)
        self.addCleanup(cache.clear)

    def test_cached_filters(self):
        response = self.client.get(reverse('admission'), data={
            'free_text': 'test'
        })
        cached_response = self.client.get(reverse('admission'))
        self.assertEqual(response.wsgi_request.GET['free_text'], cached_response.wsgi_request.GET['free_text'])

class BillingEditTestCase(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.academic_year = create_current_academic_year()
        cls.education_group = EducationGroupFactory()
        EducationGroupYearFactory(
            education_group=cls.education_group,
            academic_year=cls.academic_year
        )
        cls.formation = ContinuingEducationTrainingFactory(
            education_group=cls.education_group,
            registration_required=False
        )
        cls.manager = ContinuingEducationManagerFactory()
        cls.student_worker = PersonWithPermissionsFactory('view_admission', groups=[STUDENT_WORKERS_GROUP])
        EntityVersionFactory(
            entity=cls.formation.management_entity
        )
        a_person_information = ContinuingEducationPersonFactory(person__gender='H')
        cls.admission = AdmissionFactory(
            formation=cls.formation,
            state=SUBMITTED,
            person_information=a_person_information,
        )

    def setUp(self):
        self.client.force_login(self.manager.person.user)

    def test_billing_edit(self):
        url = reverse('billing_edit', args=[self.admission.id])
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertTemplateUsed(response, 'continuing_education/admission_billing_form.html')

    def test_billing_edit_not_found(self):
        response = self.client.get(reverse('billing_edit', kwargs={
            'admission_id': 0,
        }))
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_billing_edit_access_denied_for_student_worker(self):
        self.client.force_login(self.student_worker.user)
        url = reverse('billing_edit', args=[self.admission.id])
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertTemplateUsed(response, 'access_denied.html')

    def test_billing_edit_access_denied_if_draft(self):
        self.admission.state = DRAFT
        self.admission.save()
        url = reverse('billing_edit', args=[self.admission.id])
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertTemplateUsed(response, 'access_denied.html')

    def test_billing_edit_access_denied_if_registration_required(self):
        self.admission.formation.registration_required = True
        self.admission.formation.save()
        url = reverse('billing_edit', args=[self.admission.id])
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
        self.assertTemplateUsed(response, 'access_denied.html')

    def test_billing_edit_post_admission_found(self):
        billing_info = {
            'billing_address': self.admission.address.pk,
            'registration_type': 'PRIVATE',
            'head_office_name': 'HEAD_OFF_NAME',
            'company_number': 'COMP_NUM',
            'vat_number': 'VAT_NUM',
            'use_address_for_billing': True
        }
        data = billing_info.copy()

        url = reverse('billing_edit', args=[self.admission.pk])
        response = self.client.post(url, data=data)
        self.assertRedirects(response, reverse('admission_detail', args=[self.admission.id]) + '#billing')
        self.admission.refresh_from_db()
        self.assertEqual(self.admission.registration_type, billing_info['registration_type'])
        self.assertEqual(self.admission.head_office_name, billing_info['head_office_name'])
        self.assertEqual(self.admission.company_number, billing_info['company_number'])
        self.assertEqual(self.admission.vat_number, billing_info['vat_number'])
        self.assertEqual(self.admission.use_address_for_billing, billing_info['use_address_for_billing'])


import numpy as np
import tensorflow as tf
from datetime import datetime
import os
import sys
import json
sys.path.append('../')
import retriever

cdnd_dir = os.path.dirname(os.path.realpath(__file__))
vocab_file =  cdnd_dir+'/data/metadata/w2v_vocab.json'
embed_path =  cdnd_dir+'/data/metadata/w2v.bin'

class Model():
    def __init__(self,
                 batch_size=200,
                 num_hidden=200,

                 #Image's vector size.
                 img_dims=4096,

                 #Spaital features length.
                 bbox_dims=8,
                 lr=0.5, # learning rate.
                 decay_steps=10000,
                 decay_rate=0.9,
                 embed_size=100,
                 params_dir=cdnd_dir+'/CDNDParams/',

                 # Whether to uses prefix levlel attention or not.
                 use_wordAttn=False,

                 # Whther to use bidirectional rnn
                 useBidirectionalRnn=False,

                 # We scale the VGG16 LN outputs by IMGscale
                 IMGscale=0.023,
                 # We scale the language LN outputs by Qscale
                 Qscale=0.16
                 ):

        self.batch_size = batch_size
        self.img_dims = img_dims
        self.bbox_dims = bbox_dims
        self.num_hidden = num_hidden
        self.embed_size = embed_size
        self.lr = lr
        self.IMGscale=IMGscale
        self.Qscale=Qscale
        self.params_dir = params_dir
        self.vocab, embed_vecs = self.load_datasets()

        self.queries = tf.placeholder(tf.int32, [None, None], name='queries')
        self.img  = tf.placeholder(tf.float32, [None, None, self.img_dims], name='img')# VGG output vectors
        self.bboxes = tf.placeholder(tf.float32, [None, None, self.bbox_dims], name='bboxes')# spatial bbox's features.

        # attn_idx: inicates whether attention box is a pad (0) or not (1).
        self.attn_idx = tf.placeholder(tf.float32, [None, None], name='attn_idx')
        self.labels = tf.placeholder(tf.float32, [None, None], name='labels')

        # Dropout ratio for rnn's inputs and outpouts
        self.dropout_in = tf.placeholder(tf.float32, name='dropoutIn_holder')
        self.dropout_out = tf.placeholder(tf.float32, name='dropoutOut_holder')

        # Dropout ratio for attention vector (for the final attention layer before the loss function)
        self.dropout_img = tf.placeholder(tf.float32, name='dropoutImg_holder')
        # Dropout ratio for query vector (for the final attention layer before the loss function)
        self.dropout_q = tf.placeholder(tf.float32, name='dropoutImg_holder')

        self.isTrain = tf.placeholder(tf.bool, name='isTrain_holder')
        self.queries_lens = self.length(self.queries) # list of all the lengths of the batch's queriey

        # Concatinate images vectors and their spaital features.
        # These vectors wlll be used for attenionn when
        # we calculate the loss function.
        attn_vecs = tf.concat([self.img, self.bboxes], 2)
        voc_size = len(self.vocab)

        # Load pre-trained word imaddings.
        # w2v_embed is not trainable.
        with tf.variable_scope('w2v'):
            w2v_embed = tf.get_variable('w2v_embed', initializer=embed_vecs, trainable=False)
            w2v_queries = tf.nn.embedding_lookup(w2v_embed, self.queries, name='w2v_queries')

        with tf.variable_scope('embed'):
            embed = tf.get_variable('embed', shape=[voc_size, self.embed_size],
                                    initializer=tf.random_uniform_initializer(minval=-0.1, maxval=0.1))
            embed_queries_tmp = tf.nn.embedding_lookup(embed, self.queries, name='embed_queries')

        embed_queries = embed_queries_tmp+w2v_queries

        with tf.variable_scope('rnn'):
            if use_wordAttn:
                normed_image = self.IMGscale*tf.contrib.layers.layer_norm(self.img, begin_norm_axis=2)
                attn_vecs = tf.concat([normed_image, self.bboxes], 2)
                cell = ALSTM(num_units=self.num_hidden,
                            img_attn_dim=self.img_dims+self.bbox_dims,
                            img_attn_states=attn_vecs,
                            img_attn_idx=self.attn_idx,
                            batch_size=self.batch_size,
                            dropout_in=self.dropout_in, dropout_out=self.dropout_out)
            else:
                tmp = tf.nn.rnn_cell.LSTMCell(self.num_hidden, state_is_tuple=True)
                cell = tf.contrib.rnn.DropoutWrapper(
                    tmp,
                    input_keep_prob=self.dropout_in,
                    output_keep_prob=self.dropout_out
                )

            if useBidirectionalRnn:
                if use_wordAttn:
                    cell_bw = ALSTM(num_units=self.num_hidden,
                            img_attn_dim=self.img_dims+self.bbox_dims,
                            img_attn_states=attn_vecs,
                            img_attn_idx=self.attn_idx,
                            batch_size=self.batch_size,
                            dropout_in=self.dropout_in, dropout_out=self.dropout_out)
                else:
                    tmp_bw = tf.nn.rnn_cell.LSTMCell(self.num_hidden, state_is_tuple=True)
                    cell_bw = tf.contrib.rnn.DropoutWrapper(
                        tmp,
                        input_keep_prob=self.dropout_in,
                        output_keep_prob=self.dropout_out
                    )

                self.outputs, self.last_states = tf.nn.bidirectional_dynamic_rnn(
                    cell_fw=cell,
                    cell_bw=cell_bw,
                    dtype=tf.float32,
                    sequence_length=self.queries_lens,
                    inputs=embed_queries)

                state = tf.concat([self.last_states[0][1], self.last_states[1][1]], -1)

            else:
                self.outputs, self.last_states = tf.nn.dynamic_rnn(
                    cell=cell,
                    dtype=tf.float32,
                    sequence_length=self.queries_lens,
                    inputs=embed_queries)
                state = self.last_states[1]

        self.scores = self.CDN_attention(state)

        # Cross entophy loss for each of the queries in the batch.
        self.loss = tf.reduce_mean(-tf.reduce_sum(
                        self.labels*tf.log(self.scores+0.00000001)+
                            (1-self.labels)*tf.log((1-self.scores)+0.00000001),
                        axis=-1))

        ##############
        # Optimizers #
        ##############

        starter_learning_rate = self.lr
        self.global_step = tf.Variable(0, name='global_step', trainable=False)
        self.learning_rate = tf.train.exponential_decay(starter_learning_rate,
                                                        self.global_step,
                                                        decay_steps=decay_steps,
                                                        decay_rate=decay_rate,
                                                        staircase=True)

        self.optimizer =  tf.train.GradientDescentOptimizer(
                    learning_rate=self.learning_rate).minimize(self.loss, global_step=self.global_step)

        if not os.path.exists(self.params_dir):
                os.makedirs(self.params_dir)
        self.saver = tf.train.Saver()

    def load_datasets(self):
        # loading vocabulary
        with open(vocab_file, 'r') as f:
            vocab = json.loads(f.read())
        vocab['<unk>'] = len(vocab)

        # Words vectors
        embed_vecs = np.load(open(embed_path, 'rb'))
        embed_vecs = np.concatenate([embed_vecs, np.zeros((1, 100))], 0).astype(np.float32)

        return vocab, embed_vecs

    def length(self, seq):
        '''
        Retruns real lengths (before addings) of all queries in seq  .
        '''
        return tf.cast(tf.reduce_sum(tf.sign(tf.abs(seq)), reduction_indices=1), tf.int32)

    def linear(self, inputs, output_dim, scope='linear', bias=True, reuse=False):

        with tf.variable_scope(scope, reuse=reuse):
            W1 = tf.get_variable('W1', initializer=tf.contrib.layers.xavier_initializer(),
                                shape=(inputs.get_shape()[-1], output_dim))
            if bias:
                b = tf.get_variable('b', initializer=tf.constant_initializer(0.1),
                               shape=[1, output_dim])
                return tf.matmul(inputs, W1) + b

            return tf.matmul(inputs, W1)

    def CDN_attention(self, q_embed):
        '''
        Given the queries embeddings, calculate the attention over all the query's
        bounding boxes vectors using CDN, That is, calculate:

        probs = softmax(relu(context(Sq+Satt+b)))

        Where:
        Sq = <Wq, queries_states>
        Sattn = <Wattn, attention_bboxes_vectors>

        The  bounding box with the highest attention score will be chosen as the correct bounding box.

        Params:
            q_embed: Tensor of shape (batch size x num_hidden), queries embeddings.

        Returns:
            probs: Tensor of shape (batch_size x max bbox number for query).
                   Attention score for each bbox.
        '''

        # Shape: (batch size x num_hidden)
        self.Urnn = self.Qscale*tf.contrib.layers.layer_norm(q_embed)
        UattTmp = self.IMGscale*tf.contrib.layers.layer_norm(self.img, begin_norm_axis=2)
        self.Uatt = tf.concat([UattTmp, self.bboxes], 2)
        with tf.variable_scope('bnorm_l1') as scope:
            b = tf.get_variable(
                    'b',
                    initializer=tf.constant_initializer(0.1),
                    shape=[1, self.num_hidden])

            context = tf.get_variable(
                    'context',
                    initializer=tf.random_uniform_initializer(minval=-0.1, maxval=0.1),
                    shape=[self.num_hidden, 1])

            Sq = tf.nn.dropout(
                self.linear(self.Urnn, self.num_hidden, bias=False, scope='Sq'),
                self.dropout_q)

            Sattn = tf.nn.dropout(
                        tf.reshape(
                            self.linear(
                                tf.reshape(self.Uatt, (-1, self.img_dims+self.bbox_dims)),
                                self.num_hidden,
                                bias=False, scope='Sattn'),
                             [self.batch_size, -1, self.num_hidden]),
                        self.dropout_img)

            out = tf.nn.relu(tf.expand_dims(Sq, 1) + Sattn + b)
            logits = tf.reshape(tf.matmul(tf.reshape(out, (-1, tf.shape(out)[-1])),  context), (tf.shape(out)[0], -1))

            # Calculate logits's masked softmax. We use self.attn_idx for
            # masking the padded BBOXes.
            max_logits = tf.reduce_max(logits*self.attn_idx, axis=-1, keepdims=True)
            masked_logits = tf.exp(logits-max_logits)*self.attn_idx
            probs = masked_logits/tf.reduce_sum(masked_logits, axis=-1, keepdims=True)

            return probs

    def q_padding(self, seq, max_length):
        '''
        Pad  seq with vocab['<pad>'] (0) to max_length length.
        '''
        return seq + [self.vocab['<pad>']]*(max_length-len(seq))

    def build_data(self, data, start, end):
        '''
        Build batch.
        ------------

        Params:
            data: each entry in this list has the following structure:
                  [query indexes, [bounding box vector (VGG), bounding box spaital features], ...,
                  [bounding box vector (VGG), bounding box spaital features], index of the true label]

            start/end: batch data is built from data[start:end]

        Returns:
            attn_idx: attn_idx[i, j]=1 if the j'th bbox in the i'th query is not padding, else equals to 0.

            padded_queries: list of queries, padded to the length of the longest query in the batch.
                            Note: vocab['p<pad>']=0

            padded_im: list of bounding boxes vectors, padded to the maximum number of bbox per query.
                       Note: padded vector is vector of zeros.

            padded_bbox: list of bounding boxes spatial features, padded to the maximum number of bbox per query.
                         Note: padded vector is vector of zeros.

            dist_labels: dist_labels[i][j]=1 if j is the true bbox for query i, else dist_labels[i][j]=0

        '''

        qlen = max([len(data[i][0]) for i in range(start, end)]) # Length fo the longest query
        imlen = max([len(data[i]) for i in range(start, end)])-2 # Maximum number of bbox per query.
        padded_queries, padded_im, padded_bbox, attn_idx = [], [], [], []

        # Build one hot labels from the labels index, given in the data.
        labels = [item[-1] for item in data[start:end]] # data[i][-1]=index of the true bbox of query i
        dist_labels = np.zeros((end-start, imlen)) #label distribution
        dist_labels[[i for i in np.arange(end-start)], [l for l in labels]]=1

        im_dim, bbox_dim = data[0][1][0].shape[1], data[0][1][1].shape[1]
        for i in range(start, end):
            padded_queries.append(self.q_padding(data[i][0], qlen))

            attn_idx.append([1 for _ in range(len(data[i])-2)]+[0 for _ in range(imlen-(len(data[i])-2))])

            padded_im.append(np.concatenate([data[i][j][0] for j in range(1, len(data[i])-1)] +
                                       [np.full((imlen-(len(data[i])-2), im_dim), self.vocab['<pad>'], dtype=np.float32)], axis=0))

            padded_bbox.append(np.concatenate([data[i][j][1] for j in range(1, len(data[i])-1)] +
                                       [np.full((imlen-(len(data[i])-2),bbox_dim), self.vocab['<pad>'], dtype=np.float32)], axis=0))

        return np.array(attn_idx), np.array(padded_queries, dtype=np.int32), np.array(padded_im), np.array(padded_bbox), np.array(dist_labels)

    def ground(self, data=None, start=None, end=None,
               sess=None, feed_dict = None, scores=[]):
        '''
        Given a query and a list of bboxes, the function returns the index of the chosen bbox and the ground truth bbox.

        Params:
            data: A numpy array with datasat's data points
            start/end: The function only take data points from data[start:end]
            imScale: whether to scale the images vectors
        '''
        isSess = (sess==None)
        if isSess:
            sess = tf.Session()
        with sess.as_default():
            if isSess:
                tf.global_variables_initializer().run()
                ckpt = tf.train.get_checkpoint_state(self.params_dir)
                if ckpt and ckpt.model_checkpoint_path:
                    self.saver.restore(sess, ckpt.model_checkpoint_path) # restore all variables
                else:
                    print('Initializing variables')
            if feed_dict is None:
                attn_idx, padded_queries, padded_im, padded_bbox, labels = self.build_data(
                    data, start, end)

                feed_dict = {
                        self.queries:padded_queries,
                        self.img:padded_im,
                        self.bboxes:padded_bbox,
                        self.labels:labels,
                        self.attn_idx:attn_idx}

            if len(scores)==0:
                feed_dict[self.isTrain]=False
                feed_dict[self.dropout_in]=1.
                feed_dict[self.dropout_out]=1.
                feed_dict[self.dropout_img]=1.
                feed_dict[self.dropout_q]=1.
                scores = sess.run(self.scores, feed_dict=feed_dict) # get score for each bbox

        return np.argmax(scores, axis=1), np.argmax(feed_dict[self.labels], axis=1)

    def iou_accuracy(self, data=None, start=None, end=None, sess=None,
                     feed_dict=None, threshold=0.5, test=False, scores=[]):
        '''
        Calculate the IOU score between the Model bbox and the true bbox.

         Params:
            data: A numpy array with datasat's data points
            start/end: The function only take data points from data[start:end]
            imScale: whether to scale the images vectors
            threshold: If IOU>0.5 this is a true positive
        '''

        # Get score for each bbox (labels) and th true bbox index (gt_idx)
        labels, gt_idx = self.ground(data, start, end, sess=sess, feed_dict=feed_dict, scores=scores)
        acc = 0

        for i in range(start, end):
            gt = data[i][gt_idx[i-start]+1][1][0] # ground truth bbox. Note that len(data)!=len(gt_idx)=batch_size
            crops = np.expand_dims(data[i][labels[i-start]+1][1][0], axis=0) #Model chosen bbox. Note that len(data)!=len(labels)=batch_size
            acc += (retriever.compute_iou(crops, gt)[0]>threshold) #IOU for the i sample.

        return acc/(end-start)

    def accuracy(self, data=None, start=None, end=None, sess=None,
                 feed_dict=None, scores=[]):
        isSess = (sess==None)
        if isSess:
            print('Building sess')
            sess = tf.Session()
        with sess.as_default():
            if isSess:
                print('Building sess used')
                tf.global_variables_initializer().run()
                ckpt = tf.train.get_checkpoint_state(self.params_dir)
                if ckpt and ckpt.model_checkpoint_path:
                    print('3')
                    self.saver.restore(sess, ckpt.model_checkpoint_path) # restore all variables
                else:
                    print('Initializing variables')
            if feed_dict is None:
                print('Building feed_dict')
                attn_idx, padded_queries, padded_im, padded_bbox, labels = self.build_data(data, start, end)
                feed_dict = {
                        self.queries:padded_queries,
                        self.img:padded_im,
                        self.bboxes:padded_bbox,
                        self.attn_idx:attn_idx,
                        self.labels:labels,
                    }

            loss = 0
            if len(scores)==0:
                feed_dict[self.isTrain]=False
                feed_dict[self.dropout_in]=1.
                feed_dict[self.dropout_out]=1.
                feed_dict[self.dropout_img]=1.
                feed_dict[self.dropout_q]=1.
                loss, scores = sess.run([self.loss, self.scores], feed_dict=feed_dict)

            acc = sum(
                np.equal(
                    np.argmax(scores, axis=1),
                    np.argmax(feed_dict[self.labels], axis=1))/len(feed_dict[self.labels])
            )

        return loss, scores, acc

    def predict(self, queries, img, bboxes, attn_idx):
        sess = tf.Session()
        with sess.as_default():
            tf.global_variables_initializer().run()
            ckpt = tf.train.get_checkpoint_state(self.params_dir)
            if ckpt and ckpt.model_checkpoint_path:
                print('Loading parameters from', ckpt.model_checkpoint_path)
                self.saver.restore(sess, ckpt.model_checkpoint_path) # restore all variables
            else:
                print('Initializing variables')

            feed_dict = {
                    self.queries:queries,
                    self.img:img,
                    self.bboxes:bboxes,
                    self.dropout_in:1.,
                    self.dropout_out:1.,
                    self.dropout_img:1.,
                    self.dropout_q:1.,
                    self.isTrain:False,
                    self.attn_idx:attn_idx,
                    self.labels:[[]]
            }

            scores = sess.run(self.scores, feed_dict=feed_dict)
        return scores

    def train(self, trn_data, tst_data, ephocs_num,  start_ephoc=0, dropout_in=0.5,
              dropout_out=0.5, dropout_img=1., dropout_q=1.):

        '''
        Params:
             trn_data: list, train set.

             tst_data: list, test set.

             ephocs_num: number of ephocs

             start_ephoc: number of first ephoc.

            dropout_in: dropout ratio of rnn inputs.

            dropout_output: dropout ratio of rnn output.

            dropout_img: dropout ratio of images vectors before the last attention layer .

            onlyB: boolean, Wheter to train only B. By setting startA and acctivation_ephoc larger than ephocs_num
                   and setting onlyB to True, we get A out of the game

        '''

        trn_nbatch = len(trn_data)//self.batch_size
        tst_nbatch = len(tst_data)//self.batch_size

        sess = tf.Session()
        with sess.as_default():
            tf.global_variables_initializer().run()
            ckpt = tf.train.get_checkpoint_state(self.params_dir)
            if ckpt and ckpt.model_checkpoint_path:
                print('Loading parameters from', ckpt.model_checkpoint_path)
                self.saver.restore(sess, ckpt.model_checkpoint_path) # restore all variables
            else:
                print('Initializing variables')

            for ephoc in range(start_ephoc, ephocs_num):

                ############
                # Training #
                ############

                startTime = datetime.now().replace(microsecond=0)
                print('='*50,'\nTrain, ephoc:',ephoc)
                np.random.shuffle(trn_data)
                trn_loss, trn_acc, trn_iou = 0, 0, 0
                qMean, qSTD, qRange, imgMean, imgSTD, imgRange = 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
                g_img, g_rnn = 0.0, 0.0

                for b in range(trn_nbatch):
                    attn_idx, padded_queries, padded_im, padded_bbox, labels = self.build_data(trn_data,
                                                                                b*self.batch_size,
                                                                                (b+1)*self.batch_size)

                    feed_dict = {
                        self.queries:padded_queries,
                        self.img:padded_im,
                        self.bboxes:padded_bbox,
                        self.attn_idx:attn_idx,
                        self.labels: labels,
                        self.dropout_in:dropout_in,
                        self.dropout_out:dropout_out,
                        self.dropout_img:dropout_img,
                        self.dropout_q:dropout_q,
                        self.isTrain:True
                    }

                    scores, loss, lr, gs,  _ = sess.run([self.scores,
                                                        self.loss, self.learning_rate,
                                                        self.global_step, self.optimizer], feed_dict=feed_dict)

                    loss, scores, acc = self.accuracy(sess=sess, feed_dict=feed_dict)
                    iou_acc = self.iou_accuracy(trn_data, b*self.batch_size, (b+1)*self.batch_size,
                                                sess=sess, feed_dict=feed_dict, scores=scores)

                    trn_acc += acc/trn_nbatch
                    trn_loss += loss/trn_nbatch
                    trn_iou += iou_acc/trn_nbatch

                    if b%50==0:
                        print('b:%d'%(b),
                                ';lr:%.3f'%(lr),
                                ';loss:%.2f'%(loss), ';acc:%.2f'%(acc),
                                ';iou:%.2f'%(iou_acc),
                                ';time:', datetime.now().replace(microsecond=0)-startTime)

                print('\n*Tr loss: %.3f'%(trn_loss),';Tr acc: %.3f'%(trn_acc),
                        ';IOU acc: %.3f'%(trn_iou),  ';Time:', datetime.now().replace(microsecond=0)-startTime, '\n')

                self.saver.save(sess, self.params_dir + "/model.ckpt", global_step=ephoc)

                ###########
                # Testing #
                ###########

                print('Testing, ephoc:',ephoc)
                tstTime = datetime.now().replace(microsecond=0)
                tst_loss, tst_acc, tst_iou = 0, 0, 0
                for b in range(tst_nbatch):
                    attn_idx, padded_queries, padded_im, padded_bbox, labels = self.build_data(tst_data,
                                                                                b*self.batch_size,
                                                                                (b+1)*self.batch_size)
                    feed_dict = {
                        self.queries:padded_queries,
                        self.img:padded_im,
                        self.bboxes:padded_bbox,
                        self.attn_idx:attn_idx,
                        self.labels: labels,
                        self.dropout_in:1.,
                        self.dropout_out:1.,
                        self.dropout_img:1.,
                        self.dropout_q:1.,
                        self.isTrain:False
                    }
                    scores, loss = sess.run([self.scores, self.loss], feed_dict=feed_dict)
                    _,_, acc = self.accuracy(sess=sess, feed_dict=feed_dict, scores=scores)
                    iou_acc = self.iou_accuracy(
                        tst_data, b*self.batch_size, int(b+1)*self.batch_size, sess=sess,
                        feed_dict=feed_dict, scores=scores)

                    tst_acc += acc/tst_nbatch
                    tst_loss += loss/tst_nbatch
                    tst_iou += iou_acc/tst_nbatch
                    if b%50==0:
                        print('batch:', b, ';loss: %.3f'%(loss), ';acc: %.3f'%(acc),
                               ';iou_acc: %.3f'%(iou_acc), ';time:',
                              datetime.now().replace(microsecond=0)-startTime)

                print('\n*Test loss: %.3f'%(tst_loss), ';Test accuracy %.3f'%(tst_acc),
                      ';Test IOU: %.3f'%(tst_iou), ';Time:', datetime.now().replace(microsecond=0)-startTime)
            print('='*50,'\n')


import logging
from datetime import datetime

from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.triggers.cron import CronTrigger
from django.conf import settings
from django.core.mail import mail_managers, EmailMultiAlternatives
from django.core.management.base import BaseCommand
from django_apscheduler import util
from django_apscheduler.jobstores import DjangoJobStore
from django_apscheduler.models import DjangoJobExecution

from django.contrib.auth.models import User
from news.models import Post, Category, Subscription

from news.email_sending import mass_email_sending

logger = logging.getLogger(__name__)

# last_send_date = None

def get_last_send_date():
    executions = DjangoJobExecution.objects.filter(job_id="new_post_mass_notification").filter(status="Executed").order_by("-run_time").values_list("run_time", flat=True)[:1]
    if executions.count() > 0:
        return executions[0]
    else:
        return None

def new_post_mass_notification_func():
    mass_email_sending()

@util.close_old_connections
def delete_old_job_executions(max_age=604_800):
    DjangoJobExecution.objects.delete_old_job_executions(max_age)

class Command(BaseCommand):
    help = "Runs APScheduler."

    def handle(self, *args, **options):
        scheduler = BlockingScheduler(timezone=settings.TIME_ZONE)
        scheduler.add_jobstore(DjangoJobStore(), "default")

        scheduler.add_job(
            new_post_mass_notification_func,
            trigger=CronTrigger(day_of_week="fri", hour="18", minute="00"),
            # trigger=CronTrigger(second="*/5"),  # Every 10 seconds
            id="new_post_mass_notification",  # The `id` assigned to each job MUST be unique
            max_instances=1,
            replace_existing=True,
        )
        logger.info("Added job 'new_post_mass_notification'.")

        scheduler.add_job(
            delete_old_job_executions,
            trigger=CronTrigger(
                day_of_week="mon", hour="00", minute="00"
            ),
            id="delete_old_job_executions",
            max_instances=1,
            replace_existing=True,
        )
        logger.info("Added weekly job: 'delete_old_job_executions'.")

        try:
            logger.info("Starting scheduler...")
            scheduler.start()
        except KeyboardInterrupt:
            logger.info("Stopping scheduler...")
            scheduler.shutdown()
            logger.info("Scheduler shut down successfully!")


##
##

import logging
import os
import configparser
from typing import Union, List
from azure.identity import AzureCliCredential
from azure.mgmt.compute import ComputeManagementClient
from azure.mgmt.network import NetworkManagementClient
from azure.mgmt.resource.resources import ResourceManagementClient
from azure.mgmt.resource.subscriptions import SubscriptionClient
from couchformation.config import AuthMode
from couchformation.exception import FatalError, NonFatalError
from couchformation.azure.driver.constants import get_auth_directory, get_config_default, get_config_main
from couchformation.azure.driver.constants import AzureDiskTiers

logger = logging.getLogger('couchformation.azure.driver.base')
logger.addHandler(logging.NullHandler())
logging.getLogger("azure").setLevel(logging.ERROR)
logging.getLogger("urllib3").setLevel(logging.ERROR)

class AzureDriverError(FatalError):
    pass

class EmptyResultSet(NonFatalError):
    pass

class CloudBase(object):

    def __init__(self, parameters: dict):
        self.parameters = parameters
        self.auth_directory = get_auth_directory()
        self.config_default = get_config_default()
        self.config_main = get_config_main()
        self.cloud_name = 'AzureCloud'
        self.local_context = None
        self.azure_subscription_id = None
        self.credential = None
        self.azure_resource_group = None
        self.azure_location = None
        self.azure_availability_zones = []
        self.azure_zone = None
        self.subscription_client = None

        if not parameters.get('auth_mode') or AuthMode[parameters.get('auth_mode')] == AuthMode.default:
            self.credential, self.azure_subscription_id = self.default_auth()
        else:
            raise AzureDriverError(f"Unsupported auth mode {parameters.get('auth_mode')}")

        if not self.credential or not self.azure_subscription_id:
            raise AzureDriverError("unauthorized (use az login)")

        self.resource_client = ResourceManagementClient(self.credential, self.azure_subscription_id)
        self.compute_client = ComputeManagementClient(self.credential, self.azure_subscription_id)
        self.network_client = NetworkManagementClient(self.credential, self.azure_subscription_id)

        self.azure_location = parameters.get('region')

        self.zones()

    @staticmethod
    def default_auth():
        try:
            credential = AzureCliCredential()
            subscription_client = SubscriptionClient(credential)
            subscriptions = subscription_client.subscriptions.list()
            azure_subscription_id = next((s.subscription_id for s in subscriptions), None)
            return credential, azure_subscription_id
        except Exception as err:
            raise AzureDriverError(f"Azure: unauthorized (use az login): {err}")

    def test_session(self):
        if len(self.azure_availability_zones) == 0:
            raise AzureDriverError(f"Unable to determine availability zones for location {self.azure_location}")

    @staticmethod
    def disk_size_to_tier(value: Union[int, str]):
        size = int(value)
        size_list = [int(i['disk_size']) for i in AzureDiskTiers.disk_tier_list]
        value = min([s for s in size_list if s >= size])
        return next(t for t in AzureDiskTiers.disk_tier_list if t['disk_size'] == str(value))

    @staticmethod
    def disk_caching(value: Union[int, str]):
        size = int(value)
        if size > 4095:
            return "None"
        else:
            return "ReadWrite"

    def read_config(self):
        if os.path.exists(self.config_main):
            config = configparser.ConfigParser()
            try:
                config.read(self.config_main)
            except Exception as err:
                raise AzureDriverError(f"can not read config file {self.config_main}: {err}")

            if 'cloud' in config:
                if 'name' in config['cloud']:
                    self.cloud_name = config['cloud']['name']

            if 'local_context' in config:
                try:
                    self.local_context = list(config['local_context'].keys())[0]
                except IndexError:
                    pass

        if os.path.exists(self.config_default):
            config = configparser.ConfigParser()
            try:
                config.read(self.config_default)
            except Exception as err:
                raise AzureDriverError(f"can not read config file {self.config_default}: {err}")

            if self.cloud_name in config:
                self.azure_subscription_id = config[self.cloud_name].get('subscription', None)

    def zones(self) -> list:
        zone_list = self.compute_client.resource_skus.list(filter=f"location eq '{self.azure_location}'")
        for group in list(zone_list):
            if group.resource_type == 'virtualMachines':
                for resource_location in group.location_info:
                    for zone_number in resource_location.zones:
                        self.azure_availability_zones.append(zone_number)

        self.azure_availability_zones = sorted(set(self.azure_availability_zones))

        if len(self.azure_availability_zones) == 0:
            raise AzureDriverError("can not get Azure availability zones")

        self.azure_zone = self.azure_availability_zones[0]
        return self.azure_availability_zones

    def create_rg(self, name: str, location: str, tags: Union[dict, None] = None) -> dict:
        if not tags:
            tags = {}
        if not tags.get('type'):
            tags.update({"type": "couch-formation"})
        try:
            if self.resource_client.resource_groups.check_existence(name):
                return self.get_rg(name, location)
            else:
                result = self.resource_client.resource_groups.create_or_update(
                    name,
                    {
                        "location": location,
                        "tags": tags
                    }
                )
                return result.__dict__
        except Exception as err:
            raise AzureDriverError(f"error creating resource group: {err}")

    def get_rg(self, name: str, location: str) -> Union[dict, None]:
        try:
            if self.resource_client.resource_groups.check_existence(name):
                result = self.resource_client.resource_groups.get(name)
                if result.location == location:
                    return result.__dict__
        except Exception as err:
            raise AzureDriverError(f"error getting resource group: {err}")

        return None

    def list_rg(self, location: Union[str, None] = None, filter_keys_exist: Union[List[str], None] = None) -> List[dict]:
        rg_list = []

        try:
            resource_groups = self.resource_client.resource_groups.list()
        except Exception as err:
            raise AzureDriverError(f"error getting resource groups: {err}")

        for group in list(resource_groups):
            if location:
                if group.location != location:
                    continue
            rg_block = {'location': group.location,
                        'name': group.name,
                        'id': group.id}
            rg_block.update(self.process_tags(group.tags))
            if filter_keys_exist:
                if not all(key in rg_block for key in filter_keys_exist):
                    continue
            rg_list.append(rg_block)

        if len(rg_list) == 0:
            raise EmptyResultSet(f"no resource groups found")

        return rg_list

    def delete_rg(self, name: str):
        try:
            if self.resource_client.resource_groups.check_existence(name):
                request = self.resource_client.resource_groups.begin_delete(name)
                request.wait()
        except Exception as err:
            raise AzureDriverError(f"error deleting resource group: {err}")

    def list_locations(self) -> List[dict]:
        location_list = []
        locations = self.subscription_client.subscriptions.list_locations(self.azure_subscription_id)
        for group in list(locations):
            location_block = {
                'name': group.name,
                'display_name': group.display_name
            }
            location_list.append(location_block)
        return location_list

    @staticmethod
    def process_tags(struct: dict) -> dict:
        block = {}
        if struct:
            for tag in struct:
                block.update({tag.lower() + '_tag': struct[tag]})
        block = dict(sorted(block.items()))
        return block

    def rg_switch(self):
        image_rg = f"cf-image-{self.azure_location}-rg"
        if self.get_rg(image_rg, self.azure_location):
            resource_group = image_rg
        else:
            resource_group = self.azure_resource_group
        return resource_group

    @property
    def region(self):
        return self.azure_location


import math
import sys
import six
import time

import numpy as np
import tensorflow as tf
import tf_extended as tfe
import tf_utils
from tensorflow.python.framework import ops

from datasets import dataset_factory
from nets import nets_factory
from preprocessing import preprocessing_factory
from nets import np_methods

slim = tf.contrib.slim
tf.enable_eager_execution()
# =========================================================================== #
# Some default EVAL parameters
# =========================================================================== #
# List of recalls values at which precision is evaluated.
LIST_RECALLS = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.85,
                0.90, 0.95, 0.96, 0.97, 0.98, 0.99]
DATA_FORMAT = 'NHWC'

# =========================================================================== #
# SSD evaluation Flags.
# =========================================================================== #
tf.app.flags.DEFINE_float(
    'select_threshold', 0.01, 'Selection threshold.')
tf.app.flags.DEFINE_integer(
    'select_top_k', 400, 'Select top-k detected bounding boxes.')
tf.app.flags.DEFINE_integer(
    'keep_top_k', 200, 'Keep top-k detected objects.')
tf.app.flags.DEFINE_float(
    'nms_threshold', 0.45, 'Non-Maximum Selection threshold.')
tf.app.flags.DEFINE_float(
    'matching_threshold', 0.5, 'Matching threshold with groundtruth objects.')
tf.app.flags.DEFINE_integer(
    'eval_resize', 4, 'Image resizing: None / CENTRAL_CROP / PAD_AND_RESIZE / WARP_RESIZE.')
tf.app.flags.DEFINE_integer(
    'eval_image_size', None, 'Eval image size.')
tf.app.flags.DEFINE_boolean(
    'remove_difficult', True, 'Remove difficult objects from evaluation.')

# =========================================================================== #
# Main evaluation flags.
# =========================================================================== #
tf.app.flags.DEFINE_integer(
    'num_classes', 21, 'Number of classes to use in the dataset.')
tf.app.flags.DEFINE_integer(
    'batch_size', 1, 'The number of samples in each batch.')
tf.app.flags.DEFINE_integer(
    'max_num_batches', None,
    'Max number of batches to evaluate by default use all.')
tf.app.flags.DEFINE_string(
    'master', '', 'The address of the TensorFlow master to use.')
tf.app.flags.DEFINE_string(
    'checkpoint_path', 'checkpoints/VGG_VOC0712_SSD_300x300_ft_iter_120000.ckpt',
    'The directory where the model was written to or an absolute path to a '
    'checkpoint file.')
tf.app.flags.DEFINE_string(
    'eval_dir', '/tmp/tfmodel/', 'Directory where the results are saved to.')
tf.app.flags.DEFINE_integer(
    'num_preprocessing_threads', 4,
    'The number of threads used to create the batches.')
tf.app.flags.DEFINE_string(
    'dataset_name', 'pascalvoc_2007', 'The name of the dataset to load.')
tf.app.flags.DEFINE_string(
    'dataset_split_name', 'test', 'The name of the train/test split.')
tf.app.flags.DEFINE_string(
    'dataset_dir', 'assets/PASCAL_VOC/', 'The directory where the dataset files are stored.')
tf.app.flags.DEFINE_string(
    'model_name', 'ssd_300_vgg', 'The name of the architecture to evaluate.')
tf.app.flags.DEFINE_string(
    'preprocessing_name', None, 'The name of the preprocessing to use. If left '
                                'as `None`, then the model_name flag is used.')
tf.app.flags.DEFINE_float(
    'moving_average_decay', None,
    'The decay to use for the moving average.'
    'If left as None, then moving averages are not used.')
tf.app.flags.DEFINE_float(
    'gpu_memory_fraction', 0.1, 'GPU memory fraction to use.')
tf.app.flags.DEFINE_boolean(
    'wait_for_checkpoints', False, 'Wait for new checkpoints in the eval loop.')

FLAGS = tf.app.flags.FLAGS

def main(_):
    if not FLAGS.dataset_dir:
        raise ValueError('You must supply the dataset directory with --dataset_dir')

    tf.logging.set_verbosity(tf.logging.INFO)
    with tf.Graph().as_default():
        tf_global_step = slim.get_or_create_global_step()

        # =================================================================== #
        # Dataset + SSD model + Pre-processing
        # =================================================================== #
        dataset = dataset_factory.get_dataset(
            FLAGS.dataset_name, FLAGS.dataset_split_name, FLAGS.dataset_dir)

        # Get the SSD network and its anchors.
        ssd_class = nets_factory.get_network(FLAGS.model_name)
        ssd_params = ssd_class.default_params._replace(num_classes=FLAGS.num_classes)
        ssd_net = ssd_class(ssd_params)

        # Evaluation shape and associated anchors: eval_image_size
        net_shape = (300, 300)
        ssd_shape = net_shape
        ssd_anchors = ssd_net.anchors(ssd_shape)

        # Select the preprocessing function.
        preprocessing_name = FLAGS.preprocessing_name or FLAGS.model_name
        image_preprocessing_fn = preprocessing_factory.get_preprocessing(
            preprocessing_name, is_training=False)

        tf_utils.print_configuration(FLAGS.__flags, ssd_params,
                                     dataset.data_sources, FLAGS.eval_dir)
        # =================================================================== #
        # Create a dataset provider and batches.
        # =================================================================== #
        with tf.device('/cpu:0'):
            with tf.name_scope(FLAGS.dataset_name + '_data_provider'):
                provider = slim.dataset_data_provider.DatasetDataProvider(
                    dataset,
                    common_queue_capacity=2 * FLAGS.batch_size,
                    common_queue_min=FLAGS.batch_size,
                    shuffle=False)
            # Get for SSD network: image, labels, bboxes.
            [image, shape, glabels, gbboxes] = provider.get(['image', 'shape',
                                                             'object/label',
                                                             'object/bbox'])
            if FLAGS.remove_difficult:
                [gdifficults] = provider.get(['object/difficult'])
            else:
                gdifficults = tf.zeros(tf.shape(glabels), dtype=tf.int64)

            # Pre-processing image, labels and bboxes.

            gpu_options = tf.GPUOptions(allow_growth=True)
            config = tf.ConfigProto(log_device_placement=False, gpu_options=gpu_options)
            isess = tf.InteractiveSession(config=config)

            net_shape = (300, 300)
            data_format = 'NHWC'
            img_input = tf.placeholder(tf.uint8, shape=(None, None, 3))

            image_pre, glabels, gbboxes, gbbox_img = \
                image_preprocessing_fn(image, glabels, gbboxes,
                                       out_shape=ssd_shape,
                                       data_format=DATA_FORMAT,
                                       resize=FLAGS.eval_resize,
                                       difficults=None)
            image_4d = tf.expand_dims(image_pre, 0)

            from nets import ssd_vgg_300

            ssd_net = ssd_vgg_300.SSDNet()
            with slim.arg_scope(ssd_net.arg_scope(data_format=data_format)):
                rpredictions, rlocalisations, _, _ = ssd_net.net(image_4d, is_training=False, reuse=tf.AUTO_REUSE)
            print('yes')
            ckpt_filename = 'checkpoints/ssd_300_vgg.ckpt'
            isess.run(tf.global_variables_initializer())
            saver = tf.train.Saver()
            saver.restore(isess, ckpt_filename)

            ssd_anchors = ssd_net.anchors(net_shape)
            bbox_img = gbbox_img

            select_threshold = 0.5
            nms_threshold = .45
            net_shape = (300, 300)
            # Run SSD network.

            rbbox_img = gbbox_img

            print(image_4d)
            # rimg, rpredictions, rlocalisations, rbbox_img = isess.run([image_4d, rpredictions, rlocalisations, gbbox_img])
            # rclasses, rscores, rbboxes = np_methods.ssd_bboxes_select(
            #     rpredictions, rlocalisations, ssd_anchors,
            #     select_threshold=select_threshold, img_shape=net_shape, num_classes=21, decode=True)
            #
            #
            # rbboxes = np_methods.bboxes_clip(rbbox_img, rbboxes)
            # rclasses, rscores, rbboxes = np_methods.bboxes_sort(rclasses, rscores, rbboxes, top_k=400)
            # rclasses, rscores, rbboxes = np_methods.bboxes_nms(rclasses, rscores, rbboxes, nms_threshold=nms_threshold)
            # # Resize bboxes to original image shape. Note: useless for Resize.WARP!
            # rbboxes = np_methods.bboxes_resize(rbbox_img, rbboxes)

def np_ssd_bboxes_select(rpredictions, rlocalisations, net_shape):
    return np_methods.ssd_bboxes_select(rpredictions, rlocalisations, ssd_anchors,
                                        select_threshold=FLAGS.select_threshold, img_shape=net_shape, num_classes=21,
                                        decode=True)

def py_postprocess(rpredictions, rlocalisations, ssd_anchors, net_shape, rbbox_img):
    from nets import np_methods
    rclasses, rscores, rbboxes = np_methods.ssd_bboxes_select(
        rpredictions, rlocalisations, ssd_anchors,
        select_threshold=FLAGS.select_threshold, img_shape=net_shape, num_classes=21, decode=True)

    rbboxes = np_methods.bboxes_clip(rbbox_img, rbboxes)
    rclasses, rscores, rbboxes = np_methods.bboxes_sort(rclasses, rscores, rbboxes, top_k=400)
    rclasses, rscores, rbboxes = np_methods.bboxes_nms(rclasses, rscores, rbboxes, nms_threshold=FLAGS.nms_threshold)
    # Resize bboxes to original image shape. Note: useless for Resize.WARP!
    rbboxes = np_methods.bboxes_resize(rbbox_img, rbboxes)

    return rscores, rbboxes

if __name__ == '__main__':
    tf.app.run()


import cv2, imutils, os
import numpy as np
import matplotlib.pyplot as plt

wait = 1

def stitch(prev, im, result, posx,posy, ratio=0.75, reprojThresh=4.0,
	show=False):

	offsetx,offsety,H = generate_homography(prev,im)

	posx+=offsetx
	posy-=offsety
	print(posx,posy)
	if(offsetx>0):
		print('right')
	elif(offsetx<0):
		print('left')
	if(offsety>0):
		print('down')
	elif(offsety<0):
		print('up')
	print()

	#prev[offsety:im.shape[0]+offsety, offsetx:im.shape[1]+offsetx] = im

	result = append_images(result,im,-offsetx,-offsety)

	if show:
		global wait
		cv2.imshow("result",result)
		cv2.imshow("current",im)
		k = cv2.waitKey(wait)
		if(k==27):
			cv2.destroyAllWindows()
			exit(1)
		if(k==32):
			wait = not wait

	return result,posx,posy

def analyze(imageA, imageB, ratio = 0.75,reprojThresh=4.0):
	(kpsA, featuresA) = detectAndDescribe(imageA)
	(kpsB, featuresB) = detectAndDescribe(imageB)
	M = matchKeypoints(kpsA, kpsB,
		featuresA, featuresB, ratio, reprojThresh)
	if M is None:
		return None
	(matches, H, status) = M
	return find_shift(kpsA, kpsB, matches, status)

def append_images(imageA,imageB,shift_x,shift_y):
	right,shift_x = (abs(shift_x),0) if shift_x<0 else (0,shift_x)
	down,shift_y = (abs(shift_y),0) if shift_y<0 else (0,shift_y)
	result = np.zeros((max(imageA.shape[0]+shift_y,imageB.shape[0]+down),max(imageA.shape[1]+shift_x,imageB.shape[1]+right),3))
	result[:,:]=-np.inf
	#print(imageA.shape,result.shape)
	result[shift_y:(imageA.shape[0]+shift_y),shift_x:(imageA.shape[1]+shift_x),:]=\
	np.where(result[shift_y:(imageA.shape[0]+shift_y),shift_x:(imageA.shape[1]+shift_x),:]==-np.inf,\
	imageA,result[shift_y:(imageA.shape[0]+shift_y),shift_x:(imageA.shape[1]+shift_x),:])
	result[down:(imageB.shape[0]+down),right:(imageB.shape[1]+right),:]= \
	np.where(result[down:(imageB.shape[0]+down),right:(imageB.shape[1]+right),:]==-np.inf, \
	imageB,result[down:(imageB.shape[0]+down),right:(imageB.shape[1]+right),:])
	result = result.astype(np.uint8)
	return result

def generate_homography(imageA, imageB, ratio=0.75, reprojThresh=4.0):
	grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
	grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)
	descriptor = cv2.xfeatures2d.SIFT_create()
	(kpsA, featuresA) = descriptor.detectAndCompute(imageA, None)
	(kpsB, featuresB) = descriptor.detectAndCompute(imageB, None)
	kpsA = np.float32([kp.pt for kp in kpsA])
	kpsB = np.float32([kp.pt for kp in kpsB])

	M = matchKeypoints(kpsA, kpsB, featuresA, featuresB, ratio, reprojThresh)
	if M is None:
		return None
	(matches, H, status) = M
	H_inv = np.linalg.inv(H)
	ds = np.dot(H_inv, np.array([imageA.shape[1], imageA.shape[0], 1]))
	ds = ds/ds[-1]
	f1 = np.dot(H_inv, np.array([0,0,1]))
	f1 = f1/f1[-1]
	H_inv[0][-1] += abs(f1[0])
	H_inv[1][-1] += abs(f1[1])
	ds = np.dot(H_inv, np.array([imageA.shape[1], imageA.shape[0], 1]))
	offsety = int(f1[1])
	offsetx = int(f1[0])
	return offsetx, offsety, H

def matchKeypoints(kpsA, kpsB, featuresA, featuresB,
	ratio, reprojThresh):
	# compute the raw matches and initialize the list of actual
	# matches
	matcher = cv2.DescriptorMatcher_create("BruteForce")
	rawMatches = matcher.knnMatch(featuresA, featuresB, 2)
	matches = []

	# loop over the raw matches
	for m in rawMatches:
		# ensure the distance is within a certain ratio of each
		# other (i.e. Lowe's ratio test)
		if len(m) == 2 and m[0].distance < m[1].distance * ratio:
			matches.append((m[0].trainIdx, m[0].queryIdx))

	# computing a homography requires at least 4 matches
	if len(matches) > 4:
		# construct the two sets of points
		ptsA = np.float32([kpsA[i] for (_, i) in matches])
		ptsB = np.float32([kpsB[i] for (i, _) in matches])

		# compute the homography between the two sets of points
		(H, status) = cv2.findHomography(ptsA, ptsB, cv2.RANSAC,
			reprojThresh)

		# return the matches along with the homograpy matrix
		# and status of each matched point
		return (matches, H, status)

	# otherwise, no homograpy could be computed
	return None

def drawMatches(imageA, imageB, kpsA, kpsB, matches, status):
	# initialize the output visualization image
	(hA, wA) = imageA.shape[:2]
	(hB, wB) = imageB.shape[:2]
	vis = np.zeros((max(hA, hB), wA + wB, 3), dtype="uint8")
	vis[0:hA, 0:wA] = imageA
	vis[0:hB, wA:] = imageB

	# loop over the matches
	for ((trainIdx, queryIdx), s) in zip(matches, status):
		# only process the match if the keypoint was successfully
		# matched
		if s == 1:
			# draw the match
			ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1]))
			ptB = (int(kpsB[trainIdx][0]) + wA, int(kpsB[trainIdx][1]))
			cv2.line(vis, ptA, ptB, (0, 255, 0), 1)

	# return the visualization
	return vis

def find_shift(kpsA, kpsB, matches, status):
	avgA=[0,0]
	avgB=[0,0]

	maxxA = -np.inf
	maxyA = -np.inf
	minxA = np.inf
	minyA = np.inf

	maxxB = -np.inf
	maxyB = -np.inf
	minxB = np.inf
	minyB = np.inf
	# loop over the matches
	for ((trainIdx, queryIdx), s) in zip(matches, status):
		# only process the match if the keypoint was successfully
		# matched
		if s == 1:
			ptA = (int(kpsA[queryIdx][0]), int(kpsA[queryIdx][1]))
			ptB = (int(kpsB[trainIdx][0]), int(kpsB[trainIdx][1]))

			if(ptA[0]>maxxA):
				maxxA = ptA[0]
			if(ptA[0]<minxA):
				minxA = ptA[0]
			if(ptA[1]>maxyA):
				maxyA = ptA[1]
			if(ptA[1]<minyA):
				minyA = ptA[1]

			if(ptB[0]>maxxB):
				maxxB = ptB[0]
			if(ptB[0]<minxB):
				minxB = ptB[0]
			if(ptB[1]>maxyB):
				maxyB = ptB[1]
			if(ptB[1]<minyB):
				minyB = ptB[1]

			avgA[0]+=ptA[0]/len(kpsA)
			avgA[1]+=ptA[1]/len(kpsA)
			avgB[0]+=ptB[0]/len(kpsB)
			avgB[1]+=ptB[1]/len(kpsB)

	return (int(avgA[0]),int(avgA[1])),(int(avgB[0]),int(avgB[1])), ((maxxA - minxA),(maxyA - minyA)), ((maxxB - minxB),(maxyB - minyB))

if __name__ == '__main__':
	prev = None
	result = None
	posx = 0
	posy = 0
	count=-1
	lst = sorted(os.listdir(os.fsencode('../../../data/frames/continuous/ACHA UNH/')))
	while count < len(lst):
		file = lst[count]
		filename = os.fsdecode(file)
		if(filename.endswith('.jpg') == False):
			continue

		count+=1
		if(count%1==0):
			#print(str(float(count)/float(len(lst))*100)+"%")
			im = cv2.imread('../../../data/frames/continuous/ACHA UNH/'+filename)
			im = cv2.resize(im,(480, 320))
			if(prev is None):
				result = im
				prev = im
				continue

			result,posx,posy = stitch(prev, im, result, posx,posy, show=True)
			prev = im


import http.server
import socketserver
import subprocess
import urllib.parse

PORT = 2728

class MyHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            with open('index.html', 'rb') as f:
                self.wfile.write(f.read())
        else:
            self.send_response(404)
            self.end_headers()
            self.wfile.write(b'Not Found')

    def do_POST(self):
        if self.path == '/process':
            content_length = int(self.headers['Content-Length'])
            post_data = self.rfile.read(content_length).decode('utf-8')
            query = urllib.parse.parse_qs(post_data)
            user_name = query['name'][0]

            php_script_output = self.run_php_script(user_name)

            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(b'<!DOCTYPE html>')
            self.wfile.write(f'
Hello, {user_name}!
'.encode('utf-8'))
            self.wfile.write(f'
PHP Output:

{php_script_output}
'.encode('utf-8'))
            self.wfile.write(b'')
        else:
            self.send_response(404)
            self.end_headers()
            self.wfile.write(b'Not Found')

    def run_php_script(self, user_name):
        php_script = f'php -r "$html_content = \'Hello, {user_name}!\'; include(\'php_script.php\');"'
        try:
            php_output = subprocess.check_output(php_script, shell=True)
            return php_output.decode('utf-8')
        except subprocess.CalledProcessError as e:
            return f'Error: {e}'

if __name__ == '__main__':
    with socketserver.TCPServer(("", PORT), MyHandler) as httpd:
        print(f"Serving at port {PORT}")
        httpd.serve_forever()