"""
match.py

Takes as input csv files for students, mentors and questions and outputs (near)
optimal groups.

Written by Kenneth Huynh
"""
# need to double check references here (does updating pair.student update student?)

import csv
import os

TEST_FOLDER = os.path.dirname(os.path.abspath(__file__)) + "/tests"

class Person:
    def __init__(self):
        self.responses = []
        self.inGroup = False

class Student(Person):
    def __init__(self, id):
        Person.__init__(self)
        self.id = id
        self.pairs = []

class Mentor(Person):
    def __init__(self, id):
        Person.__init__(self)
        self.id = id

class Question:
    def __init__(self):
        self.n_responses = 0
        self.weight = 0

class Pair:
    def __init__(self, student, mentor):
        self.student = student
        self.mentor = mentor

        self.score = 0

class Group:
    MAX_STUDENTS = 3

    def __init__(self):
        self.students = []
        self.mentor = None

'''
add_student creates a new Student object and adds it to the students array based
on their responses
'''
def add_student(responses, students, id):
    new_student = Student(id)
    # add all the responses for each question into the new student's responses
    for response in responses:
        # remove brackets from response
        response = response.strip("()")
        response_list = response.split(";")

        # convert everything to integers
        for index in range(len(response_list)):
            response_list[index] = int(response_list[index])

        new_student.responses.append(response_list)

    # add new student to students
    students.append(new_student)

'''
add_mentor creates a new Mentor object and adds it to the mentors array based
on their responses
'''
def add_mentor(responses, mentors, id):
    new_mentor = Mentor(id)
    # add all the responses for each question into the new mentor's responses
    for response in responses:
        # remove brackets from response
        response = response.strip("()")
        response_list = response.split(";")

        # convert everything to integers
        for index in range(len(response_list)):
            response_list[index] = int(response_list[index])

        new_mentor.responses.append(response_list)

    # add new mentor to mentors
    mentors.append(new_mentor)

'''
add_question creates a new Question object and adds it to the questions array
'''
def add_question(row, questions):
    new_question = Question()
    new_question.n_responses = int(row[0])
    new_question.weight = int(row[1])

    questions.append(new_question)

'''
read_inputs reads in the input data and creates mentors, students and questions
based off it
'''
def read_inputs(mentors, students, questions):
    # students
    student_id = 0
    with open(os.path.join(TEST_FOLDER,'s_input.txt')) as csv_file:
        csv_reader = csv.reader(csv_file, delimiter=',')
        line_count = 0
        for row in csv_reader:
            if line_count == 0:
                # header row (do nothing)
                line_count = line_count + 1
            else:
                add_student(row, students, student_id)
                line_count = line_count + 1

                student_id = student_id + 1

    # mentors
    mentor_id = 0
    with open(os.path.join(TEST_FOLDER, 'm_input.txt')) as csv_file:
        csv_reader = csv.reader(csv_file, delimiter=',')
        line_count = 0
        for row in csv_reader:
            if line_count == 0:
                # header row (do nothing)
                line_count = line_count + 1
            else:
                add_mentor(row, mentors, mentor_id)
                line_count = line_count + 1

                mentor_id = mentor_id + 1

    # questions
    with open(os.path.join(TEST_FOLDER, 'questions.txt')) as csv_file:
        csv_reader = csv.reader(csv_file, delimiter=',')
        line_count = 0
        for row in csv_reader:
            if line_count == 0:
                # header row (do nothing)
                line_count = line_count + 1
            else:
                add_question(row, questions)
                line_count = line_count + 1

'''
generate_pairs creates all combinations of mentors and students
'''
def generate_pairs(students, mentors, pairs):
    for student in students:
        for mentor in mentors:
            new_pair = Pair(student, mentor)
            pairs.append(new_pair)

'''
calculate_pair_score calculates the score of each pair based off the matches
in their responses to the questions
'''
def calculate_pair_score(pair, questions):
    # iterate through questions
    for question_i in range(len(questions)):
        matched_responses = 0
        # iterate through each possible response and compare between student/mentor
        for response_i in range(questions[question_i].n_responses):
            if pair.student.responses[question_i][response_i] == pair.mentor.responses[question_i][response_i]:
                matched_responses = matched_responses + 1

        # increment pair score
        pair.score = pair.score + matched_responses * questions[question_i].weight

'''
associate_pairs updates the pairs array associated with each student from the
global pairs array
'''
def associate_pairs(pairs, students):
    for student in students:
        for pair in pairs:
            if pair.student == student:
                student.pairs.append(pair)

'''
generate_groups generates (as close to) optimal groups (as possible) based off
the pairs
'''
def generate_groups(groups, students):
    # iterate for the number of students in each group
    for n_students_in_group in range(Group.MAX_STUDENTS):
        # iterate through each student's pairs and find the highest scored pair
        optimal_pairs = []

        for student in students:
            mentor = None
            score = 0

            for pair in student.pairs:
                if (pair.score > score) and (pair.student.inGroup == False):
                    score = pair.score
                    mentor = pair.mentor

            # if mentor != None:
            optimal_pairs.append(Pair(student, mentor))

        # iterate through the highest scoring pairs per student by mentor and add
        # to groups
        for pair in optimal_pairs:
            # if initial
            if n_students_in_group == 0:
                if (pair.student.inGroup == False) and (pair.mentor.inGroup == False):
                    new_group = Group()
                    new_group.students.append(pair.student)
                    new_group.mentor = pair.mentor
                    groups.append(new_group)

                    pair.student.inGroup = True
                    pair.mentor.inGroup = True
            else:
                if pair.student.inGroup == False:
                    # get group with optimal mentor
                    for group in groups:
                        if pair.mentor == group.mentor:
                            group.students.append(pair.student)

                            pair.student.inGroup = True

'''
output_groups takes the generated groups and outputs it into a csv file
'''
def output_groups(groups):
    with open(os.path.join(TEST_FOLDER,'groups.csv'), mode='w') as groups_file:
        groups_writer = csv.writer(groups_file, delimiter=',')

        for group in groups:
            out_list = [group.mentor.id]
            for student in group.students:
                out_list.append(student.id)

            groups_writer.writerow(out_list)

def main():
    # initialise variables
    mentors = []
    students = []
    questions = []
    pairs = []
    groups = []

    # read inputs
    read_inputs(mentors, students, questions)

    # generate pairs
    generate_pairs(students, mentors, pairs)

    # calculate score for each pair
    for pair in pairs:
        calculate_pair_score(pair, questions)

    # associate pairs with each student
    associate_pairs(pairs, students)

    # generate groups based on optimal pairings for each student
    generate_groups(groups, students)

    # output groups
    output_groups(groups)

if __name__ == '__main__':
    main()


# https://www.codewars.com/kata/51c8e37cee245da6b40000bd/python

# Strip Comments

# 4 kyu

def strip_comments(strng, markers):
  nStrng = strng.splitlines()
  for i in markers:
    nStrng = [s.split(i)[0].rstrip() for s in nStrng]
  return '\n'.join(nStrng)


# -*- coding: utf-8 -*-
from PyQt5.QtCore import QAbstractTableModel, Qt, QVariant, QSortFilterProxyModel, pyqtSignal, QModelIndex
from PyQt5.QtWidgets import QPushButton, QItemDelegate, QHBoxLayout, QWidget
import json

class GameConfig(object):
    def __init__(self):
        self._id = 0
        self._user = None
        self._game = None
        self._config = None
        self._platform = None
        self._name = ""

    @property
    def Config(self):
        return json.loads(self._config)

    @property
    def Name(self):
        return self._name

    @property
    def Id(self):
        return self._id

    @property
    def Game(self):
        return self._game

    @Game.setter
    def Game(self, v):
        self._game = v

    @property
    def User(self):
        return self._user

    @User.setter
    def User(self, v):
        self._user = v

    @property
    def Platform(self):
        return self._platform

    @Platform.setter
    def Platform(self, v):
        self._platform = v

    def parseQuery(self, query):
        self._id = query.value('id')
        self._config = query.value('config')
        self._name = query.value('name')

        return self

    def __str__(self):
        return "{GameConfig [name=%s]}" % self._name

class GameConfigTableModel(QAbstractTableModel):
    HheadLabels = ['ID', '名称', '操作']
    HheadKey = ['Id', 'Name']

    def headerData(self, p_int, Qt_Orientation, role=None):
        if role != Qt.DisplayRole:
            return QVariant()
        if Qt_Orientation == Qt.Horizontal:
            return self.HheadLabels[p_int]
        else:
            return QVariant()

    # def flags(self, index):
    #     print(index.column(), "flags")
    #     if (index.column() == 0):
    #         return Qt.ItemIsEditable | Qt.ItemIsEnabled
    #     else:
    #         return Qt.ItemIsEnabled

    def rowCount(self, parent=None, *args, **kwargs):
        return len(self.dataList)

    def columnCount(self, parent=None, *args, **kwargs):
        return len(self.HheadLabels)

    def data(self, QModelIndex, role=None):
        if not QModelIndex.isValid():
            return QVariant()

        col = QModelIndex.column()
        row = QModelIndex.row()
        if role == Qt.TextAlignmentRole:
            return Qt.AlignCenter
        elif role == Qt.DisplayRole:
            if col > len(self.HheadKey) - 1:
                return QVariant()
            else:
                key = self.HheadKey[col]
                pro = getattr(GameConfig, key)
                currentData = pro.fget(self.dataList[row])
                return currentData

    def getRowData(self, QModelIndex):
        if not QModelIndex.isValid():
            return QVariant()

        return self.dataList[QModelIndex.row()]

    def removeRow(self, qIndex):
        print("removeRow", qIndex.row())
        self.beginRemoveRows(qIndex.parent(), qIndex.row(), qIndex.row())
        del self.dataList[qIndex.row()]
        self.beginRemoveRows()
        return True

    def setDataList(self, dataList):
        self.beginResetModel()
        self.dataList = dataList
        self.endResetModel()

class GameConfigSortProxyModel(QSortFilterProxyModel):
    def __init__(self):
        super().__init__()
        #  If the value is -1, the keys will be read from all columns.
        self.setFilterKeyColumn(-1)

    def getRowData(self, itemIndex):
        sorceIndex = self.mapToSource(itemIndex)
        return self.sourceModel().getRowData(sorceIndex)

    def removeRow(self, itemIndex):
        sorceIndex = self.mapToSource(itemIndex)
        return self.sourceModel().removeRow(sorceIndex)

class ButtonDeletage(QItemDelegate):
    selectSignal = pyqtSignal(object)
    deleteSignal = pyqtSignal(object)

    def __init__(self, parent):
        super().__init__(parent)

    def paint(self, painter, option, index):
        if not self.parent().indexWidget(index):
            selButton = QPushButton(
                self.tr('选择'),
                self.parent(),
            )
            delButton = QPushButton(
                self.tr('删除'),
                self.parent(),
            )
            delButton.index = index
            selButton.index = index
            delButton.setObjectName("delete")
            selButton.setObjectName("select")
            delButton.clicked.connect(self.onButtonClick)
            selButton.clicked.connect(self.onButtonClick)
            hBoxLayout = QHBoxLayout()
            hBoxLayout.addWidget(selButton)
            hBoxLayout.addWidget(delButton)
            hBoxLayout.setContentsMargins(0, 0, 0, 0)
            hBoxLayout.setAlignment(Qt.AlignCenter)
            widget = QWidget()
            widget.setLayout(hBoxLayout)
            self.parent().setIndexWidget(index, widget)

    def onButtonClick(self):
        sender = self.sender()
        print("onButtonClick", sender.objectName(), sender.index.row())
        if sender.objectName() == "delete":
            self.deleteSignal.emit(sender.index)
        elif sender.objectName() == "select":
            self.selectSignal.emit(sender.index)


from ConfigSpace.configuration_space import ConfigurationSpace
from ConfigSpace.hyperparameters import CategoricalHyperparameter, \
    UniformIntegerHyperparameter

