from django.shortcuts import redirect
import pytz
import os
from datetime import timedelta

from a_helper.modal_helper.views import (
    AjaxCreateView,
    AjaxDeleteView,
    AjaxUpdateView
)
from issuer.models import Event, Issuer
from gui.templatetags.template_tags import format_reference_number
from rating_process.models.issue_decision import IssueDecision

from rating_process.forms import (
    AddCommitteeMemberForm,
    EditDecidedAdjustmentFactor,
    EditDecidedSubfactor,
    EditProposedAdjustmentFactor,
    EditProposedSubfactor,
    EditRatingDecision,
    RatingDecisionIssueAddSeniority,
    RatingDecisionIssueEditDecided,
    RatingDecisionIssueEditProposed,
    StartRatingProcess,
    AddEditorForm,
    UpdateAdminControlEditorForm,
    UpdateAdminControlIssuerForm,
    RatingDecisionInsiderLinkForm,
    EditPressReleaseForm,
    IssueRatingDecisionAddForm,
    IssueRatingDecisionEditForm
)
from .models.job_member import JobMember, Group, Role
from .models.internal_score_data import InternalScoreData
from .models.rating_decision import RatingDecision
from .models.rating_decision_issue import RatingDecisionIssue
from .models.questions import ControlQuestion
from .models.temporary_storage import Tmp
from .models.process import Process
from .models.insider_link import RatingDecisionInsiderLink
from .models.press_release import PressRelease
from rating_process.tasks import refresh_issue_decision_attributes
from integrations.mailchimp.tasks import run_create_campaign
from django.contrib import messages

from .tasks import refresh_decision_attributes, update_issue_rating

from a_helper.mail.tasks import send_email, send_outlook_invitation

from .util import send_public_report
from django.views.generic import DetailView
from rating.util.generate_context import return_company_context

from django.utils import timezone
from django import http
from .const import (
    SETUP_HEADER,
    SETUP_BODY,
    PRE_COMMITTEE_HEADER,
    PRE_COMMITTEE_BODY,
    ANALYTICAL_PHASE_HEADER,
    ANALYTICAL_PHASE_BODY,
    CHAIR_FINAL_APPROVAL_BODY,
    CHAIR_FINAL_APPROVAL_HEADER,
    ANALYST_FINAL_APPROVAL_BODY,
    ANALYST_FINAL_APPROVAL_HEADER,
    EDITOR_EMAIL,
    EDITOR_HEADER,
    MEMBER_ADDED_HEADER,
    MEMBER_ADDED_BODY,
    ISSUE_DECISION_HEADER,
    ISSUE_DECISION_BODY
)

# pylint: disable=too-many-ancestors
class RatingProcessCreateView(AjaxCreateView):
    """RatingProcessCreateView."""

    form_class = StartRatingProcess

    def get_success_url(self):
        """Custom get_success_url method."""
        return self.request.META.get('HTTP_REFERER') + '#score'

    def get_form_kwargs(self):
        """Custom get_form_kwargs method."""

        kwargs = super(RatingProcessCreateView, self).get_form_kwargs()
        # update the kwargs for the form init method with yours
        kwargs.update(self.kwargs)  # self.kwargs contains all url conf params

        return kwargs

    def form_valid(self, form):
        """Custom form_valid method."""

        issuer_id = self.kwargs['issuer_pk']
        issuer_obj = Issuer.objects.get(id=issuer_id)

        obj = form.save(commit=False)
        obj.issuer = issuer_obj
        obj.initiated_by = self.request.user

        # We need the object instance to save the event, hence saving here
        obj.save()

        # Log the event
        Event.objects.create(
            issuer=issuer_obj,
            event_type_id=24,
            triggered_by_user=self.request.user
        )

        return super(RatingProcessCreateView, self).form_valid(form)

# pylint: disable=too-many-ancestors
class InternalScoreDataSubFactorUpdateView(AjaxUpdateView):
    """InternalScoreDataSubFactorUpdateView view."""

    model = InternalScoreData
    pk_url_kwarg = 'subscore_pk'

    def get_form_class(self):
        if self.kwargs['decided'] == '1':
            return EditDecidedSubfactor
        else:
            return EditProposedSubfactor

    def get_form_kwargs(self):

        kwargs = super(InternalScoreDataSubFactorUpdateView,
                       self).get_form_kwargs()
        kwargs.update({'edit_weight': int(self.kwargs['edit_weight'])})

        return kwargs

    def form_valid(self, form):

        if self.kwargs['decided'] != '1':
            obj = form.save(commit=False)
            obj.decided_score = form.cleaned_data['proposed_score']

            obj.save()

        return super(InternalScoreDataSubFactorUpdateView,
                     self).form_valid(form)

# pylint: disable=too-many-ancestors
class InternalScoreDataAdjustmentUpdateView(AjaxUpdateView):
    """InternalScoreDataAdjustmentUpdateView view."""

    model = InternalScoreData
    pk_url_kwarg = 'subscore_pk'

    def get_form_class(self):
        if self.kwargs['decided'] == '1':
            return EditDecidedAdjustmentFactor
        else:
            return EditProposedAdjustmentFactor

    def form_valid(self, form):

        if self.kwargs['decided'] != '1':
            obj = form.save(commit=False)
            obj.decided_notch_adjustment = form.cleaned_data[
                'proposed_notch_adjustment']

            obj.save()

        return super(InternalScoreDataAdjustmentUpdateView,
                     self).form_valid(form)

# pylint: disable=too-many-ancestors
class RatingDecisionUpdateView(AjaxUpdateView):
    """RatingDecisionUpdateView view."""

    model = RatingDecision
    form_class = EditRatingDecision
    pk_url_kwarg = 'rating_decision_pk'

    def get_form_kwargs(self):
        """Custom method for a kwargs to pass to form class."""

        rating_decision_obj = RatingDecision.objects.get(
            id=self.kwargs['rating_decision_pk'])

        kwargs = super(RatingDecisionUpdateView, self).get_form_kwargs()
        kwargs.update(
            {'field': self.kwargs['field'],
             'rating_decision_obj': rating_decision_obj}
        )

        return kwargs

    def form_valid(self, form):
        """Custom method for a valid form."""

        # date_time_committee
        date_time_committee = form.cleaned_data[
                'date_time_committee']

        if date_time_committee is not None:

            # We manually enter a date in Oslo/Stockholm time in the GUI
            # so we need to convert it into UTC
            obj = form.save(commit=False)
            datetime_in_utc = date_time_committee.astimezone(pytz.utc)
            obj.date_time_committee = datetime_in_utc
            obj.save()

        return super(RatingDecisionUpdateView, self).form_valid(form)

# pylint: disable=too-many-ancestors
class CommitteeMemberCreateView(AjaxCreateView):
    """CommitteeMemberCreateView view."""

    form_class = AddCommitteeMemberForm

    def get_success_url(self):
        return self.request.META.get('HTTP_REFERER')

    def get_form_kwargs(self):
        kwargs = super(CommitteeMemberCreateView, self).get_form_kwargs()

        # update the kwargs for the form init method with yours
        kwargs.update(self.kwargs)  # self.kwargs contains all url conf params
        return kwargs

    def form_valid(self, form):

        group_obj = Group.objects.get(pk=1)

        obj = form.save(commit=False)

        rating_decision_obj = form.cleaned_data['rating_decision']

        obj.rating_decision = rating_decision_obj
        obj.group = group_obj

        obj.save()

        r_name = obj.role.role_name.lower()
        if r_name == 'voter':
            role = 'a voter'
        else:
            role = 'an observer'

        # Send notification to analyst
        send_outlook_invitation.delay(
            header=MEMBER_ADDED_HEADER % obj.rating_decision.issuer,
            body=MEMBER_ADDED_BODY % (
                obj.member.first_name,
                self.request.user.first_name,
                role,
                obj.rating_decision.issuer,
            ),
            attendee=obj.member.email,
            start_time=obj.rating_decision.date_time_committee,
            end_time=obj.rating_decision.date_time_committee
        )

        return super(CommitteeMemberCreateView, self).form_valid(form)

# pylint: disable=too-many-ancestors
class EditorCreateView(AjaxCreateView):
    """Add an editor to the rating job."""

    form_class = AddEditorForm

    def get_success_url(self):
        return self.request.META.get('HTTP_REFERER')

    def get_form_kwargs(self):
        kwargs = super(EditorCreateView, self).get_form_kwargs()

        # update the kwargs for the form init method with yours
        kwargs.update(self.kwargs)  # self.kwargs contains all url conf params
        return kwargs

    def form_valid(self, form):

        obj = form.save(commit=False)

        rating_decision_obj = form.cleaned_data['rating_decision']
        group_obj = Group.objects.get(pk=2)
        role_obj = Role.objects.get(pk=3)

        obj.rating_decision = rating_decision_obj
        obj.group = group_obj
        obj.role = role_obj
        obj.save()

        return super(EditorCreateView, self).form_valid(form)

# pylint: disable=too-many-ancestors
class AdminControlUpdateView(AjaxUpdateView):
    """Add an editor to the rating job."""

    model = Tmp
    pk_url_kwarg = 'tmp_pk'

    def get_form_class(self):

        if self.kwargs['mode'] == '0':
            return UpdateAdminControlEditorForm
        else:
            return UpdateAdminControlIssuerForm

    def form_valid(self, form):

        obj = form.save(commit=False)

        # This is to store a null value if there is no link
        try:
            if len(obj.editor_admin_control_link) == 0:
                obj.editor_admin_control_link = None
        except:  # noqa E722
            obj.editor_admin_control_link = None

        try:
            if len(obj.issuer_admin_control_link) == 0:
                obj.issuer_admin_control_link = None
        except:  # noqa E722
            obj.issuer_admin_control_link = None

        obj.save()

        return super(AdminControlUpdateView, self).form_valid(form)

    def get_success_url(self):
        return self.request.META.get('HTTP_REFERER')

