# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import operator
import json
from collections import defaultdict, OrderedDict
from datetime import datetime

from django.http import Http404, HttpResponse
from django.shortcuts import \
    get_object_or_404, redirect, render, render_to_response
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required, user_passes_test
from django.utils.decorators import method_decorator
from django.utils import timezone
from django.views.generic import ListView, DetailView
from django.db import transaction
from django.db.models import Q, Count

from .fdsn.fdsn_manager import \
    FdsnMotionManager, FdsnShakemapManager, \
    FdsnWaveformManager, FdsnDataselectManager, \
    OdcApiManager

from .models import \
    SearchEvent, SearchPeakMotions, \
    SearchCombined, SearchCustom
from .forms import \
    SearchEventsForm, SearchPeakMotionsForm, \
    SearchCombinedForm, SearchCustomForm

from .logger import RrsmLoggerMixin

class HomeListView(ListView):
    model = None
    context_object_name = 'events'
    template_name = 'events.html'

    def get_queryset(self):
        return None

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        motion_data, ws_url = FdsnMotionManager().get_event_list(days_back=31)
        context['breadcrumb'] = 'Home (events from last 31 days)'
        context['motion_data'] = motion_data
        context['ws_url'] = ws_url
        context['is_homepage'] = True
        return context

class AboutListView(ListView):
    model = None
    template_name = 'about.html'

    def get_queryset(self):
        return None

class RecentEventsListView(ListView):
    model = None
    context_object_name = 'events'
    template_name = 'events.html'

    def get_queryset(self):
        return None

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        motion_data, ws_url = FdsnMotionManager().get_event_list(
            days_back=int(self.kwargs.get('days'))
            )
        context['breadcrumb'] = 'Recent events'
        context['motion_data'] = motion_data
        context['ws_url'] = ws_url
        context['is_homepage'] = True
        return context

class EventDetailsListView(ListView, RrsmLoggerMixin):
    model = None
    context_object_name = None
    template_name = 'event_details.html'

    def __init__(self):
        RrsmLoggerMixin.__init__(self)

    def get_queryset(self):
        try:
            queryset = None
        except:
            raise
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        event_id = self.kwargs.get('event_public_id')
        fdsn_motion_man = FdsnMotionManager()
        fdsn_shakemap_man = FdsnShakemapManager()
        fdsn_waveform_man = FdsnWaveformManager()

        motion_data, ws_url = fdsn_motion_man.get_event_details(event_id)
        chart_pga, chart_pgv = self.get_sensor_channels_charts(motion_data)
        shakemap_url = fdsn_shakemap_man.get_shakemap_url(event_id)
        waveform_url = fdsn_waveform_man.get_waveform_url(event_id)

        context['motion_data'] = motion_data
        context['ws_url'] = ws_url
        context['chart_pga'] = chart_pga
        context['chart_pgv'] = chart_pgv
        context['shakemap_url'] = shakemap_url
        context['waveform_url'] = waveform_url
        return context

    def get_sensor_channels_charts(self, motion_data):
        try:
            if not motion_data:
                return None, None

            data_pga = defaultdict(dict)
            data_pgv = defaultdict(dict)
            chart_data_pga = {}
            chart_data_pgv = {}

            for s in motion_data.stations:
                for sc in s.sensor_channels:
                    data_pga[sc.channel_code][s.epicentral_distance] = \
                        sc.pga_value * 100
                    data_pgv[sc.channel_code][s.epicentral_distance] = \
                        sc.pgv_value * 100

            for key in data_pga:
                _tmp = sorted(data_pga[key].items())
                chart_data_pga[key] = _tmp

            for key in data_pgv:
                _tmp = sorted(data_pgv[key].items())
                chart_data_pgv[key] = _tmp

            chart_pga = {
                'exporting': {
                    'chartOptions': {
                        'plotOptions': {
                            'series': {
                            'dataLabels': {
                                'enabled': 'true'
                            }
                        }
                    }
                },
                'fallbackToExportServer': 'false'
            },
                'chart': {'type': 'scatter'},
                'title': {'text': 'PGA vs Epicentral distance'},
                'xAxis': {
                    'title': {
                        'text': 'Epicentral distance [km]'
                    },
                    'type': 'logarithmic',
                },
                'yAxis': {
                    'title': {
                        'text': 'PGA [cm/s^2]'
                    },
                    'type': 'logarithmic'
                },
                'series': []
            }

            chart_pgv = {
                'exporting': {
                    'chartOptions': {
                        'plotOptions': {
                            'series': {
                            'dataLabels': {
                                'enabled': 'true'
                            }
                        }
                    }
                },
                'fallbackToExportServer': 'false'
            },
                'chart': {'type': 'scatter'},
                'title': {'text': 'PGV vs Epicentral distance'},
                'xAxis': {
                    'title': {
                        'text': 'Epicentral distance [km]'
                    },
                    'type': 'logarithmic'
                },
                'yAxis': {
                    'title': {
                        'text': 'PGV [cm/s]'
                    },
                    'type': 'logarithmic'
                },
                'series': []
            }

            for t in chart_data_pga:
                chart_pga['series'].append({
                    'name': t,
                    'data': chart_data_pga[t]
                })

            for t in chart_data_pgv:
                chart_pgv['series'].append({
                    'name': t,
                    'data': chart_data_pgv[t]
                })

            dump_pga = json.dumps(chart_pga)
            dump_pgv = json.dumps(chart_pgv)
            return dump_pga, dump_pgv
        except:
            self.log_exception()
            return None, None

class StationStreamsListView(ListView, RrsmLoggerMixin):
    model = None
    context_object_name = None
    template_name = 'station_streams.html'

    def __init__(self):
        RrsmLoggerMixin.__init__(self)

    def get_queryset(self):
        try:
            queryset = None
        except:
            raise
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        event_id = self.kwargs.get('event_public_id')
        network_code = self.kwargs.get('network_code')
        station_code = self.kwargs.get('station_code')
        fdsn_motion_man = FdsnMotionManager()

        dataselect_url = FdsnDataselectManager(
            'NL', 'DBN', '2018-11-18T12:00:00'
        ).get_dataselect_url()

        motion_data, ws_url = fdsn_motion_man.get_event_details(
            event_id, network_code, station_code, True
        )
        chart_psa, chart_sd = self.get_spectra_charts(motion_data.stations[0])

        if motion_data:
            context['station_data'] = motion_data.stations[0]
        else:
            context['motion_data'] = None

        # waveform_pic = self.get_waveform_pic(motion_data)
        wafeform_picture = self.get_wafeform_picture(motion_data)

        context['chart_psa'] = chart_psa
        context['chart_sd'] = chart_sd
        # context['waveform_pic'] = waveform_pic
        context['ws_url'] = ws_url
        context['dataselect_url'] = dataselect_url
        context['wafeform_picture'] = wafeform_picture

        return context

    def get_spectra_charts(self, station_data):
        try:
            if not station_data:
                return None, None

            data_psa = defaultdict(dict)
            data_sd = defaultdict(dict)
            chart_data_psa = {}
            chart_data_sd = {}

            for sc in station_data.sensor_channels:
                for sa_psa in filter(
                    lambda x: x.type.lower() == 'psa', sc.spectral_amplitudes
                ):
                    data_psa[sc.channel_code][sa_psa.period] = sa_psa.amplitude
                for sa_sd in filter(
                    lambda x: x.type.lower() == 'sd', sc.spectral_amplitudes
                ):
                    data_sd[sc.channel_code][sa_sd.period] = sa_sd.amplitude

            for key in data_psa:
                _tmp = sorted(data_psa[key].items())
                chart_data_psa[key] = _tmp

            for key in data_sd:
                _tmp = sorted(data_sd[key].items())
                chart_data_sd[key] = _tmp

            chart_psa = {
                'exporting': {
                    'chartOptions': {
                        'plotOptions': {
                            'series': {
                            'dataLabels': {
                                'enabled': 'true'
                            }
                        },
                    }
                },
                'fallbackToExportServer': 'false'
            },
                'chart': {'type': 'scatter'},
                'plotOptions':{
                    'scatter':{
                        'lineWidth': '1'
                    }
                },
                'title': {'text': 'Pseudo-Spectral Acceleration'},
                'xAxis': {
                    'title': {
                        'text': 'Time [s]'
                    },
                    'type': 'logarithmic',
                },
                'yAxis': {
                    'title': {
                        'text': 'PSA [cm/s*s]'
                    },
                    'type': 'logarithmic'
                },
                'series': []
            }

            chart_sd = {
                'exporting': {
                    'chartOptions': {
                        'plotOptions': {
                            'series': {
                            'dataLabels': {
                                'enabled': 'true'
                            }
                        }
                    }
                },
                'fallbackToExportServer': 'false'
            },
                'chart': {'type': 'scatter'},
                'plotOptions':{
                    'scatter':{
                        'lineWidth': '1'
                    }
                },
                'title': {'text': 'Spectral Displacement Data'},
                'xAxis': {
                    'title': {
                        'text': 'Time [s]'
                    },
                    'type': 'logarithmic'
                },
                'yAxis': {
                    'title': {
                        'text': 'SD [cm]'
                    },
                    'type': 'logarithmic'
                },
                'series': []
            }

            for t in chart_data_psa:
                chart_psa['series'].append({
                    'name': t,
                    'data': chart_data_psa[t]
                })

            for t in chart_data_sd:
                chart_sd['series'].append({
                    'name': t,
                    'data': chart_data_sd[t]
                })

            dump_psa = json.dumps(chart_psa)
            dump_sd = json.dumps(chart_sd)
            return dump_psa, dump_sd
        except:
            self.log_exception()
            return None, None

    def get_wafeform_picture(self, station_data):
        try:
            data_wf = {}
            oam = OdcApiManager(station_data.stations[0])
            data = oam.get_waveform_data()

            if not data:
                return None

            for trace in data['payload'][0]['traces']:
                _tmp = []
                for e in trace['data']:
                    if not e[1]:
                        continue
                    # Mo need to convert the UNIX timestamp
                    # dt = datetime.utcfromtimestamp(int(e[0])/1000)
                    _tmp.append([e[0], e[1]])
                data_wf[trace['name']] = _tmp

            chart = {
                'exporting': {
                    'chartOptions': {
                        'plotOptions': {
                            'series': {
                            'dataLabels': {
                                'enabled': 'true'
                            }
                        }
                    }
                },
                'fallbackToExportServer': 'false'
            },
                'chart': {'type': 'spline'},
                'title': {'text': 'Waveform'},
                'plotOptions': {
                    'series': {
                        'lineWidth': 1
                    }
                },
                'xAxis': {
                    'type': 'datetime',
                    'labels': {
                        'format': '{value:%H:%M:%S}'
                    }
                },
                'series': []
            }

            for t in data_wf:
                chart['series'].append({
                    'name': t,
                    'data': data_wf[t]
                })

            dump = json.dumps(chart)
            return dump
        except:
            raise

def search_events(request):
    if request.method == 'POST':
        form = SearchEventsForm(request.POST)
        if form.is_valid():
            data, ws_url = FdsnMotionManager().get_event_list(
                event_id=form.cleaned_data['event_id'],
                date_start=form.cleaned_data['date_start'],
                date_end=form.cleaned_data['date_end'],
                magnitude_min=form.cleaned_data['magnitude_min'],
                network_code=form.cleaned_data['network_code'],
                station_code=form.cleaned_data['station_code'],
                event_lat_min=form.cleaned_data['event_lat_min'],
                event_lat_max=form.cleaned_data['event_lat_max'],
                event_lon_min=form.cleaned_data['event_lon_min'],
                event_lon_max=form.cleaned_data['event_lon_max']
            )

            return render(
                request,
                'events.html',
                {
                    'motion_data': data,
                    'breadcrumb': 'Search result (events)',
                    'ws_url': ws_url,
                    'is_homepage': True
                }
            )
        else:
            return render(
                request,
                'search.html',
                {
                    'form': form,
                    'show_info': True
                }
            )
    else:
        form = SearchEventsForm(instance=SearchEvent())
        return render(
            request,
            'search.html',
            {
                'form': form,
                'show_info': True
            }
        )

