# coding: utf-8
import logging
from django.conf import settings
from django.contrib import messages
from django.http import HttpResponse, Http404
from django.template import loader, Context
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.shortcuts import render_to_response
from django.utils.translation import ugettext as _
from django.template.context import RequestContext
from django.template.defaultfilters import slugify
from django.forms.models import inlineformset_factory
from django.forms.formsets import formset_factory
from django.contrib.auth.decorators import login_required, permission_required, user_passes_test
from django.db.models import Max

from journalmanager.models import Journal, JournalMission, Issue
from journalmanager.forms import RestrictedJournalForm, JournalMissionForm

from editorialmanager import notifications
from scielomanager.tools import get_paginated
from audit_log import helpers

from . import forms
from . import models

logger = logging.getLogger(__name__)

def _get_order_for_new_members(board, role_pk, new_member_pk):
    """
    return the value for new members order field.
    - if does not exist other members in board, return 1 (first board member)
    - if does not exist other members with the same role of the new member, return the max order of board + 1 (member with a new role on board)
    - if exist members with the same role, return the order field value of those members.
    """
    # collect all board members except the new one
    board_members = board.editorialmember_set.all().exclude(pk=new_member_pk)
    if board_members.count() > 0:  # already exist others members on this board
        # collect all others board members with the same role as the new members
        members_with_role = board_members.filter(role__pk=role_pk)
        if members_with_role.count() > 0:
            # already exist others members with the same role as the new member
            # the result will be the same order field that those members found
            return members_with_role[0].order
        else:
            # no members with this role, so return the maximum order found + 1
            max_order_of_board = board_members.aggregate(Max('order'))
            return max_order_of_board['order__max'] + 1
    else:
        # no members with this board, this should be the first one
        return 1

def _update_member_order(member, old_role):
    """
    update the @param ``member``'s order attribute, depending on this situations:
    - (A) more than one user with role == ``old_role``, ``member`` is moved to a new role that DOES NOT have any members yet.
    - (B) more than one user with role == ``old_role``, ``member`` is moved to a new role that ALREADY have other members.
    - (C) ``member`` is the last one with role == ``old_role``, ``member`` is moved to a new role that DOES NOT have any members yet.
    - (D) ``member`` is the last one with role == ``old_role``, ``member`` is moved to a new role that ALREADY have other members.
    """
    board = member.board
    board_members = board.editorialmember_set.all()
    members_of_old_role = board_members.filter(role__pk=old_role.pk).exclude(pk=member.pk)
    members_of_new_role = board_members.filter(role__pk=member.role.pk).exclude(pk=member.pk)
    new_order_value = None

    if members_of_old_role.count() == 0:
        # ``member`` WAS the only member with role == old_role
        previous_order = member.order
        if members_of_new_role.count() == 0:
            # ``member`` IS the FIRST member with role == new_role -----> (C)
            max_order_of_board = board_members.aggregate(Max('order'))
            new_order_value = max_order_of_board['order__max'] + 1
        else:
            # there are more members with role == new_role -----> (D)
            new_order_value = members_of_new_role[0].order

        # update member.order to: new_order_value
        member.order = new_order_value
        member.save()

        # decrease order by one, to avoid the empty "bucket" of old_role let by the change
        for m in board.editorialmember_set.filter(order__gte=previous_order):
            m.order = m.order - 1
            m.save()
    else:
        # there are more members with role == old_role
        if members_of_new_role.count() == 0:
            # ``member`` IS the FIRST member with role == new_role -----> (A)
            max_order_of_board = board_members.aggregate(Max('order'))
            new_order_value = max_order_of_board['order__max'] + 1
        else:
            # there are more members with role == new_role  -----> (B)
            new_order_value = members_of_new_role[0].order

        # update member.order to: new_order_value
        member.order = new_order_value
        member.save()

def _update_members_order_when_delete(member_deleted):
    """
    Checks if ``member_deleted`` is the last one of this board member with this role,
    and if any members with an order bigger than ``member_deleted.order`` then update it,
    to decrease the order value by one
    """
    board = member_deleted.board
    other_board_members = board.editorialmember_set.filter(role=member_deleted.role).exclude(pk=member_deleted.pk)
    # after member delete, still more members with this role ?
    if other_board_members.count() == 0:
        # the ``member_deleted`` was the last one of this role, so
        # update all members other greater than ``member_deleted.order`` to decrease 1
        for member in board.editorialmember_set.filter(order__gt=member_deleted.order):
            member.order -= 1
            member.save()

def _do_move_board_block(board_pk, position, direction):
    """
    moves de members the board with pk == board_pk), and with order == position, to direction ``direction`` (up, or down).
    * when direction is up, and have more members above, make a swap, updating member's order attribute.
    * when direction is down, and have more members below, make a swap, updating member's order attribute.
    """
    board = models.EditorialBoard.objects.get(pk=board_pk)

    # creating a list of members to reallocate, becasue simply querysets are lazy
    target_members = [m for m in board.editorialmember_set.filter(order=position)]

    if direction.upper() == "UP":
        position_above = int(position) - 1
        if position_above > 0:
            # move members above to target_members's current position
            board.editorialmember_set.filter(order=position_above).update(order=position)
            # move target_members's position to a higher one
            for m in target_members:
                m.order = position_above
                m.save()

    elif direction.upper() == "DOWN":
        lowest_position_possible = board.editorialmember_set.all().order_by('role__pk').distinct('role__pk').count()
        position_below = int(position) + 1
        if position_below <= lowest_position_possible:
            # move members above to target_members's current position
            board.editorialmember_set.filter(order=position_below).update(order=position)
            # move target_members's position to a lower one
            for m in target_members:
                m.order = position_below
                m.save()
    else:
        # direction is not UP nor DOWN, so, ignore it, do nothing, skip it
        logger.error(u"Trying to move a board (pk: %s) block (position: %s) in this direction: %s is not possible, so doing nothing!" %  (board_pk, position, direction))

def _user_has_access(user):
    return user.is_superuser or user.get_profile().is_editor or user.get_profile().is_librarian or user.get_profile().is_trainee

def _get_journals_by_user_access(user):
    user_profile = user.get_profile()
    if user_profile.is_editor:
        journals = Journal.userobjects.active().filter(editor=user)
    elif user_profile.is_librarian or user.is_superuser or user_profile.is_trainee:
        journals = Journal.userobjects.active()
    else:
        journals = []
    return journals

def _get_journal_or_404_by_user_access(user, journal_id):
    journals = _get_journals_by_user_access(user)
    return get_object_or_404(journals, id=journal_id)

@login_required
@user_passes_test(_user_has_access, login_url=settings.AUTHZ_REDIRECT_URL)
def index(request):
    journals = _get_journals_by_user_access(request.user)

    objects = get_paginated(journals, request.GET.get('page', 1))
    context = {
        'objects_journal': objects,
    }
    return render_to_response('journal/journal_list.html', context, context_instance=RequestContext(request))

@login_required
@user_passes_test(_user_has_access, login_url=settings.AUTHZ_REDIRECT_URL)
def journal_detail(request, journal_id):
    journal = _get_journal_or_404_by_user_access(request.user, journal_id)
    context = {
        'journal': journal,
    }
    return render_to_response('journal/journal_detail.html', context, context_instance=RequestContext(request))

@login_required
@user_passes_test(_user_has_access, login_url=settings.AUTHZ_REDIRECT_URL)
def edit_journal(request, journal_id):
    """
    Handles only existing journals
    """
    user_profile = request.user.get_profile()
    if request.user.is_superuser or user_profile.is_librarian:
        # redirect to full edit view:
        return HttpResponseRedirect(reverse('journal.edit', args=[journal_id, ]))

    journal = _get_journal_or_404_by_user_access(request.user, journal_id)

    JournalMissionFormSet = inlineformset_factory(Journal, JournalMission, form=JournalMissionForm, extra=1, can_delete=True)

    if request.method == "POST":
        journalform = RestrictedJournalForm(request.POST, request.FILES, instance=journal, prefix='journal')
        missionformset = JournalMissionFormSet(request.POST, instance=journal, prefix='mission')

        # this view only handle existing journals, so always exist previous values.
        audit_old_values = helpers.collect_old_values(journal, journalform, [missionformset, ])

        if journalform.is_valid() and missionformset.is_valid():
            journal = journalform.save()
            missionformset.save()

            audit_data = {
                'user': request.user,
                'obj': journal,
                'message': helpers.construct_change_message(journalform, [missionformset, ]),
                'old_values': audit_old_values,
                'new_values': helpers.collect_new_values(journalform, [missionformset, ]),
            }
            # this view only handle existing journals, so always log changes.
            helpers.log_change(**audit_data)

            messages.success(request, _(u'Journal updated successfully.'))

            if request.user.get_profile().is_editor:
                return HttpResponseRedirect(reverse('editorial.index'))
            else:
                return HttpResponseRedirect(reverse('journal.dash', args=[journal.id]))
        else:
            messages.error(request, _(u'Check mandatory fields.'))

    else:
        journalform = RestrictedJournalForm(instance=journal, prefix='journal')
        missionformset = JournalMissionFormSet(instance=journal, prefix='mission')

    return render_to_response('journal/edit_journal.html', {
                              'add_form': journalform,
                              'missionformset': missionformset,
                              }, context_instance=RequestContext(request))

@login_required
@user_passes_test(_user_has_access, login_url=settings.AUTHZ_REDIRECT_URL)
def board(request, journal_id):
    journal = _get_journal_or_404_by_user_access(request.user, journal_id)
    issues = journal.issue_set.all().order_by('-publication_year', '-volume', '-number')
    context = {
        'journal': journal,
        'issues': issues,
    }
    return render_to_response('board/board_list.html', context, context_instance=RequestContext(request))

@login_required
@permission_required('editorialmanager.change_editorialmember', login_url=settings.AUTHZ_REDIRECT_URL)
def edit_board_member(request, journal_id, member_id):
    """
    Handles only existing editorial board member
    """

    if request.is_ajax():
        template_name = 'board/board_member_edit_form.html'
    else:
        template_name = 'board/board_member_edit.html'

    # check if user have correct access to view the journal:
    if not Journal.userobjects.active().filter(pk=journal_id).exists():
        messages.error(request, _(u'The journal is not available for you.'))
        return HttpResponseRedirect(reverse('editorial.index'))

    board_member = get_object_or_404(models.EditorialMember, id=member_id)
    post_url = reverse('editorial.board.edit', args=[journal_id, member_id, ])
    board_url = reverse('editorial.board', args=[journal_id, ])
    context = {
        'board_member': board_member,
        'post_url': post_url,
        'board_url': board_url,
    }

    if request.method == "POST":
        old_role = board_member.role
        form = forms.EditorialMemberForm(request.POST, instance=board_member)
        # this view only handle existing editorial board member, so always exist previous values.
        audit_old_values = helpers.collect_old_values(board_member, form)

        if form.is_valid():
            board_member = form.save()
            if 'role' in form.changed_data:  # change role -> change order
                _update_member_order(board_member, old_role)

            audit_data = {
                'user': request.user,
                'obj': board_member,
                'message': helpers.construct_change_message(form),
                'old_values': audit_old_values,
                'new_values': helpers.collect_new_values(form),
            }
            # this view only handle existing editorial board member, so always log changes.
            helpers.log_change(**audit_data)
            # notify librarians
            notifications.board_members_send_email_by_action(board_member, request.user, audit_data['message'], 'board_edit_member')

            messages.success(request, _(u'Board Member updated successfully.'))
            return HttpResponseRedirect(board_url)
        else:
            messages.error(request, _(u'Check mandatory fields.'))
            context['form'] = form
            return render_to_response(template_name, context, context_instance=RequestContext(request))
    else:
        form = forms.EditorialMemberForm(instance=board_member)

    context['form'] = form
    return render_to_response(template_name, context, context_instance=RequestContext(request))