# pylint: disable=too-many-ancestors
class CommitteeMemberDeleteView(AjaxDeleteView):
    model = JobMember
    pk_url_kwarg = 'committee_member_id'

# pylint: disable=too-many-ancestors
class RatingDecisionIssueCreateView(AjaxCreateView):
    """Add seniority of an issue to the rating decision."""

    form_class = RatingDecisionIssueAddSeniority

    def get_success_url(self):
        return self.request.META.get('HTTP_REFERER')

    def get_form_kwargs(self):
        kwargs = super(RatingDecisionIssueCreateView, self).get_form_kwargs()

        # update the kwargs for the form init method with yours
        kwargs.update(self.kwargs)  # self.kwargs contains all url conf params

        return kwargs

    def form_valid(self, form):

        obj = form.save(commit=False)

        obj.rating_decision = form.cleaned_data['rating_decision']

        return super(RatingDecisionIssueCreateView, self).form_valid(form)

# pylint: disable=too-many-ancestors
class RatingDecisionIssueUpdateView(AjaxUpdateView):
    """RatingDecisionIssueUpdateView view."""

    model = RatingDecisionIssue
    pk_url_kwarg = 'rating_decision_issue_pk'

    def get_form_class(self):

        if self.kwargs['decided'] == '1':
            return RatingDecisionIssueEditDecided
        else:
            return RatingDecisionIssueEditProposed

    def form_valid(self, form):

        if self.kwargs['decided'] != '1':
            obj = form.save(commit=False)
            obj.decided_lt = form.cleaned_data['proposed_lt']

            obj.save()

        return super(RatingDecisionIssueUpdateView, self).form_valid(form)

def answer_question(request, control_question_pk, step):
    """Updates a question during the rating process."""

    control_question_obj = ControlQuestion.objects.get(pk=control_question_pk)

    control_question_obj.answered_on = timezone.now()
    control_question_obj.answered_by = request.user
    control_question_obj.answer_correct = True

    # Save changes
    control_question_obj.save()

    return http.HttpResponseRedirect(request.META.get(
        'HTTP_REFERER', '/') + '#setup_step_' + step)

# pylint: disable=too-many-ancestors
class LinkRaingJobInsiderView(AjaxCreateView):
    """View to add an insider to send list."""

    form_class = RatingDecisionInsiderLinkForm

    def get_form_kwargs(self):
        """Overwrite get_form_kwargs."""

        kwargs = super(LinkRaingJobInsiderView, self).get_form_kwargs()
        kwargs.update(self.kwargs)  # self.kwargs contains all url conf params

        return kwargs

# pylint: disable=too-many-ancestors
class UnlinkRaingJobInsiderView(AjaxDeleteView):
    """View to remove an insider from send list."""

    model = RatingDecisionInsiderLink
    pk_url_kwarg = 'pk'

def pending_confirm_parameter(request, action, rating_decision_pk):
    """Update steps in the rating process model Process."""

    hash = None

    if action:

        rating_decision_obj = RatingDecision.objects.get(pk=rating_decision_pk)

        rating_process_obj, created = Process.objects.get_or_create(
                rating_decision=rating_decision_obj)

        rating_job_id = format_reference_number(number=rating_decision_obj.id,
                                                object_type='rating_decision',)

        if action == 'setup_done':
            hash = 'setup_step_2'

            rating_decision_obj.process_step = 2
            rating_process_obj.setup_done = timezone.now()

            to_list = [
                rating_decision_obj.chair.email
            ]

            # Notify analysts and relationship manager
            cc_list = [
                rating_decision_obj.issuer.analyst.
                primary_analyst.email,
                rating_decision_obj.issuer.analyst.
                secondary_analyst.email,
                rating_decision_obj.issuer.relationship_manager.email,
            ]

            # We want to notify Compliance in production
            if os.environ['ENVIRONMENT_MODE'] == 'PROD':
                cc_list.append('compliance@nordiccreditrating.com')

            header = SETUP_HEADER.format(
                rating_decision_obj.issuer,
                rating_job_id,
            )
            body = SETUP_BODY % rating_decision_obj.chair.first_name

            # Send notification to chair
            send_email.delay(
                header=header,
                body=body,
                to=to_list,
                from_sender=None,
                cc=cc_list)

        elif action == 'pre_committee_done':
            hash = 'setup_step_3'

            rating_decision_obj.process_step = 3
            rating_process_obj.pre_committee_done = timezone.now()
            rating_decision_obj.chair_confirmed = True
            rating_decision_obj.date_time_committee_confirmed = True

            to_list = [
                rating_decision_obj.issuer.analyst.
                primary_analyst.email,
                rating_decision_obj.issuer.analyst.
                secondary_analyst.email,
            ]
            cc_list = [
                rating_decision_obj.issuer.relationship_manager.email,
                rating_decision_obj.chair.email,
            ]

            # We want to notify Compliance in production
            if os.environ['ENVIRONMENT_MODE'] == 'PROD':
                cc_list.append('compliance@nordiccreditrating.com')

            header = PRE_COMMITTEE_HEADER.format(
                rating_decision_obj.issuer,
                rating_decision_obj.chair.get_full_name(),
                rating_job_id,
            )

            # Send notification
            send_email.delay(
                header=header,
                body=PRE_COMMITTEE_BODY % rating_decision_obj.issuer.analyst.
                primary_analyst.first_name,
                to=to_list,
                from_sender=None,
                cc=cc_list,)

        elif action == 'analytical_phase_done':
            hash = 'setup_step_4'

            rating_decision_obj.process_step = 4
            rating_process_obj.analytical_phase_done = timezone.now()

            # Send notification to chair and members of committee
            to_list = [
                rating_decision_obj.issuer.analyst.primary_analyst.email,
                rating_decision_obj.issuer.analyst.secondary_analyst.email,
                rating_decision_obj.chair.email,
            ]
            cc_list = [
                rating_decision_obj.issuer.relationship_manager.email,
            ]

            # We want to notify Compliance in production
            if os.environ['ENVIRONMENT_MODE'] == 'PROD':
                cc_list.append('compliance@nordiccreditrating.com')

            # Get all members
            committee_members = list(
                JobMember.objects.confirmed_members().filter(
                    rating_decision=rating_decision_obj,
                    group_id=1))
            for item in committee_members:
                to_list.append(item.member.email)

            local_dt = timezone.localtime(
                rating_decision_obj.date_time_committee,
                timezone.get_fixed_timezone(60))

            header = ANALYTICAL_PHASE_HEADER.format(
                rating_decision_obj.issuer,
                local_dt.strftime('%Y-%m-%d %H:%M'),
                rating_job_id,
            )

            send_email.delay(
                header=header,
                body=ANALYTICAL_PHASE_BODY,
                to=to_list,
                cc=cc_list,
                from_sender=None)

        elif action == 'post_committee_done':
            hash = 'setup_step_5'

            rating_decision_obj.process_step = 5
            rating_process_obj.post_committee_done = timezone.now()

            # Contains the name and email of the editor
            editor_obj = JobMember.objects.get(
                rating_decision=rating_decision_obj,
                group=Group.objects.get(pk=2))

            to_list = [
                editor_obj.member.email,
            ]
            cc_list = [
                rating_decision_obj.issuer.analyst.primary_analyst.email,
                rating_decision_obj.issuer.analyst.secondary_analyst.email,
                rating_decision_obj.chair.email,
                rating_decision_obj.issuer.relationship_manager.email,
            ]

            # We want to notify Compliance in production
            if os.environ['ENVIRONMENT_MODE'] == 'PROD':
                cc_list.append('compliance@nordiccreditrating.com')

            header = EDITOR_HEADER.format(
                rating_decision_obj.issuer,
                rating_job_id,
            )

            # Send email with link to admin control to editor
            send_email.delay(header=header,
                             body=EDITOR_EMAIL % (
                                 editor_obj.member.first_name,
                                 rating_decision_obj.issuer.analyst.
                                 primary_analyst.first_name),
                             to=to_list,
                             cc=cc_list,
                             from_sender=None,)

        elif action == 'editor_phase_done':
            """Here, we're sending the draft report to the issuer."""

            hash = 'setup_step_6'

            rating_decision_obj.process_step = 6

            # External analysis
            send_public_report(rating_decision_obj)

            # Send notification to chair
            to_list = [
                rating_decision_obj.issuer.relationship_manager.email,
            ]

            header = "{} | the draft report has been sent to the " \
                     "issuer".format(rating_decision_obj.issuer,)

            send_email.delay(
                header=header,
                body=' ',
                to=to_list,
                from_sender=None,)

            # Set a timestamp when se sent the report to the issuer
            rating_decision_obj.date_time_communicated_issuer = timezone.now()
            rating_process_obj.editor_review_done = timezone.now()

        elif action == 'issuer_confirmation_phase_done':
            hash = 'setup_step_7'

            rating_decision_obj.process_step = 7

            to_list = [
                rating_decision_obj.chair.email,
                rating_decision_obj.issuer.analyst.primary_analyst.email,
                rating_decision_obj.issuer.analyst.secondary_analyst.email,
                rating_decision_obj.issuer.relationship_manager.email,
            ]

            # We want to notify Compliance in production
            if os.environ['ENVIRONMENT_MODE'] == 'PROD':
                to_list.append('compliance@nordiccreditrating.com')

            header = "{} | The issuer has confirmed the accuracy of the " \
                     "draft report for rating job {}".format(
                        rating_decision_obj.issuer,
                        rating_job_id, )

            # Send notification
            send_email.delay(
                header=header,
                body='',
                to=to_list,
                from_sender=None,)

            rating_process_obj.issuer_confirmation_done = timezone.now()

        elif action == 'analyst_final_approval_phase_done':
            hash = 'setup_step_8'

            rating_process_obj.final_sign_off_analyst_done = timezone.now()
            rating_decision_obj.process_step = 8

            to_list = [
                rating_decision_obj.issuer.analyst.primary_analyst.email,
            ]
            cc_list = [
                rating_decision_obj.chair.email,
                rating_decision_obj.issuer.analyst.secondary_analyst.email,
                rating_decision_obj.issuer.relationship_manager.email,
            ]

            # We want to notify Compliance in production
            if os.environ['ENVIRONMENT_MODE'] == 'PROD':
                cc_list.append('compliance@nordiccreditrating.com')

            header = ANALYST_FINAL_APPROVAL_HEADER.format(
                rating_decision_obj.issuer
            )

            # Send notification to chair
            send_email.delay(
                header=header,
                body=ANALYST_FINAL_APPROVAL_BODY %
                rating_decision_obj.issuer.analyst.
                primary_analyst.first_name,
                to=to_list,
                from_sender=None,
                cc=cc_list)

        elif action == 'chair_final_approval_phase_done':
            hash = 'setup_step_9'

            rating_decision_obj.process_step = 9
            rating_process_obj.final_sign_off_chair_done = timezone.now()

            to_list = [
                rating_decision_obj.issuer.analyst.primary_analyst.email,
                rating_decision_obj.issuer.analyst.secondary_analyst.email,
            ]
            cc_list = [
                rating_decision_obj.chair.email,
                rating_decision_obj.issuer.relationship_manager.email,
            ]

            # We want to notify Compliance in production
            if os.environ['ENVIRONMENT_MODE'] == 'PROD':
                cc_list.append('compliance@nordiccreditrating.com')

            header = CHAIR_FINAL_APPROVAL_HEADER.format(
                rating_decision_obj.issuer
            )

            # Send notification to analyst
            send_email.delay(header=header,
                             body=CHAIR_FINAL_APPROVAL_BODY %
                             rating_decision_obj.issuer.analyst.
                             primary_analyst.first_name,
                             to=to_list,
                             from_sender=None,
                             cc=cc_list,)

        elif action == 'publishing_phase_done':
            hash = ""

            rating_decision_obj.date_time_published = timezone.now()

            to_list = [
                rating_decision_obj.issuer.analyst.primary_analyst.email,
                rating_decision_obj.issuer.analyst.secondary_analyst.email,
                rating_decision_obj.chair.email,
                rating_decision_obj.issuer.relationship_manager.email,
            ]

            # We want to notify Compliance in production
            if os.environ['ENVIRONMENT_MODE'] == 'PROD':
                to_list.append('compliance@nordiccreditrating.com')

            header = "{} | the rating job has been finalized".format(
                rating_decision_obj.issuer
            )

            # Send notification
            send_email.delay(header=header,
                             body='If this a public rating, the publishing '
                                  'process will now commence.',
                             to=to_list,
                             from_sender=None,)

            """If there is a previous decision, flag it as non-current."""
            try:
                RatingDecision.objects.filter(
                    pk=rating_decision_obj.previous_rating.pk).update(
                    is_current=False,)
            except AttributeError:
                pass

            rating_decision_obj.is_current = True
            rating_process_obj.process_ended = timezone.now()

            # Create a DecisionAttribute object that
            # summarizes the rating decision
            refresh_decision_attributes(rating_decision_obj)

            # Create decisions on the issue level
            update_issue_rating(rating_decision_obj)

            # Create a draft campaign with MailChimp
            if rating_decision_obj.rating_type.id == 1:
                run_create_campaign.delay(rating_decision_obj.id)

            rating_decision_obj.process_step = 10

        # Save changes
        rating_process_obj.save()
        rating_decision_obj.save()

    redirect_url = request.META.get('HTTP_REFERER', '/') + '#' + hash

    return http.HttpResponseRedirect(redirect_url)