from dswizard.components.base import PreprocessingAlgorithm
from dswizard.components.util import check_for_bool, HANDLES_NOMINAL_CLASS, HANDLES_MISSING, HANDLES_NOMINAL, \
    HANDLES_NUMERIC, HANDLES_MULTICLASS

class PolynomialFeaturesComponent(PreprocessingAlgorithm):
    def __init__(self, degree: int = 2, interaction_only: bool = False, include_bias: bool = True, order: str = "C"):
        super().__init__('polynomial')
        self.degree = degree
        self.interaction_only = interaction_only
        self.include_bias = include_bias
        self.order = order

    def to_sklearn(self, n_samples: int = 0, n_features: int = 0, **kwargs):
        from sklearn.preprocessing import PolynomialFeatures

        self.interaction_only = check_for_bool(self.interaction_only)
        self.include_bias = check_for_bool(self.include_bias)

        return PolynomialFeatures(degree=self.degree,
                                  interaction_only=self.interaction_only,
                                  include_bias=self.include_bias,
                                  order=self.order)

    @staticmethod
    def get_properties():
        return {'shortname': 'PolynomialFeatures',
                'name': 'PolynomialFeatures',
                HANDLES_MULTICLASS: True,
                HANDLES_NUMERIC: True,
                HANDLES_NOMINAL: False,
                HANDLES_MISSING: False,
                HANDLES_NOMINAL_CLASS: True}

    @staticmethod
    def get_hyperparameter_search_space(**kwargs):
        # More than degree 3 is too expensive!
        degree = UniformIntegerHyperparameter("degree", 2, 3, 2)
        interaction_only = CategoricalHyperparameter("interaction_only", [False, True], False)
        include_bias = CategoricalHyperparameter("include_bias", [True, False], True)

        cs = ConfigurationSpace()
        cs.add_hyperparameters([degree, interaction_only, include_bias])

        return cs


#!/usr/bin/env python

import re

class Instr:
    patt = re.compile(r"(.+) (\d+)")

    def __init__(self, direct, n):
        self.dir = direct
        self.n = n

    def __repr__(self):
        return "Instr(dir={} n={})".format(self.dir, self.n)

    @classmethod
    def apply(cls, text):
        m = Instr.patt.match(text)
        if m:
            return Instr(m.group(1), int(m.group(2)))
        else:
            return None

def move(filename, debug=False):
    x = 0
    y = 0
    with open(filename, "r") as fh:
        for l in fh:
            ins = Instr.apply(l.strip())
            if ins.dir == 'forward':
                x += ins.n
            elif ins.dir == 'up':
                y -= ins.n
            elif ins.dir == 'down':
                y += ins.n
            if debug:
                print(ins, x, y)
    return x*y

def move2(filename, debug=False):
    x = 0
    y = 0
    aim = 0
    with open(filename, "r") as fh:
        for l in fh:
            ins = Instr.apply(l.strip())
            if ins.dir == 'forward':
                x += ins.n
                y += aim * ins.n
            elif ins.dir == 'up':
                aim -= ins.n
            elif ins.dir == 'down':
                aim += ins.n
            if debug:
                print(ins, x, y, aim)
    return x*y

print(move("test.txt", debug=True))

print(move("input.txt"))

print(move2("test.txt", debug=True))

print(move2("input.txt"))

        #m=patt.match(l.strip())
        #if m:
        #    print(m.group(1), m.group(2))
        #print(l.strip())


"""
Tests for :meth:`ugrid_checks.check.Checker.structure_report()`.

"""
import re

import numpy as np
from pytest import fixture
from ugrid_checks.nc_dataset_scan import NcDimSummary, NcVariableSummary

from .. import simple_scan
from ... import cdl_scanner
from ..test_check_dataset__checks import (
    DatasetChecker,
    scan_0d_mesh,
    scan_1d_mesh,
    scan_2d_mesh,
)

# Yes, we do need these imports.
cdl_scanner
simple_scan
scan_0d_mesh
scan_1d_mesh
scan_2d_mesh

from ugrid_checks.check import Checker

@fixture
def nomesh_scan(cdl_scanner):
    test_cdl = """
    netcdf data_C4 {
    dimensions:
        x = 8 ;

    variables:
        double var_1(x) ;
    }
    """
    scan = cdl_scanner.scan(test_cdl)
    return scan

@fixture
def simple_scan_w_nonmesh(simple_scan):
    # Add an extra non-mesh dim and variable, for nonmesh output testing.
    scan = simple_scan
    scan.dimensions["x_dim"] = NcDimSummary(3)
    scan.variables["x_data"] = NcVariableSummary(
        name="x_data", dimensions=("x_dim",), shape=(3,), dtype=np.int32
    )
    return scan

class Test_StructureReport(DatasetChecker):
    def get_report(self, scan, include_nonmesh=False):
        checker = Checker(scan, max_mb_checks=0)
        text = checker.structure_report(include_nonmesh=include_nonmesh)
        return text

    def expect_in_text(self, text, expects):
        """
        Check that a (list of) expected regular-expressions occur in a text,
        in the order given.

        If they aren't all present, produce an informative message showing
        which are missing.
        N.B. uses "assert list == []", for intelligible error messages.
        E.G.
            self.expect_in_text(['XXXX', '.*'], "this"") == []
            -->
                ['XXXX'] != []

                Expected :[]
                Actual   :['XXXX']

        """
        if isinstance(expects, str):
            expects = [expects]
        unmatched_expects = []
        for expect in expects:
            # Match each 'expect' entry to the result text
            match = re.search(expect, text, re.DOTALL)
            if match:
                # Item found.  Continue from after match
                text = text[match.end() :]
            else:
                # This not found.  Continue from same place
                unmatched_expects.append(expect)
        assert unmatched_expects == []

    def test_empty__nomesh__meshonly(self, nomesh_scan):
        result = self.get_report(nomesh_scan)
        expect = "Meshes : <none>"
        self.expect_in_text(result, expect)

    def test_empty__nomesh__w_nonmesh(self, nomesh_scan):
        # Do "include_nonmesh", on a no-mesh dataset.
        result = self.get_report(nomesh_scan, include_nonmesh=True)
        expects = [
            "Meshes : <none>",
            r'Non-mesh[^\n]*\n *dimensions:\n *"x"',
            r'variables:\n *"var_1"',
        ]
        self.expect_in_text(result, expects)

    def test_basic__meshonly(self, simple_scan_w_nonmesh):
        # Simple case : just for this, do a full text comparison.
        text = self.get_report(simple_scan_w_nonmesh)
        text_lines = text.split("\n")
        expected_lines = [
            "Meshes",
            '    "topology"',
            '        node("num_node")',
            '            coordinates : "node_lat", "node_lon"',
            "",
            "Mesh Data Variables",
            '    "sample_data"',
            '        mesh : "topology"',
            '        location : "node"',
        ]
        assert text_lines == expected_lines

    def test_basic__w_nonmesh(self, simple_scan_w_nonmesh):
        # Check the "nonmesh" output : another full-text check.
        text = self.get_report(simple_scan_w_nonmesh, include_nonmesh=True)
        text_lines = text.split("\n")
        expected_lines = [
            "Meshes",
            '    "topology"',
            '        node("num_node")',
            '            coordinates : "node_lat", "node_lon"',
            "",
            "Mesh Data Variables",
            '    "sample_data"',
            '        mesh : "topology"',
            '        location : "node"',
            "",
            "Non-mesh variables and/or dimensions",
            "    dimensions:",
            '        "x_dim"',
            "    variables:",
            '        "x_data"',
        ]
        assert text_lines == expected_lines

    def test_missing_node_coords(self, simple_scan):
        scan = simple_scan
        del scan.variables["topology"].attributes["node_coordinates"]
        text = self.get_report(scan)
        # text = self.get_report(scan)
        expects = [
            (
                'Meshes\n *"topology"\n *'
                r"\<\? no node coordinates or dimension \?\>"
            ),
        ]
        self.expect_in_text(text, expects)

    def test_multiple_locations(self, scan_2d_mesh):
        # Test with locations other than 'node'
        text = self.get_report(scan_2d_mesh)
        expects = [
            (
                r'  node\("num_node"\)'
                '\n *coordinates : "node_lat", "node_lon"\n'
            ),
            r'  face\("face_dim"\)\n',
            '  face_node_connectivity : "face_nodes"\n',
            '  coordinates : "latitude", "longitude"\n',
        ]
        self.expect_in_text(text, expects)

    def test_no_face_coords(self, scan_2d_mesh):
        # Test with node coords but no face coords
        del scan_2d_mesh.variables["topology"].attributes["face_coordinates"]
        text = self.get_report(scan_2d_mesh)
        assert 'coordinates : "latitude", "longitude"' not in text

    def test_lis(self, scan_0d_mesh):
        # Test with a location-index set
        scan = scan_0d_mesh
        self._convert_to_lis(scan)
        text = self.get_report(scan)
        expects = [
            "\nLocation Index Sets",
            r'  "lis" \(lis_nodes\)',
            '  mesh : "topology"',
            "  location : node",
        ]
        self.expect_in_text(text, expects)

    def test_optional_connectivities(self, scan_2d_mesh):
        scan = scan_2d_mesh
        self._add_edges(scan, with_facedge_conn=True)
        text = self.get_report(scan)
        expects = [
            "optional connectivities",
            'face_edge_connectivity : "face_edges_var"',
        ]
        self.expect_in_text(text, expects)

    def test_blank_connectivity(self, scan_2d_mesh):
        scan = scan_2d_mesh
        self._add_edges(scan)
        # Add an extra 'face_edge_connectitivity' attribute that does *not*
        # target a valid variable.
        meshvar = scan.variables["topology"]
        meshvar.attributes["face_edge_connectivity"] = "bad_feconn"
        # This should appear with a reservation
        text = self.get_report(scan)
        expects = [
            "optional connectivities",
            r'  face_edge_connectivity : \<\?nonexistent\?\> "bad_feconn"',
        ]
        self.expect_in_text(text, expects)

    def test_orphan_connectivities(self, scan_2d_mesh):
        scan = scan_2d_mesh
        self._add_edges(scan, with_facedge_conn=True)
        del scan.variables["topology"].attributes["face_edge_connectivity"]
        text = self.get_report(scan)
        expects = [
            ("\n" r"\?\? Connectivities with no mesh \?\?" "\n"),
            r'  "face_edges_var" \("face_n_edges", "num_vertices"\)',
            '  cf_role = "face_edge_connectivity"',
        ]
        self.expect_in_text(text, expects)

    def test_extra_datavar_prop(self, scan_2d_mesh):
        # Test output when a mesh datavar has an extra 'lis' attribute
        scan = scan_2d_mesh
        scan.variables["sample_data"].attributes["location_index_set"] = "junk"
        text = self.get_report(scan)
        expects = [
            "Mesh Data Variables",
            '  "sample_data"',
            '    location : "face"',
            r'    location_index_set : \<\?unexpected\?\> "junk"',
        ]
        self.expect_in_text(text, expects)

    def test_missing_datavar_prop(self, scan_2d_mesh):
        # Test output when a mesh datavar has no 'location' attribute
        scan = scan_2d_mesh
        del scan.variables["sample_data"].attributes["location"]
        text = self.get_report(scan)
        expects = [
            "Mesh Data Variables",
            '  "sample_data"',
            '    mesh : "topology"',
            r"    location : \<\?missing\?\>",
        ]
        self.expect_in_text(text, expects)

    def test_nonmesh_coordbounds(self, scan_1d_mesh):
        scan = scan_1d_mesh
        # Add an extra variable, suitable for node-longitude bounds
        scan.variables["lon_bounds"] = NcVariableSummary(
            name="lon_bounds",
            dimensions=["num_node", "num_ends"],
            shape=(8, 2),
            dtype=np.dtype(np.float32),
        )
        # At this point, it appears as a 'non-mesh var'
        text = self.get_report(scan, include_nonmesh=True)
        expects = [
            "Non-mesh variables and/or dimensions",
            "  variables:",
            '    "lon_bounds"',
        ]
        self.expect_in_text(text, expects)

        # Reference the new var as a mesh coord bounds
        scan.variables["node_lon"].attributes["bounds"] = "lon_bounds"
        text = self.get_report(scan, include_nonmesh=True)
        # Now *NO* "non-mesh" section, because it is considered a mesh var
        assert "Non-mesh" not in text

    def test_nonmesh_connectivity_dims(self, scan_2d_mesh):
        scan = scan_2d_mesh
        self._add_edges(scan)
        # Add an extra dimension
        scan.dimensions["face_n_edges"] = NcDimSummary(5)
        # This should appear in a non-mesh section
        text = self.get_report(scan, include_nonmesh=True)
        expects = [
            "Non-mesh variables and/or dimensions",
            "  dimensions:",
            '    "face_n_edges"',
        ]
        self.expect_in_text(text, expects)
        # No optional connectivitites
        assert "optional conn" not in text

        # Now add a face-edge connectivity, mapping this dimension
        self._add_faceedge_conn(scan)
        text = self.get_report(scan, include_nonmesh=True)
        expects = [
            "optional connectivities",
            '  face_edge_connectivity : "face_edges_var"',
        ]
        self.expect_in_text(text, expects)
        # Now *NO* "non-mesh" section, because it is considered a mesh var
        assert "Non-mesh" not in text

    def test_nonmesh_orphan_connectivity_dims(self, scan_0d_mesh):
        scan = scan_0d_mesh
        # Add an extra dim..
        scan.dimensions["extra_dim"] = NcDimSummary(3)
        # .. which appears as a 'nonmesh' dim
        text = self.get_report(scan, include_nonmesh=True)
        expects = [
            "Non-mesh variables and/or dimensions",
            "  dimensions:",
            '    "extra_dim"',
        ]
        self.expect_in_text(text, expects)
        assert "Connectivities with no mesh" not in text

        # Add an orphan connectivity which maps this dim..
        scan.variables["extra_conn"] = NcVariableSummary(
            name="extra",
            dimensions=(
                "num_node",
                "extra_dim",
            ),
            shape=(3,),
            dtype=np.dtype(np.int64),
            attributes={"cf_role": "face_edge_connectivity"},
        )
        # .. which *prevents* the dim appearing as a non-mesh section
        text = self.get_report(scan, include_nonmesh=True)
        expects = [
            r"\?\? Connectivities with no mesh \?\?",
            r'  "extra_conn" \("num_node", "extra_dim"\)',
            '  cf_role = "face_edge_connectivity"',
        ]
        self.expect_in_text(text, expects)
        assert "Non-mesh" not in text