@login_required
@permission_required('editorialmanager.add_editorialmember', login_url=settings.AUTHZ_REDIRECT_URL)
def add_board_member(request, journal_id, issue_id):
    """
    Handles only NEW editorial board member
    """
    if request.is_ajax():
        template_name = 'board/board_member_edit_form.html'
    else:
        template_name = 'board/board_member_edit.html'

    # check if user have correct access to view the journal:
    if not Journal.userobjects.active().filter(pk=journal_id).exists():
        messages.error(request, _(u'The journal is not available for you.'))
        return HttpResponseRedirect(reverse('editorial.index'))

    # get the issue, then the board if exist
    issue = Issue.userobjects.active().filter(pk=issue_id)
    if len(issue) != 1:
        messages.error(request, _(u'The issue is not available for you.'))
        return HttpResponseRedirect(reverse('editorial.index'))

    issue = issue[0]
    # get the related issue::board, if none: create one
    if not hasattr(issue, 'editorialboard'):
        board = models.EditorialBoard(issue=issue)
        board.save()

    board = issue.editorialboard

    board_url = reverse('editorial.board', args=[journal_id, ])
    post_url = reverse('editorial.board.add', args=[journal_id, issue_id, ])

    context = {
        'post_url': post_url,
        'board_url': board_url,
    }
    if request.method == "POST":
        form = forms.EditorialMemberForm(request.POST)
        if form.is_valid():
            new_member = form.save(commit=False)
            new_member.board = board
            new_member.save()
            new_member.order = _get_order_for_new_members(new_member.board, new_member.role.pk, new_member.pk)
            new_member.save()

            audit_data = {
                'user': request.user,
                'obj': new_member,
                'message': helpers.construct_create_message(form),
                'old_values': None,
                'new_values': helpers.collect_new_values(form),
            }
            # this view only handle NEW editorial board member, so always log create.
            helpers.log_create(**audit_data)
            # notify librarians
            notifications.board_members_send_email_by_action(new_member, request.user, audit_data['message'], 'board_add_member')

            messages.success(request, _(u'Board Member created successfully.'))
            return HttpResponseRedirect(board_url)
        else:
            messages.error(request, _(u'Check mandatory fields.'))
            context['form'] = form
            return render_to_response(template_name, context, context_instance=RequestContext(request))
    else:
        form = forms.EditorialMemberForm()

    context['form'] = form
    return render_to_response(template_name, context, context_instance=RequestContext(request))

@login_required
@permission_required('editorialmanager.delete_editorialmember', login_url=settings.AUTHZ_REDIRECT_URL)
def delete_board_member(request, journal_id, member_id):
    # check if user have correct access to view the journal:
    if not Journal.userobjects.active().filter(pk=journal_id).exists():
        messages.error(request, _(u'The journal is not available for you.'))
        return HttpResponseRedirect(reverse('editorial.index'))

    if request.is_ajax():
        template_name = 'board/board_member_delete_data.html'
    else:
        template_name = 'board/board_member_delete.html'

    board_member = get_object_or_404(models.EditorialMember, id=member_id)
    board_url = reverse('editorial.board', args=[journal_id, ])
    post_url = reverse('editorial.board.delete', args=[journal_id, member_id, ])
    context = {
        'board_member': board_member,
        'post_url': post_url,
        'board_url': board_url,
    }
    if request.method == "POST":
        # save the audit log
        audit_message = helpers.construct_delete_message(board_member)
        helpers.log_delete(request.user, board_member, audit_message)
        _update_members_order_when_delete(board_member)
        # notify librarians
        notifications.board_members_send_email_by_action(board_member, request.user, audit_message, 'board_delete_member')
        # delete member!
        board_member.delete()
        messages.success(request, _(u'Board Member DELETED successfully.'))
        return HttpResponseRedirect(board_url)

    return render_to_response(template_name, context, context_instance=RequestContext(request))

@login_required
@permission_required('editorialmanager.change_editorialmember', login_url=settings.AUTHZ_REDIRECT_URL)
def board_move_block(request, journal_id):
    board_url = reverse('editorial.board', args=[journal_id, ])

    if not Journal.userobjects.active().filter(pk=journal_id).exists():
        messages.error(request, _(u'The journal is not available for you.'))
        return HttpResponseRedirect(board_url)

    if request.method == "POST":
        # gather data and validate with a form
        data = {
            'journal_pk': request.POST.get("journal_pk", None),
            'issue_pk': request.POST.get("issue_pk", None),
            'board_pk': request.POST.get("board_pk", None),
            'role_name': request.POST.get("role_name", None),
            'role_position': request.POST.get("role_position", None),
            'direction': request.POST.get("direction", None),
        }
        form = forms.BoardMoveForm(data)

        if form.is_valid():
            _do_move_board_block(
                board_pk=data['board_pk'],
                position=data['role_position'],
                direction=data['direction'])

            messages.success(request, _(u'Board block moved successfully.'))
        else:
            messages.error(request, _(u'Board block can not be moved'))
            logger.error(u"Board block can not be moved. form is not valid. Errors: %s" % form.errors)

    return HttpResponseRedirect(board_url)

@login_required
@permission_required('editorialmanager.add_roletype', login_url=settings.AUTHZ_REDIRECT_URL)
def add_role_type(request, journal_id):
    """
    Handles only NEW editorial Role Types
    """
    if request.is_ajax():
        template_name = 'board/role_type_edit_form.html'
    else:
        template_name = 'board/role_type_edit.html'

    board_url = reverse('editorial.board', args=[journal_id, ])
    post_url = reverse('editorial.role.add', args=[journal_id, ])
    context = {
        'post_url': post_url,
        'board_url': board_url,
    }

    if request.method == "POST":
        form = forms.RoleTypeForm(request.POST)
        if form.is_valid():
            new_role = form.save()
            audit_data = {
                'user': request.user,
                'obj': new_role,
                'message': helpers.construct_create_message(form),
                'old_values': None,
                'new_values': helpers.collect_new_values(form),
            }
            # this view only handle NEW editorial board member, so always log create.
            helpers.log_create(**audit_data)
            messages.success(request, _(u'Role created successfully.'))
            return HttpResponseRedirect(board_url)
        else:
            messages.error(request, _(u'Check mandatory fields.'))
            context['form'] = form
            return render_to_response(template_name, context, context_instance=RequestContext(request))
    else:
        form = forms.RoleTypeForm()

    context['form'] = form
    return render_to_response(template_name, context, context_instance=RequestContext(request))

@login_required
@permission_required('editorialmanager.change_roletype', login_url=settings.AUTHZ_REDIRECT_URL)
def edit_role_type(request, journal_id, role_id):
    """
    Handle only existing editorial Role Types
    """
    if request.is_ajax():
        template_name = 'board/role_type_edit_form.html'
    else:
        template_name = 'board/role_type_edit.html'

    # check if user have correct access to view the journal:
    if not Journal.userobjects.active().filter(pk=journal_id).exists():
        messages.error(request, _(u'The journal is not available for you.'))
        return HttpResponseRedirect(reverse('editorial.index'))

    role_type = get_object_or_404(models.RoleType, id=role_id)
    post_url = reverse('editorial.role.edit', args=[journal_id, role_id, ])
    board_url = reverse('editorial.board', args=[journal_id, ])
    context = {
        'role_type': role_type,
        'post_url': post_url,
        'board_url': board_url,
    }

    if request.method == "POST":
        form = forms.RoleTypeForm(request.POST, instance=role_type)
        audit_old_values = helpers.collect_old_values(role_type, form)

        if form.is_valid():
            role_type = form.save()
            # audit
            audit_data = {
                'user': request.user,
                'obj': role_type,
                'message': helpers.construct_change_message(form),
                'old_values': audit_old_values,
                'new_values': helpers.collect_new_values(form),
            }
            # this view only handle existing roles, so always log changes.
            helpers.log_change(**audit_data)
            messages.success(request, _(u'Role updated successfully.'))
            return HttpResponseRedirect(board_url)
        else:
            messages.error(request, _(u'Check mandatory fields.'))
            context['form'] = form
            return render_to_response(template_name, context, context_instance=RequestContext(request))
    else:
        form = forms.RoleTypeForm(instance=role_type)

    context['form'] = form
    return render_to_response(template_name, context, context_instance=RequestContext(request))

@login_required
def list_role_type(request, journal_id):
    if request.is_ajax():
        template_name = 'board/role_type_list_data.html'
    else:
        template_name = 'board/role_type_list.html'

    # check if user have correct access to view the journal:
    if not Journal.userobjects.active().filter(pk=journal_id).exists():
        messages.error(request, _(u'The journal is not available for you.'))
        return HttpResponseRedirect(reverse('editorial.index'))

    board_url = reverse('editorial.board', args=[journal_id, ])
    roles = models.RoleType.objects.all().order_by('name')
    context = {
        'board_url': board_url,
        'journal_id': journal_id,
        'roles': roles,
    }
    return render_to_response(template_name, context, context_instance=RequestContext(request))

@login_required
@permission_required('editorialmanager.change_roletype', login_url=settings.AUTHZ_REDIRECT_URL)
def translate_role_type(request, journal_id, role_id):
    template_name = 'board/role_type_translate.html'
    # check if user have correct access to view the journal:
    if not Journal.userobjects.active().filter(pk=journal_id).exists():
        messages.error(request, _(u'The journal is not available for you.'))
        return HttpResponseRedirect(reverse('editorial.index'))

    role_type = get_object_or_404(models.RoleType, id=role_id)
    post_url = reverse('editorial.role.translate', args=[journal_id, role_id, ])
    board_url = reverse('editorial.board', args=[journal_id, ])

    context = {
        'journal_id': journal_id,
        'role_type': role_type,
        'post_url': post_url,
        'board_url': board_url,
    }

    RoleTypeTranslationFormSet = inlineformset_factory(
                                    models.RoleType, models.RoleTypeTranslation,
                                    form=forms.RoleTypeForm, formset=forms.RoleTypeTranslationFormSet,
                                    extra=3, can_delete=True)

    if request.method == "POST":
        formset = RoleTypeTranslationFormSet(request.POST, instance=role_type, prefix="role-translations-formset")
        audit_old_values = helpers.collect_old_values(role_type, form=None, formsets=[formset, ])

        if formset.is_valid():
            formset.save()

            # audit
            audit_data = {
                'user': request.user,
                'obj': role_type,
                'message': helpers.construct_change_message(form=None, formsets=[formset, ]),
                'old_values': audit_old_values,
                'new_values': helpers.collect_new_values(form=None, formsets=[formset, ]),
            }
            # this view only handle existing roles, so always log changes.
            helpers.log_change(**audit_data)

            messages.success(request, _(u'Role updated successfully.'))
            return HttpResponseRedirect(board_url)
        else:
            messages.error(request, _(u'Check mandatory fields.'))
    else:
        formset = RoleTypeTranslationFormSet(instance=role_type, prefix="role-translations-formset")

    context['formset'] = formset
    return render_to_response(template_name, context, context_instance=RequestContext(request))