def confirm_parameter(request, pk):
    """Confirm date, chair or member."""

    action = request.GET.get('action')

    if action:

        if action == 'member':

            committee_member_obj = JobMember.objects.get(
                pk=pk)
            committee_member_obj.committee_member_confirmed = True
            committee_member_obj.save()

        return redirect(
            'dashboard')

# pylint: disable=too-many-ancestors
class PressReleaseUpdateView(AjaxUpdateView):
    """PressReleaseUpdateView view."""

    model = PressRelease
    form_class = EditPressReleaseForm
    pk_url_kwarg = 'press_release_pk'

    def get_form_kwargs(self):
        """Custom method for a kwargs to pass to form class."""

        press_release_obj = PressRelease.objects.get(
            id=self.kwargs['press_release_pk'])

        kwargs = super(PressReleaseUpdateView, self).get_form_kwargs()
        kwargs.update(
            {'field': self.kwargs['field'],
             'press_release_obj': press_release_obj}
        )

        return kwargs

class ViewRatingJob(DetailView):
    """View a rating job View."""

    model = RatingDecision
    template_name = 'issuer/rating_job/index_stand_alone.html'
    pk_url_kwarg = 'rating_decision_pk'

    def get_context_data(self, **kwargs):
        """Custom get_context_data method."""

        # Call the base implementation first to get a context
        context = super().get_context_data(**kwargs)

        b = return_company_context(self.request,
                                   self.kwargs['pk'],
                                   self.kwargs['rating_decision_pk'],)

        # Merge dicts
        context = {**context, **b}

        return context

# pylint: disable=too-many-ancestors
class IssueRatingDecisionAddView(AjaxCreateView):
    """Add seniority of an issue to the rating decision."""

    model = IssueDecision
    form_class = IssueRatingDecisionAddForm

    def get_success_url(self):
        return self.request.META.get('HTTP_REFERER')

    def get_form_kwargs(self):
        kwargs = super(IssueRatingDecisionAddView, self).get_form_kwargs()

        # update the kwargs for the form init method with yours
        kwargs.update(self.kwargs)  # self.kwargs contains all url conf params

        return kwargs

    def form_valid(self, form):

        obj = form.save(commit=False)

        obj.proposed_by = self.request.user
        obj.issue_id = form.cleaned_data['issue_id']
        obj.is_current = False
        obj.rating_decision_issue_id = form.cleaned_data[
            'rating_decision_issue_id']
        obj.rationale = form.cleaned_data[
            'rationale']

        if form.cleaned_data['ready_for_decision']:
            obj.process_step = 2

            messages.add_message(
                self.request,
                messages.INFO,
                'Sent issue rating decision for final approval.')

        else:

            messages.add_message(
                self.request,
                messages.INFO,
                'Saved issue rating decision.')

        obj.save()

        return super(IssueRatingDecisionAddView, self).form_valid(form)

# pylint: disable=too-many-ancestors
class IssueRatingDecisionEditView(AjaxUpdateView):
    """Add seniority of an issue to the rating decision."""

    model = IssueDecision
    pk_url_kwarg = 'issue_decision_pk'
    form_class = IssueRatingDecisionEditForm

    def get_form_kwargs(self):
        """Custom get_form_kwargs method."""

        kwargs = super(IssueRatingDecisionEditView, self).get_form_kwargs()

        # update the kwargs for the form init method with yours
        kwargs.update(self.kwargs)  # self.kwargs contains all url conf params

        return kwargs

    def get_success_url(self):
        """Custom get_success_url method."""

        return self.request.META.get('HTTP_REFERER')

    def form_valid(self, form):
        """Custom form_valid method."""

        obj = form.save(commit=False)

        # If all is clear for a decision, push the object
        # forward for someone to approve it
        if form.cleaned_data['ready_for_decision']:
            obj.process_step = 2

            messages.add_message(
                self.request,
                messages.INFO,
                'Sent issue rating decision for final approval.')

        elif form.cleaned_data['give_final_approval']:

            # This makes the decision valid and official
            obj.is_current = True

            # set chair = request.user
            obj.chair = self.request.user

            # Finish the process
            obj.process_step = 10

            # Committee timestamp is now
            obj.date_time_committee = timezone.now()

            # Send an email to issuer with the decision
            to_list = [
                obj.issue.issuer.analyst.primary_analyst.email,
            ]

            cc_list = [
                obj.issue.issuer.analyst.primary_analyst.email,
                obj.issue.issuer.analyst.secondary_analyst.email,
                obj.issue.issuer.relationship_manager.email,
            ]

            header = ISSUE_DECISION_HEADER.format(obj.issue.isin)
            body = ISSUE_DECISION_BODY.format(
                obj.get_decided_lt_display(),
                obj.issue.isin,
            )

            # Send notification
            send_email.delay(
                header=header,
                body=body,
                to=to_list,
                cc=cc_list,
            )

            # Set a timestamp when we informed the issuer
            obj.date_time_communicated_issuer = timezone.now()

            # We publish immediately following decision for issues
            obj.date_time_published = timezone.now() + timedelta(seconds=10)

            # Set all the attribute flags
            refresh_issue_decision_attributes(obj,
                                              is_between_committees=True)

            messages.add_message(
                self.request,
                messages.INFO,
                'Final decision for issue confirmed and saved.')

        else:

            messages.add_message(
                self.request,
                messages.INFO,
                'Saved issue rating decision.')

        obj.save()

        return super(IssueRatingDecisionEditView, self).form_valid(form)