import math

UCODE_LENGTH = 80
UCODE_ADDR_LENGTH = 12

bits = ("rd", )

enums = (
    ("command", ("halt", "bdos",)),
    ("ucode_goto", ("decode1", "decode_cb", "decode_xy", "decode_xybits", "decode_ed", "goto_now", "goto_ncc", "goto_nccr", "goto_z", "goto_loop")),
    ("ram_addr_sel", ("addr_sel_ip", "addr_sel_dout16", "addr_sel_alu16", "addr_sel_tmp", "addr_sel_tmp_p1", "io_addr_sel_dout8", "io_addr_sel_tmp_lo")),
    ("read_target", ("rd_ir", "rd_tmp_lo", "rd_tmp_hi", "rd_tmp2_lo", "rd_tmp2_hi", "rd_tmp_from_dout16", "rd_tmp2_from_dout16", "rd_xy_off", "rd_tmp_lo_from_io")),
    ("ram_wr_sel", ("ram_wr_dout8", "ram_wr_tmp_lo", "ram_wr_tmp_hi", "ram_wr_tmp2_lo", "ram_wr_tmp2_hi", "ram_wr_ip_hi", "ram_wr_ip_lo", "io_wr_dout8", "io_wr_tmp_lo", "io_wr_0", "ram_wr_alu8")),
    ("din8_target", ("din8_dst_ir210", "din8_dst_ir543", "din8_dst_ir543_no_xy", "din8_dst_a", "din8_dst_b", "din8_dst_h", "din8_dst_l","din8_dst_r", "din8_dst_i" )),
    ("din8_source", ("din8_src_dout8", "din8_src_ram", "din8_src_alu8", "din8_src_io")),
    ("dout8_sel", ("dout8_sel_ir543", "dout8_sel_ir210_no_xy", "dout8_sel_ir210",
                   "dout8_sel_rega", "dout8_sel_regb", "dout8_sel_regc", "dout8_sel_reg_h", "dout8_sel_reg_l",
                   "dout8_sel_reg_r", "dout8_sel_reg_i")),
    ("din16_source", ("din16_src_tmp", "din16_src_tmp2", "din16_src_dout16", "din16_src_alu16")),
    ("din16_sel", ("din16_sel_ir54rp1", "din16_sel_ir54rp2", "din16_sel_sp","din16_sel_hl","din16_sel_de", "din16_sel_bc")),
    ("dout16_sel", ("dout16_sel_bc", "dout16_sel_hl", "dout16_sel_sp", "dout16_sel_de", "dout16_sel_ir54rp","dout16_sel_ir54rp2",)),
    ("flags_source", ("flags_source_alu8", "flags_source_alu16", "flags_source_alu_blk")),
    ("alu8_source", ("alu8_src_ram", "alu8_src_dout8","alu8_src_tmp_lo", "alu8_src_io", "alu8_src_ram_addr")),
    ("alu8_op", ("alu8_op_ip543", "alu8_op_ip543b", "alu8_op_ip543_cbrot", "alu8_op_inc", "alu8_op_dec", "alu8_op_bit",
                 "alu8_op_res", "alu8_op_set", "alu8_op_in", "alu8_op_neg", "alu8_op_rrd_1", "alu8_op_rrd_2", "alu8_op_rld_1", "alu8_op_rld_2", "alu8_op_bit_fixup")),
    ("ip_op", ("inc_ip", "ip_from_tmp", "ip_from_rel_tmp", "ip_from_rst")),
    ("alu16_op", ("alu16_op_inc", "alu16_op_dec", "alu16_op_dec_ld", "alu16_op_add","alu16_op_sbc","alu16_op_adc")),
    ("xy_sel", ("xy_sel_clear", "xy_sel_ix", "xy_sel_iy")),
    ("blk_latch", ("blk_latch_data","blk_latch_io",)),
    ("blk_op", ("blk_op_ld", "blk_op_cp", "blk_op_out", "blk_op_ini", "blk_op_ind")),
    )

def generate_ucode_headers():
  global bits
  global enums
  commands = {} # map of commands to constants
  current = UCODE_ADDR_LENGTH
  with open(HEADER_FILE, "w") as f:
    with open(UCODE_SIGNALS_FILE, "w") as g:
      f.write(f"parameter UCODE_LENGTH = {UCODE_LENGTH};\n")
      f.write(f"parameter UCODE_ADDR_LENGTH = {UCODE_ADDR_LENGTH};\n")
      for command in bits:
        name = command.upper()
        f.write(f"parameter UC_{name} = {1 << current};\n")
        g.write(f"wire uc_{command};\n")
        g.write(f"assign uc_{command} = ucode[{current}];\n")
        current += 1
        commands[command] = f"UC_{name}";
      for enum_name, enum_set in enums:
        n = len(enum_set) + 1 # plus one for not used
        bits = math.ceil(math.log2(n))
        enum_const = enum_name.upper()
        g.write(f"wire [{bits-1}:0] uc_{enum_name};\n")
        g.write(f"assign uc_{enum_name} = ucode[{current+bits-1}:{current}];\n")
        value = 1
        for enum in enum_set:
          f.write(f"parameter [{UCODE_LENGTH-1}:0] UC_{enum.upper()} = {value} << {current};\n")
          f.write(f"parameter VAL_{enum.upper()} = {value};\n")
          value += 1
          commands[enum] = f"UC_{enum.upper()}";
        current += bits;
  if current > UCODE_LENGTH:
      raise Exception(f"microcode too wide, increase UCODE_LENGTH to {current}")
  return commands

def make_codes(decode_map, opcode, chosen, uc_addr):
  if len(opcode) == 0:
    decode_map[int(chosen, 2)] = uc_addr
    return
  bit = opcode[0]
  opcode = opcode[1:]
  if bit == "0" or bit == "1":
    make_codes(decode_map, opcode, chosen + bit, uc_addr)
  else:
    make_codes(decode_map, opcode, chosen + "0", uc_addr)
    make_codes(decode_map, opcode, chosen + "1", uc_addr)

def parse(uc_file, f, commands, ucodes, decode1, decode_cb, decode_xy, decode_xybits, decode_ed):
  labels = {}
  current = 0;
  for line in f:
    p = line.find(';')
    if p >= 0:
      line = line[:p]
    line = line.rstrip()
    if len(line) == 0:
      continue
    if line.startswith("__opcode "):
      parts = line.split(maxsplit=2)
      opcode = parts[1]
      comment = parts[2]
      make_codes(decode1, opcode, "", current)
    elif line.startswith("__opcode_cb "):
      parts = line.split(maxsplit=2)
      opcode = parts[1]
      comment = parts[2]
      make_codes(decode_cb, opcode, "", current)
    elif line.startswith("__opcode_xy "):
      parts = line.split(maxsplit=2)
      opcode = parts[1]
      comment = parts[2]
      make_codes(decode_xy, opcode, "", current)
    elif line.startswith("__opcode_xybits "):
      parts = line.split(maxsplit=2)
      opcode = parts[1]
      comment = parts[2]
      make_codes(decode_xybits, opcode, "", current)
    elif line.startswith("__opcode_ed "):
      parts = line.split(maxsplit=2)
      opcode = parts[1]
      comment = parts[2]
      make_codes(decode_ed, opcode, "", current)
    elif line[0].isspace():
      # microcode line
      next_uc_addr = current + 1
      uc = []
      parts = line.strip().split()
      if parts[0] == "nop":
        parts = []
      i = 0
      while i < len(parts):
        if parts[i] == "goto":
          next_uc_addr = labels[parts[i+1]]
          i += 2
          parts.append("goto_now") # imply gotonow
          continue
        if parts[i] == "gotoncc":
          next_uc_addr = labels[parts[i+1]]
          i += 2
          parts.append("goto_ncc")
          continue
        if parts[i] == "gotonccr":
          next_uc_addr = labels[parts[i+1]]
          i += 2
          parts.append("goto_nccr")
          continue
        if parts[i] == "gotoz":
          next_uc_addr = labels[parts[i+1]]
          i += 2
          parts.append("goto_z")
          continue
        if parts[i] == "gotoloop":
          next_uc_addr = labels[parts[i+1]]
          i += 2
          parts.append("goto_loop")
          continue
        uc.append(commands[parts[i]])
        i += 1
      uc.append(str(next_uc_addr))
      uc_file.write(f'      {current}: ucode <= {" | ".join(uc)};\n')
      current += 1
    else:
      # has to be a label
      if not line.endswith(":"):
        raise Exception("unexpected syntax: " + line)
      label = line[:-1]
      labels[label] = current

def write_decode_rom(filename, decode_map):
  with open(filename, "w") as f:
    for value in sorted(decode_map.keys()):
      uc_addr = decode_map[value]
      f.write(f"      {value}: uc_addr = {uc_addr};\n")

UCODE_SRC_FILE = "z80.ucode"
HEADER_FILE = "ucode_consts.vh"
UCODE_SIGNALS_FILE = "ucode_signals.vh"
UCODE_FILE = "ucode.vh"
DECODE1_FILE = "decode1.vh"
DECODE_CB_FILE = "decode_cb.vh"
DECODE_XY_FILE = "decode_xy.vh"
DECODE_XYBITS_FILE = "decode_xybits.vh"
DECODE_ED_FILE = "decode_ed.vh"