def search_peak_motions(request):
    if request.method == 'POST':
        form = SearchPeakMotionsForm(request.POST)
        if form.is_valid():
            data, ws_url = FdsnMotionManager().get_event_list(
                pga_min=form.cleaned_data['pga_min'],
                pga_max=form.cleaned_data['pga_max'],
                pgv_min=form.cleaned_data['pgv_min'],
                pgv_max=form.cleaned_data['pgv_max']
            )

            return render(
                request,
                'events.html',
                {
                    'motion_data': data,
                    'breadcrumb': 'Search result (peak motions)',
                    'ws_url': ws_url,
                    'is_homepage': True
                }
            )
        else:
            return render(
                request,
                'search.html',
                {
                    'form': form
                }
            )
    else:
        form = SearchPeakMotionsForm(instance=SearchPeakMotions())
        return render(
            request,
            'search.html',
            {
                'form': form
            }
        )

def search_combined(request):
    if request.method == 'POST':
        form = SearchCombinedForm(request.POST)
        if form.is_valid():
            data, ws_url = FdsnMotionManager().get_event_list(
                magnitude_min=form.cleaned_data['magnitude_min'],
                pga_min=form.cleaned_data['pga_min'],
                pga_max=form.cleaned_data['pga_max'],
                pgv_min=form.cleaned_data['pgv_min'],
                pgv_max=form.cleaned_data['pgv_max'],
                stat_lat_min=form.cleaned_data['stat_lat_min'],
                stat_lat_max=form.cleaned_data['stat_lat_max'],
                stat_lon_min=form.cleaned_data['stat_lon_min'],
                stat_lon_max=form.cleaned_data['stat_lon_max']
            )

            return render(
                request,
                'events.html',
                {
                    'motion_data': data,
                    'breadcrumb': 'Search result (combined)',
                    'ws_url': ws_url,
                    'is_homepage': True
                }
            )
        else:
            return render(
                request,
                'search.html',
                {
                    'form': form,
                    'show_info': True
                }
            )
    else:
        form = SearchCombinedForm(instance=SearchCombined())
        return render(
            request,
            'search.html',
            {
                'form': form,
                'show_info': True
            }
        )

def search_custom(request):
    if request.method == 'POST':
        form = SearchCustomForm(request.POST)
        if form.is_valid():
            data, ws_url = FdsnMotionManager().get_event_list(
                event_id=form.cleaned_data['event_id'],
                date_start=form.cleaned_data['date_start'],
                date_end=form.cleaned_data['date_end'],
                magnitude_min=form.cleaned_data['magnitude_min'],
                network_code=form.cleaned_data['network_code'],
                station_code=form.cleaned_data['station_code'],
                pga_min=form.cleaned_data['pga_min'],
                pga_max=form.cleaned_data['pga_max'],
                pgv_min=form.cleaned_data['pgv_min'],
                pgv_max=form.cleaned_data['pgv_max'],
                stat_lat_min=form.cleaned_data['stat_lat_min'],
                stat_lat_max=form.cleaned_data['stat_lat_max'],
                stat_lon_min=form.cleaned_data['stat_lon_min'],
                stat_lon_max=form.cleaned_data['stat_lon_max'],
                event_lat_min=form.cleaned_data['event_lat_min'],
                event_lat_max=form.cleaned_data['event_lat_max'],
                event_lon_min=form.cleaned_data['event_lon_min'],
                event_lon_max=form.cleaned_data['event_lon_max']
            )

            return render(
                request,
                'events.html',
                {
                    'motion_data': data,
                    'breadcrumb': 'Search result (custom)',
                    'ws_url': ws_url,
                    'is_homepage': True
                }
            )
        else:
            return render(
                request,
                'search_custom.html',
                {
                    'form': form
                }
            )
    else:
        form = SearchCustomForm(instance=SearchCustom())
        return render(
            request,
            'search_custom.html',
            {
                'form': form
            }
        )

def download_waveforms(request):
    if request.method == 'POST':
        urls = request.POST.get('urls')
        return HttpResponse(
            urls,
            content_type="text/plain"
        )
    else:
        return None

def custom_404(request, exception=None):
    '''HTTP 404 custom handler
    '''
    return render_to_response('404.html', {'exception': exception})

def custom_500(request, exception=None):
    '''HTTP 500 custom handler
    '''
    return render_to_response('500.html', {'exception': exception})


# -*- coding: utf-8 -*-

"""Views to run the personalized canvas email action."""

from datetime import datetime, timedelta
from typing import Optional

import pytz
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import user_passes_test
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils.translation import ugettext, ugettext_lazy as _

from ontask.action.forms import CanvasEmailActionForm
from ontask.action.payloads import (
    CanvasEmailPayload, get_or_set_action_info, set_action_payload,
)
from ontask.action.send import send_canvas_emails
from ontask.core.decorators import get_workflow
from ontask.core.permissions import is_instructor
from ontask.models import Action, Log, OAuthUserToken, Workflow
from ontask.oauth.views import get_initial_token_step1, refresh_token
from ontask.tasks import run_task

def run_canvas_email_action(
    req: WSGIRequest,
    workflow: Workflow,
    action: Action,
) -> HttpResponse:
    """Request data to send JSON objects.

    Form asking for subject, item column (contains ids to select unique users),
    confirm items (add extra step to drop items), export workflow and
    target_rul (if needed).

    :param req: HTTP request (GET)
    :param workflow: workflow being processed
    :param action: Action begin run
    :return: HTTP response
    """
    # Get the payload from the session, and if not, use the given one
    action_info = get_or_set_action_info(
        req.session,
        CanvasEmailPayload,
        initial_values={
            'action_id': action.id,
            'prev_url': reverse('action:run', kwargs={'pk': action.id}),
            'post_url': reverse('action:canvas_email_done'),
        },
    )

    # Create the form to ask for the email subject and other information
    form = CanvasEmailActionForm(
        req.POST or None,
        column_names=[
            col.name for col in workflow.columns.filter(is_key=True)],
        action=action,
        form_info=action_info)

    if req.method == 'POST' and form.is_valid():
        # Request is a POST and is valid

        if action_info['confirm_items']:
            # Create a dictionary in the session to carry over all the
            # information to execute the next pages
            action_info['button_label'] = ugettext('Send')
            action_info['valuerange'] = 2
            action_info['step'] = 2
            set_action_payload(req.session, action_info.get_store())
            continue_url = 'action:item_filter'
        else:
            continue_url = 'action:canvas_email_done'

        # Check for the CANVAS token and proceed to the continue_url
        return canvas_get_or_set_oauth_token(
            req,
            action_info['target_url'],
            continue_url)

    # Render the form
    return render(
        req,
        'action/request_canvas_email_data.html',
        {'action': action,
         'num_msgs': action.get_rows_selected(),
         'form': form,
         'valuerange': range(2),
         'rows_all_false': action.get_row_all_false_count()})

@user_passes_test(is_instructor)
def canvas_get_or_set_oauth_token(
    request: WSGIRequest,
    oauth_instance_name: str,
    continue_url: str,
) -> HttpResponse:
    """Check for OAuth token, if not present, request a new one.

    Function that checks if the user has a Canvas OAuth token. If there is a
    token, the function goes straight to send the messages. If not, the OAuth
    process starts.

    :param request: Request object to process

    :param oauth_instance_name: Locator for the OAuth instance in OnTask

    :param continue_url: URL to continue if the token exists and is valid

    :return: Http response
    """
    # Get the information from the payload
    oauth_info = settings.CANVAS_INFO_DICT.get(oauth_instance_name)
    if not oauth_info:
        messages.error(
            request,
            _('Unable to obtain Canvas OAuth information'),
        )
        return redirect('action:index')

    # Check if we have the token
    token = OAuthUserToken.objects.filter(
        user=request.user,
        instance_name=oauth_instance_name,
    ).first()
    if not token:
        # There is no token, authentication has to take place for the first
        # time
        return get_initial_token_step1(
            request,
            oauth_info,
            reverse(continue_url))

    # Check if the token is valid
    now = datetime.now(pytz.timezone(settings.TIME_ZONE))
    dead = now > token.valid_until - timedelta(
        seconds=settings.CANVAS_TOKEN_EXPIRY_SLACK)
    if dead:
        try:
            refresh_token(token, oauth_info)
        except Exception as exc:
            # Something went wrong when refreshing the token
            messages.error(
                request,
                _('Error when invoking Canvas API: {0}.'.format(str(exc))),
            )
            return redirect('action:index')

    return redirect(continue_url)

@user_passes_test(is_instructor)
@get_workflow(pf_related='actions')
def run_canvas_email_done(
    request: WSGIRequest,
    action_info: Optional[CanvasEmailPayload] = None,
    workflow: Optional[Workflow] = None,
) -> HttpResponse:
    """Create the log object, queue the operation request and render done.

    :param request: HTTP request (GET)
    :param action_info: Dictionary containing all the required parameters. If
    empty, the dictionary is taken from the session.
    :return: HTTP response
    """
    # Get the payload from the session if not given
    action_info = get_or_set_action_info(
        request.session,
        CanvasEmailPayload,
        action_info=action_info)
    if action_info is None:
        # Something is wrong with this execution. Return to action table.
        messages.error(
            request,
            _('Incorrect canvas email action invocation.'))
        return redirect('action:index')

    # Get the information from the payload
    action = workflow.actions.filter(pk=action_info['action_id']).first()
    if not action:
        return redirect('home')

    # Log the event
    log_item = Log.objects.register(
        request.user,
        Log.SCHEDULE_CANVAS_EMAIL_EXECUTE,
        action.workflow,
        {
            'action': action.name,
            'action_id': action.id,
            'from_email': request.user.email,
            'subject': action_info['subject'],
            'exclude_values': action_info['exclude_values'],
            'item_column': action_info['item_column'],
            'target_url': action_info['target_url'],
            'status': 'Preparing to execute',
        })

    # Update the last_execution_log
    action.last_executed_log = log_item
    action.save()

    # Send the emails!
    run_task.delay(request.user.id, log_item.id, action_info.get_store())

    # Reset object to carry action info throughout dialogs
    set_action_payload(request.session)
    request.session.save()

    # Successful processing.
    return render(
        request,
        'action/action_done.html',
        {'log_id': log_item.id, 'download': action_info['export_wf']})


from cirilib.imports import *

try:
    import tabula
except ImportError:
    print("Please install tabula for python and try again\n",
          "tabula-py==2.1.0")

def export_from_pdf(file_name):
    file_path = os.path.join(PDF_DIR, file_name)
    print("Extracting table from", file_name, "...")
    df = tabula.read_pdf(file_path, pages='all', pandas_options={'header': None})[1:]

    table_begin_index = 1

    for i in range(len(df)):
        if any(word in str(df[i][0][0]) for word in TABLE_START_WORD):
            table_begin_index = i

    if len(df) > 1:
        df = df[0].append(df[table_begin_index:], ignore_index=True)
    print(file_name, "extracted!")
    return df

# convert to csv, maybe use it?
# tabula.convert_into(file_path, "output.csv", output_format="csv", pages='all')

# file = "20200401-sitrep-72-covid-19.pdf"

files = os.listdir(PDF_DIR)

if __name__ == '__main__':
    reports = []
    reports.append(export_from_pdf(files[0]))
    print(reports)


import concurrent.futures as cf

def calc(data):
	standing, res = 0, 0
	for i, d in enumerate(data):
		if d == '0':
			continue
		if standing < i:
			new = i - standing
			standing += new
			res += new
		standing += int(d)
	return res