import csv

with open ('/Users/Carol/Desktop/date_hms.csv','rU') as f1, open ("out_date_hms.sql",'w') as out:

	data=csv.reader(f1)
	next(data, None)

	for line in data:
		BRD_id = line[1]
		name = line[3]
		lincs_id =line[0]
		date=line[5]

		# To skip rows were the cell contain NULL values
		# if lincs_id == 'NULL':
		# 	continue

		# To skip rows were the cell is empty
		#if not lincs_ids:
		#	continue

		#print("UPDATE small_molecules SET "name = + """ + sm + """ + "WHERE `broad_id` = "+ """+ BRD_id + """ + ";")

		#print("UPDATE datasets SET `date_publicly_available` =" + " '"+ date + "'" + "WHERE `lincs_id` = "+ "'"+ lincs_id + "'"+";")

		#line_out = "UPDATE small_molecules SET `name` = \"" + name + "\" WHERE `broad_id` = \"" + BRD_id + "\";\n"
		line_out = "UPDATE datasets SET `data_publicly_available` = \"" + date + "\" WHERE `lincs_id` = \"" + lincs_id + "\";\n"
		out.write(line_out)


from functools import reduce

def persistence(n):
    # your code
    numscount = 0
    numlist = map(int, str(n))
    for i in range(10):
        if len(numlist) == 1:
            return numscount
        else:
            n = (reduce(lambda x, y: x * y, numlist))
            numlist = map(int, str(n))
            numscount = numscount+1

"""
def persistence(n):
    nums = [int(x) for x in str(n)]
    sist = 0
    while len(nums) > 1:
        newNum = reduce(lambda x, y: x * y, nums)
        nums = [int(x) for x in str(newNum)]
        sist = sist + 1
    return sist
"""

"""
import operator
def persistence(n):
    i = 0
    while n>=10:
        n=reduce(operator.mul,[int(x) for x in str(n)],1)
        i+=1
    return i
"""

"""
from operator import mul
def persistence(n):
    return 0 if n<10 else persistence(reduce(mul,[int(i) for i in str(n)],1))+1
"""


# Copyright 2018-2019 Ori Ben-Moshe - All rights reserved.
import cv2
import json
from numpy import array

from ..thresholds.color_.color import Color
from ..thresholds.color_.multi_color import MultiColor

LinearDiscriminantAnalysis = None

class HSVCalibration(object):
    """
    The calibration object that executes the machine learning
    Order: (img_mean, lower_bound)
    """
    def __init__(self, vision, class_amount, filter_min_ratio=0.9, min_area=100):
        """
        HSVCalibration object is an object that preforms a HSVCalibration function and runs during setup.
        :param vision: The Vision process to be calibration, a Vision object or Vision Json
        :param class_amount: The amount of ranges to be taken from every range for every image per class
        :param filter_min_ratio: a list with the ratio for each filter or just a float, for custom value per filter
                                 function.
        :param min_area: the minimum area a contour must have
        :returns: The calibration object
        """
        self.vision = vision
        self.color = vision.color
        self.vision.filters.remove()

        if type(filter_min_ratio) is float:
            self.rating_ratio = filter_min_ratio ** len(self.vision.get_filters())
        elif type(filter_min_ratio) is list:
            for ratio in filter_min_ratio:
                self.rating_ratio *= ratio
        self.min_area = min_area
        self.class_amount = class_amount
        self.s_clf = None
        self.v_clf = None

    class ContourRating:
        def __init__(self, contour, rating):
            self.rating = rating
            self.area = cv2.contourArea(contour)

    def brute_force(self, img, color, start_v=25, start_s=13, s_buffer=20, v_buffer=20):
        """
        Action: Calculates the Rating of each contour that passes all of the filters
        :param img: The image from which the contours should be taken
        :param color: the color that
        :param start_v: the starting v value from which to iterate, should not be 0 or negative
        :param start_s: the starting s value from which to iterate, should not be 0 or negative
        :param s_buffer: a buffer on the lower bound of color from which to check all possibilities
        :param v_buffer: a buffer on the lower bound of color from which to check all possibilities
        :return:
        """
        s_values = color.low_s + v_buffer
        v_values = color.low_v + s_buffer
        if s_values > 255 or s_values < 0:
            raise ValueError('Invalid range value,"s" must be between 0 and 255 (s = lower s + given buffer)')
        if v_values > 255 or v_values < 0:
            raise ValueError('Invalid range value,"v" must be between 0 and 255 (v = lower v + given buffer)')
        h = color.low_h
        high_range = color.high_bound
        final_list = []
        for s in range(start_s, s_values + 1):
            for v in range(start_v, v_values + 1):
                current_range = Color([h, s, v], high_range)
                self.vision.contours = self.vision.get_contours(img, color=current_range)
                self.vision.apply_all_filters(apply_all=True)
                filter_ratio_product = self.vision.apply_all_filters(apply_all=False)
                ratings = [1] * len(self.vision.contours)
                for filter_ratio in filter_ratio_product:
                    for idx, r in enumerate(filter_ratio):
                        ratings[idx] *= HSVCalibration.ContourRating(self.vision.contours[idx], r)
                ratings.sort(key=lambda k: k.rating)
                if len(ratings) >= self.vision.target_amount:
                    fail = False
                    for contour_rating in ratings[0: self.vision.target_amount]:
                        if type(contour_rating) is HSVCalibration.ContourRating:
                            if contour_rating.area <= self.min_area:
                                fail = True
                            elif contour_rating.rating < self.rating_ratio:
                                fail = True
                    if fail:
                        final_list.append([current_range.low_bound, False])
                    else:
                        final_list.append([current_range.low_bound, ratings])
                else:
                    final_list.append([current_range.low_bound, False])

        return final_list

    def find_data(self, img_list, color=None, start_v=25, start_s=13, s_buffer=20, v_buffer=20):
        """
        Action: Finds Working and non working color ranges and turns them into a list
        :param img_list: list of images from which to calibrate
        :param color: the color object
        :param start_v: the minimum valid v value
        :param start_s: the minimum valid s value
        :param s_buffer: the additional value values to check below low value
        :param v_buffer: the additional saturation values to check below low value
        :return: the Classifiers for Saturation and Value and the numpy array of data
        """
        global LinearDiscriminantAnalysis
        if LinearDiscriminantAnalysis is None:
            from sklearn.discriminant_analysis import LinearDiscriminantAnalysis

        if color is None:
            color = self.color

        def get_data_from_color(color_obj, img_obj, img_mean):
            s_data = []
            v_data = []
            s_classifier = []
            v_classifier = []
            rating_list = self.brute_force(img_obj, color_obj, start_v, start_s, s_buffer, v_buffer)
            class1 = 0
            class2 = 0

            for obj in rating_list[0:self.class_amount]:
                if type(obj) is list:
                    bound, rating = obj
                    s_data.append([img_mean[1], bound[1]])
                    s_classifier.append(1)
                    class1 += 1
                    v_data.append([img_mean[2], bound[2]])
                    v_classifier.append(1)
                    class1 += 1

            for bound, rating in rating_list:
                if rating is False and class2 < self.class_amount:
                    s_data.append([img_mean[1], bound[1]])
                    s_classifier.append(2)
                    class2 += 1
                    v_data.append([img_mean[2], bound[2]])
                    v_classifier.append(2)
                    class2 += 1
            return s_data, s_classifier, v_data, v_classifier

        if type(color) is Color:
            saturation_data, saturation_classes, value_data, value_classes = [], [], [], []
            for img in img_list:
                tmp = get_data_from_color(color, img, cv2.mean(img))
                tmp_saturation_data = tmp[0]
                tmp_saturation_classes = tmp[1]
                tmp_value_data = tmp[2]
                tmp_value_class = tmp[3]
                saturation_data.extend(tmp_saturation_data)
                saturation_classes.extend(tmp_saturation_classes)
                value_classes.extend(tmp_value_class)
                value_data.extend(tmp_value_data)
            if not (len(saturation_data) == len(saturation_classes) == len(value_classes) == len(value_data)):
                raise ValueError("Data isn't equal in size!")
            if saturation_data is []:
                raise ValueError("No data")
            self.s_clf = LinearDiscriminantAnalysis(solver='eigen')
            self.s_clf.fit(array(saturation_data), array(saturation_classes))
            self.v_clf = LinearDiscriminantAnalysis(solver='eigen')
            self.v_clf.fit(array(value_data), array(value_classes))

        elif type(color) is MultiColor:
            saturation_data, saturation_classes, value_data, value_classes = [], [], [], []
            for img in img_list:
                mean = cv2.mean(img)
                for color in color.colors:
                    tmp = get_data_from_color(color, img, mean)
                    tmp_saturation_data = tmp[0]
                    tmp_saturation_classes = tmp[1]
                    tmp_value_data = tmp[2]
                    tmp_value_class = tmp[3]
                    saturation_data.extend(tmp_saturation_data)
                    saturation_classes.extend(tmp_saturation_classes)
                    value_classes.extend(tmp_value_class)
                    value_data.extend(tmp_value_data)
            if not (len(saturation_data) == len(saturation_classes) == len(value_classes) == len(value_data)):
                raise ValueError("Data isn't equal in size!")
            if saturation_data is []:
                raise ValueError("No data")
            saturation_data, saturation_classes = array(saturation_data), array(saturation_classes)
            value_data, value_classes = array(value_data), array(value_classes)
            self.s_clf = LinearDiscriminantAnalysis(solver='eigen')
            self.s_clf.fit(saturation_data, saturation_classes)
            self.v_clf = LinearDiscriminantAnalysis(solver='eigen')
            self.v_clf.fit(value_data, value_classes)
            return self.s_clf, self.v_clf

    def json_serialize(self, file_name=None):
        """
        Action: Turns a Trained HSVCalibration object to a Json File for use in a Vision program
                And saves it as a json file
        :return: The Translation of the Object to Python
        """
        s_wc1, s_wc2 = self.s_clf.coef_[0]
        s_w_intercept = self.s_clf.intercept_
        s_mean = self.s_clf.means_

        v_wc1, v_wc2 = self.v_clf.coef_[0]
        v_w_intercept = self.v_clf.intercept_
        v_mean = self.v_clf.means_

        translation = {
            'saturation_weight_vector': {'coefficient1': s_wc1,
                                         'coefficient2': s_wc2,
                                         'inter': s_w_intercept,
                                         'mean': s_mean},
            'brightness_weight_vector': {'coefficient1': v_wc1,
                                         'coefficient2': v_wc2,
                                         'inter': v_w_intercept,
                                         'mean': v_mean}}
        if file_name is not None:
            if not file_name.endswith('.json'):
                print(None, 'Target file is not a json file')
            with open(file_name, 'r') as f:
                f.write(json.dumps(translation))
        return translation