@login_required
def export_csv(request, journal_id, issue_id=None):
    '''
    Export a list of members following this format:

    journal, issn_print, issn_eletronic, issue_year, issue_volume, issue_number,
    role_name, first_name, last_name, full_name, email, institution, link_cv,
    state, country, country_code, country_code_alpha3, research_id, orcid

    19 fields separeted by comma.

    '''

    # check if user have correct access to view the journal:
    if not Journal.userobjects.active().filter(pk=journal_id).exists():
        messages.error(request, _(u'The journal is not available for you.'))
        return HttpResponseRedirect(reverse('editorial.index'))

    filters = {}
    journal = _get_journal_or_404_by_user_access(request.user, journal_id)
    journal_slug = slugify(journal.title)

    if issue_id:
        filters['pk'] = issue_id
        try:
            issue_year, issue_volume, issue_number = models.Issue.objects.filter(pk=issue_id).values_list(
                'publication_year', 'volume', 'number')[0]
        except models.Issue.DoesNotExist:
            raise Http404("Requested Issue does not exist")
        filename = u'board_%s_%s_v%s_n%s' % (journal_slug, issue_year, issue_volume, issue_number)
    else:
        filename = u'full_board_%s' % journal_slug

    issues = journal.issue_set.filter(**filters).order_by('-publication_year', '-volume', '-number')

    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = u'attachment; filename="%s.csv"' % filename

    template = loader.get_template('board/export_member_csv.txt')
    context = Context({'journal': journal, 'issues': issues})

    def gen_csv_data_windows_line():
        csv_data = template.render(context)
        for line in csv_data.splitlines():
            yield line.rstrip() + u'\r\n'

    response.write(u''.join(gen_csv_data_windows_line()))

    return response


"""Unit tests for metadata generation."""
import pytest

import upload_rest_api.metax

@pytest.mark.parametrize('verify', [True, False])
def test_metax_ssl_verification(requests_mock, verify, mock_config):
    """Test Metax HTTPS connection verification.

    HTTPS connection to Metax should be verified if
    METAX_SSL_VERIFICATION option is used.

    :param requests_mock: HTTP request mocker
    :param verify: value for MetaxClient `verify` parameter
    :param mock_config: Configuration
    """
    # Mock metax
    requests_mock.get('/rest/v2/datasets/qux', json={})

    # Set METAX_SSL_VERIFICATION option
    mock_config['METAX_SSL_VERIFICATION'] = verify

    # "Reset" Metax client so that new Metax instance is created using
    # the mocked configuration
    upload_rest_api.metax.singleton = {'metax_client': None}

    # Try to get a dataset from Metax.
    client = upload_rest_api.metax.get_metax_client()
    client.get_dataset('qux')
    assert requests_mock.last_request.verify is verify


import matplotlib.pyplot as plt

plt.interactive(False)

class MplPlot:

    def __init__(self, data, original_data=None):
        self._data = data
        self._original_data = original_data

    @staticmethod
    def with_data(data):
        return MplPlot(data)

    @staticmethod
    def with_data_and_its_original_form(data, original_data):
        return MplPlot(data, original_data)

    def show_pca_scatter_plot(self):
        scatter_plot = MplPlot.PcaScatterPlot(
            data=self._data,
            original_data=self._original_data
        )
        scatter_plot.show()

    class PcaScatterPlot:

        COLORS = ['black', 'blue', 'purple', 'yellow', 'white', 'red', 'lime', 'cyan', 'orange', 'gray']

        TITLE = "PCA Scatter Plot"
        X_LABEL = "First Principal Component"
        Y_LABEL = "Second Principal Component"

        def __init__(self, data, original_data):
            self._data = data
            self._original_data = original_data

        def show(self):
            self._prepare_scatter_data()
            self._add_legend()
            self._add_labels()
            self._add_title()
            plt.show(block=True)

        def _prepare_scatter_data(self):
            for i in range(len(MplPlot.PcaScatterPlot.COLORS)):
                x = self._data[:, 0][self._original_data.target == i]
                y = self._data[:, 1][self._original_data.target == i]
                plt.scatter(x, y, c=MplPlot.PcaScatterPlot.COLORS[i])

        def _add_legend(self):
            plt.legend(self._original_data.target_names, bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0)

        @classmethod
        def _add_labels(cls):
            plt.xlabel(cls.X_LABEL)
            plt.ylabel(cls.Y_LABEL)

        @classmethod
        def _add_title(cls):
            plt.title(cls.TITLE)

if __name__ == "__main__":
    from data.load.sklearn.dataset import get_digits
    from data.prepare.pca import PCAExample

    base_data = get_digits()

    pca = PCAExample(base_data.data)
    reduced_data = pca.get_randomized_pca()

    plots = MplPlot.with_data_and_its_original_form(reduced_data, base_data)
    plots.show_pca_scatter_plot()


import logging
from fastapi import FastAPI, Request
from API.users import users_router
from API.assets import assets_router
from Domain.users.factory import InvalidUsername
from starlette.responses import JSONResponse

logging.basicConfig(
    filename="finance.log",
    level=logging.DEBUG,
    format="%(asctime)s _ %(levelname)s _ %(name)s _ %(message)s",
)

app = FastAPI(
    debug=True,
    title="Fintech Portfolio API",
    description="A webserver with a REST API for keeping track of your different financial assets,"
    " stocks & crypto, and see/compare their evolution",
    version="0.3.0",
)

app.include_router(users_router)
app.include_router(assets_router)

@app.exception_handler(InvalidUsername)
def return_invalid_username(_: Request, e: InvalidUsername):
    return JSONResponse(status_code=400, content="Username is not valid! Error: " + str(e))

if __name__ == "__main__":
    import subprocess
    logging.info("Starting webserver ...")
    try:
        subprocess.run(["uvicorn", "main:app", "--reload"])
    except KeyboardInterrupt as e:
        logging.warning("Keyboard interrupt.")
    except Exception as e:
        logging.warning("Webserver has stopped. Reason: " + str(e))


"""
Coursera: Introduction to Bioinformatics
Module implements requirements of lesson 1.3 from week 1.
25 December 2021 - Bahadir Arslan
"""

from pathlib import Path
import sys

PROJECT_DIRECTORY = Path(__file__).parent.parent
sys.path.append(str(PROJECT_DIRECTORY))

from week1.lesson_1_2 import pattern_count

def frequency_map(string: str, length: int) -> dict:

    freq_map = {}
    for i in range(len(string) - length + 1):
        pattern = string[i: i + length]
        freq_map[pattern] = pattern_count(string, pattern)
    return freq_map

def frequent_words(string: str, length: str) -> list:

    words = []
    freq_map = frequency_map(string, length)
    max_val = max(freq_map.values())
    for word, count in freq_map.items():
        if count == max_val:
            words.append(word)
    return sorted(words)


'''
Created on Jul 7, 2015

@author: Yohan
'''
import csv
import re
from collections import defaultdict

class Quiz:

    def __init__(self, params):
        pass

    @staticmethod
    def quizzes(db_path):
        fields = ['id','parent_id','open_time','soft_close_time','hard_close_time','maximum_submissions','title','duration','quiz_type','proctoring_requirement','authentication_required','deleted','last_updated']
        for line in open(db_path):
            if not line.startswith("INSERT INTO `quiz_metadata` VALUES "): continue
            for item in csv.DictReader( map(lambda s: re.sub('^\\(','', re.sub('\\);?$','',s)), re.split('(?<=\\)),(?=\\()', line.replace("INSERT INTO `quiz_metadata` VALUES ",""))), quotechar="'", fieldnames=fields ):
                yield item

    @staticmethod
    def quiz_submissions(db_path):
        fields = ['id','item_id','session_user_id','submission_time','submission_number','raw_score','grading_error','authenticated_submission_id']
        for line in open(db_path):
            if not line.startswith("INSERT INTO `quiz_submission_metadata` VALUES "): continue
            for item in csv.DictReader( map(lambda s: re.sub('^\\(','', re.sub('\\);?$','',s)), re.split('(?<=\\)),(?=\\()', line.replace("INSERT INTO `quiz_submission_metadata` VALUES ",""))), quotechar="'", fieldnames=fields ):
                yield item

    @staticmethod
    def valid_quizzes(db_path, threshold):
        quiz_users = defaultdict(set)
        for submission in Quiz.quiz_submissions(db_path):
            quiz_users[submission['item_id']].add(submission['session_user_id'])
        return set( [quiz_id for quiz_id,users in quiz_users.iteritems() if len(users) >= threshold] )


import boto3
from botocore.exceptions import *
import logging

def get_ec2_info():
    try:
        ec2 = boto3.resource('ec2')
        print_instances = ec2.instances.all()

        print('Outputting EC2 Info...\n')

        for ec2instance in print_instances:
            print(
                "ID: {}\nType: {} \nPublic IPv4: {}\nAMI: {}\nState: {}\nTags: {}\nSecurityGroups: {}\n".format(
                    ec2instance.id, ec2instance.instance_type, ec2instance.public_ip_address, ec2instance.image.id,
                    ec2instance.state, ec2instance.tags, ec2instance.security_groups
                )
            )

    except ClientError as e:
        logging.error(e)

    except Exception as e:
        logging.debug('Checking resource')
        logging.warning(e)
        print(e)

if __name__ == '__main__':
    get_ec2_info()
else:
    print('Running As Imported...')
    get_ec2_info()

Python-For-DevOps/Interacting with Python in AWS/List EC2 Instances/listec2.py


"""
Models
"""
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql.sqltypes import DateTime
from sqlalchemy.orm import relationship

Base = declarative_base()

class Person(Base):
    """Information about Person"""

    __tablename__ = "person"
    id = Column(Integer, primary_key=True)
    name = Column(String(50), nullable=False)
    phones = relationship("Phones", cascade="all, delete", backref="person")
    birthday = Column(DateTime, nullable=True)

class Phones(Base):
    """Information about Person phones"""

    __tablename__ = "phones"
    id = Column(Integer, primary_key=True)
    phone = Column(Integer)
    person_id = Column(Integer, ForeignKey("person.id", ondelete="CASCADE"))


from PIL import Image
import numpy as np

import os

src_dir = "celegans"
dst_dir = "celegans_multi_cond"
time_interval = 8

# assume the file is read in order