def main():
	T = int(input())
	results = []
	with cf.ProcessPoolExecutor(max_workers=8) as executor:
		for _ in range(T):
			l, data = input().split()
			assert len(data) == int(l) + 1
			results.append(executor.submit(calc, data))
	for cs, result in enumerate(results):
		print('Case #{}: {}'.format(cs + 1, result.result()))

if __name__ == '__main__':
	main()


from aiogram import Dispatcher
from aiogram.dispatcher.filters import BaseFilter
from aiogram.types import InlineQuery

from settings import DEFAULT_LOCALE

class TranslatedInlineQueryTextFilter(BaseFilter):
    inline_query_text: str

    async def __call__(self, inline_query: InlineQuery, _) -> dict[str, str]:
        query = _(inline_query.query, locale=DEFAULT_LOCALE)
        inline_query_text = _(self.inline_query_text, locale=DEFAULT_LOCALE)
        query_args = query.replace(inline_query_text, '')
        if inline_query_text in query:
            return {'query_args': query_args}

def setup(dispatcher: Dispatcher):
    dispatcher.inline_query.bind_filter(TranslatedInlineQueryTextFilter)


def ligne(n):
    for i in range(n):
        print('X', end='')
    print()
    print()

def rectangle(a,b):
    for i in range(a):
        for j in range(b):
            if i==0 or j==0 or i==a-1 or j==b-1:
                print('#', end='')
            else:
                print(' ', end='')
        print()
    print()

def triangle(c):
    for i in range(c):
        for j in range(i+1):
            if j == 0 or j == i or i == c-1:
                print('@', end='')
            else:
                print(' ', end='')
        print()

n, a, b, c = int(input()), int(input()), int(input()), int(input())
ligne(n)
rectangle(a,b)
triangle(c)


from warp import DataProcessor, XPathExtractor

class TitleExtractor(XPathExtractor):

    def configure(self):
        self.id = 'title-extractor'
        self.version = 1
        self.output_format = 'application/json'
        self.invoked = False

    def extract_from_tree(self, html_tree, input_dict):
        t = html_tree.xpath("//h1/text()")[0]
        self.invoked = True
        return dict(title=t)

class TitleDecorator(DataProcessor):

    def configure(self):
        self.id = 'title-decorator'
        self.version = 1
        self.output_format = 'application/json'
        self.invoked = False

    def process(self, input_dict):
        t = "<b>%s</b>" % input_dict['title-extractor']['title']
        self.invoked = True
        return dict(title=t)


# 05. n-gram
# 与えられたシーケンス（文字列やリストなど）からn-gramを作る関数を作成せよ．
# この関数を用い，"I am an NLPer"という文から単語bi-gram，文字bi-gramを得よ．

def ngram(str, n):
    list = []
    for i in range(len(str)-n + 1):
        list.append(str[i:i+n])
    return list

if __name__ == '__main__':

    orgstr = "I am an NLPer"

    instr = orgstr.replace(' ', '')

    rlist2 =ngram(instr, 2)
    print(rlist2)
    rlist3 =ngram(instr, 3)
    print(rlist3)
    rlist4 =ngram(instr, 4)
    print(rlist4)

    words = orgstr.split()

    print(ngram(words, 2))


import json
import os
import shutil
from enum import Enum
from pathlib import Path
from typing import Tuple
import urllib.request
import sys
import time
import zipfile
from tqdm import tqdm
from distutils.dir_util import copy_tree

import pandas as pd
import numpy as np

import sdnist.strs as strs

DEFAULT_DATASET = 'diverse_communities_data_excerpts'

class TestDatasetName(Enum):
    NONE = 1
    GA_NC_SC_10Y_PUMS = 2
    NY_PA_10Y_PUMS = 3
    IL_OH_10Y_PUMS = 4
    taxi2016 = 5
    taxi2020 = 6
    ma2019 = 7
    tx2019 = 8
    national2019 = 9

dataset_name_state_map = {
    TestDatasetName.national2019.name: 'national',
    TestDatasetName.ma2019.name: 'massachusetts',
    TestDatasetName.tx2019.name: 'texas'
}

data_challenge_map = {
    TestDatasetName.NONE: None,
    TestDatasetName.GA_NC_SC_10Y_PUMS: strs.CENSUS,
    TestDatasetName.NY_PA_10Y_PUMS: strs.CENSUS,
    TestDatasetName.IL_OH_10Y_PUMS: strs.CENSUS,
    TestDatasetName.ma2019: strs.CENSUS,
    TestDatasetName.tx2019: strs.CENSUS,
    TestDatasetName.national2019: strs.CENSUS,
    TestDatasetName.taxi2016: strs.TAXI,
    TestDatasetName.taxi2020: strs.TAXI
}

def reporthook(count, block_size, total_size):
    global start_time
    if count == 0:
        start_time = time.time() - 1
        return
    duration = time.time() - start_time
    progress_size = int(count * block_size)
    speed = int(progress_size / (1024 * duration))
    progress = int(count * block_size * 100 / total_size)
    percent = min(progress, 100)  # keeps from exceeding 100% for small data
    sys.stdout.flush()
    sys.stdout.write("\r...%d%%, %d KB, %d KB/s, %d seconds elapsed" %
                     (percent, progress_size / 1024, speed, duration))

def error_opening_zip(zip_path):
    try:
        z = zipfile.ZipFile(zip_path)
    except zipfile.BadZipfile:
        return True

    if z.testzip() is not None:
        return True
    return False

def check_exists(root: Path, name: Path, download: bool, data_name: str = strs.DATA):
    root = root.expanduser()
    if not name.exists():
        print(f"{name} does not exist.")
        zip_path = Path(root.parent, 'data.zip')
        version = "2.3.0"

        version_v = f"v{version}"
        sdnist_version = DEFAULT_DATASET

        download_link = f"https://github.com/usnistgov/SDNist/releases/download/{version_v}/{sdnist_version}.zip"
        if zip_path.exists() and error_opening_zip(zip_path):
            os.remove(zip_path)

        if not zip_path.exists() and download:
            print(f"Downloading all SDNist datasets from: \n"
                  f"{download_link} ...")
            try:
                urllib.request.urlretrieve(
                    download_link,
                    zip_path.as_posix(),
                    reporthook
                )
                print(f'\n Success! Downloaded all datasets to "{root}" directory\n')
            except:
                if zip_path.exists():
                    shutil.rmtree(zip_path)
                raise RuntimeError(f"Unable to download {name}. Try: \n   "
                                   f"- re-running the command, \n   "
                                   f"- downloading manually from {download_link} "
                                   f"and unpack the zip. \n   "
                                   f"- or download the data as part of a release: https://github.com/usnistgov/SDNist/releases\n")

        if zip_path.exists():
            # extract zipfile
            extract_path = Path(root.parent, 'sdnist_data')
            with zipfile.ZipFile(zip_path, 'r') as f:
                for member in tqdm(f.infolist(), desc='Extracting '):
                    try:
                        f.extract(member, extract_path)
                    except zipfile.error as e:
                        raise e
            # delete zipfile
            os.remove(zip_path)
            print()
            copy_from_path = str(Path(extract_path, sdnist_version))
            copy_to_path = str(Path(root))
            print(f"Copying {copy_from_path} to {copy_to_path} ...")
            copy_tree(copy_from_path, copy_to_path)
            shutil.rmtree(extract_path)
        else:
            raise ValueError(f"{name} does not exist.")

def build_name(challenge: str,
               root: Path = Path("data"),
               public: bool = False,
               test: TestDatasetName = TestDatasetName.NONE,
               data_name: str = DEFAULT_DATASET):
    root = root.expanduser()
    directory = root

    if data_name == DEFAULT_DATASET:
        directory = root
    elif challenge == strs.CENSUS:
        directory = root / "census" / "dataset"
    elif challenge == strs.TAXI:
        directory = root / "taxi" / "dataset"

    if challenge == "census":
        if public:
            fname = "IL_OH_10Y_PUMS"

        elif test != TestDatasetName.NONE:
            if test in [TestDatasetName.taxi2020, TestDatasetName.taxi2016]:
                raise ValueError(f'Invalid challenge and test-dataset combination:'
                                 f'challenge: {challenge} | test-dataset: {test.name}. '
                                 f'Available test datasets for the census challenge: '
                                 f'{TestDatasetName.GA_NC_SC_10Y_PUMS.name}',
                                 f'{TestDatasetName.NY_PA_10Y_PUMS.name}')
            fname = test.name
        else:
            fname = TestDatasetName.NY_PA_10Y_PUMS.name

    elif challenge == "taxi":
        if public:
            fname = "taxi"
        elif test != TestDatasetName.NONE:
            if test in [TestDatasetName.GA_NC_SC_10Y_PUMS, TestDatasetName.NY_PA_10Y_PUMS]:
                raise ValueError(f'Invalid challenge and test-dataset combination: '
                                 f'challenge: {challenge} | test-dataset: {test.name}. '
                                 f'Available test datasets for the taxi challenge: '
                                 f'{TestDatasetName.taxi2016.name}',
                                 f'{TestDatasetName.taxi2020.name}')
            fname = test.name
        else:
            fname = TestDatasetName.taxi2020.name

    else:
        raise ValueError(f"Unrecognized challenge {challenge}")
    if fname in dataset_name_state_map.keys():
        fname = Path(dataset_name_state_map[fname], fname)
    return directory / fname

def load_parameters(challenge: str,
                    root: Path = Path("data"),
                    public: bool = True,
                    test: TestDatasetName = TestDatasetName.NONE,
                    download: bool = True,
                    data_name: str = DEFAULT_DATASET) -> dict:
    dataset_path = build_name(challenge=challenge, root=root,
                              public=public, test=test, data_name=data_name)
    dataset_parameters = dataset_path.with_suffix('.json')
    check_exists(root, dataset_parameters, download, data_name=data_name)

    with dataset_parameters.open("r") as handler:
        params = json.load(handler)
    return params

def load_config(challenge: str,
                root: Path = Path("data"),
                public: bool = True,
                test: TestDatasetName = TestDatasetName.NONE,
                download: bool = True) -> dict:
    dataset_path = build_name(challenge=challenge, root=root, public=public, test=test)
    dataset_config_path = dataset_path.with_suffix('.config.json')
    check_exists(root, dataset_config_path, download)

    with dataset_config_path.open("r") as handler:
        config = json.load(handler)

    if strs.K_MARGINAL in config:
        km_cnf = config[strs.K_MARGINAL]
        if strs.BINS in km_cnf and len(km_cnf[strs.BINS]):
            b_cnf = km_cnf[strs.BINS]
            for f, b in b_cnf.items():
                b_cnf[f] = eval(b)
    return config