from django.shortcuts import render, redirect, HttpResponse
from . import models

def index(request):
    book_info = models.Book.objects.all()
    context = {
        "book_info": book_info
    }
    return render(request, "books_authors_app/index.html", context)

def add_book(request):
    if request.method=="POST":
        models.Book.objects.create(title = request.POST['book_title'], description = request.POST['book_descrip'])
    return redirect('/')

def book_info(request, book_id):
    books = models.Book.objects.get(id=book_id)
    context={
        "book_title": books.title,
        "book_id": books.id,
        "book_descp": books.description,
        "authors": models.Book.objects.get(id=book_id).authors.all(),
        "all_authors": models.Author.objects.all()
    }
    print(context)
    return render(request, 'books_authors_app/views.html', context)

def add_author(request,book_id):
    if request.method == "POST":
        models.Book.objects.get(id=book_id).authors.add(models.Author.objects.get(id=request.POST["author_id"]))
        return redirect('/book_info/'+book_id)

def all_authors(request):
    context={
        "all_authors": models.Author.objects.all()
    }
    return render(request, "books_authors_app/authors.html", context)

def author_info(request, author_id):
    context ={
        "all_info": models.Author.objects.get(id=author_id),
        "related_books": models.Author.objects.get(id=author_id).books.all(),
        "all_books": models.Book.objects.all()
    }
    print(context)
    return render(request, "books_authors_app/author_info.html", context)

def new_author(request):
    if request.method == "POST":
        models.Author.objects.create(first_name = request.POST["first_name"], last_name=request.POST["last_name"], notes=request.POST["notes"])
    return redirect('/authors')

def book_to_author(request, author_id):
    if request.method == "POST":
        author=models.Author.objects.get(id=author_id)
        models.Book.objects.get(id=request.POST["new_book"]).authors.add(author)
    return redirect('/authors/'+author_id)


from typing import List, Tuple, Dict, Optional, Any, Union
from collections import defaultdict

import torch
import numpy as np
from tqdm import auto as tqdm

from torch import nn
from torch.optim import Optimizer
from torch.utils.data import DataLoader

from flare.callbacks import Callback, CallbacksContainer, ProgressBar
from flare.history import ModelHistory
from flare.utilities import WrapperDataset

_ceil = lambda x: int(np.ceil(x))

def _normalize_metrics(metrics: dict, seen_samples: int) -> Dict[str, float]:
    normalized = dict()
    for m_name, m_value in metrics.items():
        # Losses are normalized by default in their internal computation
        if 'loss' in m_name:
            value = m_value
        else:
            value = m_value / seen_samples
        normalized[m_name] = value
    return normalized

def _move_to_device(something: Union[torch.Tensor, List, Tuple],
                    device: Optional[torch.device]) -> Union[torch.Tensor, List, Tuple]:
    # It is reasonable to assume that something is not a nested list
    if not device:
        return something

    if isinstance(something, (list, tuple)):
        something = [s.to(device) for s in something]
    else:
        something = something.to(device)
    return something

def evaluate_on_loader(model: nn.Module,
                       eval_gen: DataLoader,
                       loss_fn: Any,
                       batch_first: bool = False,
                       device: Optional[torch.device] = torch.device('cpu'),
                       verbosity: int = 1) -> Dict[str, float]:
    """Computes the model metrics on some evaluation data.

    # Arguments
        model: The PyTorch model to be evaluated.
        eval_gen: A DataLoader with samples to be used for evaluation
        loss_fn: The loss function from which gradients are computed.
            Its expected signature is `loss_fn(model_output, y_true)`.
        batch_first: For sequential data, if True data is expected to have the layout
             `[seq_len, batch_size, *]`, otherwise `[batch_size, seq_len, *]`.
        device:
        verbosity: 0: silent, 1: show batch progress bar.

    # Returns
        The result of the model `metric()` method.
    """
    batch_index = 0 if batch_first else 1
    was_previously_training = model.training
    model.eval()

    with torch.no_grad():
        seen_samples = 0
        eval_loss = 0
        eval_metrics = defaultdict(int)

        iterator = enumerate(eval_gen)
        if verbosity > 0:
            iterator = tqdm.tqdm(iterator, total=len(eval_gen))

        for batch_id, batch_data in iterator:
            batch_features: list = batch_data[:-1]
            batch_labels = batch_data[-1]
            n_samples: int = batch_features[0].size(batch_index)

            batch_features = [ft.to(device) for ft in batch_features]
            batch_labels = batch_labels.to(device)

            output = model(*batch_features)
            batch_metrics = model.metric(output, batch_labels)

            for m_name, m_value in batch_metrics.items():
                eval_metrics[m_name] += m_value

            eval_loss += loss_fn(output, batch_labels).item()
            eval_metrics['loss'] = eval_loss / (batch_id + 1)

            # All feature matrices should have the same amount of sample entries,
            # hence we can take any of them to figure out the batch size
            seen_samples += n_samples

    if was_previously_training:
        model.train()

    return _normalize_metrics(eval_metrics, seen_samples)

def predict_on_loader(model: nn.Module,
                      eval_gen: DataLoader,
                      device: Optional[torch.device] = torch.device('cpu'),
                      verbosity: int = 1) -> torch.Tensor:
    """Performs model prediction

    # Arguments
        model: The PyTorch model to be evaluated.
        eval_gen: Generator with data to be evaluated. It is assumed that
            the its last yold element are the labels, which are disregarded.
        device:
        verbosity: 0: silent, 1: show batch progress bar.

    # Return
        y_hat: Tensor with shape `[total_samples, *]`, as all model outputs are
        concatenated in the first axis.
    """
    preds = list()
    was_previously_training = model.training
    model.eval()

    iterator = tqdm.tqdm(eval_gen) if verbosity else eval_gen
    with torch.no_grad():
        for batch_data in iterator:
            batch_features = [ft.to(device) for ft in batch_data[:-1]]
            output = model(*batch_features)
            preds.append(output)

    if was_previously_training:
        model.train()
    return torch.cat(preds)

def train_on_loader(model: nn.Module,
                    train_gen: DataLoader,
                    val_gen: Optional[DataLoader],
                    loss_fn: Any,
                    optimizer: Optimizer,
                    n_epochs: int,
                    batch_first: bool = False,
                    device: Optional[torch.device] = torch.device('cpu'),
                    callbacks: Optional[List[Callback]] = None,
                    before_step=None,
                    verbosity: int = 2) -> ModelHistory:
    """Trains a model using data from a DataLoader.

    # Arguments
        model: The PyTorch model.
        train_gen: A DataLoader containing the training data.
        val_gen: A DataLoader containing the validation data.
        loss_fn: The loss function from which gradients are computed.
            Its expected signature is `loss_fn(model_output, y_true)`.
        optimizer: The optimizer used in the backpropagation step.
        n_epochs: How many passes should be performed over the train_gen.
        batch_first: For sequential data, if True data is expected to have the layout
             `[seq_len, batch_size, *]`, otherwise `[batch_size, seq_len, *]`.
        device:
        callbacks: List of utility callbacks to help training the model.
        verbosity: 0: silent, 1:show epoch progress bar, 2: show batch progress bar.
    # Return
        A ModelHistory object representing the model training history.
    """

    callbacks_container = CallbacksContainer(callbacks or [])
    batch_index = 0 if batch_first else 1

    model_history = ModelHistory(model)

    epoch_iterator = range(1, n_epochs + 1)
    if verbosity == 1:
        epoch_iterator = tqdm.tqdm(epoch_iterator, desc='Epoch')
    elif verbosity == 2:
        callbacks_container.append(ProgressBar(len(train_gen), n_epochs))

    for epoch in epoch_iterator:
        model.train()
        callbacks_container.on_epoch_begin(epoch, model_history)

        epoch_loss = 0
        seen_samples = 0
        training_metrics = defaultdict(int)

        for batch_id, batch_data in enumerate(train_gen):
            callbacks_container.on_batch_begin(batch_id, model_history)

            # even if batch_data = [x, y], batch_features = [x] and batch_y = [y]
            batch_features: list = batch_data[:-1]
            batch_labels = batch_data[-1]

            batch_features = [_move_to_device(ft, device) for ft in batch_features]
            batch_labels = batch_labels.to(device)

            optimizer.zero_grad()
            output = model(*batch_features)
            loss = loss_fn(output, batch_labels)
            loss.backward()

            if before_step:
                before_step(model, loss, optimizer)

            optimizer.step()

            # All feature matrices should have the same amount of sample entries,
            # hence we can take any of them to figure out the batch size
            n_samples = batch_features[0].size(batch_index)

            seen_samples += n_samples
            epoch_loss += loss.item()

            # Accumulating metrics and losses for the current epoch
            batch_metrics = model.metric(output, batch_labels)
            for m_name, m_value in batch_metrics.items():
                training_metrics[m_name] += m_value
            training_metrics['loss'] = epoch_loss / (batch_id + 1)

            # Normalizing metrics up to the current batch to display in the progress bar
            model_history.append_batch_data(_normalize_metrics(training_metrics, seen_samples))

            callbacks_container.on_batch_end(batch_id, model_history)

        model_history.append_trn_logs(_normalize_metrics(training_metrics, seen_samples))

        if val_gen:
            val_logs = evaluate_on_loader(model, val_gen, loss_fn, batch_first, device, verbosity=0)

            # Adding the val_ prefix and storing metrics over the entire validation data
            val_logs = {'val_' + m_name: m_value for m_name, m_value in val_logs.items()}
            model_history.append_dev_logs(val_logs)

        callbacks_container.on_epoch_end(epoch, model_history)
        if model_history.should_stop_training():
            break

    model_history.close(n_epochs)
    callbacks_container.on_train_end()

    return model_history