dst_dir += "_{}".format(time_interval)
for subdir, dirs, files in os.walk(src_dir):
    for fn in files:
        if len(subdir) < 9:
            continue
        src_fn = os.path.join(subdir, fn)

        slice_beg = fn.find("_t") + 2
        slice_end = fn.find(".jpg")
        time_slice = int(fn[slice_beg:slice_end])
        next_fn = fn[0:slice_beg] + \
                "{}".format(time_slice + time_interval) + \
                fn[slice_end:]
        next_src_fn = os.path.join(subdir, next_fn)

        if os.path.isfile(next_src_fn):
            images_list = [src_fn, next_src_fn]
            images = [Image.open(i) for i in images_list]

            merged_image_array = \
                    np.hstack((np.asarray(image) for image in images))
            merged_image = Image.fromarray(merged_image_array)
            new_fn = subdir[-2:] + "_" + fn
            fn_time_slice = (time_slice - 1) / 10
            dst_fn = "{}/{}/{}".format(dst_dir, fn_time_slice, new_fn)

            merged_image.save(dst_fn)


import argparse
import json
from yatcobot.client import TwitterClient
from yatcobot.config import TwitterConfig, Config

parser = argparse.ArgumentParser(description='Download a tweet to json')
parser.add_argument('tweet_id', metavar='id', type=int)
parser.add_argument('--config', '-c', dest='config', default='../config.yaml', help='Path of the config file')

args = parser.parse_args()

Config.load(args.config)

client = TwitterClient(TwitterConfig.get().consumer_key,
                       TwitterConfig.get().consumer_secret,
                       TwitterConfig.get().access_token_key,
                       TwitterConfig.get().access_token_secret)

tweet = client.get_tweet(args.tweet_id)

with open('{}.json'.format(args.tweet_id), 'w') as f:
    json.dump(tweet, f, indent=2)


import datetime as dt

import pytest
import pytz

from analytix.groups import Group, GroupItem, GroupItemList, GroupList

@pytest.fixture()
def group_data():
    return {
        "kind": "youtube#group",
        "etag": "f6g7h8i9j0",
        "id": "a1b2c3d4e5",
        "snippet": {
            "publishedAt": "2022-11-30T12:34:56.789Z",
            "title": "Barney the Dinosaur",
        },
        "contentDetails": {
            "itemCount": "69",
            "itemType": "youtube#video",
        },
    }

@pytest.fixture()
def group():
    return Group(
        kind="youtube#group",
        etag="f6g7h8i9j0",
        id="a1b2c3d4e5",
        published_at=dt.datetime(2022, 11, 30, 12, 34, 56, 789000, tzinfo=pytz.utc),
        title="Barney the Dinosaur",
        item_count=69,
        item_type="youtube#video",
    )

def test_create_group_from_json(group_data, group):
    assert Group.from_json(group_data) == group

def test_group_data_property(group_data, group):
    assert group.data == group_data

@pytest.fixture()
def group_list_data(group_data):
    return {
        "kind": "youtube#groupListResponse",
        "etag": "f6g7h8i9j0",
        "items": [group_data],
        "nextPageToken": "a1b2c3d4e5",
    }

@pytest.fixture()
def group_list(group):
    return GroupList(
        kind="youtube#groupListResponse",
        etag="f6g7h8i9j0",
        items=[group],
        next_page_token="a1b2c3d4e5",
    )

def test_create_group_list_from_json(group_list_data, group_list):
    assert GroupList.from_json(group_list_data) == group_list

def test_group_list_data_property(group_list_data, group_list):
    assert group_list.data == group_list_data

def test_group_list_get_item(group_list, group):
    assert group_list[0] == group_list.items[0] == group

@pytest.fixture()
def group_item_data():
    return {
        "kind": "youtube#groupItem",
        "etag": "f6g7h8i9j0",
        "id": "e5d4c3b2a1",
        "groupId": "a1b2c3d4e5",
        "resource": {
            "kind": "youtube#video",
            "id": "j0i9h8g7f6",
        },
    }

@pytest.mark.dependency()
def test_create_group_item_from_json(group_item_data):
    group = GroupItem.from_json(group_item_data)
    assert group.kind == "youtube#groupItem"
    assert group.etag == "f6g7h8i9j0"
    assert group.id == "e5d4c3b2a1"
    assert group.group_id == "a1b2c3d4e5"
    assert group.resource.kind == "youtube#video"
    assert group.resource.id == "j0i9h8g7f6"

@pytest.mark.dependency(depends=["test_create_group_item_from_json"])
@pytest.fixture()
def group_item(group_item_data):
    return GroupItem.from_json(group_item_data)

@pytest.mark.dependency(depends=["test_create_group_item_from_json"])
def test_group_item_data_property(group_item_data, group_item):
    assert group_item.data == group_item_data

@pytest.fixture()
def group_item_list_data(group_item_data):
    return {
        "kind": "youtube#groupItemListResponse",
        "etag": "a1b2c3d4e5",
        "items": [group_item_data],
    }

@pytest.fixture()
def group_item_list(group_item):
    return GroupItemList(
        kind="youtube#groupItemListResponse",
        etag="a1b2c3d4e5",
        items=[group_item],
    )

@pytest.mark.dependency(depends=["test_create_group_item_from_json"])
def test_create_group_item_list_from_json(group_item_list_data, group_item_list):
    assert GroupItemList.from_json(group_item_list_data) == group_item_list

@pytest.mark.dependency(depends=["test_create_group_item_from_json"])
def test_group_item_list_data_property(group_item_list_data, group_item_list):
    assert group_item_list.data == group_item_list_data

@pytest.mark.dependency(depends=["test_create_group_item_from_json"])
def test_group_item_list_get_item(group_item_list, group_item):
    assert group_item_list[0] == group_item_list.items[0] == group_item


from PIL import Image, ImageGrab
import time
from pynput.mouse import Button, Controller

STACK_COUNT = 6
INITIAL_CARDS = 6

GAME_UPPER_LEFT_X = 800
GAME_UPPER_LEFT_Y = 470
GAME_BOTTOM_RIGHT_X = 1762
GAME_BOTTOM_RIGHT_Y = 820

STARTING_X = 2
STARTING_Y = 2
CARD_WIDTH = 164
CARD_HEIGHT = 32
REF_WIDTH = 34
REF_HEIGHT = 24

class CardImageMatcher:
    def __init__(self):
        self.ref6 = list(Image.open("ref/6.png").getdata())
        self.ref7 = list(Image.open("ref/7.png").getdata())
        self.ref8 = list(Image.open("ref/8.png").getdata())
        self.ref9 = list(Image.open("ref/9.png").getdata())
        self.ref10 = list(Image.open("ref/10.png").getdata())
        self.refv = list(Image.open("ref/v.png").getdata())
        self.refd = list(Image.open("ref/d.png").getdata())
        self.refk = list(Image.open("ref/k.png").getdata())
        self.reft = list(Image.open("ref/t.png").getdata())

    def match(self, image):
        image_data = list(image.getdata())

        if image_data == self.ref6:
            return "6"
        if image_data == self.ref7:
            return "7"
        if image_data == self.ref8:
            return "8"
        if image_data == self.ref9:
            return "9"
        if image_data == self.ref10:
            return "10"
        if image_data == self.refv:
            return "v"
        if image_data == self.refd:
            return "d"
        if image_data == self.refk:
            return "k"
        if image_data == self.reft:
            return "t"
        raise Exception("Can't determine card value")

class GameApi:
    def __init__(self):
        self.stacks = []
        self.mouse = Controller()
        # Get window focus first
        self.__focus()
        self.__new_game()
        self.__populate_game_state()

    def __populate_game_state(self):
        game_image = ImageGrab.grab().crop((GAME_UPPER_LEFT_X, GAME_UPPER_LEFT_Y, GAME_BOTTOM_RIGHT_X, GAME_BOTTOM_RIGHT_Y))
        matcher = CardImageMatcher()

        for x in range(STACK_COUNT):
            stack = []
            for y in range(INITIAL_CARDS):
                current_x = STARTING_X + x * CARD_WIDTH
                current_y = STARTING_Y + y * CARD_HEIGHT
                current_card = game_image.crop((current_x, current_y, current_x + REF_WIDTH, current_y + REF_HEIGHT)).convert('L')
                stack.append(matcher.match(current_card))
            self.stacks.append(stack)

        print("Found card stacks:")
        print(self.stacks)

    def __click_on(self, position_tuple):
        self.mouse.position = position_tuple
        time.sleep(0.05)
        self.mouse.press(Button.left)
        time.sleep(0.05)
        self.mouse.release(Button.left)
        time.sleep(0.05)

    def __focus(self):
        self.__click_on((1280, 1015))

    def __new_game(self):
        self.__click_on((1280, 1215))
        time.sleep(5)

    '''
    Card tuple consists of (stack index, card index).
    '''
    def click_card(self, card_tuple):
        stack_num = card_tuple[0]
        card_num = card_tuple[1]
        x_pos = GAME_UPPER_LEFT_X + STARTING_X + (stack_num * CARD_WIDTH)
        y_pos = GAME_UPPER_LEFT_Y + STARTING_Y + (card_num * CARD_HEIGHT)
        self.__click_on((x_pos, y_pos))


import os
import requests
from lxml import etree
import time
import random

def get_pic(link, hd, proxies=None):
    page_info = requests.get(link, headers=hd, proxies=proxies)
    page_info.encoding = 'utf-8'
    page_text = page_info.text
    page_info = etree.HTML(page_text)

    _title = page_info.xpath('//*[@id="subject_tpc"]/text()')[0]
    pic_list = page_info.xpath('//div[@id="read_tpc"]/img/@src')

    fp = r'H:\下载\imgs\{}'.format(_title)
    if not os.path.exists(fp):
        os.mkdir(fp)
    else:
        pass
    print('下载路径:{}'.format(fp))
    print('开始下载')

    start_time = time.perf_counter()
    for pic in pic_list:
        pic_name = pic.split('/')[-1]
        if pic_name not in os.listdir(fp):  # 检查是该pic是否已经被下载,如果没有,则下载它
            try:
                s = requests.session()
                s.keep_alive = False  # 关闭多余连接
                pic_info = requests.get(pic).content
                time.sleep(random.randint(1, 3))
            except ConnectionError:
                print('链接错误,请重试')
                break
            else:
                pic_path = fp + '\\' + pic_name
                with open(pic_path, 'wb') as f_save:
                    f_save.write(pic_info)
                    f_save.close()
                    print(pic_name, '下载完成')
        else:
            continue
    end_time = time.perf_counter()
    print('共耗时:', round((end_time - start_time), 2))

if __name__ == '__main__':
    proxy = {'http': '127.0.0.1:57816',
             'https': '127.0.0.1:57816'}

    headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 \
    (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36'}

    url = r'https://s1.v5uh.xyz/2048/read.php?tid-1972966-fpage-14.html'
    get_pic(url, headers)


import json
import logging

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer

class QueueConsumer(WebsocketConsumer):
    def connect(self):
        self.queue_id = self.scope["url_route"]["kwargs"]["queue_id"]
        self.room_group_name = f"queue_{self.queue_id}"

        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name, self.channel_name
        )

        self.accept()
        print(f"Consumer connected to queue {self.queue_id}")
        self.send(text_data=json.dumps({"message": "Connected"}))

    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name, self.channel_name
        )

    def queue_message(self, event):
        message = event["message"]

        self.send(text_data=json.dumps(message))