def load_dataset(challenge: str,
                 root: Path = Path("data"),
                 public: bool = True,
                 test: TestDatasetName = TestDatasetName.NONE,
                 download: bool = True,
                 format_: str = "parquet",
                 data_name: str = DEFAULT_DATASET) -> Tuple[pd.DataFrame, dict]:
    """ Load one of the original SDNist datasets.

    :param challenge: str: base challenge. Must be `census` or `taxi`.
    :param root: Path: directory of the dataset.
    :param public: bool: whether to use the public dataset or the private dataset
        (see below).
    :param test: TestDatasetName: if not None, retrieve the given private dataset (see below).
    :param download: bool: download files if not present in the `root` directory.
    :param format_: str: preferred format when retrieving the files. Must be `parquet` or `csv`.
        Note that only `parquet` files are actually available for download.
    :param data_name: str: name of the dataset bundle from where to load data file
    :return: A tuple containing the requested dataset as a `pandas.DataFrame`, along with
        its corresponding schema, i.e a `dict` description of each feature of the dataset

    Regarding the public/private/test datasets:
    - during the challenge, the participants were given access to the "public"
    dataset (`public=True`, `test=False) to make experiments and inspect data.
    - their synthesizers were evaluated against two new datasets which they
    were not allowed to see before:
        1) The (`public=False`, `test=False`) corresponds to a dataset to privatize.
        The results were published on the public leaderboard. The participants
        were allowed to make several submission. In traditional machine learning scenarios,
        this would somewhat correspond to a 'validation' dataset.
        2) The (`public=False`, `test=True`) corresponds to another dataset to privatize.
        This time, the results were kepts secret until the end of the challenge.
        In traditional machine learning scenarios, this would correspond to a 'test' dataset.
        Your synthesizer should not be fine-tuned for the best score on this dataset.
    - using any information available in the 'public' dataset is allowed.
    - manually inspecting any of the two 'private' dataset is not allowed within the challenge,
        even though there is nothing preventing you from doing so.
    """
    if isinstance(root, str):
        root = Path(root)

    if public and test != TestDatasetName.NONE:
        raise ValueError("A public test dataset does not make sense.")

    name = build_name(challenge, root, public, test, data_name)
    name.parent.mkdir(exist_ok=True, parents=True)

    # Load schema
    params = load_parameters(challenge, root, public, test, download, data_name)
    config = dict()

    # TODO: remove .csv option

    # Load dataset
    if format_ == "parquet":
        dataset_name = name.with_suffix(".parquet")
        check_exists(root, dataset_name, download, data_name)
        dataset = pd.read_parquet(dataset_name)

    elif format_ == "csv":
        dataset_name = name.with_suffix(".csv")
        check_exists(root, dataset_name, download, data_name)

        columns = {name : params[strs.SCHEMA][name]["dtype"]
                   for name in params[strs.SCHEMA]}
        dataset = pd.read_csv(dataset_name, dtype=columns)

    else:
        raise ValueError(f"Unknown format {format_}")

    if data_name != DEFAULT_DATASET:
        config = load_config(challenge, root, public, test, download)
    params[strs.CONFIG] = config
    return dataset, params

if __name__ == "__main__":
    d = load_dataset("taxi")


# File Name:- Zabbix_Create_Host.py
# Service Name:- N/A
# Purpose: To coonfigure Zabbix Hearbeat
# Author Name: satish
# Create Date: 06/oct/20
# Modifed By:-06/oct/2020
# Last Modify Date:
# Current Version: 1.0
# Summary of Last Change: N/A

import sys
import os
import json
import time
import subprocess
import datetime
try:
    from pyzabbix import ZabbixAPI, ZabbixAPIException
except:
    status,out=os.system('pip3.7 install pyzabbix')

import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
import logging, time, subprocess, os, sys, json
sys.path.insert(0, os.environ['Admin_Root'] + '/zabbix/Lib')
sys.path.insert(0, os.environ['Admin_Root'] + '/zabbix/Lib/uistatus')
from uistatus import sendstatus

class Zabbix_HeartBeat():

    def __init__(self):
        self.flag = 0
        self.root = os.environ['Admin_Root']
        self.server_type = sys.argv[1]
        # self.zabbix_host = sys.argv[2]
        self.zabbix_host = subprocess.getoutput("hostname -s")
        self.zabbix_ip = subprocess.getoutput('hostname -i').split()[0]

        self.zapi = ZabbixAPI("https://{}/zabbix".format(self.zabbix_host))
        self.zapi.session.verify = False
        self.zapi.login("Admin", "zabbix")
        global_json = open(self.root + '/zabbix/Input/global.json', 'r')
        self.load_variable = json.load(global_json)
        dt = time.strftime("%Y:%m:%d-%H:%M:%S")
        self.hostname = subprocess.getoutput("hostname -s")
        self.environment = self.load_variable["Environment"]

        self.template = self.load_variable['host_creds']['tmp_name']
        self.hostgroup = self.load_variable['host_creds']['hostgroup']

        self.component = "Proxy"
        self.send_status = sendstatus()
        self.api_payload = {"ToolName": "Zabbix", "Component": self.component, "ServerFQDN": self.hostname,
                            "ServerType": self.server_type, "Environment": self.environment, "Stage": "3",
                            "Status": "1", "StatusReason": "Configuring Zabbix Hearbeat"
                            }
        self.send_status.send_logs([self.api_payload])
        self.log_file = self.root + "/zabbix/Logs/zabbix_heartbeat_" + dt + ".log"
        logging.basicConfig(filename=self.log_file,
                            format='%(asctime)s %(message)s',
                            filemode='a', datefmt='%m/%d/%Y %I:%M:%S %p')

        self.logger = logging.getLogger()
        self.logger.setLevel(logging.INFO)
        self.logger.info("Zabbix HeartBeat configuration Started.. " + dt)
        self.rules = None

    def create_host(self):
        """update host
        """
        try:
            request_host = {
                        "host": self.zabbix_host,
                        "interfaces": [
                            {
                                "type": 1,
                                "main": 1,
                                "useip": 1,
                                "ip": self.zabbix_ip,
                                "dns": "",
                                "port": "10050",

                            }
                        ],
                        "groups": [
                            {
                                "groupid": "4"
                            }
                        ],
                        "templates": [
                            {
                                "templateid": "10001"
                            }
                        ]
                    }
            # print(request_data)
            response = self.zapi.host.create(request_host)
            if response:
                # data = response['result']['actionids']
                # self.logger.info("Activated  notification action :" + ''.join(data))
                self.logger.info("Host Created success")
                subprocess.getoutput('zabbix_server -R config_cache_reload')
                self.api_payload["StatusReason"] = "Host Created Successfully"
                self.api_payload["Status"] = 2
                self.send_status.send_status([self.api_payload])
                self.send_status.send_logs([self.api_payload])

                print("3rr0rC0d3[200]")
            else:
                self.logger.info("Create Host failed")
                self.api_payload["StatusReason"] = "Host Creation Failed"
                self.api_payload["Status"] = 3
                self.send_status.send_status([self.api_payload])
                self.send_status.send_logs([self.api_payload])

                print("3rr0rC0d3[500]")

        except Exception as e:
            self.logger.info("Zabbix API Exception caught:" + str(e))

    def final_check(self):
        if self.flag == 0:
            self.logger.info("create Host Configuration Success")
            self.api_payload["StatusReason"] = "Create Host Configuration Success"
            self.api_payload["Status"] = 2
            self.send_status.send_logs([self.api_payload])
            print("3rr0rC0d3[200]")
        else:
            self.logger.info("Create Host Configuration Fail")
            self.api_payload["StatusReason"] = "Create Host Configuration Fail"
            self.api_payload["Status"] = 3
            self.send_status.send_logs([self.api_payload])
            print("3rr0rC0d3[500]")

if __name__ == '__main__':

 zabbix = Zabbix_HeartBeat()
 zabbix.create_host()
 zabbix.final_check()


#!/usr/bin/env python3
from heapq import *
from collections import defaultdict

n = int(input())
r = map(int, input().split())
H = defaultdict(int)
for t in r:
    H[t] += 1

D = [(-1 * v, k) for k, v in H.items()]
heapify(D)
ret = []
while len(D) > 2:
    a, b, c = heappop(D), heappop(D), heappop(D)

    ret.append(sorted([a[1], b[1], c[1]], reverse=True))

    for x, xi in ((i + 1, j) for i, j in (a, b, c)):
        if x:
            heappush(D, (x, xi))

print(len(ret))
for a, b, c in ret:
    print(a, b, c)


#!/usr/bin/python3

import json
import cgi
import traceback
from DBController import DBController
import re

def get_resp():

   def PrintRow(name, data, col, mode, REGIONS, TIMEZONES, OPERATORS):
      res = "{}	".format(name)

      if mode == 'TimeZoneGroup':
         for TimeZone in TIMEZONES:
            res += """{}	""".format(data[TimeZone][col])

      if mode == 'RegionGroup':
         for Region in REGIONS:
            res += """{}	""".format(data[Region][col])

      if mode == 'OperatorGroup':
         for operator in OPERATORS:
            res += """{}	""".format(data[operator][col])

      res += """{}

""".format(str(data["res"][col]))

      return res

   def PrintRowDiv(name, data, col, base, mode, REGIONS, TIMEZONES, OPERATORS):
      res = "{}	".format(name)

      if mode == 'TimeZoneGroup':
         for TimeZone in TIMEZONES:
            if data[TimeZone][base] != 0:
               res += """{}	""".format(str(data[TimeZone][col]) +
                      " ({:.1f}%)".format(data[TimeZone][col]/data[TimeZone][base] * 100))
            else:
               res += """0	"""

      if mode == 'RegionGroup':
         for Region in REGIONS:
            if data[Region][base] != 0:
               res += """{}	""".format(str(data[Region][col]) +
                      " ({:.1f}%)".format(data[Region][col]/data[Region][base] * 100))
            else:
               res += """0	"""

      if mode == 'OperatorGroup':
         for operator in OPERATORS:
            if data[operator][base] != 0:
               res += """{}	""".format(str(data[operator][col]) +
                      " ({:.1f}%)".format(data[operator][col]/data[operator][base] * 100))
            else:
               res += """0	"""

      if data["res"][base] != 0:
         res += """{}

""".format(str(data["res"][col]) + " ({:.1f}%)".format(data["res"][col]/data["res"][base] * 100))
      else:
         res += """0	"""

      return res

   def PrintDetail(data, info, TEMPLATE, mode, REGIONS, TIMEZONES, OPERATORS, is_bold):

      style = ""

      if is_bold:
         style = "font-weight: bold;font-size: larger;"

      res = """

               {}	""".format(style,str(len(TEMPLATE)),data)

      res += PrintRow("Всего звонков", info[data], "COUNT", mode, REGIONS, TIMEZONES, OPERATORS)
      res += '
'.format(style) + PrintRowDiv("Отправлено", info[data], "SEND", "COUNT", mode, REGIONS, TIMEZONES, OPERATORS)
      res += '
'.format(style) + PrintRowDiv("Сделок", info[data], "BID", "SEND", mode, REGIONS, TIMEZONES, OPERATORS)
      res += '
'.format(style) + PrintRowDiv("Повторная обработка", info[data], "RECALL", "COUNT", mode, REGIONS, TIMEZONES, OPERATORS)
      res += '
'.format(style) + PrintRowDiv("Плохие данные", info[data], "BADDATA", "COUNT", mode, REGIONS, TIMEZONES, OPERATORS)
      res += '
'.format(style) + PrintRowDiv("Отказ", info[data], "NEGATIV", "COUNT", mode, REGIONS, TIMEZONES, OPERATORS)
      res += '
'.format(style) + PrintRowDiv("ОДП", info[data], "ODP", "COUNT", mode, REGIONS, TIMEZONES, OPERATORS)
      res += '
'.format(style) + PrintRowDiv("Уникальных контактов", info[data], "UNIQ_COUNT", "COUNT", mode, REGIONS, TIMEZONES, OPERATORS)
      res += '