def train(model: nn.Module,
          x_trn: torch.Tensor,
          y_trn: torch.Tensor,
          loss_fn: Any,
          optimizer: Optimizer,
          n_epochs: int,
          batch_size: int,
          batch_first: bool = False,
          device: Optional[torch.device] = torch.device('cpu'),
          validation_frac: Optional[float] = None,
          x_val: Optional[torch.Tensor] = None,
          y_val: Optional[torch.Tensor] = None,
          shuffle: bool = True,
          callbacks: Optional[List[Callback]] = None,
          verbosity: int = 2) -> ModelHistory:
    """Trains a model using data in PyTorch tensors.

    This function expects the model to implement a `metric()` with the following
    signature:

    ```python
        def metric(self, prediction: torch.Tensor, y_true: torch.Tensor) -> dict:
            pass.
    ```

    See `examples/` for details.

    # Arguments
        model: The PyTorch model.
        x_trn: Tensors representing the sample features.
        y_trn: Tensors sample labels.
        loss_fn: The loss function from which gradients are computed.
            Its expected signature is `loss_fn(model_output, y_true)`.
        optimizer: The optimizer used in the backpropagation step.
        n_epochs: How many passes should be performed over the train_gen.
        batch_size: How many samples there are in a batch. The last batch may be smaller.
        batch_first: For sequential data, if True data is expected to have the layout
             `[seq_len, batch_size, *]`, otherwise `[batch_size, seq_len, *]`.
        validation_frac: Percentage of the samples from X to be reserved for validation
            after the data is shuffled. This shuffling happens regardless the value of
            the `shuffle` parameter.
        x_val: Optional tensor representing the validation data features.
        x_val: Optional tensor representing the validation data labels.
        shuffle: Should the samples be shuffled before training?
        callbacks: List of utility callbacks to help training the model.

    # Return
        A ModelHistory object with the logs of model metrics after each epoch.
    """

    if batch_size < 1:
        raise ValueError('Each batch should have at least one sample.')

    batch_index = 0 if batch_first else 1
    if isinstance(x_trn, torch.Tensor):
        n_samples = x_trn.size(batch_index)
    else:
        n_samples = x_trn[0].size(batch_index)

    dataset_val = None
    if x_val is not None and y_val is not None:
        dataset_val = WrapperDataset(x_val, y_val)
    else:
        if validation_frac is not None:
            # Need to reserve part of training data for validation
            val_samples = _ceil(n_samples * validation_frac)

            # First permute before separating the validation samples
            permutations = torch.randperm(n_samples)
            x_trn = x_trn[permutations]
            y_trn = y_trn[permutations]

            x_val = x_trn[:val_samples]
            y_val = y_trn[:val_samples]

            x_trn = x_trn[val_samples:]
            y_trn = y_trn[val_samples:]

            dataset_val = WrapperDataset(x_val, y_val)

    dataset_trn = WrapperDataset(x_trn, y_trn)
    loader_trn = DataLoader(dataset_trn, batch_size, shuffle)

    if dataset_val:
        loader_dev = DataLoader(dataset_val, batch_size, shuffle)
    else:
        loader_dev = None

    return train_on_loader(model, loader_trn, loader_dev, loss_fn, optimizer,
                           n_epochs, batch_first, device, callbacks,
                           verbosity=verbosity)

def evaluate(model: nn.Module,
             x_eval: torch.Tensor,
             y_eval: torch.Tensor,
             loss_fn,
             batch_size: int,
             batch_first: bool = False,
             device: Optional[torch.device] = torch.device('cpu'),
             verbosity: int = 1) -> Dict[str, float]:
    """Computes the model metrics on some evaluation data.

    # Arguments
        model: The PyTorch model to be evaluated.
        x_eval: Validation samples features.
        y_eval: Validation samples labels.
        loss_fn: The loss function from which gradients are computed.
            Its expected signature is `loss_fn(model_output, y_true)`.
        batch_size: How many samples there are in a batch. The last batch may be smaller.
        batch_first: For sequential data, if True data is expected to have the layout
             `[seq_len, batch_size, *]`, otherwise `[batch_size, seq_len, *]`.
        device:
        verbosity: 0: silent, 1: show batch progress bar.

    # Returns
        The result of the model `metric()` method.
    """
    eval_gen = DataLoader(WrapperDataset(x_eval, y_eval), batch_size)
    return evaluate_on_loader(model, eval_gen, loss_fn, batch_first, device, verbosity)


########
# README
########
# This script performs a permutation test by randomly assigning the text labels and calculating cosine similarity
# Results are saved to a csv file

# Set the number of permutations in the environment section
# Set the number of threads in the environment section. For best results use the number of cores + 2
# Change which word you are testing in the permutation section

# 1000 permutations takes 12-14 hours to run at 6-10 threads. Double that time if you run permutations
# for both cosine similarity and relative cosine similarity

#####################
# Define Environment
#####################
import os
import pandas as pd
from gensim.models import Word2Vec
import random

from TextPrep import TextPrep
from cosine_function import cosine_sim

os.chdir('../Meta Data')
from key_words import key_words, key_synonyms
from stop_words import stop_words
os.chdir('..')

# set the number of threads
n_threads = 6

# set the number of permutations to run
permutations = 1000

#############################
# Import and pre-process data
#############################
tweet_df = pd.read_csv('Data/replication_text.csv')
# Create lists of text and labels
tweets = tweet_df['text']
labels = tweet_df['party']

# initialize parser for both keywords and base words
keyprep = TextPrep(stopwords = stop_words, key_words = key_words, key_synonyms = key_synonyms)
baseprep = TextPrep(stopwords = stop_words, key_words = base_words, key_synonyms = base_synonyms)
agreeprep = TextPrep(stopwords = stop_words, key_words = agree_words, key_synonyms = agree_synonyms)

# preprocess text
tweets = [keyprep.twitter_preprocess(tweet) for tweet in tweets]

#############################
# Permutation Test
#############################
# run the permutation test for cosine similarity
sim = []
for i in range(permutations):
    # permute the labels
    rlabels = random.sample(list(plabels), len(plabels))
    # Get cosine similarity
    rcosine = cosine_sim(parser = prep, keyword = 'trump', text = tweets, labels = rlabels, threads = n_threads)
    # append to list
    sim.append(rcosine)

# save results as a csv
simdf = pd.DataFrame(sim, columns = ['cosine similarity'])
simdf.to_csv('permutation.csv', index = False)

# run the permutation test for relative cosine similarity
relsim = []
for i in range(permutations):
    # permute the labels
    rlabels = random.sample(list(plabels), len(plabels))
    # Get cosine similarity
    rcosine = cosine_sim(parser = prep, keyword = 'trump', text = tweets, labels = rlabels, relative = True, threads = n_threads)
    # append to list
    relsim.append(rcosine)

# save results as a csv
relsimdf = pd.DataFrame(relsim, columns = ['relative cosine similarity'])
relsimdf.to_csv('relative_permutation.csv', index = False)


# En un cierto país, el impuesto que se debe pagar por las compras de vehículos se calcula a partir del precio bruto y el tipo del carro a partir de la siguiente regla: si el tipo del vehículo es camioneta y el precio bruto es de menos de 80, no hay impuesto (o sea, es cero); pero las camionetas con un precio bruto de 80 o más, tienen un impuesto igual a la mitad de éste precio. Para los vehículos de tipo automóvil, si el precio es inferior a 20, el impuesto es de 5 pesos; los automóviles con un precio bruto entre 20 y 40, tienen un impuesto igual a la quinta parte del precio; y los automóviles con un precio por encima de 40 tienen un impuesto de 9 pesos. Escriba un programa en Java, en la cual, a partir del precio bruto y el tipo de vehículo, encuentre el impuesto a pagar y el precio neto (precio bruto más el impuesto).
import math
tipo_vehículo = int(
    input("***Menú*** \n1. Automóvil \n2. Camionetas \n Ingrese el tipo de vehículo: "))
precio_bruto = float(input("¿Cuál es el precio bruto del vehículo: "))

if tipo_vehículo == 1:
    if precio_bruto < 20:
        impuesto = 5
        precio_neto = precio_bruto + impuesto
    elif precio_bruto >= 20 and precio_bruto <= 40:
        impuesto = precio_bruto / 5
        precio_neto = precio_bruto + impuesto

    elif precio_bruto > 40:
        impuesto = 9
        precio_neto = precio_bruto + impuesto