ucodes = [] # list of tuples with ucode and comment
decode1 = {} # map of byte to ucode addr
decode_cb = {} # map of byte to ucode addr
decode_xy = {} # map of byte to ucode addr
decode_xybits = {} # map of byte to ucode addr
decode_ed = {} # map of byte to ucode addr

commands = generate_ucode_headers()

with open(UCODE_SRC_FILE) as f:
  with open(UCODE_FILE, "w") as uc:
    parse(uc, f, commands, ucodes, decode1, decode_cb, decode_xy, decode_xybits, decode_ed)

write_decode_rom(DECODE1_FILE, decode1)
write_decode_rom(DECODE_CB_FILE, decode_cb)
write_decode_rom(DECODE_XY_FILE, decode_xy)
write_decode_rom(DECODE_XYBITS_FILE, decode_xybits)
write_decode_rom(DECODE_ED_FILE, decode_ed)


class Pair:
    def __init__(self, key, val):
        self.key = key
        self.val = val

class HashMap:
    def __init__(self):
        self.size = 0
        self.capacity = 2
        self.map = [None, None]

    def hash(self, key):
        index = 0
        for c in key:
            index += ord(c)
        return index % self.capacity

    def get(self, key):
        index = self.hash(key)

        while self.map[index] != None:
            if self.map[index].key == key:
                return self.map[index].val
            index += 1
            index = index % self.capacity
        return None

    def put(self, key, val):
        index = self.hash(key)

        while True:
            if self.map[index] == None:
                self.map[index] = Pair(key, val)
                self.size += 1
                if self.size >= self.capacity // 2:
                    self.rehash()
                return
            elif self.map[index].key == key:
                self.map[index].val = val
                return

            index += 1
            index = index % self.capacity

    def remove(self, key):
        if not self.get(key):
            return

        index = self.hash(key)
        while True:
            if self.map[index].key == key:
                # wary of open addressing
                self.map[index] = None
                self.size -= 1
                return
            index += 1
            index = index % self.capacity

    def rehash(self):
        self.capacity = 2 * self.capacity
        newMap = []
        for i in range(self.capacity):
            newMap.append(None)

        oldMap = self.map
        self.map = newMap
        self.size = 0
        for pair in oldMap:
            if pair:
                self.put(pair.key, pair.val)

    def print(self):
        for pair in self.map:
            if pair:
                print(pair.key, pair.val)


import c3linearize as _c3linearize
import collections as _collections
import jinja2 as _jinja2
import json as _json
import pathlib as _pathlib
import pkg_resources as _pkg_resources
import ruamel.yaml as _yaml
from ruamel.yaml import YAML as _YAML
import runpy as _runpy
import subprocess as _subprocess
import sys as _sys
import yaql as _yaql
from . import scanextras as _scanextras
from .obj import Obj as _Obj
from .taskflow import TaskFlow as _TaskFlow

try:
    __version__ = _pkg_resources.get_distribution(__name__).version
except:  # noqa # pragma: no cover
    pass

ELIDE = object()

class TaggedStr(str):
    def __new__(cls, s, **kw):
        self = super().__new__(cls, s)
        self.__dict__ = kw
        return self

class TemplateResultStr(TaggedStr):
    pass

def _which(path, name):
    for path_entry in path:
        path_name = _pathlib.Path(path_entry) / name
        if path_name.exists():
            return path_name

    return None

def _checked_which(path, name):
    result = _which(path, name)
    if result is None:
        raise FileNotFoundError(name, path)
    return result

class Anygen:
    def __init__(self, produce_func):
        self._produce_func = produce_func

    def produce(self, *args, **kw):
        return self._produce_func(args + (kw,))

def _init_task_init(ctx):
    ctx.yaql = ctx.inp.yaql
    ctx.taskflow = _TaskFlow()
    ctx.extras = _collections.defaultdict(dict)

    for k, v in ctx.inp.extras.items():
        ctx.extras[k].update(v)

    def task(produce_ctx):
        produce_ctx.yaql_ctx = produce_ctx.inp.yaql_ctx

        produce_ctx.yaql_ctx["inp"] = ctx.yaql.expr.merge_dict_list.evaluate(
            context=ctx.yaql.ctx.create_child_context(),
            data=produce_ctx.inp.inp
        )

        produce_ctx.yaql_ctx["out"] = {}

    ctx.taskflow.add_task("init", func=task)

def _init_task_load_yamls(ctx):
    suffix = "anygen.yml"

    COMBINED = object()
    ROOT = object()

    loaded = {}
    yaml_loader = _YAML(typ='safe', pure=True)

    def load_list(classes):
        for i in classes:
            load(i)

    def load(class_):
        nonlocal loaded
        if class_ in loaded:
            return

        path_name = _checked_which(ctx.inp.path, class_ + "." + suffix)
        yaml = yaml_loader.load(path_name)
        extends = yaml.get("extends", [])
        # TODO proper error
        assert isinstance(extends, list)
        loaded[class_] = _Obj(path_name=path_name, yaml=yaml, extends=extends)
        load_list(extends)

    def load_root():
        path_name = _which(ctx.inp.path, suffix)
        if path_name is None:
            return
        yaml = yaml_loader.load(path_name)
        if "extends" in yaml:
            raise RuntimeError("root class cannot have 'extends'")

        loaded[ROOT] = _Obj(path_name=path_name, yaml=yaml)

    load_list(ctx.inp.classes)
    load_root()

    if ROOT in loaded:
        bases = [ROOT]
    else:
        bases = []

    def bases_func(class_):
        if class_ is COMBINED:
            return ctx.inp.classes + bases
        if class_ is ROOT:
            return []
        return list(loaded[class_].extends) + bases

    dep_graph = _c3linearize.build_graph(COMBINED, bases_func)
    c3linearization = _c3linearize.linearize(dep_graph, heads=[COMBINED])[COMBINED]
    assert c3linearization.pop(0) is COMBINED

    ctx.classes = list(reversed([loaded[i] for i in c3linearization]))

    for class_ in ctx.classes:
        class_.yaql_prefix = class_.yaml.get("marks", {}).get("yaql", "=")
        class_.elideable_key_suffix = class_.yaml.get("marks", {}).get("elide", "?")
        class_.merge_key_suffix = class_.yaml.get("marks", {}).get("merge", "+")
        class_.filter_key_sep = class_.yaml.get("marks", {}).get("filter", "|")

def _init_task_merge_yaml_consts(ctx):
    ctx.yaql.ctx0 = ctx.yaql.expr.merge_yaml_consts.evaluate(context=ctx.yaql.ctx, data=ctx.classes)
    ctx.yaql.ctx = ctx.yaql.ctx0.create_child_context()

def _init_task_py_deps(ctx):
    missing_requires = set()

    for class_ in ctx.classes:
        for req_str in class_.yaml.get('py_requires', []):
            req = _pkg_resources.Requirement.parse(req_str)
            try:
                _pkg_resources.working_set.resolve([req])
            except _pkg_resources.DistributionNotFound:
                missing_requires.add(req)

    if missing_requires:
        if _sys.base_prefix == _sys.prefix:
            raise RuntimeError(
                "missing Python requirements and not running in venv -- will not try to autoinstall",
                list(missing_requires)
            )

        _subprocess.check_call(
            [_pathlib.Path(_sys.executable).parent / "pip", "install"] +
            [str(i) for i in missing_requires]
        )

def _init_task_load_plugins(ctx):
    extras = ctx.extras

    origins = _collections.defaultdict(list)

    for entry_point in _pkg_resources.iter_entry_points("anygen", "extras"):
        these_extras = entry_point.load()
        for k1, v1 in these_extras.items():
            for k2, v2 in v1.items():
                origins[(k1, k2)].append(str(entry_point))
                extras[k1][k2] = v2

    dups = []
    for k, these_origins in origins.items():
        if len(these_origins) > 1:
            dups.append(('%s_%s' % k, these_origins))

    if dups:
        raise RuntimeError(
            "conflicting plugins installed. Some 'extras' are defined in multiple plugins",
            dups
        )

def _init_task_load_py(ctx):
    extras = ctx.extras

    for class_ in ctx.classes:
        py_fname = class_.path_name.with_suffix(".py")

        if not py_fname.exists():
            continue

        py_dict = _runpy.run_path(py_fname)

        these_extras = _scanextras.scan(py_dict)

        for k1, v1 in these_extras.items():
            for k2, v2 in v1.items():
                extras[k1][k2] = v2

def _init_task_setup_yaql_extras(ctx):
    for k, v in ctx.extras["yaql"].items():
        ctx.yaql.ctx.register_function(_yaql.language.specs.name(k)(v))

def _init_task_setup_jinja(ctx):
    jinja_env = ctx.jinja_env = _jinja2.Environment(
        loader=_jinja2.ChoiceLoader(
            [_jinja2.DictLoader(class_.yaml.get("templates", {})) for class_ in reversed(ctx.classes)] +
            [_jinja2.FileSystemLoader(ctx.inp.path)]
        ),
        keep_trailing_newline=True
    )

    for k, v in ctx.extras["jinjaglobals"].items():
        jinja_env.globals[k] = v

    for k, v in ctx.extras["jinjafilter"].items():
        jinja_env.filters[k] = v

    for k, v in ctx.extras["jinjatest"].items():
        jinja_env.tests[k] = v

    @_yaql.language.specs.name("template")
    def yaql_template(name, **kw):
        return TemplateResultStr(jinja_env.get_template(name).render(**kw), name=name)

    ctx.yaql.ctx.register_function(yaql_template)

def yaql_ctx_to_str(ctx):
    if ctx is None:
        return '*'
    return str(dict((k, ctx.get_data(k)) for k in ctx.keys())) + ' | ' + yaql_ctx_to_str(ctx.parent)

def _init_task_prepare(ctx):
    preparers = []
    for class_ in ctx.classes:
        prepare_exprs = class_.yaml.get("prepare", [])
        if isinstance(prepare_exprs, str):
            prepare_exprs = [prepare_exprs]

        for expr in prepare_exprs:
            preparers.append(ctx.yaql.engine(expr))

    def task(produce_ctx):
        # print(yaql_ctx_to_str(produce_ctx.yaql_ctx))
        for i in preparers:
            produce_ctx.yaql_ctx = i.evaluate(context=produce_ctx.yaql_ctx, data=None)
            # print(yaql_ctx_to_str(produce_ctx.yaql_ctx))

    ctx.taskflow.add_task("prepare", func=task, after=["init"])

def _recursive_yaql(ctx, class_, data):
    def process_dict_item(k, v):
        pipeline_tail = []
        for suffix, yaql_filter in [
            (class_.elideable_key_suffix, 'elide'),
            (class_.merge_key_suffix, 'merge')
        ]:
            if k.endswith(suffix):
                k = k[:-len(suffix)]
                pipeline_tail.append(yaql_filter)

        k, *pipeline = k.split(class_.filter_key_sep)
        pipeline.extend(reversed(pipeline_tail))

        pipeline = [ctx.yaql.engine(i + '($)') for i in pipeline]

        def produce(yaql_ctx):
            result = v(yaql_ctx)

            for i in pipeline:
                result = i.evaluate(context=ctx.yaql.ctx, data=result)

            return result

        return k, produce

    def recursive_elide(data):
        if isinstance(data, dict):
            return dict((k, recursive_elide(v)) for k, v in data.items() if v is not ELIDE)
        elif isinstance(data, list):
            return list(recursive_elide(i) for i in data if i is not ELIDE)
        else:
            return data

    def recurse(data):
        if isinstance(data, str):
            if data.startswith(class_.yaql_prefix):
                expr = ctx.yaql.engine(data[len(class_.yaql_prefix):])
                return lambda yaql_ctx: recursive_elide(expr.evaluate(context=yaql_ctx))
            else:
                return lambda yaql_ctx: data
        elif isinstance(data, dict):
            items = list(process_dict_item(k, recurse(v)) for k, v in data.items())
            return lambda yaql_ctx: dict(
                (k, v)
                for k, cb in items
                for v in (cb(yaql_ctx),)
                if v is not ELIDE
            )
        elif isinstance(data, list):
            items = list(recurse(i) for i in data)
            return lambda yaql_ctx: list(
                v
                for cb in items
                for v in (cb(yaql_ctx),)
                if v is not ELIDE
            )
        else:
            return lambda yaql_ctx: data

    return recurse(data)