'.format(style) + PrintRowDiv("Отправлено уникальных", info[data], "UNIQ_SEND", "UNIQ_COUNT", mode, REGIONS, TIMEZONES, OPERATORS)

      return res

   form = cgi.FieldStorage()
   mode = form.getvalue('mode')
   start_date = form.getvalue('start_date')
   end_date = form.getvalue('end_date')
   detail = form.getvalue('detail')
   controller = DBController()
   cur = controller.GetCur()

   main_sql = """SELECT """

   if detail == "Month":
      main_sql += 'DATE_FORMAT(DateTime, "%m.%Y") as Detail,'
   elif detail == "Hours":
      main_sql += 'DATE_FORMAT(DateTime, "%d.%m.%Y %H") as Detail,'
   else:
      main_sql += 'DATE_FORMAT(DateTime, "%d.%m.%Y") as Detail,'

   main_sql += """ Result, IF(PrevCall_ IS NULL, false, true) as uniq, IF(Bid.State = 'Счет открыт', true, false) as BID, count(*) as CNT"""

   if mode == 'OperatorGroup':
      main_sql += ", CONCAT(Employee.Name, ' ', Employee.Surname) as grp"

   if mode == 'TimeZoneGroup':
      main_sql += ', TimeZone as grp'

   if mode == 'RegionGroup':
      main_sql += ', TimeZone.Region as grp'

   main_sql += """
FROM Call_ LEFT JOIN Bid ON Call_.ID = Bid.Call_ """

   if mode == 'OperatorGroup':
      main_sql += """INNER JOIN Operator ON Call_.Operator = Operator.ID
                     INNER JOIN Employee ON Operator.Employee = Employee.ID"""

   if mode == 'TimeZoneGroup' or mode == 'RegionGroup':
      main_sql += """INNER JOIN Call_Organization ON Call_Organization.Call_ = Call_.ID
                     INNER JOIN Organization ON Call_Organization.Organization = Organization.ID"""

   if mode == 'RegionGroup':
      main_sql += ' LEFT JOIN TimeZone ON SUBSTR(Organization.Address,1,3) >= TimeZone.StartValue AND ' \
                  'SUBSTR(Organization.Address,1,3) <= TimeZone.EndValue'

   main_sql += """
WHERE DateTime >= '{}' AND DateTime <= '{}' AND Result is NOT NULL AND Result <> 'ОДПРоботТФ'
GROUP BY """

   if detail == "Month":
      main_sql += 'MONTH( DateTime ), YEAR(DateTime)'
   elif detail == "Hours":
      main_sql += 'Date(DateTime), HOUR(DateTime)'
   else:
      main_sql += 'Date(DateTime)'

   main_sql += """, Result, IF(PrevCall_ IS NULL, false, true), IF(Bid.State = 'Счет открыт', true, false)"""

   if mode == 'OperatorGroup':
      main_sql += ", Employee.Name, Employee.Surname"

   if mode == 'TimeZoneGroup':
      main_sql += ', TimeZone'

   if mode == 'RegionGroup':
      main_sql += ', TimeZone.Region'

   cur.execute(main_sql.format(start_date,end_date))

   res = cur.fetchall()

   OPERATORS = []
   if mode == 'OperatorGroup':
      cur.execute("""SELECT Employee.Surname, Employee.`Name`
                     FROM Operator
                     INNER JOIN Employee ON Operator.Employee = Employee.ID
                     WHERE Dismissed = FALSE""")

      op_list = cur.fetchall()

      for data in res:
         if not data['grp'] in OPERATORS:
            OPERATORS.append( data['grp'] )

   info = {}
   row_res = None

   REGIONS = ["01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16",
              "17","18","19","21","22","23","24","25","26","27","28","29","30","31","32","33",
              "34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49",
              "50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65",
              "66","67","68","69","70","71","72","73","74","75","76","77","78","79","83","86",
              "87","89","91","92","95",None]

   TIMEZONES = [9,8,7,6,5,4,3,2,1,0,-1,None]

   TEMPLATE = {"COUNT":0, "SEND":0, "RECALL":0, "BADDATA":0, "NEGATIV":0, "ODP":0, "UNIQ_COUNT":0, "UNIQ_SEND":0, "BID":0}

   if mode == "NonGroup":
      row_res = TEMPLATE.copy()

   if mode == 'TimeZoneGroup':
      row_res = {}

      for TimeZone in TIMEZONES:
         row_res[TimeZone]= TEMPLATE.copy()

   if mode == 'RegionGroup':
      row_res = {}

      for Region in REGIONS:
         row_res[Region]= TEMPLATE.copy()

   if mode == 'OperatorGroup':
      row_res = {}

      for operator in OPERATORS:
         row_res[operator]= TEMPLATE.copy()

   glob_info = TEMPLATE.copy()

   for data in res:
      cur_row = None

      if not data["Detail"] in info:
         info[data["Detail"]] = {}
         cur_row = info[data["Detail"]]
         cur_row["res"] = TEMPLATE.copy()

         if mode == 'TimeZoneGroup':
            for TimeZone in TIMEZONES:
               cur_row[TimeZone]= TEMPLATE.copy()

         if mode == 'RegionGroup':
            for Region in REGIONS:
               cur_row[Region]= TEMPLATE.copy()

         if mode == 'OperatorGroup':
            for operator in OPERATORS:
               cur_row[operator]= TEMPLATE.copy()
      else:
         cur_row = info[data["Detail"]]

      cur_row["res"]["COUNT"] += data["CNT"]
      glob_info["COUNT"] += data["CNT"]

      if "grp" in data:
         row_res[data["grp"]]["COUNT"] += data["CNT"]
         cur_row[data["grp"]]["COUNT"] += data["CNT"]

      if data["Result"] == 'Отправлено':
         cur_row["res"]["SEND"] += data["CNT"]
         glob_info["SEND"] += data["CNT"]

         if "grp" in data:
            row_res[data["grp"]]["SEND"] += data["CNT"]
            cur_row[data["grp"]]["SEND"] += data["CNT"]

      if data["Result"] in ('Недозвон', 'Повторный звонок', 'Отказ. Пока не актуально', 'Дальний регион'):
         cur_row["res"]["RECALL"] += data["CNT"]
         glob_info["RECALL"] += data["CNT"]

         if "grp" in data:
            row_res[data["grp"]]["RECALL"] += data["CNT"]
            cur_row[data["grp"]]["RECALL"] += data["CNT"]

      if data["Result"] in ('Неактивен', 'Неправильный номер', 'Дубль', 'Отказ. Крым'):
         cur_row["res"]["BADDATA"] += data["CNT"]
         glob_info["BADDATA"] += data["CNT"]

         if "grp" in data:
            row_res[data["grp"]]["BADDATA"] += data["CNT"]
            cur_row[data["grp"]]["BADDATA"] += data["CNT"]

      if data["Result"] in ('ОДП'):
         cur_row["res"]["ODP"] += data["CNT"]
         glob_info["ODP"] += data["CNT"]

         if "grp" in data:
            row_res[data["grp"]]["ODP"] += data["CNT"]
            cur_row[data["grp"]]["ODP"] += data["CNT"]

      if data["Result"] in ('Отказ. Сбер', 'Отказ. Точка', 'Отказ. ТФ', 'Отказ. Др.банк', 'Отказ. Открыл', 'Отказ. Звонили', 'Отказ. Нет потребности', 'Отказ. Услуга не нужна', 'Отказ. Мск'):
         cur_row["res"]["NEGATIV"] += data["CNT"]
         glob_info["NEGATIV"] += data["CNT"]

         if "grp" in data:
            row_res[data["grp"]]["NEGATIV"] += data["CNT"]
            cur_row[data["grp"]]["NEGATIV"] += data["CNT"]

      if data["uniq"]:
         cur_row["res"]["UNIQ_COUNT"] += data["CNT"]
         glob_info["UNIQ_COUNT"] += data["CNT"]

         if "grp" in data:
            row_res[data["grp"]]["UNIQ_COUNT"] += data["CNT"]
            cur_row[data["grp"]]["UNIQ_COUNT"] += data["CNT"]

      if data["uniq"] and data["Result"] == 'Отправлено':
         cur_row["res"]["UNIQ_SEND"] += data["CNT"]
         glob_info["UNIQ_SEND"] += data["CNT"]

         if "grp" in data:
            row_res[data["grp"]]["UNIQ_SEND"] += data["CNT"]
            cur_row[data["grp"]]["UNIQ_SEND"] += data["CNT"]

      if data["BID"]:
         cur_row["res"]["BID"] += data["CNT"]
         glob_info["BID"] += data["CNT"]

         if "grp" in data:
            row_res[data["grp"]]["BID"] += data["CNT"]
            cur_row[data["grp"]]["BID"] += data["CNT"]

   res = {}
   res["statContent"] = """

Дата звонка
	"""

   if mode == 'TimeZoneGroup':
      for TimeZone in TIMEZONES:
         res["statContent"] += """{}	""".format(TimeZone)

   if mode == 'RegionGroup':
      for Region in REGIONS:
         res["statContent"] += """{}	""".format(Region)

   if mode == 'OperatorGroup':
      for operator in OPERATORS:
         res["statContent"] += """{}	""".format(operator)

   res["statContent"] += """Итого

"""

   for data in info:
      res["statContent"] += PrintDetail(data, info, TEMPLATE, mode, REGIONS, TIMEZONES, OPERATORS, False)

   row_res['res'] = glob_info
   info['Итого'] = row_res
   res["statContent"] += PrintDetail('Итого', info, TEMPLATE, mode, REGIONS, TIMEZONES, OPERATORS, True)

   res["statContent"] += "
"
   print("Content-type: application/json")
   print()
   print(json.JSONEncoder().encode(res))

try:
   get_resp()
except Exception as e:
   res = {}
   res["statContent"] = "\nОшибка:\n" + str(e) + "\n" + traceback.format_exc()
   print("Content-type: application/json")
   print()
   print(json.JSONEncoder().encode(res))


import unittest

def find_duplicate(list):
    """find a duplicate of 1..n in a list n+1 elements long"""

    n = len(list)
    ptr_1 = n
    ptr_2 = n
    # print("value 1 : " + str(list[ptr_1 - 1]) + "      value 2 : " + str(list[ptr_2 - 1]))
    while True:
        ptr_1 = list[ptr_1-1]
        ptr_2 = list[list[ptr_2-1]]
        # ptr_2 = list[ptr_2-1]
        # print("value 1 : " + str(list[ptr_1 - 1]) + "      value 2 : " + str(list[ptr_2 - 1]))
        if ptr_1 == ptr_2:
            # print(list[ptr_1-1])
            break

    ptr_2 = n
    while True:
        ptr_1 = list[ptr_1-1]
        ptr_2 = list[ptr_2-1]
        if ptr_2 == ptr_1:
            # print("value : "+str(ptr_1))
            return ptr_1

# Tests

class Test(unittest.TestCase):

    def test_just_the_repeated_number(self):
        actual = find_duplicate([1, 1])
        expected = 1
        self.assertEqual(actual, expected)

    def test_short_list(self):
        actual = find_duplicate([1, 2, 3, 2])
        expected = 2
        self.assertEqual(actual, expected)

    def test_medium_list(self):
        actual = find_duplicate([1, 2, 5, 5, 5, 5])
        expected = 5
        self.assertEqual(actual, expected)

    def test_long_list(self):
        actual = find_duplicate([4, 1, 4, 8, 3, 2, 7, 6, 5])
        expected = 4
        self.assertEqual(actual, expected)

    def test_long_list(self):
        actual = find_duplicate([5, 1, 4, 8, 3, 2, 7, 6, 4])
        expected = 4
        self.assertEqual(actual, expected)

unittest.main(verbosity=2)


# coding: utf-8
"""学生用户常用函数"""
from functools import partial

from libs.utils import com_db, Struct
from libs.models.tbkt.models import UBook
from libs.models.mobile.models import MobileUserBind, MobileOrderRegion, STUDENT_SUBJECT_CODE
from libs.models.ziyuan.models import ZYBook

def get_bind(user):
    "property: bind"
    if hasattr(user, '_bind'):
        return user._bind
    user._bind = MobileUserBind.objects.filter(user_id=user.id).first()
    return user._bind

def get_phone_number(user):
    "property: phone_number"
    bind = get_bind(user)
    return bind.phone_number if bind else ''