if tipo_vehículo == 2:
    if precio_bruto < 80:
        impuesto = 0
        precio_neto = precio_bruto + impuesto

    elif precio_bruto >= 80:
        impuesto = precio_bruto * 0.5
        precio_neto = precio_bruto + impuesto

print("Impuesto a pagar {}".format(impuesto))
print("Precio Neto {}".format(precio_neto))


import numpy as np
import cv2

picPath = 'D:/Python/workspace/PycharmSpace/opencvPython/data/pictures/'
image = cv2.imread(picPath + 'timg1.jpg')

cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow('image', image)

k = cv2.waitKey(0)
if k == ord('s'):
    cv2.imwrite('image01.png', image)

cv2.destroyAllWindows()


from  selenium import  webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.keys import Keys

import time

palabra_buscada = "sele"

chrome_options = Options()
chrome_options.add_argument("--headless")

driver = webdriver.Chrome(chrome_options=chrome_options,executable_path=r"C:\driver_selenium\chromedriver.exe")
driver.get("https://www.google.com")
time.sleep(3)

busqueda = driver.find_element_by_name("q")
busqueda.send_keys(palabra_buscada)
time.sleep(3)

for i in range(1, 11):
    elemento = driver.find_element_by_xpath('//*[@id="tsf"]/div[2]/div[1]/div[2]/div[2]/ul/li[' + str(i) + ']/div/div[2]/div/span').text
    print(elemento)

driver.close()


import random
import math

class Queue():
    def __init__(self):
        self.queue = []
    def enqueue(self, value):
        self.queue.append(value)
    def dequeue(self):
        if self.size():
            return self.queue.pop(0)
        else:
            return None
    def size(self):
        return len(self.queue)

class User:
    def __init__(self, name):
        self.name = name

class SocialGraph:
    def __init__(self):
        self.lastID = 0
        self.users = {}
        self.friendships = {}

    def addFriendship(self, userID, friendID):
        """
        Creates a bi-directional friendship
        """
        if userID == friendID:
            print("WARNING: You cannot be friends with yourself")
        elif friendID in self.friendships[userID] or userID in self.friendships[friendID]:
            print("WARNING: Friendship already exists")
        else:
            self.friendships[userID].add(friendID)
            self.friendships[friendID].add(userID)

    def addUser(self, name):
        """
        Create a new user with a sequential integer ID
        """
        self.lastID += 1  # automatically increment the ID to assign the new user
        self.users[self.lastID] = User(name)
        self.friendships[self.lastID] = set()

    def populateGraph(self, numUsers, avgFriendships):
        """
        Takes a number of users and an average number of friendships
        as arguments

        Creates that number of users and a randomly distributed friendships
        between those users.

        The number of users must be greater than the average number of friendships.
        """
        # Reset graph
        self.lastID = 0
        self.users = {}
        self.friendships = {}
        # !!!! IMPLEMENT ME

        # Add users
        for user in range(numUsers):
            self.addUser(user)

        # Create friendships
        friendship_combinations = []
        # avg Friendships = totalFriendships / numUsers
        for userID in self.users:
            for friendID in range(userID + 1, self.lastID + 1):
                if userID != friendID:
                    friendship_combinations.append((userID, friendID))
        # shuffle friendship combinations
        random.shuffle(friendship_combinations)
        totalFriendships = avgFriendships * numUsers
        # take the N Number of friendships
        friends_to_make = friendship_combinations[:math.floor( totalFriendships / 2) ]

        # make all possible friendship combinations
        for friendship in friends_to_make:
            first_friend = friendship[0]
            second_friend = friendship[1]
            self.addFriendship(first_friend, second_friend)

    def getAllSocialPaths(self, userID):
        """
        Takes a user's userID as an argument

        Returns a dictionary containing every user in that user's
        extended network with the shortest friendship path between them.

        The key is the friend's ID and the value is the path.
        """
        visited = {}  # Note that this is a dictionary, not a set

        #implement bfs
        # make a queue
        queue = Queue()
        # enqueue userID
        queue.enqueue([userID])
        # while queue isn't empty
        while queue.size():
            # dequeue the path
            path = queue.dequeue()
            # last thing on path is the new current item
            newUserID = path[-1]
            # if newUserID isn't visited
            if newUserID not in visited:
                visited[newUserID] = path
                # for each of friendID's neighbor's
                for friendID in self.friendships[newUserID]:
                    if friendID not in visited:
                        # copy path
                        new_path = path.copy()
                        # append friendID to path
                        new_path.append(friendID)
                        # enqueue new_path
                        queue.enqueue(new_path)
        return visited

if __name__ == '__main__':
    sg = SocialGraph()
    sg.populateGraph(10, 2)
    print(sg.friendships)
    connections = sg.getAllSocialPaths(1)
    print(connections)


# Script modified off of Kai Fans script
import matplotlib
matplotlib.use('Agg')  # Uncomment this when using in Kamiak/Aeolus
import pandas as pd
import numpy as np
import datetime
from datetime import timedelta
import pytz
import os
from netCDF4 import Dataset
import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import pickle
#pd.show_versions()

#import folium
#import simplekml

# Set file paths
# =============================================================================
# base_dir=r'E:/Research/Urbanova_Jordan/'
# data_dir= base_dir + '/Urbanova_ref_site_comparison/Urbanova/'
# grid_dir_4km=base_dir + '/Urbanova_ref_site_comparison/AIRPACT/2018/2018011100/MCIP37/'
# grid_dir_urb=base_dir + '/Urbanova_ref_site_comparison/Urbanova/2018/2018011100/MCIP37/'
# urb_path = data_dir
# airpact_path = base_dir + '/Urbanova_ref_site_comparison/AIRPACT/'
# =============================================================================

base_dir = '/data/lar/users/jmunson/'
data_dir = '/data/lar/projects/Urbanova/'
grid_dir_urb = '/data/lar/projects/Urbanova/2018/2018011100/MCIP37/'
grid_dir_4km='/data/lar/projects/airpact5/AIRRUN/2018/2018011100/save_MCIP37/'
urb_path = '/data/lar/projects/Urbanova/'
airpact_path = '/data/lar/projects/airpact5/saved/'

start_year = 2019
start_month = 2
start_day = 1

end_year = 2019
end_month = 2
#end_day = monthrange(end_year, end_month)[1]
end_day = 1

filetype = 'combined'
exec(open(base_dir + "/airpact_functions.py").read())
# set start and end date
start = datetime.datetime(start_year, start_month, start_day, hour=0)
end = datetime.datetime(end_year, end_month, end_day, hour=23)
timezone = pytz.timezone("utc")
start = timezone.localize(start)
end = timezone.localize(end)
# set layer
layer=0

#Set up date diff for time loop
date_diff =end -start
date_diff =  int(round( date_diff.total_seconds()/60/60/24)) # total hour duration
print("start date is "+ start.strftime("%Y%m%d") + ' combining 4km files' )
now = start

# Start time loop
modeloutputs = []
modeloutputs.append(airpact_path + now.strftime('%Y')+'/'+ now.strftime('%m')+'/aconc'+'/combined_' + now.strftime('%Y%m%d')+'.ncf')
print(modeloutputs)
now += timedelta(hours=24)
# For loop to find the combined3D files for specified time period
for t in range(0, date_diff):

    # Handles missing days
    if os.path.isfile(airpact_path + now.strftime('%Y')+'/'+ now.strftime('%m')+'/aconc'+'/combined_' + now.strftime('%Y%m%d')+'.ncf'):
        # set a directory containing Urbanova data
        modeloutputs.append(airpact_path + now.strftime('%Y')+'/'+ now.strftime('%m')+'/aconc'+'/combined_' + now.strftime('%Y%m%d')+'.ncf')
        #Changes day to next
        now += timedelta(hours=24)
    else:
        print('adding 24 hours')
        now += timedelta(hours=24)
        continue

# =============================================================================
#     if os.path.isfile(regrid_path + 'combined_' + now.strftime('%Y%m%d')+'.regrid4km.ncf'):
#         # set a directory containing Urbanova data
#         modeloutputs.append(airpact_path + now.strftime('%Y')+'/'+now.strftime('%m')+'/aconc/combined_' + now.strftime('%Y%m%d')+'.ncf')
#         #Changes day to next
#         now += timedelta(hours=24)
#     else:
#         print('adding 24 hours')
#         now += timedelta(hours=24)
# =============================================================================

print('4km file paths arrayed')

print(modeloutputs)

# open and read wrfout using netCDF function (Dataset)
if os.path.isfile(modeloutputs[0]):
    nc  = Dataset(modeloutputs[0], 'r')
    print('reading ', modeloutputs[0])
else:
    print("no file")
    exit()

# x_dim and y_dim are the x (lon) and y (lat) dimensions of the model
x_dim = len(nc.dimensions['COL'])
y_dim = len(nc.dimensions['ROW'])
nc.close()

# obtain model lat and lon - needed for AQS eval and basemap
latlon = grid_dir_4km+"/GRIDCRO2D"
fin0 = Dataset(latlon, 'r')
lat = fin0.variables['LAT'][0,0]
lon = fin0.variables['LON'][0,0]
w = (fin0.NCOLS)*(fin0.XCELL)
h = (fin0.NROWS)*(fin0.YCELL)
lat_0 = fin0.YCENT
lon_0 = fin0.XCENT
fin0.close()

airpact = get_airpact_DF(start, end, layer)