def _yaql_let(yaql_ctx, *args, **kw):
    yaql_ctx = yaql_ctx.create_child_context()

    for i, v in enumerate(args, 1):
        yaql_ctx[str(i)] = v

    for k, v in kw.items():
        yaql_ctx[k] = v

    return yaql_ctx

def _init_task_fragments(ctx):
    fragments = set()
    for class_ in reversed(ctx.classes):
        for k, v in class_.yaml.get("fragments", {}).items():
            if k in fragments:
                continue

            fragments.add(k)

            def one(k, v):
                frag_func = _recursive_yaql(ctx, class_, v)

                @_yaql.language.specs.name(k)
                def wrapper(*args, **kw):
                    return frag_func(_yaql_let(ctx.yaql.ctx0, *args, **kw))

                ctx.yaql.ctx0.register_function(wrapper)

            one(k, v)

def _init_task_produce(ctx):
    producers = [lambda yaql_ctx: yaql_ctx.get_data("out")] + [
        _recursive_yaql(ctx, class_, {"produce" + suffix: data})
        for class_ in ctx.classes
        for suffix in ['', class_.merge_key_suffix]
        for data in (class_.yaml.get("produce" + suffix, None),)
        if data is not None
    ]

    def task(produce_ctx):
        produce_ctx.out = ctx.yaql.expr.merge_dict_list.evaluate(
            context=ctx.yaql.ctx.create_child_context(),
            data=[i(produce_ctx.yaql_ctx) for i in producers]
        )["produce"]

    ctx.taskflow.add_task("produce", func=task, after=["prepare"])

def _init_task_result(ctx):
    def produce(inp):
        return ctx.taskflow.run(_Obj(
            yaql_ctx=ctx.yaql.ctx.create_child_context(),
            inp=inp
        ))
    ctx.out = produce

class _FunctionRegistry:
    def __init__(self):
        self.functions = []

    def __call__(self, f):
        self.functions.append(f)
        return f

    def fill_context(self, yaql_ctx):
        for f in self.functions:
            yaql_ctx.register_function(f)

_std_function_registry = _FunctionRegistry()

@_std_function_registry
def elide(x):
    if x is None or x == [] or x == {}:
        return ELIDE
    else:
        return x

def _register_std_functions():
    @_std_function_registry
    def to_yaml(data):
        return _yaml.dump(data, Dumper=_yaml.RoundTripDumper)

    @_std_function_registry
    def to_json(data):
        return _json.dumps(data, sort_keys=True)

_register_std_functions()

_YAQL_PREPARE_CTX = [
    "def(merge0, $.aggregate($1.merge_with({''=>$2}), {}).get(''))",
    "def(merge, merge0(merge0($)))"
]

class AnygenEngine:
    def __init__(self):
        self._taskflow = _TaskFlow()
        self._yaql_engine = \
            _yaql.YaqlFactory(allow_delegates=True) \
            .create(
                options={
                    # 'yaql.convertInputData': False
                    'yaql.convertInputData': True
                })
        self._yaql_ctx = _yaql.create_context(
            convention=_yaql.language.conventions.PythonConvention(),
            delegates=True
        )
        self._yaql_expr = _Obj(dict((k, self._yaql_engine(v)) for k, v in [
            ("merge_yaml_consts", "call(let, [none], $.aggregate($1.merge_with($2.yaml.get(const, {})), {}))"),
            ("merge_dict_list", "$.aggregate($1.merge_with($2), {})"),
        ]))

        _std_function_registry.fill_context(self._yaql_ctx)

        for i in _YAQL_PREPARE_CTX:
            self._yaql_ctx = self._yaql_engine(i).evaluate(context=self._yaql_ctx, data=None)

        self._taskflow.add_tasks([
            ('init', _init_task_init),
            ('load_yamls', _init_task_load_yamls),
            ('merge_yaml_consts', _init_task_merge_yaml_consts),
            ('py_deps', _init_task_py_deps),
            ('load_plugins', _init_task_load_plugins),
            ('load_py', _init_task_load_py),
            ('setup_yaql_extras', _init_task_setup_yaql_extras),
            ('setup_jinja', _init_task_setup_jinja),
            ('fragments', _init_task_fragments),
            ('prepare', _init_task_prepare),
            ('produce', _init_task_produce),
            ('result', _init_task_result),
        ])

    def create(
        self,
        *,
        path,
        classes,
        extras=None
    ):
        return Anygen(self._taskflow.run(_Obj(
            yaql=_Obj(
                engine=self._yaql_engine,
                ctx=self._yaql_ctx.create_child_context(),
                expr=self._yaql_expr,
            ),
            path=path,
            classes=classes,
            extras=extras or {}
        )))

_yaql.yaqlization.yaqlize(_Obj)
_yaql.yaqlization.yaqlize(TaggedStr)


""" Overview widget with list of patients. """

from PyQt5.QtWidgets import QListWidgetItem, QWidget  #pylint: disable=no-name-in-module
from PyQt5 import uic
from PyQt5.QtCore import pyqtSlot  #pylint: disable=no-name-in-module

from agenda import UIPATH
from agenda.patient import PatientStore, Patient
from agenda.misc import popupInfo