def get_unit(user):
    "property: unit 班级"
    if hasattr(user, '_unit'):
        return user._unit

    sql = """
    select
        rg.city, rg.school_id, rg.school_name, rg.unit_class_id as id, rg.grade_id,
        rg.class_id
    from mobile_order_region rg
    where rg.user_id=%s and rg.unit_class_id>0
    limit 1
    """ % user.id
    unit = com_db.fetchone_to_dict(sql, db='ketang')
    if unit:
        unit = Struct(unit)
        unit.name = u'%d%02d班' % (unit.grade_id, unit.class_id)
        unit.type = 1 if unit.grade_id <= 6 else 2
        sql = "select type from school_unit_class where id=%s" % unit.id
        row = com_db.fetchone_sql(sql, db='slave')
        if row:
            unit.type = row[0]
    user._unit = unit
    return user._unit

def get_city(user):
    "property: city_id"
    units = get_unit(user)
    return units.city if units else ''

def get_school_id(user):
    "property: school_id"
    unit = get_unit(user)
    return unit.school_id if unit else 0

def get_school_name(user):
    "property: school_name"
    unit = get_unit(user)
    return unit.school_name if unit else ''

def get_dept_type(user):
    "property: dept_type 1小学 2初中"
    unit = get_unit(user)
    return unit.type if unit else 0

def get_grade_id(user):
    "property: grade_id"
    unit = get_unit(user)
    return unit.grade_id if unit else 0

def get_openstatus(user, sid):
    "开通状态, sid是一位数学科码, 返回0/1"
    if not hasattr(user, '_opens'):
        user._opens = {}
    if sid in user._opens:
        return user._opens[sid]
    r = com_db.call_proc('sp_get_user_status', (user.id, sid), db='default')
    status = r[0][0]
    user._opens[sid] = status
    return status

def add_properties(user):
    """
    给request.user增加一些常用属性:
    - bind
    - 班级
    - 部门类型
    - 教材, 教辅
    --------------------------------
    修改人        修改时间        修改原因
    --------------------------------
    王晨光        2016-05-22  改为延迟加载, 用到的时候才读数据库, 节约不必要的访问
    """
    # 为了防止缓存, 每次先把user对象清干净
    for name in ['_bind', '_unit', '_book', '_pbook', '_opens']:
        user.__dict__.pop(name, None)

    klass = user.__class__
    # UserBind
    klass.bind = property(get_bind)
    # 电话
    klass.phone_number = property(get_phone_number)
    # 班级
    klass.unit = property(get_unit)
    # 城市
    klass.city = property(get_city)
    # 学校
    klass.school_id = property(get_school_id)
    klass.school_name = property(get_school_name)
    # 部门类型
    klass.dept_type = property(get_dept_type)
    # 年级
    klass.grade_id = property(get_grade_id)
    # 数学开通
    klass.openstatus = get_openstatus
    klass.get_book = get_book
    klass.get_pbook = get_pbook
    #英语教材
    klass.yybook = property(get_yybook)

def get_book(user, subject_id):
    "用户教材"
    ubook = UBook.objects.filter(user_id=user.id, subject_id=subject_id, is_work_book=0).first()
    if ubook:
        return ZYBook.objects.filter(pk=ubook.book_id).first()

def get_pbook(user, subject_id):
    "用户练习册"
    ubook = UBook.objects.filter(user_id=user.id, subject_id=subject_id, is_work_book=1).first()
    if ubook:
        return ZYBook.objects.filter(pk=ubook.book_id).first()

def get_yybook(user):
    return get_book(user, 91)


"""
Version 1 of this program randomly replaces a specified amount of atoms within the cell with a replacement atom

Version 2 of this program streamlines the script so that a common function can be called multiple times.
This cuts down on the redundancy of the code.
Version 3 of this program allows for multiple possible replacement atoms.
For example Ga or Al could be used to replace Sn. On each iteration, Ga or Al is selected randomly. The usefulness of this addition is up for debate.
Version 4 of this program has a check_charge function that takes input and prints the valence electron count of the standard cell and the new cell.
It also prints the electron count difference of the new cells with respect to the standard cell (aka the cell with no replacements)

Version 5 is obsolete due to errors in program planning

Version 6 allows users to input the number of replacements they want as a tuple. The program then returns a master dictionary that contains trials for each replacment value.
Printing is now completed through a function. The only catch for this version is that the tuples picked for number of replacments for A and B MUST be the same.
In other words, n-type and p-type doping cannot be automatically done yet. Upcoming verions will look to change this.

Version 7 allows the user to n-type and p-type dope. The user inputs the number of replacments in a list. The length of the two lists must be equal; order does matter.
The first values of each list are paired up together.

Version 8 reimplemented the check_charges function. For each variation of the chemical formula, the valence charge is printed.
"""

import random

def main():
    num_of_variations=1
    #aka length of each string

    num_of_a=8
    standard_a='!'
    standard_a_charge=0
    replacement_a=['KK']
    replacement_a_charge=1
    num_of_replacements_a=[1]

    num_of_b=8
    standard_b='^'
    standard_b_charge=4
    replacement_b=['GG']
    replacement_b_charge=3
    num_of_replacements_b=[0]

    assert(prereq_check(num_of_a,num_of_replacements_a))
    assert(prereq_check(num_of_b,num_of_replacements_b))

    m_MTF_A=function1(num_of_a,standard_a,replacement_a,num_of_replacements_a,num_of_variations,standard_a_charge,replacement_a_charge)
    m_MTF_B=function1(num_of_b,standard_b,replacement_b,num_of_replacements_b,num_of_variations,standard_b_charge,replacement_b_charge)

    #print out the master dicts and charges
    print_master(m_MTF_A,m_MTF_B)

    #check and print the total valence electron count of the standard vs the new cells
    #check_charge(num_of_a,num_of_b,standard_a_charge,standard_b_charge,replacement_a_charge,replacement_b_charge,num_of_replacements_a,num_of_replacements_b)

def prereq_check(num_of_XYZ, list_of_num_of_replace):
    if num_of_XYZ>=max(list_of_num_of_replace):
        return True
    else:
        return False

def first_half(tuple):
    return tuple[0]

def print_master(m_MTF_XYZ,m_MTF_MNO):
    assert len(m_MTF_XYZ.keys())==len(m_MTF_MNO.keys())
    XYZ_list_keys=sorted(list(m_MTF_XYZ.keys()),key=first_half)
    MNO_list_keys=sorted(list(m_MTF_MNO.keys()),key=first_half)
    for num in range(len(XYZ_list_keys)):
        tuple_XYZ=XYZ_list_keys[num]
        tuple_MNO=MNO_list_keys[num]

        _MTF_XYZ=m_MTF_XYZ[tuple_XYZ]
        num_of_replacements_XYZ=tuple_XYZ[1]
        num_of_XYZ=len(_MTF_XYZ.keys())
        og_charge_XYZ=_MTF_XYZ['standard_charge']
        new_charge_XYZ=_MTF_XYZ['replacement_charge']

        _MTF_MNO=m_MTF_MNO[tuple_MNO]
        num_of_replacements_MNO=tuple_MNO[1]
        num_of_MNO=len(_MTF_MNO.keys())
        og_charge_MNO=_MTF_MNO['standard_charge']
        new_charge_MNO=_MTF_MNO['replacement_charge']

        print()
        print('#########################################')
        print('num_replacements in A='+str(num_of_replacements_XYZ))
        print('num_replacements in B='+str(num_of_replacements_MNO))
        check_charge(num_of_XYZ,num_of_MNO,og_charge_XYZ,og_charge_MNO,new_charge_XYZ,new_charge_MNO,num_of_replacements_XYZ,num_of_replacements_MNO)
        print('#########################################')

        print_sub(_MTF_XYZ, _MTF_MNO)
    return

def print_sub(_MTF_XYZ,_MTF_MNO):
    for val in _MTF_XYZ.keys():
        if type(val)==int:
            print('_MTF_A'+str(val+1)+'='+str(_MTF_XYZ[val]))
    for val in _MTF_MNO.keys():
        if type(val)==int:
            print('_MTF_B'+str(val+1)+'='+str(_MTF_MNO[val]))
    return

def function1(num_of_XYZ,standard_XYZ,replacement_XYZ,list_num_of_replacements_XYZ,num_of_variations,standard_charge_XYZ,new_charge_XYZ):
    master_dict={}
    list_of_tuples_of_replacements=[]
    val=0
    for replacement in list_num_of_replacements_XYZ:
        tuple_XYZ=(val,replacement)
        list_of_tuples_of_replacements.append(tuple_XYZ)
        val+=1
    for tuple in list_of_tuples_of_replacements:
        numb_replacement=tuple[1]
        master_dict[tuple]=function(num_of_XYZ,standard_XYZ,replacement_XYZ,numb_replacement,num_of_variations,standard_charge_XYZ,new_charge_XYZ)
    return master_dict

def function(num_of_XYZ,standard_XYZ,replacement_XYZ,num_of_replacements_XYZ,num_of_variations,standard_charge_XYZ,new_charge_XYZ):

    _MTF_XYZ={}
    _MTF_XYZ['standard_charge']=standard_charge_XYZ
    _MTF_XYZ['replacement_charge']=new_charge_XYZ

    #fill dictionary with standard values
    for val in range(0,num_of_XYZ):
        _MTF_XYZ[val]=[standard_XYZ]*num_of_variations

    #begin replacing the dictionary with replacement values
    for trial in range(0,num_of_variations):
        #replace all the necessary atoms in _MTF_XYZ
        history_of_replacements_XYZ=[]
        for num in range(0,num_of_replacements_XYZ):
            #obtain random num between 0 and num_of_XYZ
            replacement_index_XYZ=random.randint(0,num_of_XYZ-1)
            #if the replacement has already been done, do it again until otherwise
            while replacement_index_XYZ in history_of_replacements_XYZ:
                replacement_index_XYZ=random.randint(0,num_of_XYZ-1)
            #add the replacement to the history of replacements
            history_of_replacements_XYZ.append(replacement_index_XYZ)
            #replacethe standard value with replacement_a
            _MTF_XYZ[replacement_index_XYZ][trial]=random.choice(replacement_XYZ)
        del history_of_replacements_XYZ

    return _MTF_XYZ

def check_charge(num_of_a,num_of_b,standard_a_charge,standard_b_charge,replacement_a_charge,replacement_b_charge,num_of_replacements_a,num_of_replacements_b):
    standard_charge_ab=num_of_a*standard_a_charge+num_of_b*standard_b_charge
    new_charge_ab=(num_of_a-num_of_replacements_a)*standard_a_charge+(num_of_b-num_of_replacements_b)*standard_b_charge+num_of_replacements_a*replacement_a_charge+num_of_replacements_b*replacement_b_charge
    print('new cell valence charge='+str(new_charge_ab))
    print('standard cell valence charge='+str(standard_charge_ab))
    print('difference='+str(new_charge_ab-standard_charge_ab))
    return

main()


#####################################################################
#
# audio.py
#
# Copyright (c) 2015, Eran Egozy
#
# Released under the MIT License (http://opensource.org/licenses/MIT)
#
#####################################################################

import sys

try:
    from common.core import register_terminate_func
except:
    # if import failed, we are most likely running from command line (python audio.py)
    # to print out audio devices. In that case, we don't need anything from common.core anyway
    pass

import pyaudio
import numpy as np
import time