# Save the dictionary to be used in Folium mapping script
def save_obj(obj, name ):
    with open(name + '.pkl', 'wb') as f:
        pickle.dump(obj, f, pickle.HIGHEST_PROTOCOL)

name = '4km_'+start.strftime("%Y%m%d")+'_'+end.strftime("%Y%m%d")
save_obj(airpact,name)


'''
/*
 * Copyright 2010-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
 '''

import boto3
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import sys
import logging

def aws_initialize(USERNAME,host="",rootCAPath="",cognitoIdentityPoolID=""):
	# Missing configuration notification
	missingConfiguration = False
	if not host:
		print("Missing '-e' or '--endpoint'")
		missingConfiguration = True
	if not rootCAPath:
		print("Missing '-r' or '--rootCA'")
		missingConfiguration = True
	if not cognitoIdentityPoolID:
		print("Missing '-C' or '--CognitoIdentityPoolID'")
		missingConfiguration = True
	if missingConfiguration:
		exit(2)

	identityPoolID = cognitoIdentityPoolID
	region = host.split('.')[2]
	cognitoIdentityClient = boto3.client('cognito-identity', region_name=region)

	temporaryIdentityId = cognitoIdentityClient.get_id(IdentityPoolId=identityPoolID)
	identityID = temporaryIdentityId["IdentityId"]

	temporaryCredentials = cognitoIdentityClient.get_credentials_for_identity(IdentityId=identityID)

	AccessKeyId = temporaryCredentials["Credentials"]["AccessKeyId"]
	SecretKey = temporaryCredentials["Credentials"]["SecretKey"]
	SessionToken = temporaryCredentials["Credentials"]["SessionToken"]

	# Init AWSIoTMQTTClient
	clientID = USERNAME + " Monitor"
	myAWSIoTMQTTClient = AWSIoTMQTTClient(clientID, useWebsocket=True)

	# AWSIoTMQTTClient configuration
	myAWSIoTMQTTClient.configureEndpoint(host, 443)
	myAWSIoTMQTTClient.configureCredentials(rootCAPath)
	myAWSIoTMQTTClient.configureIAMCredentials(AccessKeyId, SecretKey, SessionToken)
	myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
	myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1)  # Infinite offline Publish queueing
	myAWSIoTMQTTClient.configureDrainingFrequency(2)  # Draining: 2 Hz
	myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
	myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)  # 5 sec

	return myAWSIoTMQTTClient


from urllib import request
import sys
import os
from time import sleep, time

# python collector.py [url] --interval [timeStep]

timeStep = 10
outputFile = 'data.tsv'

if len(sys.argv) > 2: # if flag was given
    if sys.argv[2] == '--interval':
        if len(sys.argv) > 3 and sys.argv[3].isnumeric() and sys.argv[3] > 0: #has valid input for flag
            timeStep = int(sys.argv[3])
        else:
            print('Please give appropriate time interval value (s) to flag --interval')
            sys.exit()
    else:
        print('Please use existing flag(s) for this program:\n\t--interval [desired interval]')
        sys.exit()

url = sys.argv[1]
url = 'http://' + url.lstrip('http://')
url = url.rstrip('/') + '/stats'

if os.path.isfile(outputFile): # existing file, append
    f = open(outputFile, 'a') #append
else:   # new file, write
    f = open(outputFile, 'w') #write new file

oneHour = 3600
stop = time() + oneHour + .0001

while time() < stop:
    r = request.urlopen(url)
    valArr = r.read().decode('utf-8').split('\n')
    for val in valArr[:4]:
        v = val.split(': ')[1]
        f.write(v + '\t')
    f.write('\n')
    sleep(timeStep)


import kat as kat
import tvmaze as tvmaze
import sqlite as sqlite
from datetime import datetime, timedelta
import logging

logging.basicConfig(format='%(asctime)s %(message)s', filename='log.log', level=logging.DEBUG)

class Blackbeard:
    def __init__(self):
        self.db_client = sqlite.SQLiteDB()

    def ship(self, show, season, episode):
        logging.info('shipping a new show: {} S{}E{}'.format(show, season, episode))
        count = 0
        self.db_client.insert_show(show, season, episode)
        show_episodes = tvmaze.TvMaze(show).get_show_info()['_embedded']['episodes']
        for e in show_episodes:
            if e['season'] >= season and e['number'] >= episode:
                count += 1
                sid = self.db_client.get_show_id(show)
                airtime = '{} {}'.format(e['airdate'], e['airtime'])
                a = str(datetime.now() - timedelta(hours=1)) >= '{}'.format(airtime)
                self.db_client.insert_episode(e['name'], e['season'], e['number'], airtime, e['runtime'], 0, a, sid)
        logging.info('found {} episodes to add for show: {}'.format(count, show))

    def update(self):
        shows = self.db_client.get_shows()
        for s in shows:
            show_episodes = tvmaze.TvMaze(s[1]).get_show_info()['_embedded']['episodes']
            diff = len(show_episodes) - len(self.db_client.get_episodes_for_show(s[0]))
            if diff > 0:
                # found new episodes that are not in the db, need to insert the new ones
                for e in show_episodes:
                    if e['season'] >= s[2] and e['number'] >= s[3]:
                        e_uid = '{}_{}_S{}E{}'.format(s[0], e['name'], e['season'], e['number'])
                        if not self.db_client.is_episode_exist(e_uid):
                            airtime = '{} {}'.format(e['airdate'], e['airtime'])
                            a = str(datetime.now() - timedelta(hours=1)) >= '{}'.format(airtime)
                            self.db_client.insert_episode(e['name'], e['season'], e['number'], airtime, e['runtime'], 0, a, s[0])
        logging.info('updating available episodes..')
        # e[6] = downloaded, 4=airdate, 5=runtime, 8=e_uid
        episodes = self.db_client.get_all_episodes()
        for e in episodes:
            logging.info('checking episode: {}'.format(e[8]))
            if not e[6] and not e[7]:
                current_time = str(datetime.now())
                if current_time > '{} {}'.format(e[4], e[5]):
                    logging.info('marking episode {} as available..'.format(e[8]))
                    self.db_client.make_episode_available(e[8])

    def fix_the_fuckup(self):
        shows = self.db_client.get_shows()
        for s in shows:
            episodes = self.db_client.get_episodes_for_show(s[0])
            for e in episodes:
                logging.info('checking episode for deletion: {}'.format(e[8]))
                if e[2] < s[2]:
                    logging.info('found an episode to delete. deleting: {}'.format(e[8]))
                    print('found an episode to delete. deleting: {}'.format(e[8]))
                    self.db_client.delete_episode(e[8])
                if e[2] == s[2]:
                    if e[3] < s[3]:
                        logging.info('found an episode to delete. deleting: {}'.format(e[8]))
                        print('found an episode to delete. deleting: {}'.format(e[8]))
                        self.db_client.delete_episode(e[8])

    def plunder(self):
        logging.info('checking for plunder')
        episodes = self.db_client.get_episodes_to_download()
        for e in episodes:
            logging.info('episode {} is marked for download'.format(e[3]))
            search_str = '{} S{:02d}E{:02d}'.format(e[4], e[1], e[2])
            started_download = kat.KatTorrents(search_str).run_search()
            if started_download:
                logging.info('downloading episode {}'.format(e[3]))
                self.db_client.update_episode(e[3])

if __name__ == '__main__':
    logging.info('Starting Blackbeard...')
    b = Blackbeard()
    b.ship('Bar Rescue', 1, 1)
    b.update()
    b.plunder()
    logging.info('Ending Blackbeard')


#lifeserver.py

#imports

import sys
import logging #logging!
from lifemanager.app import LifeManager
from lifemanager.models import init_database, create_tables, User
from os import environ
from mint.mintclient import *

#start
log = logging.getLogger(__name__)

if __name__ == '__main__':
    logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(module)11s] [%(levelname)7s] %(message)s')

    #testing mintclient
    test = MintClient('****','****')
    test.refreshUserInfo()
    test.refreshTransactions()

    #database initialization?
    database = init_database()
    create_tables(database)

    #webserver initialization
    app = LifeManager(__name__)

    HOST = environ.get('SERVER_HOST', 'localhost')
    try:
        PORT = int(environ.get('SERVER_PORT', '5555'))
    except ValueError:
        PORT = 5555
    app.run(HOST, PORT)


'''
Flush out the classes below with the following:

    - Add inheritance so that Class1 is inherited by Class2 and Class2 is inherited by Class3.
    - Follow the directions in each class to complete the functionality.

'''

class Class1:
    def __init__(self):
        self.x = 10
    # define an __init__() method that sets an attribute x

class Class2(Class1): # inheritance from class one
    def __init__(self):
        self.y = 20
        super().__init__()
    # define an __init__() method that sets an attribute y and calls the __init__() method of its parent

class Class3(Class2):
    def __init__(self):
        self.z = 30
        super().__init__()

    # define an __init__() method that sets an attribute z and calls the __init__() method of its parent

# create an object of each class and print each of its attributes
object_1 = Class1()
print(object_1.x)

object_2 = Class2()
print(object_2.x, object_2.y)

object_3 = Class3()
print(object_3.x, object_3.y, object_3.z)


from tortoise import fields
from tortoise.backends.base.schema_generator import BaseSchemaGenerator

class SqliteSchemaGenerator(BaseSchemaGenerator):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.FIELD_TYPE_MAP.update({
            fields.BooleanField: 'INTEGER',
            fields.FloatField: 'REAL',
            fields.DecimalField: 'VARCHAR(40)',
        })

    def _get_primary_key_create_string(self, field_name):
        return '"{}" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL'.format(field_name)