import codecs
import struct
from collections import namedtuple
import xml.etree.ElementTree as ET

# lazy implementation, but it's something ¯\_(ツ)_/¯

def hash_label(label, bucket_count):
	val = 0
	for c in label:
		val = ((val * 0x492) + ord(c)) & 0xFFFFFFFF
	return val % bucket_count

def hash_bucket_count(string_count):
	primes = (
		2,3,5,7,11,13,17,19,23,29,31,37,41,43,
		47,53,59,61,67,71,73,79,83,89,97,101
		)
	target = (string_count // 2) + 1
	for prime in primes:
		if prime >= target:
			return prime
	return primes[-1]

def round_up(amount):
	return (amount + 0xF) & ~0xF

StartTag = namedtuple('StartTag', ('a', 'b', 'arg'))
EndTag = namedtuple('EndTag', ('a', 'b'))

# 110, 0 - ellipsis?
# 110, 3 - nickname?
# 110, 5 - phrase?

animEnums = (
	"EmotEnd", "Smiling", "Laughing", "HappyFlower", "Love", "HappyDance", "Angry", "Outraged",
	"Fuming", "Worried", "Sighing", "Thinking", "SadSpiral", "Frantic", "Crying", "Shocked",
	"Aha", "TakenAback", "Surprised", "ColdChill", "Shaking", "Nodding", "IdeaBulb", "QuestionMark",
	"BrokenHeart", "Sleepy", "Blushing", "OhGeez", "Scheming", "Clapping", "Sneezing", "Cheering",
	"Greeting", "SmugFace", "Hot", "Grin", "WrySmile", "FaintSmile", "BigSmile", "Hesitate",
	"Negative", "Oops", "Dance", "AbsentMindedness", "Shaki", "Sleep", "Special", "Silent",
	"Hello", "Confused", "ShyBoy", "Restless", "Serious", "Eh", "Apologize", "Assent",
	"Explain", "NpcShuffle", "Pardon", "NpcHoldout")

# 2e missing ('special')
animMappings = (
	0,0,1,3,4,0x24,0x25,0x26,
	6,7,8,9,0xa,0xb,0xc,0xd,
	0xe,0x18,0x1b,0x31,2,5,0x1d,0x2a,
	0,0x2c,0xf,0x10,0x11,0x12,0x16,0x29,
	0x35,0x1f,0x20,0x36,0x30,0x15,0x17,0x28,
	0x37,0x38,0x13,0x14,0x1e,0x22,0x19,0x2d,
	0,0x2b,0x1a,0x1c,0x27,0x21,0x23,0x32,
	0x33,0x34,0x2f,0x3a,0x39,0x3b)

animIDs = '|'.join([animEnums[x] for x in animMappings])

tagMetadata = {
	(0,0): ('ruby', 'u16 a', 'u16 b', 'str'),
	(0,2): ('size', 'u16 value'),
	(0,3): ('colour', 'u16 id'),
	(0,4): ('pageBreak', ),
	(10,0): ('delay', 'u16 frames'),
	(10,1): ('delay8', ),
	(10,2): ('delay20', ),
	(10,3): ('delay40', ),
	(10,4): ('10_4', ),
	(10,5): ('10_5', ),
	(10,7): ('10_7', 'u32 arg'),
	(10,8): ('10_8', 'u32 arg'),
	(10,9): ('10_9', ),
	(10,10): ('10_10', ),
	(10,11): ('10_11', ),
	(10,14): ('10_14', 'u16 arg'),
	(10,15): ('setNpcTalkChoices', 'u8 chatOption', 'u8 cancelOption'),
	(20,0): ('20_0', 'str arg'),
	(20,1): ('20_1', 'str arg'),
	(20,2): ('20_2', 'u8 arg'),
	(20,3): ('20_3', 'u8 arg'),
	(40,-1): ('anim', 'idB id '+animIDs, 'u8 target actor|player|mainNpc|subNpc', 'align', 'str targetSpec'),
	(45,-1): ('snpcAnim', 'idB id GulCollapsed|GstSurprised|DodWireless|DodRoger|TkkEh|TkkNodding|TkkQuestionMark', 'u8 target actor|player|mainNpc|subNpc', 'align', 'str targetSpec'),
	(50,4): ('capitaliseNextWord', ),
	(50,5): ('gender1', 'str m', 'str f'),
	(50,6): ('gender2', 'str m', 'str f'),
	(50,7): ('gender3', 'str m', 'str f'),
	(50,8): ('50_8', 'u8 x', 'u8 y', 'str a', 'str b', 'str c'),
	(50,9): ('50_9', 'u8 x', 'u8 y', 'str a', 'str b', 'str c'),
	(50,10): ('gender4', 'str m', 'str f'),
	(50,14): ('gender5', 'str m', 'str f'),
	(50,20): ('gender6', 'str m', 'str f'),
	(50,22): ('gender7', 'str m', 'str f'),
	(50,25): ('weatherArea', 'str north', 'str south'),
	(50,27): ('50_27', 'u8 x', 'u8 y', 'str a', 'str b', 'str c'),
	(100,3): ('100_3', 'u8 arg Katsu|PlaneAnnounceOn|PlaneAnnounceOff'),
	(198,0): ('delayByPersonality', 'u16 boy_normal', 'u16 boy_active', 'u16 boy_pride', 'u16 boy_snobby', 'u16 girl_normal', 'u16 girl_active', 'u16 girl_pride', 'u16 girl_big_sis'),
	(199,3): ('199_3', 'u16 a'),
}

def tag_to_xml(tag):
	key = (tag.a, tag.b)
	keyN = (tag.a, -1)
	if key in tagMetadata or keyN in tagMetadata:
		meta = tagMetadata.get(key, tagMetadata.get(keyN))
		elem = ET.Element(meta[0])
		offset = 0
		for argdef in meta[1:]:
			if argdef == 'str':
				elem.text = ''.join(map(chr, tag.arg[offset//2:]))
				break
			if argdef == 'align':
				if (offset & 1) == 1:
					offset += 1
				continue
			bits = argdef.split(' ')
			typ = bits[0]
			name = bits[1]
			if typ == 'str':
				arglen = tag.arg[offset // 2]
				offset += 2
				arg = ''.join(map(chr, tag.arg[offset//2:(offset+arglen)//2]))
				offset += arglen
			elif typ == 'u32':
				arg = str(tag.arg[offset//2] | (tag.arg[(offset//2) + 1] << 16))
				offset += 4
			elif typ == 'u16':
				arg = str(tag.arg[offset//2])
				offset += 2
			elif typ == 'u8':
				if (offset & 1) == 0:
					arg = str(tag.arg[offset // 2] & 0xFF)
				else:
					arg = str(tag.arg[offset // 2] >> 8)
				offset += 1
			elif typ == 'idB':
				arg = str(tag.b)
			if len(bits) > 2:
				enum = bits[2].split('|')
				num = int(arg)
				if num < len(enum):
					arg = enum[num]
			elem.set(name, arg)
		return elem

	# nope
	elem = ET.Element('tag', dict(a=str(tag.a), b=str(tag.b)))
	if tag.arg:
		arg = ' '.join(map(hex, tag.arg))
		elem.set('arg', arg)
	return elem

class Message(namedtuple('Message', ('text', 'attribute'))):
	__slots__ = ()

	@classmethod
	def from_pieces(cls, pieces, attribute):
		chars = []
		for piece in pieces:
			if isinstance(piece, StartTag):
				chars.append(0xE)
				chars.append(piece.a)
				chars.append(piece.b)
				chars.append(len(piece.arg) * 2)
				chars.extend(piece.arg)
			elif isinstance(piece, EndTag):
				chars.append(0xF)
				chars.append(piece.a)
				chars.append(piece.b)
			else:
				chars.extend(map(ord, piece))
		text = struct.pack('<%dH' % len(chars), *chars)
		return cls(text, attribute)

	def parse(self):
		chars = struct.unpack_from('<%dH' % (len(self.text) // 2), self.text)
		last = 0
		pos = 0
		while pos < len(chars):
			c = chars[pos]
			if (c == 0xE) or (c == 0xF):
				if pos > last:
					yield ''.join(map(chr, chars[last:pos]))
				# tag
				a = chars[pos + 1]
				b = chars[pos + 2]
				pos += 3
				if c == 0xE:
					arg_len = chars[pos]
					pos += 1
					assert (arg_len & 1) == 0
					arg = chars[pos:pos+(arg_len>>1)]
					pos += (arg_len >> 1)
					yield StartTag(a, b, arg)
				else:
					yield EndTag(a, b)
				last = pos
			else:
				pos += 1
		if pos > last:
			yield ''.join(map(chr, chars[last:pos]))

	def generate_xml(self, label=None):
		elem = ET.Element('message')
		if label is not None:
			elem.set('label', label)
		if self.attribute:
			elem.set('attrib', self.attribute.hex(' '))
		#elem.set('raw', self.text.hex(' ', -2))
		for bit in self.parse():
			if isinstance(bit, str):
				if len(elem) == 0:
					elem.text = bit
				else:
					elem[-1].tail = bit
			elif isinstance(bit, StartTag):
				elem.append(tag_to_xml(bit))
			elif isinstance(bit, EndTag):
				ET.SubElement(elem, 'endTag', dict(a=str(bit.a), b=str(bit.b)))
		return elem

class MSBT:
	def __init__(self):
		self.data = {}
		self.attributes = []
		self.strings = []

	def load(self, blob):
		assert blob[:8] == b'MsgStdBn'
		endian, version, section_count, file_size = struct.unpack_from('<HxxHII', blob, 8)
		assert version == 0x0301
		offset = 0x20

		for i in range(section_count):
			section_magic, section_size = struct.unpack_from('<4sI', blob, offset)
			offset += 0x10
			section = blob[offset:offset+section_size]
			offset += (section_size + 0xF) & ~0xF

			if section_magic == b'LBL1':
				labels = self._load_lbl1(section)
			elif section_magic == b'ATR1':
				attributes = self._load_atr1(section)
			elif section_magic == b'TXT2':
				strings = self._load_txt2(section)
			else:
				raise 'unknown section %r' % section_magic

		assert(len(labels) == len(strings))
		assert(len(attributes) == len(strings))
		label_list = [None] * len(labels)
		for label, index in labels.items():
			label_list[index] = label

		self.data = {}
		for label, string, attribute in zip(label_list, strings, attributes):
			self.data[label] = Message(string, attribute)

	def _load_lbl1(self, data):
		bucket_count = struct.unpack_from('<I', data, 0)[0]
		results = {}
		for i in range(bucket_count):
			string_count, offset = struct.unpack_from('<II', data, 4 + i * 8)
			for j in range(string_count):
				label_len = data[offset]
				offset += 1
				label = codecs.decode(data[offset:offset+label_len], 'ascii')
				offset += label_len
				index = struct.unpack_from('<I', data, offset)[0]
				offset += 4
				results[label] = index
				assert(hash_label(label, bucket_count) == i)
		return results

	def _load_atr1(self, data):
		count, size = struct.unpack_from('<II', data, 0)
		self.attrib_size = size
		return [data[8+i*size:8+i*size+size] for i in range(count)]

	def _load_txt2(self, data):
		count = struct.unpack_from('<I', data, 0)[0]
		offsets = list(struct.unpack_from('<%dI' % count, data, 4))
		offsets.append(len(data)) # dummy end offset
		return [bytes(data[offsets[i]:offsets[i+1]-2]) for i in range(count)]
		#return [codecs.decode(data[offsets[i]:offsets[i+1]-2], 'utf-16le') for i in range(count)]

	def _get_lbl1_size(self):
		return 4 + (8 * hash_bucket_count(len(self.data))) + (5 * len(self.data)) + sum(map(len, self.data.keys()))
	def _get_atr1_size(self):
		return 8 + (len(self.data) * self.attrib_size)
	def _get_txt2_size(self):
		return 4 + sum([len(msg.text) + 4 + 2 for msg in self.data.values()])
	def _get_file_size(self, block_sizes):
		return 0x20 + sum([0x10 + round_up(size) for size in block_sizes])

	def _pack_section_header(self, buf, offset, magic, size):
		buf[offset:offset+4] = magic
		struct.pack_into('<I', buf, offset + 4, size)
		return offset + 0x10

	def _pack_lbl1(self, buf, base):
		bucket_count = hash_bucket_count(len(self.data))
		buckets = [[] for i in range(bucket_count)]
		for index, label in enumerate(self.data.keys()):
			buckets[hash_label(label, bucket_count)].append((label, index))

		struct.pack_into('<I', buf, base, bucket_count)
		table_offset = 4
		data_offset = 4 + (8 * bucket_count)
		for bucket in buckets:
			struct.pack_into('<II', buf, base + table_offset, len(bucket), data_offset)
			table_offset += 8
			for label, index in bucket:
				buf[base+data_offset] = len(label)
				data_offset += 1
				buf[base+data_offset:base+data_offset+len(label)] = label.encode('ascii')
				data_offset += len(label)
				struct.pack_into('<I', buf, base+data_offset, index)
				data_offset += 4

		return base + data_offset

	def _pack_atr1(self, buf, offset):
		struct.pack_into('<II', buf, offset, len(self.data), self.attrib_size)
		offset += 8
		for msg in self.data.values():
			attrib = msg.attribute
			assert(len(attrib) == self.attrib_size)
			buf[offset:offset+len(attrib)] = attrib
			offset += len(attrib)
		return offset

	def _pack_txt2(self, buf, base):
		struct.pack_into('<I', buf, base, len(self.data))
		table_offset = 4
		data_offset = 4 + (4 * len(self.data))
		for message in self.data.values():
			struct.pack_into('<I', buf, base + table_offset, data_offset)
			table_offset += 4
			string = message.text
			buf[base+data_offset:base+data_offset+len(string)] = string
			data_offset += len(string) + 2
		return base + data_offset

	def pack(self):
		lbl1_size = self._get_lbl1_size()
		atr1_size = self._get_atr1_size()
		txt2_size = self._get_txt2_size()
		file_size = self._get_file_size([lbl1_size, atr1_size, txt2_size])

		buf = bytearray(file_size)
		buf[:8] = b'MsgStdBn'
		struct.pack_into('<HHHII', buf, 8, 0xFEFF, 0, 0x301, 3, file_size)

		offset = 0x20
		offset = self._pack_section_header(buf, offset, b'LBL1', lbl1_size)
		offset = self._pack_lbl1(buf, offset)
		offset = self._round_up_buffer(buf, offset)
		offset = self._pack_section_header(buf, offset, b'ATR1', atr1_size)
		offset = self._pack_atr1(buf, offset)
		offset = self._round_up_buffer(buf, offset)
		offset = self._pack_section_header(buf, offset, b'TXT2', txt2_size)
		offset = self._pack_txt2(buf, offset)
		offset = self._round_up_buffer(buf, offset)
		assert(offset == len(buf))

		return buf

	def _round_up_buffer(self, buf, offset):
		rounded = round_up(offset)
		for i in range(offset, rounded):
			buf[i] = 0xAB
		return rounded

	def generate_xml(self):
		root = ET.Element('msbt', dict(attribsize=str(self.attrib_size)))
		root.text = '\n\t'
		tag = None
		for label, message in self.data.items():
			tag = message.generate_xml(label)
			tag.tail = '\n\t'
			root.append(tag)
		if tag is not None:
			tag.tail = '\n' # no tab on last one
		return root

if __name__ == '__main__':
	import sys, sarc, zstandard

	path = sys.argv[1]
	data = open(path, 'rb').read()
	if path.endswith('.zs'):
		path = path[:-3]
		data = zstandard.ZstdDecompressor().decompress(data)
	if path.endswith('.sarc'):
		arc = sarc.SARC(data)
		if len(sys.argv) == 2:
			for f in sorted(arc.list_files()):
				print(f)
			sys.exit()
		else:
			data = arc.get_file_data(sys.argv[2])

	msbt = MSBT()
	msbt.load(data)
	assert(data == msbt.pack())
	xml = msbt.generate_xml()
	print(ET.tostring(xml, 'unicode'))
	#for label, index in sorted(msbt.labels.items()):
	#	print('%s: %r <%r>' % (label, msbt.strings[index], msbt.attributes[index]))
	#repack = msbt.pack()
	#open('tst.msbt', 'wb').write(repack)


import asyncio
from dataclasses import dataclass
from functools import cached_property
from PyQt5.QtCore import QObject, pyqtSignal
from bleak import BleakClient
from bleak.backends.device import BLEDevice

@dataclass
class QBleakClient(QObject):
    device: BLEDevice
    ecg_updated = pyqtSignal(list)
    Battery_level_read = pyqtSignal(int)

    def data_conv(self, sender, data):
        if data[0] == 0x00:
            timestamp = self.convert_to_unsigned_long(data, 1, 8)
            step = 3
            samples = data[10:]
            offset = 0
            ecg_package = []
            while offset < len(samples):
                ecg = self.convert_array_to_signed_int(samples, offset, step)
                offset += step
                ecg_package.append(ecg)
            self.ecg_updated.emit(ecg_package)

    def convert_array_to_signed_int(self, data, offset, length):
        return int.from_bytes(
            bytearray(data[offset: offset + length]), byteorder="little", signed=True,
        )

    def convert_to_unsigned_long(self, data, offset, length):
        return int.from_bytes(
            bytearray(data[offset: offset + length]), byteorder="little", signed=False,
        )

    def __post_init__(self):
        super().__init__()
        self.ECG_data = []

    @cached_property
    def client(self) -> BleakClient:
        return BleakClient(self.device, disconnected_callback=self._handle_disconnect)

    async def start(self):
        await self.client.connect()

        ## UUID for battery level ##
        BATTERY_LEVEL_UUID = "00002a19-0000-1000-8000-00805f9b34fb"

        BL = await self.client.read_gatt_char(BATTERY_LEVEL_UUID)
        Battery_level = int(BL[0])
        print(Battery_level, "%")
        self.Battery_level_read.emit(Battery_level)

        PMD_CONTROL = "FB005C81-02E7-F387-1CAD-8ACD2D8DF0C8"  ## UUID for Request of stream settings ##
        ECG_WRITE = bytearray([0x02, 0x00, 0x00, 0x01, 0x82, 0x00, 0x01, 0x01, 0x0E, 0x00])
        await self.client.write_gatt_char(PMD_CONTROL, ECG_WRITE)

        PMD_DATA = "FB005C82-02E7-F387-1CAD-8ACD2D8DF0C8"  ## UUID for Request of start stream ##
        await self.client.start_notify(PMD_DATA, self.data_conv)

    async def stop(self):
        await self.client.disconnect()

    def _handle_disconnect(self) -> None:
        print("Device was disconnected, goodbye.")
        # cancelling all tasks effectively ends the program
        for task in asyncio.all_tasks():
            task.cancel()


import setuptools

with open('README.md', 'r') as fin:
    long_description=fin.read()
exec(open('MeiTingTrunk/version.py').read())

setuptools.setup(
        name='meitingtrunk',
        version=__version__,
        author='Guangzhi XU',
        author_email='xugzhi1987@gmail.com',
        description='An open source reference management tool developed in PyQt5 and Python3.',
        long_description=long_description,
        long_description_content_type='text/markdown',
        url='https://github.com/Xunius/MeiTingTrunk',
        packages=setuptools.find_packages(),
        classifiers=[
            'Development Status :: 3 - Alpha',
            'Environment :: X11 Applications :: Qt',
            'Intended Audience :: End Users/Desktop',
            'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',
            'Natural Language :: English',
            'Operating System :: POSIX :: Linux',
            'Operating System :: MacOS',
            'Programming Language :: Python :: 3',
            'Topic :: Education'
            ],
        install_requires=[
            #'qt>=5',
            'PyQt5>=5.12',
            #'sqlite3',
            'pdfminer.six',
            'pypdf2',
            'beautifulsoup4',
            'fuzzywuzzy',
            'bibtexparser',
            'crossrefapi',
            'RISparser',
            'send2trash',
            'python-levenshtein',
            'PyQtWebEngine'
            ],
        python_requires='>=3',
        include_package_data=True,
        #package_dir={'': 'MeiTingTrunk'},
        #package_data={'': ['MeiTingTrunk/lib/pdfjs/*',
        #'MeiTingTrunk/samples/*'
        #],},
        entry_points={
            'gui_scripts': ['meitingtrunk = MeiTingTrunk.main:main']
            }
        )


import time
import torch
import torchvision

class FashionMNIST(torch.utils.data.Dataset):
    def __init__(self, batch_size, size=(28, 28)):
        super().__init__()
        self.batch_size = batch_size
        self.root = "../data/"
        transf = torchvision.transforms.Compose([torchvision.transforms.Resize(size=size),
                                                torchvision.transforms.ToTensor()])

        self.train = torchvision.datasets.FashionMNIST(root=self.root,
                                                       train=True,
                                                       transform=transf,
                                                       download=True)

        self.val = torchvision.datasets.FashionMNIST(root=self.root,
                                                     train=False,
                                                     transform=transf,
                                                     download=True)
    def get_dataloader(self, train):
        dataset = self.train if train else self.val
        return torch.utils.data.DataLoader(dataset,
                                           shuffle=train,
                                           batch_size=self.batch_size)
    def get_traindataloader(self):
        return self.get_dataloader(train=True)
    def get_valdataloader(self):
        return self.get_dataloader(train=False)

class ADAdelta_Scratch(torch.optim.Adadelta):
    def __init__(self, parameters, rho):
        super().__init__(parameters, rho)
        self.parameters = parameters
        self.rho = rho
        self.states = []
        self.eta = 10**(-6)
        self.intermidiate_states = []

        self.init_states()
    def step(self):
        for param, state, intermidiate_state in zip(self.parameters, self.states, self.intermidiate_states):
            state[:] = self.rho*state + (1-self.rho)*torch.square(param.grad)
            deltas = param.grad*torch.sqrt(intermidiate_state+ self.eta)
            squared_deltas = torch.sqrt(state+ self.eta)
            update_grad = deltas.div_(squared_deltas)
            param[:] -= update_grad
            intermidiate_state[:] = self.rho*intermidiate_state - (1-self.rho)*torch.square(update_grad)

    def zero_grad(self):
        for param in self.parameters:
            if param.grad is not None:
                param.grad.zero_()

    def init_states(self):
        for param in self.parameters:
            self.states.append(torch.zeros_like(param))
            self.intermidiate_states.append(torch.zeros_like(param))

class Timer:
    def __init__(self):
        self.times = []
    def start(self):
        self.start_time = time.time()
    def stop(self):
        self.times.append(time.time()-self.start_time)


from django.db import models
from .product import Auto_part
import datetime

class Order(models.Model):
    first_name = models.CharField(verbose_name="Ім'я:",max_length=30, blank=False)
    second_name = models.CharField(verbose_name="Прізвище:",max_length=30, blank=False)
    city = models.CharField(verbose_name="Місто:",max_length=30, blank=False)
    nova_post_number = models.CharField(verbose_name="Номер нової пошти:",max_length=30, blank=False)
    oplata_on_post = models.BooleanField(verbose_name="Оплата у відділенні:",default=True)
    date_of_order = models.DateField(verbose_name="Коли було зроблене замовлення:",default=datetime.datetime.today)
    quantity_total = models.IntegerField(verbose_name="Кількість всіх товарів:")
    price_total = models.IntegerField(verbose_name="Ціна замовлення:")
    phone_number = models.CharField(verbose_name="Номер телефону:",blank=False, max_length=15)
    product = models.TextField(verbose_name="Замовлені запчастини:",blank=False)

    def placeOrder(self):
        self.save()
class QuickOrder(models.Model):
    date_of_order = models.DateField(verbose_name="Коли було зроблене замовлення:",default=datetime.datetime.today)
    price_total = models.IntegerField(verbose_name="Ціна замовлення:")
    phone_number = models.CharField(verbose_name="Номер телефону:",blank=False, max_length=15)
    product = models.TextField(verbose_name="Замовлені запчастини:",blank=False)

    def placeQuickOrder(self):
        self.save()


#!/usr/bin/env python3

# MUD Profile Examples: https://iotanalytics.unsw.edu.au/mudprofiles

import json
import time
import nmap
import re
#from six.moves import input

#These arrays store the TCP and UDP ports that scanned
#and found open on the IOT device
nmapTCP = []
nmapUDP = []

#These arrays store the unique TCP and UDP source ports that are
#allowed to be open, according to the IoT device's MUD file
uniqueMUDtcp = []
uniqueMUDudp = []

MUDtcp = []
MUDudp = []

def jsonPorter(configx):

    #These strings are used temporarily to contain
    #all TCP and UDP substrings from the MUD file
    TCPsourceStrings = ""
    UDPsourceStrings = ""

    #creates temporary array to store the TCP and UDP
    #ports from the MUD profile as some ports are repeated
    #in the MUD profile
    # uniqueMUDTCP = []
    # uniqueMUDUDP = []

    #Takes in the JSON file from user input, formats it
    with open(configx, 'r') as MUD:
        f1 = json.load(MUD)
    MUDtext = json.dumps(f1, indent=2)

    #################### Extracting TCP ports ####################

    #Extracting TCP ports, using regex
    portSnatcher = re.compile("tcp.*\s.*\s.*\s.*: \d+")
    result = portSnatcher.findall(MUDtext)

    #Cleaning output from MUD file and
    #appending to a TCP substring for port extraction later
    for index,item in enumerate(result):
        item.split()
        if 'source-port' in item:
            TCPsourceStrings += item

    #TCP ports are extractd and appended to a temporary TCP port array
    global MUDtcp
    MUDtcp = re.findall('\d+', TCPsourceStrings)
    # tcpTemp.append(port)
    # print tcpTemp

    #################### Extracting UDP ports ####################

    #Extracting UDP ports, using regex
    portSnatcher = re.compile("udp.*\s.*\s.*\s.*: \d+")
    result = portSnatcher.findall(MUDtext)

    #Cleaning output from MUD file and
    #appending to a UDP substring for port extraction later
    for index,item in enumerate(result):
        item.split()
        if 'source-port' in item:
            UDPsourceStrings += item

    #UDP ports are extractd and appended to a temporary UDP port array
    global MUDudp
    MUDudp = re.findall('\d+', UDPsourceStrings)

    # #this function only adds the unique TCP source-ports
    # #to the global MUDTCP array which is later presented to the user
    for port in MUDtcp:
        if port not in uniqueMUDtcp:
            uniqueMUDtcp.append(port)

    uniqueMUDtcp.sort()

    # #this program only adds the unique UDP source-ports
    # #to the global MUDTCP array which is later presented to the user
    for port in MUDudp:
        if port not in uniqueMUDudp:
            uniqueMUDudp.append(port)

    uniqueMUDudp.sort()

    # all the unique TCP and UDP ports have been added
    # to allPorts and is shown to the user
    # print allPorts

    # function returns the collection of all ports
    with open('ScanResults.txt', 'a') as f:
        f.write("The MUD file requires these TCP ports to be open:\n")
        for item in uniqueMUDtcp:
            f.write(item)
            f.write("\n")
        f.write("The MUD file requires these UDP ports to be open:\n")
        for item in uniqueMUDudp:
            f.write(item)
            f.write("\n")

def nmapScan(ip, portList, flags):

    result = ""
    scannedPorts = []

    #initiating the nmap port scanner
    nm = nmap.PortScanner()

    #this function scans the port
    #that is passed in to it
    for port in portList:

        #general command for scanning
        #could be improved to be stealthy
        nm.scan(ip,str(port),flags)
        # nm.scan(hosts=ip,arguments='-Pn')

        for host in nm.all_hosts():
            #Standard scanning procedure
            for proto in nm[host].all_protocols():
                lport = nm[host][proto].keys()

                for scannedPort in lport:
                    #Presenting the ports, portocols and status
                    result += ('\n' + ('Protocol : %s\t port : %s\tstate : %s' % (proto, scannedPort, nm[host][proto][scannedPort]['state'])))

                    #Scanned ports are added to the scannedPorts array
                    scannedPorts.append(scannedPort)

                    if proto=="TCP":
                    #Scanned TCP ports are added to the nmapTCP array
                        nmapTCP.append(scannedPort)

                    if proto=="UDP":
                    #Scanned UDP ports are added to the nmapTCP array
                        nmapUDP.append(scannedPort)

    #We present the results and return the total scanned ports
    print (result)

    with open('ScanResults.txt', 'a') as f:
        f.write(result)
        f.write("\n")

def nmapFullScan(ip,flags):

    result = ""
    scannedPorts = []
    # global nmapTCP
    # global nmapUDP

    #initiating the nmap port scanner
    nm = nmap.PortScanner()

    # for port in range(1,65535):
        #general command for scanning
        #could be improved to be stealthy

    # nm.scan(ip,str('1-65535'), '-Pn')
    nm.scan(hosts=ip,arguments=flags)

    for host in nm.all_hosts():
        #Standard scanning procedure
        for proto in nm[host].all_protocols():
            lport = nm[host][proto].keys()

            #the results are solted
            # lport.sort()

            for scannedPort in lport:
                #Presenting the ports, portocols and status
                if (nm[host][proto][scannedPort]['state'] == 'open'):
                    result += ('\n' + ('Protocol : %s\t port : %s\tstate : %s' % (proto, scannedPort, nm[host][proto][scannedPort]['state'])))

                #Scanned ports are added to the scannedPorts array
                scannedPorts.append(scannedPort)

                if proto=="tcp":
                #Scanned TCP ports are added to the nmapTCP array
                    print ("Under TCP")
                    print (scannedPort)

                    nmapTCP.append(scannedPort)

                if proto=="udp":
                #Scanned UDP ports are added to the nmapTCP array
                    print ("Under UDP")
                    print (scannedPort)

                    nmapUDP.append(scannedPort)

    nmapTCP.sort()
    nmapUDP.sort()
    print ("After Sorting in full scan")
    print (nmapTCP)
    print (nmapUDP)

    #We present the results and return the total scanned ports
    print (result)

    with open('ScanResults.txt', 'a') as f:
        # f.write("\nScan results (open ports) for all ports:")
        f.write(result)

def masterScanner(inputtedIP, inputtedMUD):

    ip = inputtedIP
    MUDfile = inputtedMUD

    with open('ScanResults.txt', 'a') as f:
        f.write("- - - - - - - - - - - - - - - - - - - - \n")
        f.write("Current scanning: " + ip + "\n")

    #We present the user with which ports are mentioned in the MUD file
    jsonPorter(MUDfile)

    with open('ScanResults.txt', 'a') as f:
        f.write("\nScanning TCP ports mentioned in the MUD file:")
    nmapScan(ip,uniqueMUDtcp,'-Pn -f')
    #nmapScan(ip,uniqueMUDtcp,'-Pn')

    with open('ScanResults.txt', 'a') as f:
        f.write("\nScanning UDP ports mentioned in the MUD file:")
    nmapScan(ip,uniqueMUDudp, '-Pn -sU')
    #nmapScan(ip,uniqueMUDudp, '-Pn')

    with open('ScanResults.txt', 'a') as f:
        f.write("\nPerforming complete scan of IP, only open ports are listed:")
    nmapFullScan(ip, '-Pn -f')
    nmapFullScan(ip, '-Pn -sU')

    print ("Before else ifs")
    print (nmapTCP)
    print (nmapUDP)

    with open('ScanResults.txt', 'a') as f:

        if uniqueMUDtcp == nmapTCP:
            f.write("\nTCP Ports are compliant, no extra open ports found outside MUD instructions.\n")
        elif len(nmapTCP) > len(uniqueMUDtcp):
            f.write("\nMore TCP ports are open than needed. TCP Ports are non-compliant.\n")
        else:
            f.write("\nMUD recommends having more TCP ports open, than on IoT currently. Device is over-protected.\n")

        if uniqueMUDudp == nmapUDP:
            f.write("UDP Ports are compliant, no extra open ports found outside MUD instructions.\n")
        elif len(nmapUDP) > len(uniqueMUDudp):
            f.write("\nMore UDP ports are open on IoT than needed. UDP Ports are non-compliant.\n")
        else:
            f.write("\nMUD recommends having more UDP ports open, than on IoT currently. Device is over-protected.\n")

#Asking the user for how many IP addresses to scan.
#DO NOT REMOVE, this breaks the loop on the 'for' loop.
print ("How many IP addresses (1 or more) do you wish to scan: ")
numberofIPs = int(input())
print ("What is the name of the json MUD file (include the .json extension): ")
MUDfile = input()
print ("\nPlease note, this program determines if the NUMBER of ports on your IoT matches the number of TCP/UDP ports in the MUD.")
print ("\nThis terminal will display content for troubleshooting only. The final esults of this scan will be inside 'ScanResults.txt' in the same directory as this file.")

with open('IP_Addresses.txt','r+') as f:
    ip = f.read().splitlines()
    for i in range(0,numberofIPs):
        masterScanner(ip[i], MUDfile)


from assembla.utils import parse_datetime, parse_date, parse_file
from assembla.cursor import Cursor

from utils import import_simplejson, DateTimeJSONEncoder

json = import_simplejson()

class ResultSet(list):
    """A list like object that holds results from a Twitter API query."""

class Model(object):

    def __init__(self, api=None):
        self._api = api

    def __getstate__(self):
        # pickle
        pickle = dict(self.__dict__)
        try:
            del pickle['_api']  # do not pickle the API reference
        except KeyError:
            pass
        return pickle

    def toJSON(self):
        return json.dumps(self)

    @classmethod
    def parse(cls, api, json):
        """Parse a JSON object into a model instance."""
        raise NotImplementedError

    @classmethod
    def parse_list(cls, api, json_list):
        """Parse a list of JSON objects into a result set of model instances."""
        results = ResultSet()
        for obj in json_list:
            if obj:
                results.append(cls.parse(api, obj))
        return results

    def __str__(self):
        """Return object id for easier serialization"""
        return str(self.id)

    def __repr__(self):
        return '<%s: %s>' % (self.__class__.__name__, self.__str__())

class Space(Model):

    @classmethod
    def parse(cls, api, json):
        space = cls(api)
        for k, v in json.items():
            if (k == 'created_at' or k == 'updated_at' or k == 'commercial_from') and v:
                setattr(space, k, parse_datetime(v))
            elif (k == 'restricted_date' or k == 'last_payer_changed_at') and v:
                setattr(space, k, parse_date(v))
            elif k == 'tabs_order':
                pass # for now
            #elif k == 'place':
                #if v is not None:
                    #setattr(space, k, Place.parse(api, v))
                #else:
                    #setattr(space, k, None)
            else:
                setattr(space, k, v)
        return space

    """
            if k == 'user':
                user_model = getattr(api.parser.model_factory, 'user')
                user = user_model.parse(api, v)
                setattr(space, 'author', user)
                setattr(space, 'user', user)  # DEPRECIATED
    """

    def destroy(self):
        return self._api.delete_space(space=self.id)

    def get_milestone(self, milestone_id):
        return self._api.get_milestone(space=self.id, milestone=milestone_id)

    def get_milestones(self):
        return list(Cursor(self._api.get_milestones, space=self.id).items())

    def create_milestone(self, milestone):
        return self._api.create_milestone(milestone, space=self.id)

    def get_components(self):
        return self._api.get_ticket_components(space=self.id)

    def create_component(self, component):
        return self._api.create_ticket_component(component, space=self.id)

    def get_custom_fields(self):
        return self._api.get_custom_fields(space=self.id)

    def create_custom_field(self, field):
        return self._api.create_custom_field(field, space=self.id)

    def get_ticket_statuses(self):
        return self._api.get_ticket_statuses(space=self.id)

    def create_ticket_status(self, status):
        return self._api.create_ticket_status(status, space=self.id)

    def get_tickets(self):
        return list(Cursor(self._api.get_tickets, space=self.id).items())

    def get_ticket(self, number):
        return self._api.get_ticket(space=self.id, ticket=number)

    def create_ticket(self, ticket):
        return self._api.create_ticket(ticket, space=self.id)

    def delete_ticket(self, ticket):
        return self._api.delete_ticket(ticket=ticket, space=self.id)

class InstantMessenger(Model):

    @classmethod
    def parse(cls, api, json):
        im = cls(api)
        for k, v in json.items():
            setattr(im, k, v)
        return im

class User(Model):

    @classmethod
    def parse(cls, api, json):
        user = cls(api)
        for k, v in json.items():
            if k == 'im' or k == 'im2':
                setattr(user, k, InstantMessenger.parse(api, v))
            else:
                setattr(user, k, v)
        return user

    @classmethod
    def parse_list(cls, api, json_list):
        if isinstance(json_list, list):
            item_list = json_list
        else:
            item_list = json_list['users']

        results = ResultSet()
        for obj in item_list:
            results.append(cls.parse(api, obj))
        return results

class Ticket(Model):

    @classmethod
    def parse(cls, api, json):
        ticket = cls(api)
        for k, v in json.items():
            if (k == 'created_on' or k == 'updated_at' or k == 'completed_date') and v:
                setattr(ticket, k, parse_datetime(v))
            else:
                setattr(ticket, k, v)
        return ticket

    @classmethod
    def parse_list(cls, api, json_list):
        if isinstance(json_list, list):
            item_list = json_list
        else:
            item_list = json_list['tickets'] # XXX check

        results = ResultSet()
        for obj in item_list:
            results.append(cls.parse(api, obj))
        return results

    def toJSON(self):
        pickle = self.__getstate__()
        pickle = {'ticket': pickle}
        return json.dumps(pickle, cls=DateTimeJSONEncoder)

    def __str__(self):
        """Return ticket number for easier serialization"""
        return str(self.number)

    def get_associations(self):
        return self._api.get_associations(space=self.space_id, ticket=self.number)

    def create_association(self, association):
        return self._api.create_association(association, space=self.space_id, ticket=self.number)

    def get_comments(self):
        return list(Cursor(self._api.get_ticket_comments, space=self.space_id, ticket=self.number).items())

    def create_comment(self, comment):
        return self._api.create_ticket_comment(comment, space=self.space_id, ticket=self.number)

    def attach_file(self, filecontent, docmeta):
        docmeta.attachable_id = self.id
        return self._api.create_document(filecontent, docmeta, space=self.space_id)

    def destroy(self):
        return self._api.delete_ticket(space=self.space_id, ticket=self.number)

    def get_document(self, fileid):
        return self._api.get_document(space=self.space_id, document=fileid)

    def get_milestone(self):
        return self._api.get_milestone(space=self.space_id, milestone=self.milestone_id)

class TicketComment(Model):

    @classmethod
    def parse(cls, api, json):
        comment = cls(api)
        comment.file = None
        comment.comment = None
        for k, v in json.items():
            if (k == 'created_on' or k == 'updated_at') and v:
                v = parse_datetime(v)
            elif (k == 'comment') and v:
                if '[[file' in v or '[[image' in v:
                    comment.file = parse_file(v)
            setattr(comment, k, v)
        return comment

    def toJSON(self):
        pickle = self.__getstate__()
        pickle = {'ticket_comment': pickle}
        return json.dumps(pickle, cls=DateTimeJSONEncoder)

class TicketCustomField(Model):

    @classmethod
    def parse(cls, api, json):
        customfield = cls(api)
        for k, v in json.items():
            if (k == 'created_on' or k == 'updated_at') and v:
                setattr(customfield, k, parse_datetime(v))
            else:
                setattr(customfield, k, v)
        return customfield

    def toJSON(self):
        pickle = self.__getstate__()
        if pickle['list_options']:
            pickle['list_options'] = ','.join(pickle['list_options'])
        pickle = {'custom_field': pickle}
        return json.dumps(pickle, cls=DateTimeJSONEncoder)

class TicketComponent(Model):

    def __init__(self, name, api=None):
        super(TicketComponent, self).__init__(api = api)
        self.name = name

    @classmethod
    def parse(cls, api, json):
        component = cls(api)
        for k, v in json.items():
            setattr(component, k, v)
        return component

    def toJSON(self):
        pickle = {'component': self.name}
        return json.dumps(pickle)

    def __str__(self):
        return self.name

class TicketAssociation(Model):

    @classmethod
    def parse(cls, api, json):
        association = cls(api)
        for k, v in json.items():
            if (k == 'created_at' or k == 'updated_at') and v:
                setattr(association, k, parse_datetime(v))
            else:
                setattr(association, k, v)
        return association

    def toJSON(self):
        pickle = self.__getstate__()
        pickle = {'ticket_association': pickle}
        return json.dumps(pickle, cls=DateTimeJSONEncoder)

    def invert(self):
        tmp = self.ticket1_id
        self.ticket2_id = self.ticket1_id
        self.ticket1_id = tmp
        if self.relationship == 0:
            self.relationship = 1
        elif self.relationship == 1:
            self.relationship = 0
        elif self.relationship == 7:
            self.relationship = 8
        elif self.relationship == 8:
            self.relationship = 7

class TicketStatus(Model):

    @classmethod
    def parse(cls, api, json):
        status = cls(api)
        for k, v in json.items():
            if (k == 'created_at' or k == 'updated_at') and v:
                setattr(status, k, parse_datetime(v))
            else:
                setattr(status, k, v)
        return status

    def toJSON(self):
        pickle = self.__getstate__()
        pickle = {'status': pickle}
        return json.dumps(pickle, cls=DateTimeJSONEncoder)

class Milestone(Model):

    @classmethod
    def parse(cls, api, json):
        milestone = cls(api)
        for k, v in json.items():
            if (k == 'created_at' or k == 'updated_at') and v:
                setattr(milestone, k, parse_datetime(v))
            if (k == 'completed_date' or k == 'due_date') and v:
                setattr(milestone, k, parse_date(v))
            else:
                setattr(milestone, k, v)
        return milestone

    def toJSON(self):
        pickle = self.__getstate__()
        pickle = {'milestone': pickle}
        return json.dumps(pickle, cls=DateTimeJSONEncoder)

class Document(Model):

    @classmethod
    def parse(cls, api, json):
        document = cls(api)
        for k, v in json.items():
            if (k == 'created_at' or k == 'updated_at') and v:
                setattr(document, k, parse_datetime(v))
            else:
                setattr(document, k, v)
        return document

    def toJSON(self):
        pickle = self.__getstate__()
        pickle = {'document': pickle}
        return json.dumps(pickle, cls=DateTimeJSONEncoder)

class MergeRequest(Model):

    @classmethod
    def parse(cls, api, json):
        mergerequest = cls(api)
        for k, v in json.items():
            if k in ['applied_at', 'created_at', 'updated_at'] and v:
                setattr(mergerequest, k, parse_datetime(v))
            else:
                setattr(mergerequest, k, v)
        return mergerequest

    def toJSON(self):
        pickle = self.__getstate__()
        pickle = {'merge_request': pickle}
        return json.dumps(pickle, cls=DateTimeJSONEncoder)

class JSONModel(Model):

    @classmethod
    def parse(cls, api, json):
        return json

class IDModel(Model):

    @classmethod
    def parse(cls, api, json):
        if isinstance(json, list):
            return json
        else:
            return json['ids']

class ModelFactory(object):
    """
    Used by parsers for creating instances
    of models. You may subclass this factory
    to add your own extended models.
    """

    space = Space
    user = User

    ticket = Ticket
    ticketcomment = TicketComment
    ticketcustomfield = TicketCustomField
    ticketcomponent = TicketComponent
    ticketassociation = TicketAssociation
    ticketstatus = TicketStatus

    document = Document
    milestone = Milestone
    mergerequest = MergeRequest

    json = JSONModel
    ids = IDModel