class Audio(object):
    """
    Audio input and output stream manager. Only one `Audio` object should be created. Audio output
    (sent to speakers) is supplied by setting an audio Generator class. Audio input (from microphone) is
    provided via a callback function. You can also "tap into" the output audio using a callback.

    :param num_channels: Number of output channels. Can be 1 (mono) or 2 (stereo)

    :param listen_func: if provided, call with audio buffer about to be sent out to speaker with
        parameters ``(audio, num_channels)``

    :param input_func: if provided, input streaming is enabled, and function will be called when
        input data is available with parameters ``(audio, num_channels)``

    :param num_input_channels: stream 1 (mono) or 2 (stereo) input channels. Note that some devices
        may not support stereo input.

    The following parameters are class-parameters and can be set before creating the Audio class:

    :param Audio.sample_rate: Audio sample rate to use. Defaults to 44100.

    :param Audio.buffer_size: Internal buffer size. A smaller buffer will lower latency, at the risk of buffer underrun.
        Default is 512.

    :param Audio.out_dev: Can specify a non-default audio output device (via integer index).
        See :meth:`print_audio_devices`. Default is None, which chooses the default output device.

    :param Audio.in_dev: Can specify a non-default audio input device (via integer index).
        See :meth:`print_audio_devices`. Default is None, which chooses the default input device.

    .. note::
        On Windows, if ASIO drivers are installed, you can run the whole python app with
        command-line-args `-asio`. That will cause ASIO drivers to be used instead of the default in/out
        devices. ASIO typically has much lower latency than the built-in Windows drivers.

        Example: ``python myapp.py -asio``

    """

    # audio configuration parameters:
    sample_rate = 44100
    buffer_size = 512
    out_dev = None
    in_dev = None

    def __init__(self, num_channels, listen_func = None, input_func = None, num_input_channels = 1):
        super(Audio, self).__init__()

        assert(num_channels == 1 or num_channels == 2)
        self.num_channels = num_channels
        self.listen_func = listen_func
        self.input_func = input_func
        self.audio = pyaudio.PyAudio()

        self.num_input_channels = num_input_channels

        # on windows, if '-asio' found in command-line-args, use ASIO drivers
        if '-asio' in sys.argv:
            Audio.out_dev, Audio.in_dev = self._find_asio_devices()

        print('using audio params:')
        print('  samplerate: {}\n  buffersize: {}\n  outputdevice: {}\n  inputdevice: {}'.format(
            Audio.sample_rate, Audio.buffer_size, Audio.out_dev, Audio.in_dev))

        # create output stream
        self.stream = self.audio.open(format = pyaudio.paFloat32,
                                      channels = num_channels,
                                      frames_per_buffer = Audio.buffer_size,
                                      rate = Audio.sample_rate,
                                      output = True,
                                      input = False,
                                      output_device_index = Audio.out_dev)

        # create input stream
        self.input_stream = None
        if input_func:
            self.input_stream = self.audio.open(format = pyaudio.paFloat32,
                                                channels = self.num_input_channels,
                                                frames_per_buffer = Audio.buffer_size,
                                                rate = Audio.sample_rate,
                                                output = False,
                                                input = True,
                                                input_device_index = Audio.in_dev)

        self.generator = None
        self.cpu_time = 0
        register_terminate_func(self._close)

    def set_generator(self, gen):
        """
        Sets a Generator object that must supply audio data to Audio. Generator must define the
        method ``generate(num_frames, num_channels)``, which returns a numpy array of
        length *(num_frames * num_channels)*.

        :param gen: The generator object. May be `None`.

        """
        self.generator = gen

    def get_cpu_load(self):
        """
        :returns: Time spent (in milliseconds) processing audio input and output.
            Returned value is smoothed out somewhat. If value is too high (above 16ms), then too much time
            is being spent calculating audio.
        """
        return 1000 * self.cpu_time

    def on_update(self):
        """
        Must be called by the app (`MainWidget`) very often - usually 60 times per second. Typically,
        Audio.on_update() should be called from MainWidget.on_update().
        """

        t_start = time.time()

        # get input audio if desired
        if self.input_stream:
            try:
                num_frames = self.input_stream.get_read_available() # number of frames to ask for
                if num_frames:
                    data_str = self.input_stream.read(num_frames, False)
                    data_np = np.fromstring(data_str, dtype=np.float32)
                    self.input_func(data_np, self.num_input_channels)
            except IOError as e:
                print('got error', e)

        # Ask the generator to generate some audio samples.
        num_frames = self.stream.get_write_available() # number of frames to supply
        if self.generator and num_frames != 0:
            (data, continue_flag) = self.generator.generate(num_frames, self.num_channels)

            # make sure we got the correct number of frames that we requested
            assert len(data) == num_frames * self.num_channels, \
                "asked for (%d * %d) frames but got %d" % (num_frames, self.num_channels, len(data))

            # convert type if needed and write to stream
            if data.dtype != np.float32:
                data = data.astype(np.float32)
            self.stream.write(data.tostring())

            # send data to listener as well
            if self.listen_func:
                self.listen_func(data, self.num_channels)

            # continue flag
            if not continue_flag:
                self.generator = None

        # how long this all took
        dt = time.time() - t_start
        a = 0.9
        self.cpu_time = a * self.cpu_time + (1-a) * dt

    def _close(self):
        self.stream.stop_stream()
        self.stream.close()
        if self.input_stream:
            self.input_stream.stop_stream()
            self.input_stream.close()

        self.audio.terminate()

    # look for the ASIO devices and return them (output, input)
    def _find_asio_devices(self):
        out_dev = in_dev = None

        cnt = self.audio.get_host_api_count()
        for i in range(cnt):
            api = self.audio.get_host_api_info_by_index(i)
            if api['type'] == pyaudio.paASIO:
                out_dev = api['defaultOutputDevice']
                in_dev = api['defaultInputDevice']
                print('Found ASIO device at index', i)

        return out_dev, in_dev

def get_audio_devices():
    """
    :returns: Available input and output devices as `{ 'input': <list>, 'output': <list> }`.
        `<list>` is a list of device descriptors, each being a dictionary:
        `{'index': <integer>, 'name': <string>, 'latency': (low, high), 'channels': <max # of channels>`.
    """

    def add_device(arr, io_type, dev):
        info = {}
        info['index'] = dev['index']
        info['name'] = dev['name']
        info['latency'] = (dev['defaultLow' + io_type + 'Latency'],
                           dev['defaultHigh' + io_type + 'Latency'])
        info['channels'] = dev['max' + io_type + 'Channels']
        arr.append(info)

    audio = pyaudio.PyAudio()

    out_devs = [{'index':'None', 'name':'Default', 'channels':0, 'latency':(0,0)}]
    in_devs  = [{'index':'None', 'name':'Default', 'channels':0, 'latency':(0,0)}]

    cnt = audio.get_device_count()
    for i in range(cnt):
        dev = audio.get_device_info_by_index(i)

        if dev['maxOutputChannels'] > 0:
            add_device(out_devs, 'Output', dev)

        if dev['maxInputChannels'] > 0:
            add_device(in_devs, 'Input', dev)

    audio.terminate()
    return {'output': out_devs, 'input': in_devs}

def print_audio_devices():
    """
    Prints the list of accessible input and output audio devices. This easiest way to see
    these results is to run ``python audio.py``, which just calls this function.
    """

    devs = get_audio_devices()

    print("\nOutput Devices")
    print('{:>5}: {:<40} {:<6} {}'.format('idx', 'name', 'chans', 'latency'))
    for d in devs['output']:
        print('{index:>5}: {name:<40} {channels:<6} {latency[0]:.3f} - {latency[1]:.3f}'.format(**d))

    print("\nInput Devices")
    print('{:>5}: {:<40} {:<6} {}'.format('idx', 'name', 'chans', 'latency'))
    for d in devs['input']:
        print('{index:>5}: {name:<40} {channels:<6} {latency[0]:.3f} - {latency[1]:.3f}'.format(**d))

if __name__ == "__main__":
    print_audio_devices()


def test(case=None):
    "Run tests by name...helpful in jupyter notebook"
    import sys

    unis._runtime_cache = {}
    print("Cleared unis Runtime cache")

    if case is None:
        module = sys.modules[__name__]
        suite = unittest.TestLoader().loadTestsFromModule(module)
    else:
        suite = unittest.TestLoader().loadTestsFromTestCase(case)
    unittest.TextTestRunner().run(suite)


"""
Routes and views for the bottle application.
"""

from bottle import route, view
from datetime import datetime

# kilroy added this for static content
from bottle import static_file

@route('/')
@route('/home')
@view('index')
def home():
    """Renders the home page."""
    return dict(
        year=datetime.now().year
    )

@route('/contact')
@view('contact')
def contact():
    """Renders the contact page."""
    return dict(
        title='Contact',
        message='You can contact me at rob 5 at uw dot edu.',
        year=datetime.now().year
    )

@route('/about')
@view('about')
def about():
    """Renders the about page."""
    return dict(
        title='About',
        message='djargon (on the Bottle web framework (Python)) is documentation plus some API testing on Azure.',
        year=datetime.now().year
    )

# This is copycat of '/about' and so on; it doesn't render a static page (which is what I want at the moment)
# @route('/doc')
# @view('doc')
# def doc():
#     """Renders the simple doc page."""
#     return dict(
#         title='Doc',
#         message='Simple documentation page.',
#         year=datetime.now().year
#     )

# kilroy: documentation.html at ./static/html is the older version; but this was a pain to build out of
#   OneNote; took several tedious steps.

@route('/api')
@view('api')
def api_no_args():
    """Renders the api page as documentation"""
    return dict(title='API', message='Format = djargon.a.n/api/command=hydrograph^start=date^end=date^station=stnID^interval=time_interval', year=datetime.now().year)

# kilroy: Major problem is the parse character '&' works on localhost but not on Azure.
#   '+' fails as well but in a different way; karet '^' seems to work.
@route('/api/<command>')
@view('api')
def api(command):
    """Interprets an API call."""
    # kilroy debug: return dict(title='API', message=command, year=datetime.now().year)

    failed = 0
    args = command.split('&')
    thisCmd = 'null'

    for c in args:
        thisArgPair = c.split('=')
        if thisArgPair[0] == 'command':
            thisCmd = thisArgPair[1]
            break

    if thisCmd == 'hydrograph':
        startFound = False
        endFound = False
        stationFound = False
        intervalFound = False
        goodMsg = str(len(args)) + ' args; '
        for i in range(len(args)):
            thisQual = args[i].split('=')
            if len(thisQual) != 2:
                failed = 3
                break
            if thisQual[0] == 'start':
                startFound = True
                goodMsg += 'start ' + thisQual[1] + ', '
            if thisQual[0] == 'end':
                endFound = True
                goodMsg += 'end ' + thisQual[1] + ', '
            if thisQual[0] == 'station':
                stationFound = True
                goodMsg += 'Pardon me boys: Is this the ' + thisQual[1] + ' station?, '
            if thisQual[0] == 'interval':
                intervalFound = True
                goodMsg += 'interval ' + thisQual[1]
        if not startFound: failed = 3
        if not endFound: failed = 4
        if not stationFound: failed = 5
        if not intervalFound: failed = 6
    elif thisCmd == 'music':
        goodMsg = 'You have selected the song How High The Moon. The Sarah Vaughan cover on YouTube is recommended.'
    else:
        failed = 2

    if failed > 0: return dict(title='API', message='Dammit qualifier parse failed with error = ' + str(failed), year=datetime.now().year)
    else:
        return dict(title='API', message=goodMsg, year=datetime.now().year)

# kilroy: in this case we look for a filename fitting the regular expression *\.png where
#   I put hockney.png in the appropriate folder. So url/images/hockney.png or url/image/hockney.png works...
#   but without suitable machinery (kilroy) it is just vestigial.
@route('/images/<filename:re:.*\.png>')
@route('/image/<filename:re:.*\.png>')
def send_image(filename):
    return static_file(filename, root='./static/images', mimetype='image/png')