class PatientOverview(QWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.ui = uic.loadUi(UIPATH + '/PatientOverview.ui', self)

        self.patientStore = PatientStore.Instance()
        self.activePatient = None

        self.updatePatientList()

        # Connect ui signals
        self.ui.listWidget.currentItemChanged.connect(self.selectPatient)
        self.ui.pushButton.clicked.connect(self.newPatient)
        self.patientStore.sigUpdated.connect(self.updatePatientList)

        self.patientEditWidget.sigBusy.connect(self._disableNewButton)
        self.patientEditWidget.sigDone.connect(self._enableNewButton)

    @pyqtSlot()
    def _disableNewButton(self):
        self.ui.pushButton.setEnabled(False)
        self.ui.listWidget.setEnabled(False)

    @pyqtSlot()
    def _enableNewButton(self):
        self.ui.pushButton.setEnabled(True)
        self.ui.listWidget.setEnabled(True)

    @pyqtSlot()
    def newPatient(self):
        self.activePatient = Patient()
        self.ui.patientEditWidget.openPatient(self.activePatient)
        self.ui.patientEditWidget.sigSave.connect(self.saveNewPatient)

    @pyqtSlot()
    def saveNewPatient(self):
        self.patientStore.addPatient(self.activePatient)
        popupInfo("New patient '{}' was saved".format(self.activePatient))
        self.ui.patientEditWidget.sigSave.disconnect(self.saveNewPatient)

    @pyqtSlot(QListWidgetItem, QListWidgetItem)
    def selectPatient(self, item, _):
        if item is not None: # In case list is cleared no patient is selected
            selectedPatient = self.patientStore.getPatient(item.text())
            self.ui.patientEditWidget.openPatient(selectedPatient)

    @pyqtSlot()
    def updatePatientList(self):
        self.ui.listWidget.clear()
        self.ui.listWidget.addItems(self.patientStore.getPatientNames())


#!/usr/bin/python3

import numpy as np

class Entity:
    def __init__(self, environment, inititial_state = 0):
        self.environment = environment
        self.state = inititial_state
        self.reward = 0

    def move(self,action):
        next_state = self.environment.action_state(self.state,action)
        reward = self.environment.get_reward(next_state)
        self.reward += reward
        self.state = next_state

    def get_possible_actions(self):
        return ['up', 'down', 'left', 'right']

    def __repr__(self):
        environment = self.environment.environment.copy().reshape(-1)
        environment[self.state] = 2
        environment = environment.reshape(self.environment.environment.shape)

        s = ''
        for row in environment:
            for column in row:
                if column == 1:
                    s += ' . '
                elif column == 2:
                    s += ' o '
                else:
                    s += ' x '
            s += '\n'
        return s


import matplotlib
import pandas as pd

from vaep.sklearn.types import ResultsSplit

def plot_split_auc(result: ResultsSplit, name: str, ax: matplotlib.axes.Axes) -> matplotlib.axes.Axes:
    col_name = f"{name} (auc: {result.auc:.3f})"
    roc = pd.DataFrame(result.roc, index='fpr tpr cutoffs'.split()
                       ).rename({'tpr': col_name})
    ax = roc.T.plot('fpr', col_name,
                    xlabel='false positive rate',
                    ylabel='true positive rate',
                    ax=ax)
    return ax

def plot_split_prc(result: ResultsSplit, name: str, ax: matplotlib.axes.Axes) -> matplotlib.axes.Axes:
    col_name = f"{name} (aps: {result.aps:.3f})"
    roc = pd.DataFrame(result.prc, index='precision recall cutoffs'.split()
                       ).rename({'precision': col_name})
    ax = roc.T.plot('recall', col_name,
                    xlabel='true positive rate',
                    ylabel='precision',
                    ax=ax)
    return ax


# 1
from nltk import sent_tokenize

text_sample = """
Share full article

About a half dozen writers and actors carrying signs, and some wearing hats, picket outside Paramount studios in Los Angeles.
Striking Hollywood writers and actors picketing outside Paramount Studios in Los Angeles last week.Credit...Mario Tama/Getty Images

Brooks BarnesJohn Koblin
By Brooks Barnes and John Koblin
Sept. 25, 2023, 1:09 a.m. ET
"""
tokenized_sentences = sent_tokenize(text_sample)
print(tokenized_sentences)

# 2
from nltk import word_tokenize

sentence = (
    "The faster Harry got to the store, the faster Harry, the faster, would get home."
)
words = word_tokenize(sentence)
words

# 3
from nltk.tokenize import WordPunctTokenizer

sentence = "it's nothing that you don't already know the answer to most people aren't aware of how their inner world works."
words = WordPunctTokenizer().tokenize(sentence)
words

# 4
import csv
from konlpy.tag import Okt
from gensim.models import word2vec

f = open("data/ratings_train.txt", "r", encoding="utf-8")
rdr = csv.reader(f, delimiter="\t")
rdw = list(rdr)
f.close()

# 5
twitter = Okt()

result = []
for line in rdw:
    malist = twitter.pos(line[1], norm=True, stem=True)
    r = []
    for word in malist:
        if not word[1] in ["Josa", "Eomi", "Punctuation"]:
            r.append(word[0])
    rl = (" ".join(r)).strip()
    result.append(rl)
    print(rl)

# 6
with open("NaverMovie.nlp", "w", encoding="utf-8") as fp:
    fp.write("\n".join(result))

# 7
mData = word2vec.LineSentence("NaverMovie.nlp")
mModel = word2vec.Word2Vec(mData, vector_size=200, window=10, hs=1, min_count=2, sg=1)
mModel.save("NaverMovie.model")

# 8
import nltk
from nltk.corpus import stopwords

nltk.download("stopwords")
nltk.download("punkt")
from nltk.tokenize import word_tokenize

sample_text = "One of first things I noticed about the book was the unusual way author chose to narrate story. Instead of telling it from perspective of one or two characters, author chose to tell it from perspective of an entire town."
text_tokens = word_tokenize(sample_text)

tokens_without_sw = [
    word for word in text_tokens if not word in stopwords.words("english")
]
print(f"불용어 제거 미적용: {text_tokens}", "\n")
print(f"불용어 제거 적용: {tokens_without_sw}")

# 9
from nltk.stem import PorterStemmer

stemmer = PorterStemmer()

print(stemmer.stem("obsesse"), stemmer.stem("obsessed"))
print(stemmer.stem("standardizes"), stemmer.stem("standardization"))
print(stemmer.stem("national"), stemmer.stem("nation"))

# 10 lancaster
from nltk.stem import LancasterStemmer

stemmer = LancasterStemmer()

print(stemmer.stem("obsesse"), stemmer.stem("obsessed"))
print(stemmer.stem("standardizes"), stemmer.stem("standardization"))
print(stemmer.stem("national"), stemmer.stem("nation"))

# 11 wordnet
import nltk

nltk.download("wordnet")

from nltk.stem import WordNetLemmatizer

lemma = WordNetLemmatizer()
print(stemmer.stem("obsesse"), stemmer.stem("obsessed"))
print(lemma.lemmatize("obsesse"), lemma.lemmatize("obsessed"))
print(stemmer.stem("standardizes"), stemmer.stem("standardization"))
print(lemma.lemmatize("standardizes"), lemma.lemmatize("standardization"))
print(stemmer.stem("national"), stemmer.stem("nation"))
print(lemma.lemmatize("national"), lemma.lemmatize("nation"))
print(stemmer.stem("saying"), stemmer.stem("said"))
print(lemma.lemmatize("saying"), lemma.lemmatize("said"))
print(stemmer.stem("university"), stemmer.stem("universe"))
print(lemma.lemmatize("university"), lemma.lemmatize("universe"))

print(lemma.lemmatize("obsesse", "v"), lemma.lemmatize("obsessed", "a"))
print(lemma.lemmatize("standardizes", "v"), lemma.lemmatize("standardization", "n"))
print(lemma.lemmatize("national", "a"), lemma.lemmatize("nation", "n"))
print(lemma.lemmatize("saying", "r"), lemma.lemmatize("said"), "v")
print(lemma.lemmatize("university", "r"), lemma.lemmatize("universe", "n"))


from os import system
from time import sleep
import signup_page as signup
import login_page as login

def main():
    while True:
        print("""\n            Main Menu
======================================
1. Login
2. Sign-up
3. Exit
======================================
        """)

        try:
            option = int(input("Enter an option: "))

            if option == 1:
                login.login_menu()
                continue
            elif option == 2:
                signup.signup_menu()
                continue
            elif option == 3:
                print("\nExiting...")
                break
            else:
                print("\nInvalid Option! Try Again...\n")
                sleep(1)
                continue
        except ValueError:
            print("\nPlease, enter a valid numeric number.\n")
            sleep(1)
            continue
        except KeyboardInterrupt:
            print("\nYou have choosen to leave.")
            sleep(1)
            break
        finally:
            print("======================================")

if __name__ == '__main__':
    main()


import os

ANY_CACHE = os.path.join(os.environ['HOME'], ".any")
ANY_KUBECONFIG = os.getenv("ANY_KUBECONFIG", os.getenv("KUBECONFIG"))

class Directory:
    @staticmethod
    def root(organization, repository):
        """
        Path to the directory of the given repository.
        :param organization:
        :param repository:
        :return:
        """
        path = os.path.join(ANY_CACHE, organization, repository)
        os.makedirs(path, exist_ok=True)
        return path


#!/usr/bin/env python
# coding: utf-8
import os
import re
from setuptools import setup

path = os.path.dirname(__file__)
desc_fd = os.path.join(path, 'README.rst')
hist_fd = os.path.join(path, 'HISTORY.rst')

long_desc = ''
short_desc = 'A Magpie authentication handler for python-requests'

if os.path.isfile(desc_fd):
    with open(desc_fd) as fd:
        long_desc = fd.read()

if os.path.isfile(hist_fd):
    with open(hist_fd) as fd:
        long_desc = '\n\n'.join([long_desc, fd.read()])

def get_version():
    """
    Simple function to extract the current version using regular expressions.
    """
    reg = re.compile(r'__version__ = [\'"]([^\'"]*)[\'"]')
    with open('requests_magpie.py') as fd:
        matches = list(filter(lambda x: x, map(reg.match, fd)))

    if not matches:
        raise RuntimeError(
            'Could not find the version information for requests_magpie'
            )

    return matches[0].group(1)

setup(
    name='requests-magpie',
    description=short_desc,
    long_description=long_desc,
    author='David Caron',
    author_email='david.caron@crim.ca',
    url='https://github.com/ouranosinc/requests-magpie',
    py_modules=['requests_magpie'],
    license='ISC',
    version=get_version(),
    install_requires=[
        'requests>=1.1.0',
    ],
    extras_require={
    },
    tests_require=['mock'],
    classifiers=[
        "Development Status :: 4 - Beta",
        "Environment :: Console",
        "Intended Audience :: Developers",
        "License :: OSI Approved :: ISC License (ISCL)",
        "Operating System :: OS Independent",
        "Programming Language :: Python",
        "Programming Language :: Python :: 3 :: Only",
        "Topic :: Software Development :: Libraries :: Python Modules",
    ],
)


"""Server for student lab pairs."""

from flask import Flask, render_template, redirect, request, flash, session
from model import connect_to_db
import crud

from jinja2 import StrictUndefined

app = Flask(__name__)
app.secret_key = 'SECRETSECRETSECRET'
app.jinja_env.undefined = StrictUndefined

##################################################################
# GENERAL ROUTES #
##################################################################

@app.route("/")
def show_homepage():
    return render_template("index.html")

##################################################################
# COHORT ROUTES #
##################################################################

@app.route("/add-cohort", methods=["GET", "POST"])
def add_cohort_form():
    """Display form to add cohort or process form with cohort id & grab data from API."""

    if request.method == "GET":
        return render_template("add-cohort.html")

    elif request.method == "POST":
        cohort_id = request.form.get("cohort_id")
        cohort_id = crud.format_cohort_id(cohort_id)

        does_exist = crud.is_cohort_in_db(cohort_id)
        if does_exist:
            flash("Cohort is already in your database!")
            return redirect("/")

        cohort = crud.create_new_cohort_with_api(cohort_id)
        if not cohort:
            flash("Cohort not found! Please try again.")
            return redirect("/")

        flash(f"Cohort {cohort.title} added!")
        return redirect("/")

@app.route("/manually-add-cohort", methods=["POST"])
def manually_add_cohort_form():
    """Process form with cohort data and add to db."""

    cohort_id = request.form.get("cohort_id")
    title = request.form.get("title", None)
    cohort_number = request.form.get("cohort_number", None)
    description = request.form.get("description", None)
    nickname = request.form.get("nickname", None)
    start_date = request.form.get("start_date", None)
    end_date = request.form.get("end_date", None)
    json_url = request.form.get("json_url", None)

    cohort_id = crud.format_cohort_id(cohort_id)

    does_exist = crud.is_cohort_in_db(cohort_id)
    if does_exist:
        flash("Cohort is already in your database!")
        return redirect("/")

    cohort = crud.create_new_cohort_directly(
        cohort_id,
        title,
        cohort_number,
        description,
        nickname,
        start_date,
        end_date,
        json_url
        )
    flash(f"Cohort {cohort.cohort_id} added!")
    return redirect("/")

@app.route("/view-cohorts", methods=["GET"])
def show_all_cohorts():
    """Display all cohorts in db."""

    cohorts = crud.get_all_active_cohorts()

    if not cohorts:
        flash("There are no cohorts in the database.")
        return redirect("/add-cohort")

    return render_template("view-cohorts.html", cohorts=cohorts)

@app.route("/update-cohort", methods=["GET", "POST"])
def update_cohort():
    """Displays form or updates a cohort status"""

    if request.method == "GET":
        cohorts = crud.get_all_cohorts()
        return render_template("update-cohort.html", cohorts=cohorts)

    elif request.method == "POST":
        cohort_id = request.form.get("cohort_id")
        active_status = request.form.get("active_status", None)

        if active_status:
            if active_status == "active":
                cohort = crud.update_cohort_status(cohort_id, active_status=True)
            elif active_status == "inactive":
                cohort = crud.update_cohort_status(cohort_id, active_status=False)
            print(f"Cohort active status updated to {active_status}!")

        return redirect("/view-cohorts")

    return redirect("/")

##################################################################
# STUDENT ROUTES #
##################################################################

@app.route("/add-students", methods=["GET", "POST"])
def add_students_form():
    """Display form to add students or process form with student data & add to db."""

    if request.method == "GET":
        cohorts = crud.get_all_active_cohorts()
        return render_template("add-students.html", cohorts=cohorts)

    elif request.method == "POST":
        fname = request.form.get("fname")
        lname = request.form.get("lname")
        tech_level = request.form.get("tech_level", None)
        cohort_id = request.form.get("cohort_id")
        discord_name = request.form.get("discord_name", None)

        student = crud.create_new_student_all_fields(fname, lname, cohort_id, tech_level, discord_name)
        flash(f"Student {student.fname} {student.lname} added!")
        return redirect("/add-students")

@app.route("/update-students", methods=["GET", "POST"])
def update_students_form():
    """Display form to update students or process updates to student data & add to db."""

    if request.method == "GET":
        cohorts = crud.get_all_active_cohorts()
        return render_template("update-students.html", cohorts=cohorts)

    elif request.method == "POST":
        student_id = request.form.get("student_id")
        fname = request.form.get("fname", None)
        lname = request.form.get("lname", None)
        tech_level = request.form.get("tech_level", None)
        discord_name = request.form.get("discord_name", None)
        active_status = request.form.get("active_status", None)
        print(">>>>>>>>>>>>")
        print(f"Student ID: {student_id}")

        student = crud.find_student(student_id)
        print(student)
        if tech_level:
            student = crud.update_student_tech_level(student.student_id, tech_level)
            print("Tech level updated!")

        if fname or lname:
            student = crud.update_student_name(student.student_id, fname, lname)
            print("Student name updated!")

        if discord_name:
            student = crud.update_student_discord_name(student.student_id, discord_name)
            print("Discord name updated!")

        if active_status:
            if active_status == "active":
                student = crud.update_student_active_status(student.student_id, active_status=True)
            elif active_status == "inactive":
                student = crud.update_student_active_status(student.student_id, active_status=False)
            print(f"Student active status updated to {active_status}!")

        print(">>>>>>>>>>>>")
        flash(f"The changes you requested for {student.fname} {student.lname} were completed!")
        return redirect("/update-students")

##################################################################
# LAB PAIR ROUTES #
##################################################################

@app.route("/create-pairs", methods=["GET", "POST"])
def create_pairs():
    """Display form to create lab pairs or process and display lab pairs."""

    cohorts = crud.get_all_active_cohorts()
    labs = crud.get_all_labs()
    pairs = []

    if request.method == "GET":
        # display form
        return render_template("form-create-pairs.html", cohorts=cohorts, labs=labs, pairs=pairs)

    elif request.method == "POST":
        # receive form data & create pairs
        cohort_id = request.form.get("cohort_id")
        pair_date = request.form.get("pair_date")
        lab_id = request.form.get("lab_id")
        print(f"Lab id is {lab_id}")
        process = request.form.get("process")

        cohort = crud.find_cohort(cohort_id)
        lab = crud.find_lab(lab_id)

        pairs = crud.make_pairs(cohort, pair_date, lab, process)

        return render_template("view-pairs.html", lab=lab, pairs=pairs, pair_date=pair_date)

@app.route("/manually-create-pairs", methods=["POST"])
def manually_create_pairs():
    """Process pairs entered by user, add to db, and view pairs."""

    # receive data from form
    pass
    cohort_id = request.form.get("cohort_id")
    pair_date = request.form.get("pair_date")
    lab_id = request.form.get("lab_id")

    cohort = crud.find_cohort(cohort_id)
    lab = crud.find_lab(lab_id)

    pairs_to_add = []

    # pair 1
    p1_user_id = request.form.get("p1_user_id", None)
    p1_pair_id = request.form.get("p1_pair_id", None)
    pairs_to_add.append((p1_user_id, p1_pair_id))

    # pair 2
    p2_user_id = request.form.get("p2_user_id", None)
    p2_pair_id = request.form.get("p2_pair_id", None)
    pairs_to_add.append((p2_user_id, p2_pair_id))

    # pair 3
    p3_user_id = request.form.get("p3_user_id", None)
    p3_pair_id = request.form.get("p3_pair_id", None)
    pairs_to_add.append((p3_user_id, p3_pair_id))

    # pair 4
    p4_user_id = request.form.get("p4_user_id", None)
    p4_pair_id = request.form.get("p4_pair_id", None)
    pairs_to_add.append((p4_user_id, p4_pair_id))

    # pair 5
    p5_user_id = request.form.get("p5_user_id", None)
    p5_pair_id = request.form.get("p5_pair_id", None)
    pairs_to_add.append((p5_user_id, p5_pair_id))

    # pair 6
    p6_user_id = request.form.get("p6_user_id", None)
    p6_pair_id = request.form.get("p6_pair_id", None)
    pairs_to_add.append((p6_user_id, p6_pair_id))

    # pair 7
    p7_user_id = request.form.get("p7_user_id", None)
    p7_pair_id = request.form.get("p7_pair_id", None)
    p7_third_id = request.form.get("p7_third_id", None)
    pairs_to_add.append((p7_user_id, p7_pair_id))
    if p7_third_id:
        pairs_to_add.append((p7_user_id,p7_third_id))
        pairs_to_add.append((p7_pair_id, p7_third_id))

    for pair in pairs_to_add:
        if pair[0]:
            is_added = crud.add_lab_pair(pair[0], pair[1], pair_date, lab_id)

    flash("Pairs added!")
    return redirect("/view-cohort-pairs")

@app.route("/update-pair-experience", methods=["GET", "POST"])
def update_pair_experience():
    """Display pair experience update form OR process form."""
    cohorts = crud.get_all_active_cohorts()

    if request.method == "GET":
        # display form
        return render_template("form-pair-experience.html", cohorts=cohorts)

    elif request.method == "POST":
        # get form data, query/update db, redirect
        user_id = request.form.get("user_id")
        pair_id = request.form.get("pair_id")
        pair_date = request.form.get("pair_date")
        experience = request.form.get("experience")

        lab_pairs = crud.find_lab_pair(user_id, pair_id, pair_date)

        if lab_pairs:
            if experience == "negative":
                bad_experience = True
            elif experience == "positive":
                bad_experience =  False
            lab_pairs = crud.update_pair_experience(lab_pairs, bad_experience)
            flash("Pair experience updated!")
        else:
            flash("No pair found to update!")
        return redirect("/update-pair-experience")

    return redirect("/")

@app.route("/view-cohort-pairs", methods=["GET", "POST"])
def view_cohort_pairs():
    """Display cohort pairs form or process & display cohort pairs"""
    cohorts = crud.get_all_active_cohorts()

    if request.method == "GET":
        #display form
        return render_template("form-cohort-pairs.html", cohorts=cohorts)

    elif request.method == "POST":
        # get form data, query db, display pairs
        cohort_id = request.form.get("cohort_id")
        pair_date = request.form.get("pair_date")

        cohort = crud.find_cohort(cohort_id)

        if pair_date:
            # find all pairs on that date and return list
            pairs = crud.pairs_by_date(cohort, pair_date)
        else:
            # find all pairs on all dates and return dict
            # {(date, lab): [[student, pair], ...] ...}
            pairs = crud.pairs_without_date(cohort)

        ##### maybe need a LabDate table to easily grab lab info? #####
        return render_template("view-cohort-pairs.html", pairs=pairs, pair_date=pair_date, cohort=cohort)
    return redirect("/")

@app.route("/view-student-pairs", methods=["GET", "POST"])
def view_student_pairs():
    """Display student pairs form or process & display student pairs"""
    cohorts = crud.get_all_active_cohorts()

    if request.method == "GET":
        return render_template("form-student-pairs.html", cohorts=cohorts)
    elif request.method == "POST":
        student_id = request.form.get("student_id")
        student = crud.find_student(student_id)

        labs_and_pairs = crud.get_labs_and_pairs(student_id)
        pairs_count = crud.get_students_pair_count(student_id)

        return render_template("view-student-pairs.html", student=student, labs_and_pairs=labs_and_pairs, pairs_count=pairs_count)
    return redirect("/")

@app.route("/update-pair", methods=["GET", "POST"])
def update_pair():
    cohorts = crud.get_all_active_cohorts()
    labs = crud.get_all_labs()

    if request.method == "GET":
        return render_template("form-update-pair.html", cohorts=cohorts, labs=labs)

    elif request.method == "POST":
        # get form data, query/update db, redirect
        user_id = request.form.get("user_id")
        pair_id = request.form.get("pair_id")
        pair_date = request.form.get("pair_date")
        lab_id = request.form.get("lab_id")
        update = request.form.get("update")

        if update == "delete":
            is_deleted = crud.delete_lab_pair(user_id, pair_id, pair_date, lab_id)
            if is_deleted:
                flash("Lab pairs deleted!")
            else:
                flash("No lab pairs found!")

        elif update == "add":
            is_added = crud.add_lab_pair(user_id, pair_id, pair_date, lab_id)
            if is_added:
                flash("Lab pairs added!")
            else:
                flash("Lab pairs already exist!")

        return redirect("/update-pair")

    return redirect("/")

if __name__ == "__main__":
    # DebugToolbarExtension(app)
    connect_to_db(app)
    app.run(host="0.0.0.0", debug=True)


from typing import Any

from echo_service.core.helpers.cache.base import BaseBackend
from echo_service.core.helpers.redis import redis

class RedisBackend(BaseBackend):
    async def get(self, key: str) -> str:
        result = redis.get(key)
        return result

    async def set(self, value: str, key: str, ttl: int = None) -> None:
        redis.set(name=value, value=key, ex=ttl)

    async def incr(self, key: str) -> None:
        redis.incr(key)

    async def delete(self, key: str) -> None:
        redis.getdel(key)


from restaurant.models import RestaurantProfile, RestaurantBooking
from core.errors import Error, APIError

def get_restaurant(id: int) -> RestaurantProfile:
    try:
        return RestaurantProfile.objects.get(pk=id)
    except RestaurantProfile.DoesNotExist:
        raise APIError(Error.INSTANCE_NOT_FOUND)

def get_restaurant_booking(restaurant: RestaurantProfile):
    try:
        return RestaurantBooking.objects.get(
            restaurant=restaurant
        )
    except RestaurantBooking.DoesNotExist:
        raise APIError(Error.INSTANCE_NOT_FOUND)


"""
Reverse String Problem
Author : donghak park
Contact: donghark03@naver.com
"""

class Solution:
    def reverseString(self, s) -> None:
        s.reverse()

if __name__=="__main__":
    solution = Solution()
    s = ["h","e","l","l","o"]
    solution.reverseString(s)
    print(s)


# from "python -m pip install cupy" -- MUST HAVE CUDA SETUP
from scipy.sparse import dok_matrix, csr_matrix
from scipy.sparse.linalg import lsqr
import numpy as np
import cupy as cp
import cupyx.scipy.sparse as cp_sparse
from timeit import default_timer as timer
from lib import (
    get_nodes,
    get_vs,
    get_neighbour_components,
    get_components,
)

def benchmark_MNA(df):
    components = get_components(df)
    nodes = get_nodes(components)

    # get unknown ivs
    voltage_sources = get_vs(components)
    num_nodes = len(nodes)

    # generate b matrix, and initialize zero-filled A-matrix
    b = construct_b(voltage_sources, num_nodes)
    A = construct_A(b, components, voltage_sources, num_nodes)

    start_cuda = timer()
    mid_cuda = -1
    try:
        cupy_A = cp_sparse.csr_matrix(A)
        cupy_b = cp.asarray(b)
        mid_cuda = timer()
        cupy_x = cp_sparse.linalg.lsqr(cupy_A, cupy_b)[0]
    except:
        print('failed to solve on gpu')
        mid_cuda = -1
    end_cuda = timer()

    start_cpu = timer()
    A = csr_matrix(A)
    mid_cpu = timer()
    x = lsqr(A, b)
    end_cpu = timer()

    if mid_cuda == -1:
        cuda_trans_diff = ""
        cuda_solve_diff = ""
    else:
        cuda_trans_diff = mid_cuda - start_cuda
        cuda_solve_diff = end_cuda - mid_cuda
    return {
        'matrix_size': A.shape,
        'num_components': len(components),
        'cpu': {
            'total': end_cpu - start_cpu,
            'data transfer': mid_cpu - start_cpu,
            'linear solve': end_cpu - mid_cpu
        },
        'cuda': {
            'total': end_cuda - start_cuda,
            'data transfer': cuda_trans_diff,
            'linear solve': cuda_solve_diff
        }
    }

def compute_MNA(df, print_eqs=False):
    components = get_components(df)
    nodes = get_nodes(components)

    # get unknown ivs
    voltage_sources = get_vs(components)
    num_nodes = len(nodes)

    # generate b matrix, and initialize zero-filled A-matrix
    b = construct_b(voltage_sources, num_nodes)
    A = construct_A(b, components, voltage_sources, num_nodes)

    cupy_A = cp_sparse.csr_matrix(A)
    cupy_b = cp.asarray(b)
    cupy_x = cp_sparse.linalg.lsqr(cupy_A, cupy_b)[0]

    if print_eqs:
        print("---" * A.shape[0], "\n", cupy_A, "\n", "---" * A.shape[0])
        print(cupy_x)
        print(cupy_b)

    return cp.asnumpy(cupy_x)

def construct_A(b, components, voltage_sources, num_nodes):
    len_u_nodes = num_nodes - 1
    len_kcl = num_nodes - 1
    len_ivs = len(voltage_sources)
    len_vs = len(voltage_sources)
    A = dok_matrix((len_u_nodes + len_ivs, len(b)))

    # Parse components into A-matrix
    height, width = A.shape
    time = 1
    # Assign KCL equations
    for i in range(0, len_kcl):
        row_components = get_neighbour_components(i + 1, components)
        for component in row_components:
            if component.symbol == "R":
                A[i, i] += 1 / component.val
                other_node = component.get_other_node(i + 1)
                if other_node != 0:
                    A[i, other_node - 1] += -1 / component.val
            elif component.symbol == "V":
                A[i, len_u_nodes + component.id - 1] += component.get_dir(i + 1)
            elif component.symbol == "I":
                b[i] += -1 * component.val * component.get_dir(i + 1)
            elif component.symbol == "C":
                A[i, i] += component.val * time
                other_node = component.get_other_node(i + 1)
                if other_node != 0:
                    A[i, other_node - 1] += -1 * component.val * time

    # Assign voltage equations
    for i in range(len_kcl, len_kcl + len_vs):
        if voltage_sources[i - len_kcl].start_node != 0:
            A[i, voltage_sources[i - len_kcl].start_node - 1] = 1
        if voltage_sources[i - len_kcl].end_node != 0:
            A[i, voltage_sources[i - len_kcl].end_node - 1] = -1

    return A

def construct_b(voltage_sources, num_nodes):
    len_kcl = num_nodes - 1
    len_b = len_kcl + len(voltage_sources)
    b = np.zeros((len_b,))
    for i in range(len_kcl, len_b):
        b[i] = voltage_sources[i - len_kcl].val
    return b


"""Recipe models module."""

from typing import Any

from django.db import models

from .food import Food
from .nutrients import NUTRIENT_LIST, Nutrients

class Recipe(Food):
    """Recipe models class."""

    description = models.TextField(
        blank=True,
    )

    nutrients_from_ingredients = models.BooleanField(
        default=False,
        verbose_name="Calculate nutrients from ingredients",
    )

    @property
    def num_ingredients(self) -> int:
        """Get number of ingredients.

        Returns:
            str: number of ingredients.
        """
        return self.ingredients.count()

    def __str__(self) -> str:
        """Get string representation of the object.

        Returns:
            str: string representation of the object.
        """
        return self.name

class RecipeIngredient(Nutrients):
    """RecipeIngredient models class."""

    recipe = models.ForeignKey(
        "foods.Recipe",
        on_delete=models.CASCADE,
        related_name="ingredients",
    )

    food = models.ForeignKey(
        "foods.Serving",
        on_delete=models.CASCADE,
        related_name="recipes",
    )

    num_servings = models.DecimalField(
        max_digits=10,
        decimal_places=1,
        default=1,
    )

    def __str__(self) -> str:
        """Get string representation of the object.

        Returns:
            str: string representation of the object.
        """
        return (
            f"{self.recipe.name} - {str(self.food)}"
            f" {self.food.size * self.num_servings} ({self.food.unit})"
        )

    @property
    def weight(self) -> int:
        """Get the weight of the serving.

        Returns:
            int: the weight of the serving.
        """
        return self.food.weight

    def save(self, *args: Any, **kwargs: Any) -> None:
        """Save instance into the db.

        Args:
            args (list): arguments.
            kwargs (dict): keyword arguments.
        """
        for nutrient in NUTRIENT_LIST:
            value = getattr(self.food, nutrient) or 0
            setattr(self, nutrient, value * self.num_servings)

        super().save(*args, **kwargs)


import sys
from scipy import linalg
import numpy as np
import utils as utils
import time
import random
from mgwr.gwr import GWR, MGWR
from mgwr.sel_bw import Sel_BW
from numpy.linalg import inv
from SMGWR.Cache import Cache
from utils import local_dist, kernel_funcs, alt, calculate_dependent

class SMGWRModel:
    """
        The class for the S-MGWR
    """

    def __init__(self, X_training, y_training, coords_training, config={}):
        """
        :param X_training: training features
        :param y_training:  training values
        :param coords_training:  training coordinates
        :param config: S-MGWR setting
            - cutoff indicates the cutoff value. If -1, there would be no cutoff
            - kernel function indicates the weight function applied to points around regression point
            - spherical indicates whether the coordinates are longitude and latitude or not
            - fixed indicates whether the bandwdith is adaptive or fixed
            - enable_cache indicates enable_cache to use cache or not
        """

        self.config = {"cutoff": -1, "kernel_function": 'gaussian', "eps": 1.0000001,
                       "spherical": True, "fixed": False, "enable_cache": True, "validation": "normal"}
        for key in config.keys():
            self.config[key] = config[key]

        bw = Sel_BW(coords_training, y_training, X_training, kernel='gaussian').search(criterion='CV')
        gwrModel = GWR(coords_training, y_training, X_training, bw=bw, fixed=False,
                       kernel='gaussian', spherical=True)
        gwrModelFit = gwrModel.fit()

        X_training = np.insert(X_training, 0, 1, axis=1)

        self.X_training = X_training
        self.y_training = y_training
        self.coords_training = coords_training
        self.train_len = self.X_training.shape[0]
        self.numberOfFeatures = self.X_training.shape[1]

        self.trainB = gwrModelFit.params
        pred = np.sum(self.trainB * X_training, axis=1).reshape((-1, 1))
        self.y_residuals = self.y_training - pred
        self.defaultB = np.copy(self.trainB)
        self.default_y_residuals = np.copy(self.y_residuals)
        self.bandwidths = [bw for _ in range(X_training.shape[1])]

        self.distances = np.zeros((self.train_len, self.train_len))
        for i in range(len(self.coords_training)):
            self.distances[i, :] = local_dist(
                self.coords_training[i], self.coords_training, self.config["spherical"]).reshape(-1)

        if self.config["enable_cache"]:
            self.cache = Cache(self.train_len, self.train_len)

    def _select_training(self, index, wi, indices):
        """
        selecting all the non zero weights
        :param index: index of the item, remove for cross validation
        :param wi: weights related to i-th observation
        :return: selected training, selected dependent, selected weights
        """
        if self.config["cutoff"] != -1:
            nonzero = list(np.where(~np.all(wi == 0, axis=1))[0])
            intersect = np.intersect1d(nonzero, indices)
            return intersect
        else:
            return indices

    def _build_wi(self, index, bandwidth, data, indices):
        """
        builds the weight matrix for regression point
        :param index: index of the regression point
        :param bandwidth: bandwidth value for the weight kernel
        :param data: rest of the values
        :param indices: indices to consider (the weight matrix is only build for these indices)
        :return: returns the weight matrix built around regression point for points indicated in indices
        """
        flag = False
        if self.config["enable_cache"] and type(data) == str:
            flag, values = self.cache.isHit(bandwidth, index)
        if flag:
            weights = values
        else:
            weights = np.zeros(self.train_len)
            if self.config["fixed"]:
                bw = float(bandwidth)
            else:
                pilot = min(int(bandwidth), len(indices) - 1)
                if type(data) == str and data == "training_data":
                    bw = np.partition(self.distances[index][indices], pilot - 1)[pilot - 1] * self.config["eps"]
                else:
                    dist = local_dist(data, self.coords_training[indices], self.config["spherical"]).reshape(-1)
                    bw = np.partition(dist, pilot - 1)[pilot - 1] * self.config["eps"]
            bw = max(bw, 1)

            if type(data) == str and data == "training_data":
                weights[indices] = kernel_funcs(self.distances[index][indices] / bw, self.config["kernel_function"])
            else:
                weights[indices] = kernel_funcs(dist[indices] / bw, self.config["kernel_function"])

            if self.config["cutoff"] > 0:
                pilot = int(bandwidth * self.config["cutoff"])
                if pilot < self.train_len:
                    idx = np.argpartition(weights, -pilot)
                    weights[idx[:-pilot]] = 0

            if self.config["enable_cache"] and type(data) == str and data == "training_data":
                self.cache.update(bandwidth, index, weights)

        return weights

    def _local_fit(self, i, j, bw, data, indices):
        """
        fits a weighted regression around the regression point for specific feature
        :param i: regression point index
        :param j: feature index
        :param bw: bandwidth
        :param data: rest of the points
        :param indices: indices to consider
        :return: returns the coefficient of the fitted model
        """
        wi = self._build_wi(i, bw[j], data, indices)
        wi = wi.reshape((-1, 1))
        indices = self._select_training(i, wi, indices)
        x_in = self.X_training[indices, :]
        y_in = self.y_training[indices, :]
        w_in = wi[indices, :]
        position = -1
        x = x_in[:, j].reshape((-1, 1))

        if self.y_residuals is None:
            temp = (x_in * self.trainB)
            temp = np.delete(temp, j, 1)
            pred = np.sum(temp, axis=1).reshape((-1, 1))
            y_in = y_in - pred
        else:
            if position == -1:
                y_in = self.y_residuals[indices] + ((x_in[:, j] * self.trainB[indices, j]).reshape((-1, 1)))
            else:
                new_ind = np.delete(indices, position)
                y_in = self.y_residuals[new_ind] + ((x_in[:, j] * self.trainB[indices, j]).reshape((-1, 1)))
        temp = np.transpose(x*w_in)
        left = np.dot(temp, x)
        right = np.dot(temp, y_in)
        if left == 0:
            return 0
        return inv(left)*right

    def fit(self, bw, indices=[], iterations=3):
        """
        fit a model for a specific bandwdith
        :param bw: bandwdith
        :param indices: indices to consider
        :param iterations: number of iterations the adjustment of coefficients is repeated
        :return: the error for the fitted model of the specific bandwidth
        """
        if len(indices) == 0:
            indices = list(range(self.train_len))
        indices = np.asarray(indices)

        self.trainB[indices] = np.copy(self.defaultB[indices])
        self.y_residuals[indices] = np.copy(self.default_y_residuals[indices])

        convergence_it = 0
        old_pred = 10
        new_pred = utils.R2(self.y_training[indices],
                            utils.calculate_dependent(self.trainB[indices, :], self.X_training[indices, :]))

        while convergence_it < iterations and (new_pred / old_pred) < 0.999:
            convergence_it += 1
            old_pred = new_pred
            for j in range(self.numberOfFeatures):
                for i in range(len(indices)):
                    self.trainB[indices[i], j] = self._local_fit(indices[i], j, bw, "training_data", indices)
                    if self.y_residuals is not None:
                        self.y_residuals[indices[i]] = self.y_training[indices[i]] - np.sum(
                            self.trainB[indices[i], :] * self.X_training[indices[i], :])

            new_pred = utils.R2(self.y_training[indices], self.y_training[indices] + self.y_residuals[indices])

    def setB(self, B):
        self.trainB = B
        pred = np.sum(self.trainB * self.X_training, axis=1).reshape((-1, 1))
        self.y_residuals = self.y_training - pred

        self.defaultB = np.copy(self.trainB)
        self.default_y_residuals = np.copy(self.y_residuals)

    def setB_ind(self, B, indices):
        self.trainB[indices, :] = B
        pred = np.sum(
            self.trainB[indices, :] * self.X_training[indices, :], axis=1).reshape((-1, 1))
        self.y_residuals[indices] = self.y_training[indices] - pred

        self.defaultB[indices, :] = np.copy(self.trainB[indices, :])
        self.default_y_residuals = np.copy(self.y_residuals[indices, :])

    def validate(self, state, validation_indices, train_indices):
        """
        predicts the values for the state based on the validation method and indices
        :param state: the state to be evaluated
        :return: returns the prediction values for the validation set
        """
        coefficients = np.zeros((len(validation_indices), len(self.bandwidths)))

        for j in range(self.numberOfFeatures):
            for i in range(len(validation_indices)):
                coefficients[i, j] = self._local_fit(
                    validation_indices[i], j, state, "training_data", train_indices)

        predictions = calculate_dependent(coefficients, self.X_training[validation_indices, :])
        return predictions

    def predict(self, test_coords, test_x):
        """
        :return: prediction of the values based on the model and bandwidths.
        """
        test_x = np.insert(test_x, 0, 1, axis=1)
        if self.config["enable_cache"]:
            self.cache.clear()
        indices = np.asarray(list(range(self.train_len)))
        coefficients = np.zeros((len(test_x), len(self.bandwidths)))
        test_indices = np.arange(len(test_coords))
        for j in range(self.numberOfFeatures):
            for i in range(len(test_coords)):
                coefficients[i, j] = self._local_fit(
                    i, j, self.bandwidths, test_coords[i], indices)

        predictions = calculate_dependent(coefficients, test_x)
        if self.config["enable_cache"]:
            self.cache.clear()
        return predictions


import cv2
import numpy as np

videoHeight = 240
videoWidth = 320

capture = cv2.VideoCapture(0)
capture.set(cv2.CAP_PROP_FPS, 10)

# Load the Haar cascade for face detection
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

while True:
    ret, frame = capture.read()
    if not ret:
        print('unable to read the video...')
        break

    frame = cv2.resize(frame, (videoWidth, videoHeight))
    frame = np.frombuffer(frame, dtype=np.uint8).reshape(videoHeight, videoWidth, 3)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, scaleFactor=1.3, minNeighbors=5)

    for (x, y, w, h) in faces:
        faceCenter = (int(x+w/2), int(y+h/2))
        # cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2) # face region
        cv2.putText(frame, 'x: %s, y: %s'%faceCenter, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) # face center coordinates
        cv2.line(frame, faceCenter, (int(videoWidth/2), int(videoHeight/2)), (0, 0, 255), 2) # face center to frame center line
        cv2.rectangle(frame, (int(videoWidth*0.25), int(videoHeight*0.25)), (int(videoWidth*0.75), int(videoHeight*0.75)), (255, 0, 0), 2)
        # face = gray[y:y+h, x:x+w] # choose the face region from gray

        if faceCenter[0] > videoWidth*0.75:
            print('Too right...')
        elif faceCenter[0] < videoWidth*0.25:
            print('Too left...')

        if faceCenter[1] > videoHeight*0.75:
            print('Too low...')
        elif faceCenter[1] < videoHeight*0.25:
            print('Too high...')

    cv2.imshow('Captured Video', frame)

    # stop video calling if type q
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cv2.destroyAllWindows()
capture.release()