# kilroy added these routes to bind to static pdf files
#   The better way to do this is with a /pdf/Name approach but I'm in a hurry at the moment.
@route('/pdf/<static_pdf_file>')
def pdf_chooser(static_pdf_file):
    theText = str(static_pdf_file);
    theBase = theText.split('.')[0]
    allowedBases = ['Doc01_PythonWebFrameworkBasics', \
                    'Doc02_Djargon_on_Azure_Add_PDF_Button', \
                    'Doc03_AzureAPIBasics', \
                    'Doc04_AzureContentDeliveryNetworkBasics', \
                    'Doc05_JupyterNotebookBasics', \
                    'Doc06_MapBasics', \
                    'Doc07_BDSUserInterfaceBasics', \
                    'Doc08_BDSQueryBasics', \
                    'Doc09_BDSMetadataBasics', \
                    'Doc10_BDSMATLABFunctionsInMETemplates', \
                    'Doc11_BDSEEMPARAFAC', \
                    'Doc12_BDSDataEgress', \
                    'Doc20_PythonBasics', \
                    'Doc30_DIF_API',\
                    'Doc40_AWS_IOT_Arduino_Yun',\
                    'Doc41_AWSCloudFormationNetworkClusterBasics',\
                    'Doc42_HIPAA_on_AWS',\
                    'Doc43_Jupyter_on_AWS',\
                    'Doc44_Rosetta_on_AWS',\
                    'Doc45_IAM_on_AWS',\
                    'Doc46_EC2_Resources_on_AWS',\
                    'Doc70_OMC_Charter',\
                    'Doc71_OMC_Problems',\
                    'Doc72_OMC_Dialogs',\
                    'Doc73_OMC_FirstHandout',\
                    'Doc74_OMC_ConsentForm',\
                    'Doc75_OMC_TeamProjects',\
                    'Doc76_OMC_Journal',\
                    'Doc77_OMC_Constitution'\
                    ]
    if theBase in allowedBases:
        return static_file(theBase + '.pdf', root='./static/pdf/')


from t2listing import *
import numpy as np
from t2data import *

print('Parsing output file\n')

liquid_density_kgPm3   = 1000
water_molecular_weight = 0.018
kpaPpa                 = 1.e-3
R_value                = 8.3145
mPmm                   = 1.e-3
dayPs                  = 1./(3600*24)
T_kelven               = 273.15

title = 'flow.out'
# #--- post-process the output ---------------------------
lst = t2listing(title)
dat = t2data('flow.inp')

# #read element and connection
connection_first_distance  = np.array([blk.distance[0] for blk in dat.grid.connectionlist])
element_coordinate_m       = np.array([j.centre for j in inp.grid.blocklist])
element_x_ay_all           = element_coordinate_m[:,0]
element_y_ay_all           = element_coordinate_m[:,1]
element_z_ay_all           = element_coordinate_m[:,2]
ele_depth_m                = -element_z_ay_all
con_depth_m                = -1*(element_z_ay_all[1:]-connection_first_distance)

# #read element column
#lst.element.column_name
#['P', 'T', 'SG', 'SL', 'XAIRG', 'XAIRL', 'PAIR', 'PCAP', 'DG', 'DL']
# XAIRG   ----   mass fraction of air in gas phase
# XAIRL   ----   mass fraction of air in liquid phase  ???? what does this mean?
# PAIR    ----   parial pressure of air ( i think it is air pressure)
# PCAP    ----   capillary pressure
# DL      ----   liquid density
# DG      ----   gas density
gas_pressure_xt_mtx_pa            = np.array([lst.history(('e',i,'P'   ))[1] for i in lst.element.row_name])
temperature_degree_xt_mtx         = np.array([lst.history(('e',i,'T'   ))[1] for i in lst.element.row_name])
gas_saturation_xt_mtx             = np.array([lst.history(('e',i,'SG'  ))[1] for i in lst.element.row_name])
liq_saturation_xt_mtx             = np.array([lst.history(('e',i,'SL'  ))[1] for i in lst.element.row_name])
vapor_mass_fraction_in_gas_xt_mtx = 1-np.array([lst.history(('e',i,'XAIRG'))[1] for i in lst.element.row_name])
air_pressure_xt_mtx_pa            = np.array([lst.history(('e',i,'PAIR'))[1] for i in lst.element.row_name])
capillary_pressure_xt_mtx_pa      = np.array([lst.history(('e',i,'PCAP'))[1] for i in lst.element.row_name])
gas_density_xt_mtx_kgPm3          = np.array([lst.history(('e',i,'DG' ) )[1] for i in lst.element.row_name])          # two brackets is needed!
liq_density_xt_mtx_kgPm3          = np.array([lst.history(('e',i,'DL'  ))[1] for i in lst.element.row_name])

# #read connection column
#lst.connection.column_name
#['FLOH', 'FLOH/FLOF', 'FLOF', 'FLO(GAS)', 'VAPDIF', 'FLO(LIQ.)', 'VEL(GAS)', 'VEL(LIQ.)']
gas_flow_xt_mtx_kgPs           = -np.array([lst.history(('c',i,'FLO(GAS)'))[1] for i in lst.connection.row_name])
vapor_diff_flow_xt_mtx_kgPs    = -np.array([lst.history(('c',i,'VAPDIF'))[1] for i in lst.connection.row_name])    # c means connection, but why negative? negative may related to BETAX, which is -1 in this case
liquid_flow_xt_mtx_kgPs        = -np.array([lst.history(('c',i,'FLO(LIQ.)'))[1] for i in lst.connection.row_name])

# #unit change
gas_flow_xt_mtx_mmPday         = gas_flow_xt_mtx_kgPs/dat.grid.connectionlist[0].area/(liq_density_xt_mtx_kgPm3[1:]+liq_density_xt_mtx_kgPm3[:-1])/2/mPmm/dayPs
vapor_diff_flow_xt_mtx_mmPday  = vapor_diff_flow_xt_mtx_kgPs/dat.grid.connectionlist[0].area/(liq_density_xt_mtx_kgPm3[1:]+liq_density_xt_mtx_kgPm3[:-1])/2/mPmm/dayPs
liquid_flow_xt_mtx_mmPday      = liquid_flow_xt_mtx_kgPs/dat.grid.connectionlist[0].area/(liq_density_xt_mtx_kgPm3[1:]+liq_density_xt_mtx_kgPm3[:-1])/2/mPmm/dayPs

# gas_adv_velocity_mPs         = -1*np.array([lst.history(('c',lst.connection.row_name[i],'VEL(GAS)'))[1] for i in range(lst.connection.num_rows)])
# gas_adv_velocity_kgPs        = gas_adv_velocity_mPs*gas_density_kgPm3[1:]*dat.grid.connectionlist[0].area*dat.grid.rocktype['SAND '].porosity
# gas_adv_velocity_mmPday      = gas_adv_velocity_mPs*dat.grid.rocktype['SAND '].porosity/mPmm/dayPs
# liquid_adv_velocity_mPs      = -1*np.array([lst.history(('c',lst.connection.row_name[i],'VEL(LIQ.)'))[1] for i in range(lst.connection.num_rows)])
# liquid_adv_velocity_kgPs     = liquid_adv_velocity_mPs*liq_density_kgPm3[1:]*dat.grid.connectionlist[0].area*dat.grid.rocktype['SAND '].porosity
# liquid_adv_velocity_mmPday   = liquid_adv_velocity_mPs*dat.grid.rocktype['SAND '].porosity/mPmm/dayPs
# vapor_adv_velocity_mmPday    = vapor_mass_fraction_in_gas[1:]*gas_adv_velocity_mmPday
# vapor_adv_velocity_kgPs      = vapor_mass_fraction_in_gas[1:]*gas_adv_velocity_kgPs
# vapor_adv_flow_top_mmPday    = vapor_adv_velocity_mmPday[0]
# vapor_adv_flow_second_mmPday = vapor_adv_velocity_mmPday[1]

# #For plot
vapor_adv_xt_mtx_mmPday       = vapor_mass_fraction_in_gas_xt_mtx[1:]*gas_flow_xt_mtx_mmPday
vapor_adv_xt_mtx_kgPs         = vapor_mass_fraction_in_gas_xt_mtx[1:]*gas_flow_xt_mtx_kgPs

liquid_flow_top_mmPday        = liquid_flow_xt_mtx_mmPday[0]
gas_flow_top_mmPday           = gas_flow_xt_mtx_mmPday[0]
vapor_adv_top_mmPday          = vapor_adv_xt_mtx_mmPday[0]
vapor_diff_flow_top_mmPday    = vapor_diff_flow_xt_mtx_mmPday[0]

liquid_flow_second_mmPday     = liquid_flow_xt_mtx_mmPday[1]
gas_flow_second_mmPday        = gas_flow_xt_mtx_mmPday[1]
vapor_adv_second_mmPday       = vapor_adv_xt_mtx_mmPday[1]
vapor_diff_flow_second_mmPday = vapor_diff_flow_xt_mtx_mmPday[1]

water_flow_top_mmPday       = liquid_flow_top_mmPday+vapor_adv_top_mmPday+vapor_diff_flow_top_mmPday
cumsum_water_flow_top_mm    = np.cumsum(water_flow_top_mmPday*np.insert(np.diff(lst.times),0,lst.times[0])*dayPs)
water_flow_second_mmPday    = liquid_flow_second_mmPday+vapor_adv_second_mmPday+vapor_diff_flow_second_mmPday
cumsum_water_flow_second_mm = np.cumsum(water_flow_second_mmPday*np.insert(np.diff(lst.times),0,lst.times[0])*dayPs)

# #read generation column
#lst.generation.column_name
#['GENERATION RATE', 'ENTHALPY', 'FF(GAS)', 'FF(LIQ.)', 'P(WB)']
water_generation_kgPs                 = np.array([lst.history(('g',i,'GENERATION RATE'))[1] for i in lst.generation.row_name])
water_generation_mmPday               = water_generation_kgPs/dat.grid.connectionlist[0].area/liquid_density_kgPm3/mPmm/dayPs
water_generation_vapor_mass_fraction  = np.array([lst.history(('g',i,'FF(GAS)' ))[1] for i in lst.generation.row_name])
water_generation_liquid_mass_fraction = np.array([lst.history(('g',i,'FF(LIQ.)'))[1] for i in lst.generation.row_name])           # what is FF(LIQ) mean and how did it distribute to both?
water_generation_vapor_mmPday         = water_generation_mmPday*water_generation_vapor_mass_fraction
water_generation_liquid_mmPday        = water_generation_mmPday*water_generation_liquid_mass_fraction

# #read primary column
#lst.primary.column_name
#['K(GAS)', 'K(LIQ.)', 'H(GAS)', 'H(LIQ.)', 'DX1', 'DX2', 'DX3', 'X1', 'X2', 'X3']
# gas_relative_permeability   = np.array([lst.history(('p',lst.primary.row_name[i],'K(GAS)'))[1] for i in range(lst.primary.num_rows)])  TO 203004 what is primary
# liq_relative_permeability   = np.array([lst.history(('p',lst.primary.row_name[i],'K(LIQ.)'))[1] for i in range(lst.primary.num_rows)])
# GAS_H                       = np.array([lst.history(('p',lst.primary.row_name[i],'H(GAS)'))[1] for i in range(lst.primary.num_rows)])
# Liq_H                       = np.array([lst.history(('p',lst.primary.row_name[i],'H(LIQ.)'))[1] for i in range(lst.primary.num_rows)])
# First_thermodynamic_change  = np.array([lst.history(('p',lst.primary.row_name[i],'DX1'))[1] for i in range(lst.primary.num_rows)])
# Second_thermodynamic_change = np.array([lst.history(('p',lst.primary.row_name[i],'DX2'))[1] for i in range(lst.primary.num_rows)])
# Third_thermodynamic_change  = np.array([lst.history(('p',lst.primary.row_name[i],'DX3'))[1] for i in range(lst.primary.num_rows)])
# First_thermodynamic_var     = np.array([lst.history(('p',lst.primary.row_name[i],'X1'))[1] for i in range(lst.primary.num_rows)])
# Second_thermodynamic_var    = np.array([lst.history(('p',lst.primary.row_name[i],'X2'))[1] for i in range(lst.primary.num_rows)])
# Third_thermodynamic_var     = np.array([lst.history(('p',lst.primary.row_name[i],'X3'))[1] for i in range(lst.primary.num_rows)])