from django import forms
from django.conf import settings
from django.forms import TextInput, RadioSelect, modelformset_factory
from django.utils.translation import ugettext_lazy as _

from accounts.models import CircusUser
from clients.models import Client
from finance.models import PAYMENT_CHOICES
from projects.models import Project, ProjectJobOptions
from services.managers import DEFAULT_INDUSTRY_CODE
from shared.widgets import DateTimeWidget
from django.forms.widgets import HiddenInput

from services.models import ServiceType
from django.forms.widgets import CheckboxSelectMultiple
from tinymce import TinyMCE
from shared.group_permissions import PROTECTED_HEALTH_INFORMATION_GROUP

RESTRICTED_CHOICES = (
        (True, _(u'Restricted Access')),
        (False, _(u'Unrestricted Access')),
    )

SECURE_JOB_CHOICES= (
        (True, _(u'Limit Team')),
        (False, _(u'Full Team')),
    )

class ProjectNewAutoJobForm(forms.ModelForm):
    project_reference_name = forms.CharField(required=False, max_length=100)

    def __init__(self, *args, **kwargs):
        super(ProjectNewAutoJobForm, self).__init__(*args, **kwargs)
        self.fields['name'].label = _('Job Name')
        self.fields['name'].help_text = ''
        self.fields['name'].required = True

        self.fields['job_number'].required = False

        self.fields['client_poc'].queryset = CircusUser.objects.none()

        self.fields['project_reference_name'].label = _('Client Job Reference')
        self.fields['internal_via_project'].label = _('No Client View')

        clients = Client.objects.filter(is_deleted=False).order_by('name')
        self.fields['client'].queryset = clients
        if len(clients) == 1:
            self.fields['client'].initial = clients[0].pk

        from shared.forms import form_get_client_id
        client_id = form_get_client_id(self)

        if client_id:
            # client is known. Now I can display the matching children.
            from shared.forms import form_get_client_poc
            client_poc =  form_get_client_poc(client_id)
            self.fields['client_poc'].queryset = client_poc
            if len(client_poc) == 1:
                self.fields['client_poc'].initial = client_poc[0].pk

    class Meta:
        model = Project
        exclude = ['kit', 'status', 'estimate_type']
        fields = [
            'job_number',
            'name',
            'client',
            'client_poc',
            'project_reference_name',
            'internal_via_project',
        ]
        widgets = {
            'job_number': TextInput(attrs={'placeholder': '(Leave this to auto-generate)'}),
        }

class ProjectAutoJobContinueForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ProjectAutoJobContinueForm, self).__init__(*args, **kwargs)
        self.fields['source_locale'].queryset = self.fields['source_locale'].queryset.filter(available=True)
        self.fields['source_locale'].required = True
        self.fields['target_locales'].help_text = ''
        self.fields['target_locales'].required = True
        self.fields['target_locales'].queryset = self.fields['target_locales'].queryset.filter(available=True)
        self.fields['services'].label = ''
        self.fields['services'].help_text = ''
        self.fields['services'].required = True

        self.fields['is_restricted_job'].label = _('Access')
        self.fields['is_restricted_job'].help_text = ''
        self.fields['is_restricted_job'].initial = False

        self.fields['is_secure_job'].label = _('Secure Job')
        self.fields['is_secure_job'].help_text = ''
        self.fields['is_secure_job'].initial = False

        self.fields['services'].queryset = ServiceType.objects.filter(
            clientservice__client=self.instance.client,
            clientservice__available=True,
            available=True
        ).order_by('-description')

        self.fields['industry'].queryset = self.fields['industry'].queryset.exclude(code=DEFAULT_INDUSTRY_CODE)

    class Meta:
        model = Project
        fields = [
            'industry',
            'source_locale',
            'target_locales',
            'services',
            'is_restricted_job',
            'is_secure_job'
        ]
        widgets = {
            'services': CheckboxSelectMultiple(),
            'is_restricted_job': RadioSelect(choices=RESTRICTED_CHOICES),
            'is_secure_job': RadioSelect(choices=SECURE_JOB_CHOICES),
        }

    def clean(self):
        cleaned_data = super(ProjectAutoJobContinueForm, self).clean()
        if not self.instance.kit.file_count():
            raise forms.ValidationError("Please upload at least one file for translation.")
        return cleaned_data

class ProjectForm(forms.ModelForm):

    payment_method = forms.ChoiceField(choices=PAYMENT_CHOICES, initial='ca', widget=forms.RadioSelect())
    ca_invoice_number = forms.CharField(required=False, max_length=100)
    rush_estimate = forms.BooleanField(required=False)
    project_reference_name = forms.CharField(required=False, max_length=100)
    reschedule_all_due_dates = forms.BooleanField(required=False, widget=forms.HiddenInput())

    instructions = forms.CharField(widget=TinyMCE(attrs={'cols': settings.TINYMCE_COLS, 'rows': settings.TINYMCE_ROWS}), required=False)
    instructions_via = forms.CharField(widget=TinyMCE(attrs={'cols': settings.TINYMCE_COLS, 'rows': settings.TINYMCE_ROWS}), required=False)
    instructions_vendor = forms.CharField(widget=TinyMCE(attrs={'cols': settings.TINYMCE_COLS, 'rows': settings.TINYMCE_ROWS}), required=False)

    def __init__(self, *args, **kwargs):
        super(ProjectForm, self).__init__(*args, **kwargs)
        self.fields['name'].label = _('Job Name')
        self.fields['name'].help_text = ''
        self.fields['name'].required = True

        self.fields['source_locale'].help_text = ''
        self.fields['source_locale'].required = True
        self.fields['source_locale'].queryset = self.fields['source_locale'].queryset.filter(available=True)

        self.fields['target_locales'].help_text = ''
        self.fields['target_locales'].required = True
        self.fields['target_locales'].queryset = self.fields['target_locales'].queryset.filter(available=True)
        if self.instance.id:
            target_count = u" (%s)" % (self.instance.target_locales_count(),)
            self.fields['target_locales'].label += target_count

        if self.instance.is_phi_secure_job:
            via_users = CircusUser.objects.filter(user_type=settings.VIA_USER_TYPE, groups__name=PROTECTED_HEALTH_INFORMATION_GROUP).order_by('first_name')
        else:
            via_users = CircusUser.objects.filter(user_type=settings.VIA_USER_TYPE).order_by('first_name')

        self.fields['project_manager'].queryset = via_users
        self.fields['account_executive'].queryset = via_users
        self.fields['estimator'].queryset = via_users
        self.fields['industry'].queryset = self.fields['industry'].queryset.exclude(code=DEFAULT_INDUSTRY_CODE)

        self.fields['is_restricted_job'].label = _('Access')
        self.fields['is_restricted_job'].help_text = ''
        self.fields['is_restricted_job'].initial = False

        self.fields['is_secure_job'].label = _('Secure Job')
        self.fields['is_secure_job'].help_text = ''
        self.fields['is_secure_job'].initial = False

        self.fields['is_phi_secure_job'].label = _('PHI Job')
        self.fields['is_phi_secure_job'].help_text = ''
        self.fields['is_phi_secure_job'].initial = False

        self.fields['delay_job_po'].label = _('PO Basis')
        self.fields['delay_job_po'].help_text = ''
        self.fields['delay_job_po'].initial = False

        self.fields['restricted_locations'].help_text = ''
        self.fields['restricted_locations'].label = _('Restricted Region')
        self.fields['restricted_locations'].required = False
        if self.instance.id:
            restricted_locations_count = u" (%s)" % (self.instance.restricted_locations.count(),)
            self.fields['restricted_locations'].label += restricted_locations_count

        self.fields['completed'].label = _('Downloaded Date')

        self.fields['job_number'].required = False

        self.fields['project_reference_name'].label = _('Client Job Reference')
        self.fields['internal_via_project'].label = _('No Client Access')
        self.fields['no_express_option'].label = _('No Client Express')
        self.fields['ignore_holiday_flag'].label = _('Holidays Ignored')

        if not self.instance.can_edit_locales():
            # select widgets don't necessarily have a readonly attribute, but
            # we can use this to get select2.js to use read-only mode.
            self.fields['source_locale'].widget.attrs['readonly'] = True
            self.fields['target_locales'].widget.attrs['readonly'] = True
            # if select2 isn't working, we can discourage editing by limiting
            # the querysets to the current values. Not entirely bulletproof
            # against removing current targets, but will discourage other changes.
            self.fields['source_locale'].queryset = self.fields['source_locale'].queryset.filter(id=self.instance.source_locale_id)
            self.fields['target_locales'].queryset = self.instance.target_locales

        self.fields['ca_invoice_number'].label = 'Purchase Order'
        if self.instance.payment_details:
            self.fields['payment_method'].initial = self.instance.payment_details.payment_method
            self.fields['ca_invoice_number'].initial = self.instance.payment_details.ca_invoice_number

        # chained select filter based upon the chosen Client Company

        self.fields['client_poc'].queryset = CircusUser.objects.none()

        clients = Client.objects.filter(is_deleted=False).order_by('name')
        self.fields['client'].queryset = clients
        if len(clients) == 1:
            self.fields['client'].initial = clients[0].pk

        # client_id = self.fields['client'].initial or \
        #             self.initial.get('client')
        from shared.forms import form_get_client_id
        client_id = form_get_client_id(self)

        if client_id:
            # client is known. Now I can display the matching children.
            from shared.forms import form_get_client_poc
            client_poc =  form_get_client_poc(client_id)
            self.fields['client_poc'].queryset = client_poc
            if len(client_poc) == 1:
                self.fields['client_poc'].initial = client_poc[0].pk

        if self.instance.canceled or self.instance.is_canceled_status():
            self.fields['canceled'].label = _('Canceled')
            self.fields['canceled'].initial = self.instance.canceled
        else:
            self.fields['canceled'].label = ''
            self.fields["canceled"].widget = HiddenInput()

    class Meta:
        model = Project
        exclude = ['kit', 'status', 'estimate_type']
        fields = [
            'job_number',
            'name',
            'project_speed',
            'client',
            'client_poc',
            'industry',
            'project_manager',
            'account_executive',
            'estimator',
            'source_locale',
            'target_locales',
            'quote_due',
            'quoted',
            'started_timestamp',
            'due',
            'delivered',
            'completed',
            'payment_method',
            'ca_invoice_number',
            'rush_estimate',
            'instructions',
            'instructions_via',
            'instructions_vendor',
            'is_restricted_job',
            'delay_job_po',
            'restricted_locations',
            'project_reference_name',
            'internal_via_project',
            'canceled',
            'no_express_option',
            'ignore_holiday_flag',
            'price_per_document',
            'is_secure_job',
            'is_phi_secure_job'
        ]
        widgets = {
            'quote_due': DateTimeWidget(),
            'quoted': DateTimeWidget(),
            'started_timestamp': DateTimeWidget(),
            'due': DateTimeWidget(),
            'delivered': DateTimeWidget(),
            'completed': DateTimeWidget(),
            'job_number': TextInput(attrs={'placeholder': '(Leave this to auto-generate)'}),
            'is_restricted_job': RadioSelect(choices=RESTRICTED_CHOICES),
            'is_secure_job': RadioSelect(choices=SECURE_JOB_CHOICES),
            'delay_job_po': RadioSelect(),
            'canceled': DateTimeWidget(),
        }

class ViaInstructionsForm(forms.Form):
    via_instructions = forms.CharField()

class ProjectJobOptionsForm(forms.ModelForm):
    editable_source = forms.BooleanField(required=False)
    recreation_source = forms.BooleanField(required=False)
    translation_unformatted = forms.BooleanField(required=False)
    translation_billingual = forms.BooleanField(required=False)

    class Meta:
        model = ProjectJobOptions
        fields = '__all__'

    def __init__(self, *args, **kwargs):
        super(ProjectJobOptionsForm, self).__init__(*args, **kwargs)
        self.fields['editable_source'].label = 'Editable Source on Approval (MS Office, InDesign, etc.)'
        self.fields['recreation_source'].label = 'Full Recreation of Source from PDFs (MS Office) '
        self.fields['translation_unformatted'].label = 'Translation Only as an Unformatted MS Word document'
        self.fields['translation_billingual'].label = 'Translation Only as a Bilingual MS Word document (two columns)'

ProjectJobOptionsSet = modelformset_factory(ProjectJobOptions, fields='__all__', exclude=None, can_delete=True)

class ViaWorkflowForm(forms.ModelForm):
    cost = forms.DecimalField(required=False)
    price = forms.DecimalField(required=False)
    express_cost = forms.DecimalField(required=False)
    express_price = forms.DecimalField(required=False)

    def __init__(self, *args, **kwargs):
        super(ViaWorkflowForm, self).__init__(*args, **kwargs)
        self.fields['name'].label = _('Job Name')
        self.fields['name'].help_text = ''
        self.fields['name'].required = True

        self.fields['source_locale'].help_text = ''
        self.fields['source_locale'].required = True
        self.fields['source_locale'].queryset = self.fields['source_locale'].queryset.filter(available=True)

        self.fields['target_locales'].help_text = ''
        self.fields['target_locales'].required = True
        self.fields['target_locales'].queryset = self.fields['target_locales'].queryset.filter(available=True)
        if self.instance.id:
            target_count = u" (%s)" % (self.instance.target_locales.count(),)
            self.fields['target_locales'].label += target_count

        self.fields['services'].label = ''
        self.fields['services'].help_text = ''
        self.fields['services'].required = True
        self.fields['services'].queryset = ServiceType.objects.filter(available=True, workflow=True).order_by('category', 'description')

        self.fields['job_number'].required = True
        self.fields['industry'].queryset = self.fields['industry'].queryset.exclude(code=DEFAULT_INDUSTRY_CODE)

        self.fields['client_poc'].queryset = CircusUser.objects.none()

        clients = Client.objects.filter(is_deleted=False).order_by('name')
        self.fields['client'].queryset = clients
        if len(clients) == 1:
            self.fields['client'].initial = clients[0].pk

        from shared.forms import form_get_client_id
        client_id = form_get_client_id(self)

        if client_id:
            # client is known. Now I can display the matching children.
            from shared.forms import form_get_client_poc
            client_poc = form_get_client_poc(client_id)
            self.fields['client_poc'].queryset = client_poc
            if len(client_poc) == 1:
                self.fields['client_poc'].initial = client_poc[0].pk

    class Meta:
        model = Project
        fields = [
            'job_number',
            'client',
            'client_poc',
            'name',
            'source_locale',
            'target_locales',
            'industry',
            'services',
            'cost',
            'price',
            'express_cost',
            'express_price',
        ]
        widgets = {
            'services': CheckboxSelectMultiple(),
        }


from uuid import UUID

import pytest
from flask import Flask
from geolite2 import geolite2

from sinfonia.deployment_repository import DeploymentRepository
from sinfonia.matchers import match_by_location, match_by_network, match_random

GOOD_UUID = "00000000-0000-0000-0000-000000000000"
GOOD_CONTENT = """\
chart: example
version: 0.1.0
restricted: false
"""
BAD_UUID = "00000000-0000-0000-0000-000000000001"
BAD_CONTENT = """\
chart: example
"""
RESTRICTED_UUID = "00000000-0000-0000-0000-000000000002"
RESTRICTED_CONTENT = """\
chart: private
version: 0.1.0
"""

@pytest.fixture(scope="session")
def repository(tmp_path_factory):
    repo = tmp_path_factory.mktemp("repository")
    (repo / GOOD_UUID).with_suffix(".yaml").write_text(GOOD_CONTENT)
    (repo / BAD_UUID).with_suffix(".yaml").write_text(BAD_CONTENT)
    (repo / RESTRICTED_UUID).with_suffix(".yaml").write_text(RESTRICTED_CONTENT)
    return DeploymentRepository(repo)

@pytest.fixture(scope="session")
def good_uuid():
    return UUID(GOOD_UUID)

@pytest.fixture(scope="session")
def bad_uuid():
    return UUID(BAD_UUID)

@pytest.fixture(scope="session")
def restricted_uuid():
    return UUID(RESTRICTED_UUID)

@pytest.fixture(scope="session")
def flask_app():
    app = Flask("test")
    app.config["geolite2_reader"] = geolite2.reader()
    app.config["match_functions"] = [match_by_network, match_by_location, match_random]
    return app

@pytest.fixture(scope="session")
def example_wgkey():
    return "YpdTsMtb/QCdYKzHlzKkLcLzEbdTK0vP4ILmdcIvnhc="


import random
import sys
import os

import pygame as pg

main_dir = os.path.split(os.path.abspath(__file__))[0]

class Screen:
    def __init__(self, title, wh, img_path):
        # 練習１
        pg.display.set_caption(title)
        self.sfc = pg.display.set_mode(wh)
        self.rct = self.sfc.get_rect()
        self.bgi_sfc = pg.image.load(img_path)
        self.bgi_rct = self.bgi_sfc.get_rect()

    def blit(self):
        self.sfc.blit(self.bgi_sfc, self.bgi_rct)

class Bird:
    key_delta = {
        pg.K_UP:    [0, -1],
        pg.K_DOWN:  [0, +1],
        pg.K_LEFT:  [-1, 0],
        pg.K_RIGHT: [+1, 0],
    }

    def __init__(self, img_path, ratio, xy):
        self.sfc = pg.image.load(img_path) #"fig/6.png"
        self.sfc = pg.transform.rotozoom(self.sfc, 0, ratio) #2.0
        self.rct = self.sfc.get_rect()
        self.rct.center = xy #900, 400

    def blit(self, scr:Screen):
        scr.sfc.blit(self.sfc, self.rct)

    def update(self, scr:Screen):
        key_dct = pg.key.get_pressed()
        for key, delta in Bird.key_delta.items():
            if key_dct[key]:
                self.rct.centerx += delta[0]
                self.rct.centery += delta[1]
            if check_bound(self.rct, scr.rct) != (+1, +1):
                self.rct.centerx -= delta[0]
                self.rct.centery -= delta[1]
        self.blit(scr)

class Brids:
    def __init__(self, img_path, ratio, xy):
        self.sfc = pg.image.load(img_path)
        self.sfc = pg.transform.rotozoom(self.sfc, 0, ratio) #2.0
        self.rct = self.sfc.get_rect()
        self.rct.center = xy

    def blit(self, scr:Screen):
        scr.sfc.blit(self.sfc, self.rct)

class Bomb:
    def __init__(self, color, rad, vxy, scr:Screen):

        self.sfc = pg.Surface((2*rad, 2*rad)) # 正方形の空のSurface
        self.sfc.set_colorkey((0, 0, 0))
        self.color = color
        pg.draw.circle(self.sfc, color, (rad, rad), rad)
        self.rct = self.sfc.get_rect()
        self.rct.centerx = random.randint(0, scr.rct.width)
        self.rct.centery = random.randint(0, scr.rct.height)
        self.vx, self.vy = vxy

    def blit(self, scr:Screen):
        scr.sfc.blit(self.sfc, self.rct)

    def update(self, scr:Screen):
        self.rct.move_ip(self.vx, self.vy)
        yoko, tate = check_bound(self.rct, scr.rct)
        self.vx *= yoko
        self.vy *= tate
        self.blit(scr)

class Shield: #保護場の追加
    def __init__(self, color, x, y, hw, scr:Screen):
        self.sfc = pg.Surface((hw+50, hw+50))
        pg.draw.rect(self.sfc, color, (x, y, hw, hw))
        self.rct = self.sfc.get_rect()
        self.rct.centerx = random.randint(0, scr.rct.width)
        self.rct.centery = random.randint(0, scr.rct.height)

    def blit(self, scr:Screen):
        scr.sfc.blit(self.sfc, self.rct)

def load_sound(file):#音声の追加
    """because pygame can be be compiled without mixer."""
    if not pg.mixer:
        return None
    file = os.path.join(main_dir, "data", file)
    try:
        sound = pg.mixer.Sound(file)
        return sound
    except pg.error:
        print("Warning, unable to load, %s" % file)
    return None

def check_bound(obj_rct, scr_rct):
    """
    第1引数：こうかとんrectまたは爆弾rect
    第2引数：スクリーンrect
    範囲内：+1／範囲外：-1
    """
    yoko, tate = +1, +1
    if obj_rct.left < scr_rct.left or scr_rct.right < obj_rct.right:
        yoko = -1
    if obj_rct.top < scr_rct.top or scr_rct.bottom < obj_rct.bottom:
        tate = -1
    return yoko, tate

def main():
    clock =pg.time.Clock()

    # 練習１
    scr = Screen("負けるな！こうかとん", (1600, 900), "fig/pg_bg.jpg" )

    if pg.mixer:
        music = os.path.join(main_dir, "data", "house_lo.wav")
        pg.mixer.music.load(music)
        pg.mixer.music.play(-1)

    hogo = Shield("green", 300, 300, 100, scr)
    hogo.blit(scr)

    # 練習３
    kkt = Bird("fig/6.png", 2.0, (900, 400))
    kkt_friend = Bird("fig/8.png", 1.0, (1400, 100))
    kkt_friend.blit(scr)
    # scrn_sfcにtori_rctに従って，tori_sfcを貼り付ける
    kkt.update(scr)

    # 練習５
    bkd_lst = []
    colors = ["red", "green", "blue", "yellow", "magenta"]
    for i in range(0, 10):
        color = colors[i%5]
        vx = random.choice([-1, +1])
        vy = random.choice([-1, +1])
        bkd = Bomb(color, 10, (vx, vy), scr)
        bkd_lst.append(bkd)

    # 練習２
    while True:
        scr.blit()   # scrn_sfc.blit(pgbg_sfc, pgbg_rct)
        for event in pg.event.get():
            if event.type == pg.QUIT:
                return
        hogo.blit(scr)
        kkt.update(scr)
        kkt.blit(scr)
        for i in range(10):
            bkd_lst[i].update(scr)
            if kkt.rct.colliderect(bkd_lst[i].rct):
                if kkt.rct in hogo.rct: #こうかとんが保護場にいると
                    pass
                else:
                    return

        pg.display.update()
        clock.tick(1000)

if __name__ == "__main__":
    pg.init()
    main()
    pg.quit()
    sys.exit()


from sympy import expand, poly, simplify
from cartesian import *

def foot(P1, P2, P3):
    x, y = symbols('x, y')
    P1P2 = line(P1, P2)
    P3H = Eq((P1[0] - P2[0])*(x - P3[0]) + (P1[1] - P2[1])*(y - P3[1]), 0)
    return intersect(P1P2, P3H)

def circle(P1, P2, P3):
    # return F(x, y) such that F(x, y) = 0 is the circle's equation
    D, E, F, x, y = symbols('D, E, F, x, y')
    circle_eq = Eq(x**2 + y**2 + D*x + E*y + F, 0)
    circle_eqs = []
    circle_eqs.append(circle_eq.subs(x, P1[0]).subs(y, P1[1]))
    circle_eqs.append(circle_eq.subs(x, P2[0]).subs(y, P2[1]))
    circle_eqs.append(circle_eq.subs(x, P3[0]).subs(y, P3[1]))
    s = solve(circle_eqs, (D, E, F))
    return x**2 + y**2 + s[D]*x + s[E]*y + s[F]

def center(c):
    x, y = symbols('x, y')
    return cancel(-poly(c, x).nth(1)/2), cancel(-poly(c, y).nth(1)/2)

def cos2eq(a2, b2, c2, d2, e2, f2):
    left_num = fraction(cancel(a2 + b2 - c2))
    left_den1 = fraction(cancel(a2))
    left_den2 = fraction(cancel(b2))
    right_num = fraction(cancel(d2 + e2 - f2))
    right_den1 = fraction(cancel(d2))
    right_den2 = fraction(cancel(e2))
    left = left_num[0]**2*left_den1[1]*left_den2[1]*right_num[1]**2*right_den1[0]*right_den2[0]
    right = right_num[0]**2*right_den1[1]*right_den2[1]*left_num[1]**2*left_den1[0]*left_den2[0]
    return expand(left - right) == 0

def main():
    a, b, c, d, e, x, y = symbols('a, b, c, d, e, x, y')
    A, B, C, P = (0, a), (b, 0), (c, 0), (d, e)
    D, E, F = foot(B, C, P), foot(C, A, P), foot(A, B, P)
    print('D:', D)
    print('E:', E)
    print('F:', F)
    pedal_circle = circle(D, E, F)
    print('Pedal Circle:', pedal_circle, '= 0')
    M = center(pedal_circle)
    print('M:', M)
    Q = (2*M[0] - P[0], 2*M[1] - P[1])
    print('Q:', Q)
    G, H, J = foot(B, C, Q), foot(C, A, Q), foot(A, B, Q)
    print('G:', G)
    print('H:', H)
    print('J:', J)
    print('Is G on Pedal Circle?', simplify(pedal_circle.subs(x, G[0]).subs(y, G[1])) == 0)
    print('Is H on Pedal Circle?', simplify(pedal_circle.subs(x, H[0]).subs(y, H[1])) == 0)
    print('Is J on Pedal Circle?', simplify(pedal_circle.subs(x, J[0]).subs(y, J[1])) == 0)
    AB2, AC2, BC2 = dist2(A, B), dist2(A, C), dist2(B, C)
    PA2, PB2, PC2 = dist2(P, A), dist2(P, B), dist2(P, C)
    QA2, QB2, QC2 = dist2(Q, A), dist2(Q, B), dist2(Q, C)
    print('cos²∠PAB = cos²∠QAC?', cos2eq(PA2, AB2, PB2, QA2, AC2, QC2))
    print('cos²∠PAC = cos²∠QAB?', cos2eq(PA2, AC2, PC2, QA2, AB2, QB2))
    print('cos²∠PBA = cos²∠QBC?', cos2eq(PB2, AB2, PA2, QB2, BC2, QC2))
    print('cos²∠PBC = cos²∠QBA?', cos2eq(PB2, BC2, PC2, QB2, AB2, QA2))
    print('cos²∠PCA = cos²∠QCB?', cos2eq(PC2, AC2, PA2, QC2, BC2, QB2))
    print('cos²∠PCB = cos²∠QCA?', cos2eq(PC2, BC2, PB2, QC2, AC2, QA2))

if __name__ == '__main__':
    main()


#! python3
# combinePdfs.py - Combines all the PDFs in the current working directory into
#                   a single pdf.

import os, PyPDF2

# Get all the PDF filename
pdfFiles = []
for filename in os.listdir('.'):
    if filename.endswith('.pdf'):
        pdfFiles.append(filename)
pdfFiles.sort(key=str.lower) # case insensitive sorting

pdfWriter = PyPDF2.PdfFileWriter()
counter = 0
# Open each PDF
for pdf in pdfFiles:
    pdfFileObj = open(pdf, 'rb')
    pdfReader = PyPDF2.PdfFileReader(pdfFileObj)
    for page in range(1, pdfReader.numPages):
        pageObj = pdfReader.getPage(page)
        pdfWriter.addPage(pageObj)

pdfOutput = open('Chapter 13_all.pdf', 'wb')
pdfWriter.write(pdfOutput)
pdfOutput.close()

# Similar Programs:
## Cut out specific pages from PDFs.
## Reorder pages in a PDF.
## Create a PDF from only those pages that have some specific text,
    ##identified by 'extractText()'

Chapter 13/Chapter 13 - Project - Combining Select Pages from many PDFs.py


import pytest
from pydantic import ValidationError
from geocoder_studitemps.geocoder import Address
from i18naddress import InvalidAddress

def test_works_on_valid_addresses():
    addresses = [
        {
            "data" : {
                "street" : "im mediapark 4a",
                "postal_code" : "50670",
                "city" : "köln"
            },
            "str" : "<Address (im mediapark 4a, 50670 köln) (DE)>",
            "req_str" : "im mediapark 4a, 50670 köln",
        }
    ]

    for addr_info in addresses:
        addr = Address(**addr_info["data"])

        assert addr.country_code == "DE"
        assert addr.to_request_string() == addr_info["req_str"]
        assert str(addr) == addr_info["str"]

def test_raises_validation_error_on_invalid_addresses():
    addresses = [
        {
            "data" : {
                "street" : "im mediapark 4a",
                "postal_code" : "800 45",
                "city" : "Madrid"
            },
            "err" : r"Invalid address*"
        }
    ]

    for addr_info in addresses:
        with pytest.raises(InvalidAddress, match=addr_info["err"]):
            addr = Address(**addr_info["data"])
            addr.validate()


#!/usr/bin/env python
"""

Neural SPARQL Machines - Script to filter data by a given criterion.

'SPARQL as a Foreign Language' by Tommaso Soru and Edgard Marx et al., SEMANTiCS 2017
https://arxiv.org/abs/1708.07624

Version 2.0.0

"""
import argparse
import collections
import json
import os
import sys

from generator_utils import encode, save_cache, extract_encoded_entities
import importlib

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    requiredNamed = parser.add_argument_group('required named arguments')
    requiredNamed.add_argument('--dataset', dest='dataset', metavar='data_300.en', help='dataset', required=True)
    requiredNamed.add_argument('--used_resources', dest='used_resources', metavar='used_resources.json', help='json file', required=True)
    requiredNamed.add_argument('--minimum', dest='minimum', metavar='15', help='minimum number of occurence', required=True)
    requiredNamed.add_argument('--comp', dest='comp', metavar='all|any', help='require minimum for all/any resources in the query', required=True)
    args = parser.parse_args()

    dataset_file = args.dataset
    used_resources_file = args.used_resources
    MINIMUM = int(args.minimum)
    COMP = any if args.comp == 'any' else all

    importlib.reload(sys)
    sys.setdefaultencoding("utf-8")

    dataset_root, _ = os.path.splitext(dataset_file)
    used_resources_root, _ = os.path.splitext(used_resources_file)
    filtered_sparql_file = '{}_filtered_{:d}_{}.sparql'.format(dataset_root, MINIMUM, COMP.__name__)
    filtered_en_file = '{}_filtered_{:d}_{}.en'.format(dataset_root, MINIMUM, COMP.__name__)

    used_resources = collections.Counter(json.loads(open(used_resources_file).read()))
    filtered_resources = [elem_cnt for elem_cnt in list(used_resources.items()) if elem_cnt[1] >= MINIMUM]
    save_cache('{}_filter_{:d}.json'.format(used_resources_root, MINIMUM), collections.Counter(dict(filtered_resources)))
    valid_encoded_resources = [encode(elem_cnt1[0]) for elem_cnt1 in filtered_resources]
    check = lambda encoded_entity : encoded_entity in valid_encoded_resources

    valid_lines = []
    filtered_queries = []
    with open(dataset_root+'.sparql', 'r') as sparql_file:
        for linenumber, line in enumerate(sparql_file):
            entities = extract_encoded_entities(line)
            valid = COMP(list(map(check, entities)))
            if valid:
                filtered_queries.append(line)
                valid_lines.append(linenumber)

    filtered_questions = []
    with open(dataset_root+'.en', 'r') as en_file:
        for linenumber, line in enumerate(en_file):
            if linenumber in valid_lines:
                filtered_questions.append(line)

    with open(filtered_en_file, 'w') as filtered:
        filtered.writelines(filtered_questions)

    with open(filtered_sparql_file, 'w') as filtered:
        filtered.writelines(filtered_queries)


''' Implements tests for the Kako configuration parser. '''

import os
import yaml
import unittest
import coverage

import kako

class KakoConfigTestCase(unittest.TestCase):
    ''' Defines tests for the Kako configuration parser. '''

    def setUp(self):
        ''' Ensure the application is setup for testing. '''
        self.fixtures_path = os.path.join(
            os.path.dirname(os.path.abspath(__file__)),
            'fixtures/'
        )

    def tearDown(self):
        ''' Ensure everything is torn down between tests. '''
        pass

    def test_config_validation(self):
        ''' Ensures that configurations can be validated. '''
        # Ensure a valid config loads without error.
        valid = yaml.safe_load(
            open(os.path.join(self.fixtures_path, 'valid_config.yaml'))
        )
        self.assertIsNone(kako.config.validate(valid))

        # Ensure an invalid config throws an AttributeError
        invalid = yaml.safe_load(
            open(os.path.join(self.fixtures_path, 'invalid_config.yaml'))
        )
        self.assertRaises(AttributeError, kako.config.validate, invalid)


from fastapi import FastAPI, HTTPException, Body
from fastapi.encoders import jsonable_encoder
from fastapi.middleware.cors import CORSMiddleware
from datetime import datetime

#App object
app = FastAPI()

from models import Recipe, UpdateRecipeModel

from database import(
    fetch_one_recipe,
    fetch_all_recipes,
    create_recipe,
    update_recipe,
    remove_recipe
)

origins = ["*"]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials = True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# @app.get("/")
# def read_root():
#     return {"Ping":"Pong"}

@app.get("/")
async def read_root():
    response = {"hello": "world"}
    return response

@app.get("/api/recipe", tags=["Recipes"])
async def get_recipes():
    response = await fetch_all_recipes()
    return response

@app.get("/api/recipe/{id}", response_description="Get a single recipe", response_model=Recipe, tags=["Recipes"])
async def get_recipe_by_id(id: str):
    response = await fetch_one_recipe(id)
    if response:
        return response
    raise HTTPException(404, f"ID {id} not found")

@app.post("/api/recipe", response_description="Add a new recipe", response_model=Recipe, tags=["Recipes"])
async def post_recipe(recipe: Recipe = Body(...)):
    recipe = jsonable_encoder(recipe)
    response = await create_recipe(recipe)
    if response:
        return response
    raise HTTPException(400, "Something went wrong / Bad Request")

@app.put("/api/recipe/{id}", response_description="Update a recipe", response_model=Recipe, tags=["Recipes"])
async def put_recipe(id: str, recipe: UpdateRecipeModel = Body(...)):
    recipe = {k: v for k, v in recipe.dict().items() if v is not None}
    if len(recipe) >= 1:
        response = await update_recipe(id, recipe)
    if response:
        return response
    raise HTTPException(404, f"There is no recipe with this id {id}")

@app.delete("/api/recipe/{id}", response_description="Delete a recipe", tags=["Recipes"])
async def delete_recipe(id: str):
    response = await remove_recipe(id)
    if response:
        return "Successfully deleted recipe item"
    raise HTTPException(404, f"There is no recipe item with this id:{id}")


import unittest
from logging import INFO

from common import exceptions

import logger
from series import FileParser

class KnownValues(unittest.TestCase):
    File_SxxExx = {}
    File_SxxExx['FileName'] = ""
    File_SxxExx['SeriesName'] = 'Covert Affairs'
    File_SxxExx['SeasonNum'] = 1
    File_SxxExx['EpisodeNums'] = [1]
    File_SxxExx['type'] = 'episode'
    File_SxxExx['Ext'] = 'ext'

class FileParserExceptions(unittest.TestCase):

    def setUp(self):

        TRACE = 5
        VERBOSE = 15

        logger.initialize(unit_test=True, level=INFO)
#        logger.start(level=ERROR)

        self.library = FileParser()

    def test_FileParser_exception_case_001(self):
        # should raise an exception for missing or invalid patient_id
        KnownValues.File_SxxExx['FileName'] = '/mnt/Download/Bittorrent/the.big.bang.theory.season.1.avi'
        self.assertRaises(exceptions.InvalidFilename, self.library.getFileDetails, KnownValues.File_SxxExx['FileName'])

    def theSuite(self):
        suite = unittest.TestLoader().loadTestsFromTestCase(self)
        return suite

if __name__ == '__main__':
    suite = FileParserExceptions.theSuite()
    unittest.TextTestRunner(verbosity=2).run(suite)


import logging
import os
import copy

log = logging.getLogger(__name__)

# Array of all valid language codes.
VALID_LANGUAGE_CODES = (
    "eng bas n6x hex"
)

# Codes for which we have webtext but no fonts:
UNUSABLE_LANGUAGE_CODES = ""

# ASCIIをカバーしたフォント
BASE_FONTS = [
    "PixelMplus10",
    "PixelMplus12",
    "VL Gothic",
    "TakaoGothic",
    "TakaoMincho",
    "Noto Sans Mono CJK JP Regular",
    "Noto Serif CJK JP SemiBold",
    "P6mk2mode1page1font",
    "P6mk2mode5page1font",
    "P6 Printer Routine",
    "PiO Printer A",
    "N-Font_Original",
    "MSX-FONT-Wide",
]

# ASCII特化フォント(ひらがなが出せない)
BAS_FONTS = copy.deepcopy(BASE_FONTS)
BAS_FONTS.extend([
    "DotMatrix weight=101",
    "Commodore PET",
    "lcdfont",
    "Verily Serif Mono",
    "Arcade",
    "Go Mono",
])

HEX_FONTS = BAS_FONTS

# 国産BASIC特化フォント
# 美咲フォントは半角文字が小さすぎるのでASCIIのみの言語には含めない
# (ひらがな、グラフィック文字の学習に使いたい)
N6X_FONTS = copy.deepcopy(BASE_FONTS)
N6X_FONTS.extend([
    "MisakiGothic",
    "MisakiMincho",
])

VERTICAL_FONTS = []

FLAGS_webtext_prefix = os.environ.get("FLAGS_webtext_prefix", "")

# Set language-specific values for several global variables, including
#   ${TEXT_CORPUS}
#      holds the text corpus file for the language, used in phase F
#   ${FONTS[@]}
#      holds a sequence of applicable fonts for the language, used in
#      phase F & I. only set if not already set, i.e. from command line
#   ${TRAINING_DATA_ARGUMENTS}
#      non-default arguments to the training_data program used in phase T
#   ${FILTER_ARGUMENTS}[ -]
#      character-code-specific filtering to distinguish between scripts
#      (eg. CJK) used by filter_borbidden_characters in phase F
#   ${WORDLIST2DAWG_ARGUMENTS}
#      specify fixed length dawg generation for non-space-delimited lang
# TODO(dsl): We can refactor these into functions that assign FONTS,
# TEXT_CORPUS, etc. separately.
def set_lang_specific_parameters(ctx, lang):
    # The default text location is now given directly from the language code.
    TEXT_CORPUS = f"{FLAGS_webtext_prefix}/{lang}.corpus.txt"
    FILTER_ARGUMENTS = []
    WORDLIST2DAWG_ARGUMENTS = ""
    # These dawg factors represent the fraction of the corpus not covered by the
    # dawg, and seem like reasonable defaults, but the optimal value is likely
    # to be highly corpus-dependent, as well as somewhat language-dependent.
    # Number dawg factor is the fraction of all numeric strings that are not
    # covered, which is why it is higher relative to the others.
    PUNC_DAWG_FACTOR = None
    NUMBER_DAWG_FACTOR = 0.125
    WORD_DAWG_FACTOR = 0.05
    BIGRAM_DAWG_FACTOR = 0.015
    TRAINING_DATA_ARGUMENTS = []
    FRAGMENTS_DISABLED = "y"
    RUN_SHAPE_CLUSTERING = False
    AMBIGS_FILTER_DENOMINATOR = "100000"
    MEAN_COUNT = 40  # Default for latin script.
    # Language to mix with the language for maximum accuracy. Defaults to eng.
    # If no language is good, set to the base language.
    MIX_LANG = "eng"
    FONTS = ctx.fonts
    TEXT2IMAGE_EXTRA_ARGS = ["--invert=false"]
    LEADINGS = "-6 0".split()
    EXPOSURES = "0 2 4 -2 -4 -6".split()
    CHAR_SPACINGS = "0 0.7 1.4".split()

    GENERATE_WORD_BIGRAMS = None
    WORD_DAWG_SIZE = None

    LANG_IS_RTL = False
    NORM_MODE = 1

    # languages.
    if lang == "eng":
        MEAN_COUNT = "15"
        WORD_DAWG_FACTOR = 0.015
        GENERATE_WORD_BIGRAMS = 0
        TRAINING_DATA_ARGUMENTS += ["--infrequent_ratio=10000"]
        TRAINING_DATA_ARGUMENTS += ["--no_space_in_output --desired_bigrams="]
        FILTER_ARGUMENTS = ["--charset_filter=eng --segmenter_lang=eng"]
        if not FONTS:
            FONTS = HEX_FONTS
    if lang == "hex":
        MEAN_COUNT = "15"
        WORD_DAWG_FACTOR = 0.015
        GENERATE_WORD_BIGRAMS = 0
        TRAINING_DATA_ARGUMENTS += ["--infrequent_ratio=10000"]
        TRAINING_DATA_ARGUMENTS += ["--no_space_in_output --desired_bigrams="]
        FILTER_ARGUMENTS = ["--charset_filter=hex --segmenter_lang=hex"]
        if not FONTS:
            FONTS = HEX_FONTS
    elif lang == "jpn":
        MEAN_COUNT = "15"
        WORD_DAWG_FACTOR = 0.015
        GENERATE_WORD_BIGRAMS = 0
        TRAINING_DATA_ARGUMENTS += ["--infrequent_ratio=10000"]
        TRAINING_DATA_ARGUMENTS += ["--no_space_in_output --desired_bigrams="]
        FILTER_ARGUMENTS = ["--charset_filter=jpn --segmenter_lang=jpn"]
        if not FONTS:
            FONTS = N6X_FONTS
    elif lang == "n6x":
        MEAN_COUNT = "15"
        WORD_DAWG_FACTOR = 0.015
        GENERATE_WORD_BIGRAMS = 0
        TRAINING_DATA_ARGUMENTS += ["--infrequent_ratio=10000"]
        TRAINING_DATA_ARGUMENTS += ["--no_space_in_output --desired_bigrams="]
        FILTER_ARGUMENTS = ["--charset_filter=n6x --segmenter_lang=n6x"]
        if not FONTS:
            FONTS = N6X_FONTS
    elif lang == "bas":
        MEAN_COUNT = "15"
        WORD_DAWG_FACTOR = 0.015
        GENERATE_WORD_BIGRAMS = 0
        TRAINING_DATA_ARGUMENTS += ["--infrequent_ratio=10000"]
        TRAINING_DATA_ARGUMENTS += ["--no_space_in_output --desired_bigrams="]
        FILTER_ARGUMENTS = ["--charset_filter=bas --segmenter_lang=bas"]
        if not FONTS:
            FONTS = BAS_FONTS

    FLAGS_mean_count = int(os.environ.get("FLAGS_mean_count", -1))
    if FLAGS_mean_count > 0:
        TRAINING_DATA_ARGUMENTS += [f"--mean_count={FLAGS_mean_count}"]
    elif not MEAN_COUNT:
        TRAINING_DATA_ARGUMENTS += [f"--mean_count={MEAN_COUNT}"]

    # Default to Latin fonts if none have been set
    if not FONTS:
        FONTS = LATIN_FONTS

    # Default to 0 exposure if it hasn't been set
    if not EXPOSURES:
        EXPOSURES = [0]

    vars_to_transfer = {
        'ambigs_filter_denominator': AMBIGS_FILTER_DENOMINATOR,
        'bigram_dawg_factor': BIGRAM_DAWG_FACTOR,
        'exposures': EXPOSURES,
        'char_spacings': CHAR_SPACINGS,
        'filter_arguments': FILTER_ARGUMENTS,
        'fonts': FONTS,
        'fragments_disabled': FRAGMENTS_DISABLED,
        'generate_word_bigrams': GENERATE_WORD_BIGRAMS,
        'lang_is_rtl': LANG_IS_RTL,
        'leadings': LEADINGS,
        'mean_count': MEAN_COUNT,
        'mix_lang': MIX_LANG,
        'norm_mode': NORM_MODE,
        'number_dawg_factor': NUMBER_DAWG_FACTOR,
        'punc_dawg_factor': PUNC_DAWG_FACTOR,
        'run_shape_clustering': RUN_SHAPE_CLUSTERING,
        'text2image_extra_args': TEXT2IMAGE_EXTRA_ARGS,
        'text_corpus': TEXT_CORPUS,
        'training_data_arguments': TRAINING_DATA_ARGUMENTS,
        'word_dawg_factor': WORD_DAWG_FACTOR,
        'word_dawg_size': WORD_DAWG_SIZE,
        'wordlist2dawg_arguments': WORDLIST2DAWG_ARGUMENTS,
    }

    for attr, value in vars_to_transfer.items():
        if hasattr(ctx, attr):
            if getattr(ctx, attr) != value:
                log.debug(f"{attr} = {value} (was {getattr(ctx, attr)})")
                setattr(ctx, attr, value)
            else:
                log.debug(f"{attr} = {value} (set on cmdline)")
        else:
            log.debug(f"{attr} = {value}")
            setattr(ctx, attr, value)

    return ctx

# =============================================================================
# END of Language specific info
# =============================================================================


from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect, get_object_or_404

from .forms import PostForm, CommentForm
from .models import Post, Comment

@login_required(login_url='login')
def post_list(request):
    posts = Post.objects.all().order_by('-created_at')
    return render(request, 'board/post_list.html',
                  {'posts': posts})

@login_required(login_url='login')
def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    comments = Comment.objects.filter(post_id=pk).order_by('-comment_created')

    if request.method == 'POST':
        form = CommentForm(request.POST)
        if form.is_valid():
            comment_form = form.save(commit=False)
            comment_form.post = post
            comment_form.comment_writer = request.user
            comment_form.save()
            return redirect('board:post_detail', pk=post.pk)

    else:
        form = CommentForm()
        context = {
            'post': post,
            'comments': comments,
            'comment_form': form
        }
        return render(request, 'board/post_detail.html', context)

@login_required(login_url='login')
def post_upload(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            return redirect('board:post_list')
    else:
        form = PostForm()
    return render(request, 'board/post_upload.html', {
        'form': form,
    })

@login_required(login_url='login')
def post_edit(request, pk):
    item = get_object_or_404(Post, pk=pk)
    # 그 사람 id인거 인증하는거 있어야함
    if request.method == 'POST':
        form = PostForm(instance=item)
        if form.is_valid():
            item = form.save()
            messages.success(request, '포스트를 수정했습니다.')
            return redirect(item)
    else:
        form = PostForm(instance=item)
    return render(request, 'board/post_edit.html', {
        'form': form,
    })

@login_required(login_url='login')
def post_delete(request, pk):
    post = Post.objects.get(pk=pk)
    if request.method == 'POST':
        # 그 사람 id인거 인증하는거 있어야함
        post.delete()
        messages.success(request, '포스팅을 삭제했습니다.')
        return redirect('board:post_list')


import constants as keys;
from telegram.ext import *
import random
import json

import torch

from model import NeuralNet
from nltk_utils import bag_of_words, tokenize
from PyDictionary import PyDictionary
dictionary = PyDictionary()
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

with open('intents.json', 'r') as json_data:
    intents = json.load(json_data)

FILE = "data.pth"
data = torch.load(FILE)

input_size = data["input_size"]
hidden_size = data["hidden_size"]
output_size = data["output_size"]
all_words = data['all_words']
tags = data['tags']
model_state = data["model_state"]

model = NeuralNet(input_size, hidden_size, output_size).to(device)
model.load_state_dict(model_state)
model.eval()

bot_name = "levi"
print("Let's chat! (type 'quit' to exit)")

def start_command(update, context):
    update.message.reply_text("hi there! type anything you like")

def help_command(update, context):
    update.message.reply_text("try typing something else maybe?")

def error(update, context):
    return print(f"Update {update} caused error {context.error}")

def define_command(update, context):
    updater = Updater(keys.API_KEY, use_context=True)
    dp = updater.dispatcher
    update.message.reply_text("what word would it be?")
    WORD = update.message.text
    DEFINITION = dictionary.meaning(WORD)
    update.message.reply_text(DEFINITION)

def handleInput(sentence):
    if sentence == "quit":
        print(f"{bot_name}: see you!")

    sentence = tokenize(sentence)
    X = bag_of_words(sentence, all_words)
    X = X.reshape(1, X.shape[0])
    X = torch.from_numpy(X).to(device)

    output = model(X)
    _, predicted = torch.max(output, dim=1)

    tag = tags[predicted.item()]

    probs = torch.softmax(output, dim=1)
    prob = probs[0][predicted.item()]
    if prob.item() > 0.75:
        for intent in intents['intents']:
            if tag == intent["tag"]:
                return str(random.choice(intent['responses']))
    else:
        return "i'm sorry could you type something else"

def handle_message(update, context):
    sentence = str(update.message.text).lower();
    response = handleInput(sentence)
    update.message.reply_text(response)

def main():
    updater = Updater(keys.API_KEY, use_context=True)
    dp = updater.dispatcher

    dp.add_handler(CommandHandler("start", start_command))
    dp.add_handler(CommandHandler("help", help_command))
    dp.add_handler(MessageHandler(Filters.text, handle_message))

    # dp.add_error_handler(error)

    updater.start_polling() # code that starts the bot
    updater.idle() # to ensure that bot continues to stay active

main()


import threading

from transitions.extensions import GraphMachine as Machine

class Model:
    def __init__(self, name, id):
        self.name = name
        self.id = id

    def on_enter_load(self):
        print(f"{self.name}_{self.id} {self.state} state ...")
        import time
        if self.id % 2 == 0:
            print(f"{self.name}_{self.id} {self.state} sleep ...")
            time.sleep(1)
        self.trigger("next")

    def on_enter_write_plc(self):
        print(f"{self.name}_{self.id} {self.state} state ...")
        self.trigger("next")

    def on_enter_wait_plc(self):
        print(f"{self.name}_{self.id} {self.state} state ...")
        self.trigger("next")

    def on_enter_end(self):
        print(f"{self.name}_{self.id} {self.state} state ...")
        self.trigger("next")  # 接回去會無窮迴圈

class ModelThreading(threading.Thread):

    def __init__(self, station_id):
        threading.Thread.__init__(self)
        self.station_id = station_id
        self.model = Model(self.name, station_id)
        self.machine = Machine(model=self.model,
                            #    queued=False,
                               states=[
                                   'start',
                                   'load',
                                   'write_plc',
                                   'wait_plc',
                                   'end',
                               ],
                               transitions=[
                                   {'trigger': 'next', 'source': 'start', 'dest': 'load'},
                                   {'trigger': 'next', 'source': 'load', 'dest': 'write_plc'},
                                   {'trigger': 'next', 'source': 'write_plc', 'dest': 'wait_plc'},
                                   {'trigger': 'next', 'source': 'wait_plc', 'dest': 'end'},
                                   {'trigger': 'next', 'source': 'end', 'dest': 'start'}
                               ],
                               initial='start',
                               show_conditions=True)
        self.model.next()

    # def run(self):
    #     print(f"{self.name}_{self.station_id} run ...")
    #     self.model.trigger("next")

if __name__ == "__main__":
    machines = []
    for i in range(1):
        machines.append(ModelThreading(i))
        # machines[i].next()

'''
Thread-1_0 run ...
Thread-1_0 load state ...
Thread-1_0 load sleep ...
Thread-2_1 run ...
Thread-2_1 load state ...
Thread-2_1 write_plc state ...
Thread-2_1 wait_plc state ...
Thread-2_1 end state ...
Thread-3_2 run ...
Thread-3_2 load state ...
Thread-3_2 load sleep ...
Thread-4_3 run ...
Thread-4_3 load state ...
Thread-4_3 write_plc state ...
Thread-4_3 wait_plc state ...
Thread-4_3 end state ...
Thread-5_4 run ...
Thread-5_4 load state ...
Thread-5_4 load sleep ...
Thread-1_0 write_plc state ...
Thread-1_0 wait_plc state ...
Thread-1_0 end state ...
Thread-3_2 write_plc state ...
Thread-3_2 wait_plc state ...
Thread-3_2 end state ...
Thread-5_4 write_plc state ...
Thread-5_4 wait_plc state ...
Thread-5_4 end state ...
'''


# -*- coding: utf-8 -*-
"""
Created on Thu Jul  6 16:25:52 2017

@author: JFan
"""

#!python

from numpy import sin, pi, absolute, arange, linspace, sqrt
from scipy.signal import lfilter
from pylab import figure, xlabel, ylabel, ylim, title, grid, show, subplots_adjust
import matplotlib.pyplot as plt
from matplotlib import gridspec
from scipy import fft, signal
from IIRFilter import IIRFilter

#------------------------------------------------
# Digtal filter parameters
#------------------------------------------------
GAIN = 0
lowcut = 0
highcut = 0

#------------------------------------------------
# Signal parameters
#------------------------------------------------
sample_rate = 0  # sampling rate

def plotSpectrum(ax, y, Fs, color):
    """
    Plots a Single-Sided Amplitude Spectrum of y(t)
    """
    n = len(y) # length of the signal
    k = arange(n)
    T = n/Fs
    frq = k/T # two sides frequency range
    frq = frq[range(int(n/2))] # one side frequency range

    Y = fft(y)/n # fft computing and normalization
    Y = Y[range(int(n/2))]
    ax.plot(frq, abs(Y), color) # plotting the spectrum
    xlabel('Freq (Hz)')
    ylabel('Gain')

def plotSignal(before, after, interval, title, freq):
    #------------------------------------------------------
    # Plot the original and filtered sine signals
    #------------------------------------------------------
    delay = 0.5 * (4-1) / sample_rate
    # Plot in time domain
    fig = figure(2)
    gs = gridspec.GridSpec(2, 2)
    ax1 = fig.add_subplot(gs[:,0])
    ax1.plot(interval, before)
    # Plot the filtered signal, shifted to compensate for the phase delay.
    ax1.plot(interval-delay, after, 'r-')
    plt.xlabel('time (s)')
    plt.ylabel('Gain')
    plt.title('Gain vs time')
    plt.grid(True)
    plt.xlim(0, 1)
    grid(True)

    # Plot spectrum
    ax2 = fig.add_subplot(gs[0,1])
    plotSpectrum(ax2, before, sample_rate, 'b')
    plt.title('Gain vs Freq(before)')
    ax3 = fig.add_subplot(gs[1,1])
    plotSpectrum(ax3, after, sample_rate ,'r')
    plt.title('Gain vs Freq(after)')
    gs.update(wspace=0.5, hspace=0.5)
    plt.suptitle('IIR %s(%dHz)' %(title,freq))
    show()

#------------------------------------------------
# Main starts here
#------------------------------------------------
GAIN = int(input('Enter Gain:'))
lowcut = int(input('Enter low frequency:'))
highcut = int(input('Enter high frequency:'))
sample_rate = 2048 #int(input('Enter sample rate:'))

ffs = [10, 100, 1000]  # frequencies of the signal
if lowcut > 0 :
    ffs.append(lowcut)
if lowcut + highcut > 1 :
    ffs.append((lowcut + highcut)/2)
ffs.append(highcut)
if highcut/2 > 1:
    ffs.append(highcut/2)# frequencies of the signal

#------------------------------------------------
# Signal parameters
#------------------------------------------------
# Create sine wave parameters
Ts = 1.0/sample_rate
sineInterval = arange(0, 1 ,Ts) # time vector
# Create square wave parameters
squareInterval = linspace(0, 1, sample_rate, endpoint=False)
# The Nyquist rate of the signal.
nyq_rate = sample_rate / 2.0

chose_filter = 'LPF_' + str(highcut) + 'HZ'
#------------------------------------------------
# Generate signals before and after
#------------------------------------------------
iirFilter = IIRFilter()
b = iirFilter.IIRFilterConf[chose_filter]['numerator']
a = iirFilter.IIRFilterConf[chose_filter]['denominator']

#------------------------------------------------
# Plot the IIR filter coefficients and
# the magnitude response of the filter.
#------------------------------------------------
figure(1)
w, h = signal.freqz(b, a, 8000)
plt.plot((w/pi)*nyq_rate, absolute(h), linewidth=2)
plt.plot([0, 0.5 * sample_rate], [GAIN * sqrt(0.5),GAIN * sqrt(0.5)],'--')
xlabel('Frequency (Hz)')
ylabel('Gain')
title('Frequency Response')
ylim(-0.05, 1.5*GAIN)
grid(True)
subplots_adjust(hspace=.5)

squareWave = signal.square(2 * pi * ffs[0] * squareInterval)
filtered_squarewave = iirFilter.filter(chose_filter, squareWave)
plotSignal(squareWave, filtered_squarewave, squareInterval, 'Square wave', ffs[0])

for ff in ffs:
    sineWave = sin(2*pi*ff*sineInterval)
    filtered_sinewave = iirFilter.filter(chose_filter, sineWave)
    plotSignal(sineWave, filtered_sinewave, sineInterval, 'Sine wave', ff)


from flask import Blueprint, jsonify, request
from http import HTTPStatus
from config.extensions import db
from models.models import Appointment
from utilities.utilities import has_conflict
from datetime import datetime

home = Blueprint("/", __name__)

@home.route("/")
def index():
    return {"data": "OK"}

@home.route("/appointments", methods=["POST"])
def create_appointment():
    data = request.get_json()
    doctor_id = data["doctor_id"]
    patient_name = data["patient_name"]
    start_time = datetime.strptime(data["start_time"], "%Y-%m-%d %H:%M:%S")
    duration = data["duration"]

    #if no conflict then move to next step and create the appointment
    conflict_message = has_conflict(doctor_id, start_time, duration)
    if conflict_message:
        return jsonify({"error": conflict_message}), HTTPStatus.CONFLICT

    new_appointment = Appointment(
        doctor_id=doctor_id,
        patient_name=patient_name,
        start_time=start_time,
        duration=duration,
    )
    db.session.add(new_appointment)
    db.session.commit()

    return new_appointment.json(), HTTPStatus.CREATED

@home.route("/appointments/<int:doctor_id>", methods=["GET"])
def get_appointments(doctor_id):
    start_time = request.args.get("start_time")
    end_time = request.args.get("end_time")

    if not start_time or not end_time:
        return (
            jsonify({"error": "Both start_time and end_time are required"}),
            HTTPStatus.BAD_REQUEST,
        )

    start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")
    end_time = datetime.strptime(end_time, "%Y-%m-%d %H:%M:%S")

    #Returns all appointments  within that time window
    appointments = Appointment.query.filter(
        Appointment.doctor_id == doctor_id,
        Appointment.start_time >= start_time,
        Appointment.start_time <= end_time,
    ).all()

    return {
        "appointments": [appointment.json() for appointment in appointments]
    }, HTTPStatus.OK

@home.route("/appointments/first_available/<int:doctor_id>", methods=["GET"])
def get_first_available_appointment(doctor_id):
    start_time = request.args.get("start_time")

    if not start_time:
        return jsonify({"error": "start_time is required"}), HTTPStatus.BAD_REQUEST

    start_time = datetime.strptime(start_time, "%Y-%m-%d %H:%M:%S")

    #filter for appointments with that specific doctor within that time window
    appointment = (
        Appointment.query.filter(
            Appointment.doctor_id == doctor_id, Appointment.start_time >= start_time
        )
        .order_by(Appointment.start_time)
        .first()
    )

    if appointment:
        return {"appointment": appointment.json()}, HTTPStatus.OK
    else:
        return {
            "message": "No available appointments found after the specified time"
        }, HTTPStatus.NOT_FOUND


from typing import cast
from xml.etree.ElementTree import Element, parse

from py_pdf_term.pdftoxml import PDFnXMLElement, PDFnXMLPath
from py_pdf_term.tokenizers import BaseLanguageTokenizer, Term, Token, Tokenizer

from .augmenters import AugmenterCombiner, BaseAugmenter
from .classifiers import BaseTokenClassifier, MeaninglessMarker
from .data import DomainCandidateTermList, PageCandidateTermList, PDFCandidateTermList
from .filters import BaseCandidateTermFilter, BaseCandidateTokenFilter, FilterCombiner
from .splitters import BaseSplitter, SplitterCombiner
from .utils import textnode_fontsize, textnode_ncolor, textnode_text

class CandidateTermExtractor:
    """Term extractor which extracts candidate terms from a XML file.

    Args
    ----
        lang_tokenizer_clses:
            List of language tokenizer classes to tokenize texts. If None, the default
            language tokenizers are used.

        token_classifier_clses:
            List of token classifier classes to classify tokens. If None, the default
            token classifiers are used.

        token_filter_clses:
            List of token filter classes to filter tokens. If None, the default token
            filters are used.

        term_filter_clses:
            List of term filter classes to filter candidate terms. If None, the
            default term filters are used.

        splitter_clses:
            List of splitter classes to split candidate terms. If None, the default
            splitters are used.

        augmenter_clses:
            List of augmenter classes to augment candidate terms. If None, the default
            augmenters are used.
    """

    def __init__(
        self,
        lang_tokenizer_clses: list[type[BaseLanguageTokenizer]] | None = None,
        token_classifier_clses: list[type[BaseTokenClassifier]] | None = None,
        token_filter_clses: list[type[BaseCandidateTokenFilter]] | None = None,
        term_filter_clses: list[type[BaseCandidateTermFilter]] | None = None,
        splitter_clses: list[type[BaseSplitter]] | None = None,
        augmenter_clses: list[type[BaseAugmenter]] | None = None,
    ) -> None:
        lang_tokenizers = (
            list(map(lambda cls: cls(), lang_tokenizer_clses))
            if lang_tokenizer_clses is not None
            else None
        )
        self._tokenizer = Tokenizer(lang_tokenizers=lang_tokenizers)

        token_filters = (
            list(map(lambda cls: cls(), token_filter_clses))
            if token_filter_clses is not None
            else None
        )
        term_filters = (
            list(map(lambda cls: cls(), term_filter_clses))
            if term_filter_clses is not None
            else None
        )
        self._filter = FilterCombiner(token_filters, term_filters)

        classifiers = (
            list(map(lambda cls: cls(), token_classifier_clses))
            if token_classifier_clses is not None
            else None
        )
        self._marker = MeaninglessMarker(classifiers)

        splitters = (
            list(map(lambda cls: cls(classifiers), splitter_clses))
            if splitter_clses is not None
            else None
        )
        self._splitter = SplitterCombiner(splitters)

        augmenters = (
            list(map(lambda cls: cls(), augmenter_clses))
            if augmenter_clses is not None
            else None
        )
        self._augmenter = AugmenterCombiner(augmenters)

    def extract_from_domain_files(
        self, domain: str, pdfnxmls: list[PDFnXMLPath]
    ) -> DomainCandidateTermList:
        """Extract candidte terms from pairs of PDF and XML files in a domain.

        Args
        ----
            domain:
                Domain name of PDF files.
            pdfnxmls:
                List of pairs of paths to PDF and XML files in a domain.

        Returns
        -------
            DomainCandidateTermList:
                List of candidate terms in a domain.
        """

        xmls = list(map(self.extract_from_xml_file, pdfnxmls))
        return DomainCandidateTermList(domain, xmls)

    def extract_from_xml_file(self, pdfnxml: PDFnXMLPath) -> PDFCandidateTermList:
        """Extract candidate terms from a pair of PDF and XML files.

        Args
        ----
            pdfnxml:
                Pair of paths to a PDF and XML file.

        Returns
        -------
            PDFCandidateTermList:
                List of candidate terms in a PDF file.
        """

        xml_root = parse(pdfnxml.xml_path).getroot()
        xml_candidates = self._extract_from_xmlroot(pdfnxml.pdf_path, xml_root)
        return xml_candidates

    def extract_from_domain_elements(
        self, domain: str, pdfnxmls: list[PDFnXMLElement]
    ) -> DomainCandidateTermList:
        """Extract candidate terms from pairs of PDF and XML elements in a domain.

        Args
        ----
            domain:
                Domain name of PDF files.
            pdfnxmls:
                List of pairs of paths to PDF and XML elements in a domain.

        Returns
        -------
            DomainCandidateTermList:
                List of candidate terms in a domain.
        """

        xmls = list(map(self.extract_from_xml_element, pdfnxmls))
        return DomainCandidateTermList(domain, xmls)

    def extract_from_xml_element(self, pdfnxml: PDFnXMLElement) -> PDFCandidateTermList:
        """Extract candidate terms from a pair of PDF and XML elements.

        Args
        ----
            pdfnxml:
                Pair of path to a PDF and XML elements.

        Returns
        -------
            PDFCandidateTermList:
                List of candidate terms in a PDF file.
        """

        xml_candidates = self._extract_from_xmlroot(pdfnxml.pdf_path, pdfnxml.xml_root)
        return xml_candidates

    def extract_from_text(
        self, text: str, fontsize: float = 0.0, ncolor: str = ""
    ) -> list[Term]:
        """Extract candidate terms from a text. This method is mainly used for testing.

        Args
        ----
            text:
                Text to extract candidate terms.
            fontsize:
                Font size of output terms.
            ncolor:
                Color of output terms.

        Returns
        -------
            list[Term]:
                List of candidate terms in a text.
        """

        tokens = self._tokenizer.tokenize(text)
        return self._extract_from_tokens(tokens, fontsize, ncolor)

    def _extract_from_xmlroot(
        self, pdf_path: str, xml_root: Element
    ) -> PDFCandidateTermList:
        page_candidates: list[PageCandidateTermList] = []
        for page in xml_root.iter("page"):
            page_candidates.append(self._extract_from_page(page))

        return PDFCandidateTermList(pdf_path, page_candidates)

    def _extract_from_page(self, page: Element) -> PageCandidateTermList:
        page_num = int(cast(str, page.get("id")))

        candicate_terms: list[Term] = []
        for textnode in page.iter("text"):
            text = textnode_text(textnode)
            fontsize = textnode_fontsize(textnode)
            ncolor = textnode_ncolor(textnode)
            tokens = self._tokenizer.tokenize(text)
            terms = self._extract_from_tokens(tokens, fontsize, ncolor)
            candicate_terms.extend(terms)

        return PageCandidateTermList(page_num, candicate_terms)

    def _extract_from_tokens(
        self, tokens: list[Token], fontsize: float, ncolor: str
    ) -> list[Term]:
        candicate_terms: list[Term] = []
        candicate_tokens: list[Token] = []
        for idx, token in enumerate(tokens):
            if self._filter.is_partof_candidate(tokens, idx):
                candicate_tokens.append(token)
                continue

            terms = self._terms_from_tokens(candicate_tokens, fontsize, ncolor)
            candicate_terms.extend(terms)
            candicate_tokens = []

        terms = self._terms_from_tokens(candicate_tokens, fontsize, ncolor)
        candicate_terms.extend(terms)

        return candicate_terms

    def _terms_from_tokens(
        self, candicate_tokens: list[Token], fontsize: float, ncolor: str
    ) -> list[Term]:
        candidate_term = Term(candicate_tokens, fontsize, ncolor)

        candicate_terms: list[Term] = []
        splitted_candidates = self._splitter.split(candidate_term)
        for splitted_candidate in splitted_candidates:
            augmented_candidates = self._augmenter.augment(splitted_candidate)
            candicate_terms.extend(augmented_candidates)
            candicate_terms.append(splitted_candidate)

        return list(
            map(self._marker.mark, filter(self._filter.is_candidate, candicate_terms))
        )


from __future__ import  absolute_import
from __future__ import  division
import torch as t
from data.voc_dataset import VOCBboxDataset
from skimage import transform as sktsf
from torchvision import transforms as tvtsf
from data import util
import numpy as np
from utils.config import opt
import pandas as pd
import scipy.misc
import json
from PIL import Image
from PIL import ImageFilter
import csv
from .util import read_image
import ast

def inverse_normalize(img):
    if opt.caffe_pretrain:
        img = img + (np.array([122.7717, 115.9465, 102.9801]).reshape(3, 1, 1))
        return img[::-1, :, :]
    # approximate un-normalize for visualize
    return (img * 0.225 + 0.45).clip(min=0, max=1) * 255

def pytorch_normalze(img):
    """
    https://github.com/pytorch/vision/issues/223
    return appr -1~1 RGB
    """
    normalize = tvtsf.Normalize(mean=[0.485, 0.456, 0.406],
                                std=[0.229, 0.224, 0.225])
    img = normalize(t.from_numpy(img))
    return img.numpy()

def caffe_normalize(img):
    """
    return appr -125-125 BGR
    """
    img = img[[2, 1, 0], :, :]  # RGB-BGR
    img = img * 255
    mean = np.array([122.7717, 115.9465, 102.9801]).reshape(3, 1, 1)
    img = (img - mean).astype(np.float32, copy=True)
    return img

def preprocess(img, min_size=1200, max_size=1600):
    """Preprocess an image for feature extraction.

    The length of the shorter edge is scaled to :obj:`self.min_size`.
    After the scaling, if the length of the longer edge is longer than
    :param min_size:
    :obj:`self.max_size`, the image is scaled to fit the longer edge
    to :obj:`self.max_size`.

    After resizing the image, the image is subtracted by a mean image value
    :obj:`self.mean`.

    Args:
        img (~numpy.ndarray): An image. This is in CHW and RGB format.
            The range of its value is :math:`[0, 255]`.

    Returns:
        ~numpy.ndarray: A preprocessed image.

    """
    C, H, W = img.shape
    scale1 = min_size / min(H, W)
    scale2 = max_size / max(H, W)
    scale = min(scale1, scale2)
    img = img / 255.
    img = sktsf.resize(img, (C, H * scale, W * scale), mode='reflect',anti_aliasing=False)
    # both the longer and shorter should be less than
    # max_size and min_size
    if opt.caffe_pretrain:
        normalize = caffe_normalize
    else:
        normalize = pytorch_normalze
    return normalize(img)

class Transform(object):

    def __init__(self, min_size=1200, max_size=1600):
        self.min_size = min_size
        self.max_size = max_size

    def __call__(self, in_data):
        img, bbox, label = in_data
        _, H, W = img.shape
        img = preprocess(img, self.min_size, self.max_size)
        _, o_H, o_W = img.shape
        scale = o_H / H
        bbox = util.resize_bbox(bbox, (H, W), (o_H, o_W))

        # horizontally flip
        img, params = util.random_flip(
            img, x_random=True, return_param=True)
        bbox = util.flip_bbox(
            bbox, (o_H, o_W), x_flip=params['x_flip'])

        return img, bbox, label, scale

def make_csv(json_file, csv_file):
    with open(json_file, 'r') as f:
        data = json.load(f)
    cnt = 0
    root_dir = '../../malaria/images'
    Data = []
    for img in data:
        img_name = img['image']['pathname'][7:]
        img_path = root_dir+img_name
        # print(img_path)
        bounding_boxes, labels = [], []
        for obj in img['objects']:
            bb = obj['bounding_box']
            bbox = (bb['minimum']['r'], bb['minimum']['c'], bb['maximum']['r'], bb['maximum']['c'])
            bounding_boxes.append(bbox)
            labels.append(int(obj['category'] != 'red blood cell'))
        Data.append([img_path, bounding_boxes, labels])
    df = pd.DataFrame(Data, columns=['img', 'bbox', 'label'])
    df.to_csv(csv_file)
    print('data written in csv')

class Dataset:
    def __init__(self, opt):
        self.opt = opt
        # self.db = VOCBboxDataset(opt.voc_data_dir)
        make_csv('../../malaria/test.json', 'data/test.csv')
        self.data_frame = pd.read_csv('data/test.csv', header='infer')
        # print(self.data_frame)
        self.label_names = ['rbc', 'infected']
        self.tsf = Transform(opt.min_size, opt.max_size)

    def __getitem__(self, idx):
        ori_img, bbox, label = self.data_frame.iloc[idx, 1:4] #self.db.get_example(idx)
        ori_img = read_image(ori_img, color=True)
        bbox = np.stack(ast.literal_eval(bbox)).astype(np.float32)
        label = np.stack(ast.literal_eval(label)).astype(np.int32)
        # print(ori_img, bbox, label)
        img, bbox, label, scale = self.tsf((ori_img, bbox, label))
        # TODO: check whose stride is negative to fix this instead copy all
        # some of the strides of a given numpy array are negative.
        return img.copy(), bbox.copy(), label.copy(), scale

    def __len__(self):
        return len(self.data_frame)

class TestDataset:
    def __init__(self, opt, split='test', use_difficult=False):
        self.opt = opt
        make_csv('../../malaria/test.json', 'data/test.csv')
        self.data_frame = pd.read_csv('data/test.csv', header='infer')
        # print(self.data_frame)
        self.label_names = ['rbc', 'infected']
        # self.db = VOCBboxDataset(opt.voc_data_dir, split=split, use_difficult=use_difficult)

    def __getitem__(self, idx):
        # ori_img, bbox, label, difficult = self.db.get_example(idx)
        ori_img, bbox, label = self.data_frame.iloc[idx, 1:4]
        ori_img = read_image(ori_img, color=True)
        bbox = np.stack(ast.literal_eval(bbox)).astype(np.float32)
        label = np.stack(ast.literal_eval(label)).astype(np.int32)
        img = preprocess(ori_img)
        difficult = np.array([False]*label.shape[0], dtype=np.bool).astype(np.uint8)
        return img, ori_img.shape[1:], bbox, label, difficult

    def __len__(self):
        return len(self.data_frame)


from __future__ import absolute_import
import datetime as dt
from nose.tools import *  # noqa PEP8 asserts
from modularodm.exceptions import ValidationValueError, ValidationError

from framework.auth import Auth
from framework.exceptions import PermissionsError
from framework.guid.model import Guid
from website.addons.osfstorage import settings as osfstorage_settings
from website.project.model import Comment, NodeLog
from website.project.signals import comment_added
from website.util import permissions
from website import settings

from tests.base import (
    OsfTestCase,
    assert_datetime_equal,
    capture_signals
)
from tests.factories import (
    UserFactory, ProjectFactory, AuthUserFactory, CommentFactory,
)

class TestCommentViews(OsfTestCase):

    def setUp(self):
        super(TestCommentViews, self).setUp()
        self.project = ProjectFactory(is_public=True)
        self.user = AuthUserFactory()
        self.project.add_contributor(self.user)
        self.project.save()
        self.user.save()

    def test_view_project_comments_updates_user_comments_view_timestamp(self):
        url = self.project.api_url_for('update_comments_timestamp')
        res = self.app.put_json(url, {
            'page': 'node',
            'rootId': self.project._id
        }, auth=self.user.auth)
        self.user.reload()

        user_timestamp = self.user.comments_viewed_timestamp[self.project._id]['node']
        view_timestamp = dt.datetime.utcnow()
        assert_datetime_equal(user_timestamp, view_timestamp)

    def test_confirm_non_contrib_viewers_dont_have_pid_in_comments_view_timestamp(self):
        non_contributor = AuthUserFactory()
        url = self.project.api_url_for('update_comments_timestamp')
        res = self.app.put_json(url, {
            'page': 'node',
            'rootId': self.project._id
        }, auth=self.user.auth)

        non_contributor.reload()
        assert_not_in(self.project._id, non_contributor.comments_viewed_timestamp)

    def test_view_comments_updates_user_comments_view_timestamp_files(self):
        osfstorage = self.project.get_addon('osfstorage')
        root_node = osfstorage.get_root()
        test_file = root_node.append_file('test_file')
        test_file.create_version(self.user, {
            'object': '06d80e',
            'service': 'cloud',
            osfstorage_settings.WATERBUTLER_RESOURCE: 'osf',
        }, {
            'size': 1337,
            'contentType': 'img/png'
        }).save()

        url = self.project.api_url_for('update_comments_timestamp')
        res = self.app.put_json(url, {
            'page': 'files',
            'rootId': test_file._id
        }, auth=self.user.auth)
        self.user.reload()

        user_timestamp = self.user.comments_viewed_timestamp[self.project._id]['files'][test_file._id]
        view_timestamp = dt.datetime.utcnow()
        assert_datetime_equal(user_timestamp, view_timestamp)

class TestCommentModel(OsfTestCase):

    def setUp(self):
        super(TestCommentModel, self).setUp()
        self.comment = CommentFactory()
        self.auth = Auth(user=self.comment.user)

    def test_create(self):
        comment = Comment.create(
            auth=self.auth,
            user=self.comment.user,
            node=self.comment.node,
            target=self.comment.target,
            page='node',
            is_public=True,
            content='This is a comment.'
        )
        assert_equal(comment.user, self.comment.user)
        assert_equal(comment.node, self.comment.node)
        assert_equal(comment.target, self.comment.target)
        assert_equal(len(comment.node.logs), 2)
        assert_equal(comment.node.logs[-1].action, NodeLog.COMMENT_ADDED)

    def test_create_comment_content_cannot_exceed_max_length(self):
        with assert_raises(ValidationValueError):
            comment = Comment.create(
                auth=self.auth,
                user=self.comment.user,
                node=self.comment.node,
                target=self.comment.target,
                is_public=True,
                content=''.join(['c' for c in range(settings.COMMENT_MAXLENGTH + 1)])
            )

    def test_create_comment_content_cannot_be_none(self):
        with assert_raises(ValidationError) as error:
            comment = Comment.create(
                auth=self.auth,
                user=self.comment.user,
                node=self.comment.node,
                target=self.comment.target,
                is_public=True,
                content=None
        )
        assert_equal(error.exception.message, 'Value <content> is required.')

    def test_create_comment_content_cannot_be_empty(self):
        with assert_raises(ValidationValueError) as error:
            comment = Comment.create(
                auth=self.auth,
                user=self.comment.user,
                node=self.comment.node,
                target=self.comment.target,
                is_public=True,
                content=''
        )
        assert_equal(error.exception.message, 'Value must not be empty.')

    def test_create_comment_content_cannot_be_whitespace(self):
        with assert_raises(ValidationValueError) as error:
            comment = Comment.create(
                auth=self.auth,
                user=self.comment.user,
                node=self.comment.node,
                target=self.comment.target,
                is_public=True,
                content='    '
        )
        assert_equal(error.exception.message, 'Value must not be empty.')

    def test_create_sends_comment_added_signal(self):
        with capture_signals() as mock_signals:
            comment = Comment.create(
                auth=self.auth,
                user=self.comment.user,
                node=self.comment.node,
                target=self.comment.target,
                is_public=True,
                content='This is a comment.'
            )
        assert_equal(mock_signals.signals_sent(), set([comment_added]))

    def test_edit(self):
        self.comment.edit(
            auth=self.auth,
            content='edited',
            save=True
        )
        assert_equal(self.comment.content, 'edited')
        assert_true(self.comment.modified)
        assert_equal(len(self.comment.node.logs), 2)
        assert_equal(self.comment.node.logs[-1].action, NodeLog.COMMENT_UPDATED)

    def test_delete(self):
        self.comment.delete(auth=self.auth, save=True)
        assert_equal(self.comment.is_deleted, True)
        assert_equal(len(self.comment.node.logs), 2)
        assert_equal(self.comment.node.logs[-1].action, NodeLog.COMMENT_REMOVED)

    def test_undelete(self):
        self.comment.delete(auth=self.auth, save=True)
        self.comment.undelete(auth=self.auth, save=True)
        assert_equal(self.comment.is_deleted, False)
        assert_equal(len(self.comment.node.logs), 3)
        assert_equal(self.comment.node.logs[-1].action, NodeLog.COMMENT_RESTORED)

    def test_read_permission_contributor_can_comment(self):
        project = ProjectFactory()
        user = UserFactory()
        project.set_privacy('private')
        project.add_contributor(user, permissions=[permissions.READ])
        project.save()

        assert_true(project.can_comment(Auth(user=user)))

    def test_get_content_for_not_deleted_comment(self):
        project = ProjectFactory(is_public=True)
        comment = CommentFactory(node=project)
        content = comment.get_content(auth=Auth(comment.user))
        assert_equal(content, comment.content)

    def test_get_content_returns_deleted_content_to_commenter(self):
        comment = CommentFactory(is_deleted=True)
        content = comment.get_content(auth=Auth(comment.user))
        assert_equal(content, comment.content)

    def test_get_content_does_not_return_deleted_content_to_non_commenter(self):
        user = AuthUserFactory()
        comment = CommentFactory(is_deleted=True)
        content = comment.get_content(auth=Auth(user))
        assert_is_none(content)

    def test_get_content_public_project_does_not_return_deleted_content_to_logged_out_user(self):
        project = ProjectFactory(is_public=True)
        comment = CommentFactory(node=project, is_deleted=True)
        content = comment.get_content(auth=None)
        assert_is_none(content)

    def test_get_content_private_project_throws_permissions_error_for_logged_out_users(self):
        project = ProjectFactory(is_public=False)
        comment = CommentFactory(node=project, is_deleted=True)
        with assert_raises(PermissionsError):
            comment.get_content(auth=None)

    def test_find_unread_is_zero_when_no_comments(self):
        n_unread = Comment.find_n_unread(user=UserFactory(), node=ProjectFactory())
        assert_equal(n_unread, 0)

    def test_find_unread_new_comments(self):
        project = ProjectFactory()
        user = UserFactory()
        project.add_contributor(user)
        project.save()
        comment = CommentFactory(node=project, user=project.creator)
        n_unread = Comment.find_n_unread(user=user, node=project)
        assert_equal(n_unread, 1)

    def test_find_unread_includes_comment_replies(self):
        project = ProjectFactory()
        user = UserFactory()
        project.add_contributor(user)
        project.save()
        comment = CommentFactory(node=project, user=user)
        reply = CommentFactory(node=project, target=Guid.load(comment._id), user=project.creator)
        n_unread = Comment.find_n_unread(user=user, node=project)
        assert_equal(n_unread, 1)

    # Regression test for https://openscience.atlassian.net/browse/OSF-5193
    def test_find_unread_includes_edited_comments(self):
        project = ProjectFactory()
        user = AuthUserFactory()
        project.add_contributor(user)
        project.save()
        comment = CommentFactory(node=project, user=project.creator)

        url = project.api_url_for('update_comments_timestamp')
        payload = {'page': 'node', 'rootId': project._id}
        res = self.app.put_json(url, payload, auth=user.auth)
        user.reload()
        n_unread = Comment.find_n_unread(user=user, node=project)
        assert_equal(n_unread, 0)

        # Edit previously read comment
        comment.edit(
            auth=Auth(project.creator),
            content='edited',
            save=True
        )
        n_unread = Comment.find_n_unread(user=user, node=project)
        assert_equal(n_unread, 1)

    def test_find_unread_does_not_include_deleted_comments(self):
        project = ProjectFactory()
        user = AuthUserFactory()
        project.add_contributor(user)
        project.save()
        comment = CommentFactory(node=project, user=project.creator, is_deleted=True)
        n_unread = Comment.find_n_unread(user=user, node=project)
        assert_equal(n_unread, 0)


#%%
import torch
import torch.nn as nn
import torch.nn.functional as F
import sys
import torchvision.transforms as transforms

sys.path.append('/home/userfs/j/jrs596/scripts/CocoaReader/utils')

class Bottleneck(nn.Module):
    expansion = 4

    def __init__(self, in_channels, out_channels, stride=1, max_groups=32, kernel_size=3):
        super(Bottleneck, self).__init__()
        mid_channels = out_channels // self.expansion
        groups = mid_channels if mid_channels % max_groups == 0 else 1  # Ensure num_groups is a divisor of num_channels

        self.residual = nn.Sequential(
            nn.Conv2d(in_channels, mid_channels, kernel_size=1, bias=False),
            nn.GroupNorm(num_groups=groups, num_channels=mid_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(mid_channels, mid_channels, kernel_size=kernel_size, stride=stride, padding=kernel_size//2, bias=False, groups=groups),
            nn.GroupNorm(num_groups=groups, num_channels=mid_channels),
            nn.ReLU(inplace=True),
            nn.Conv2d(mid_channels, out_channels, kernel_size=1, bias=False),
            nn.GroupNorm(num_groups=groups, num_channels=out_channels)
        )

        self.shortcut = nn.Sequential()
        if stride != 1 or in_channels != out_channels:
            self.shortcut = nn.Sequential(
                nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
                nn.GroupNorm(num_groups=groups, num_channels=out_channels)
            )
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        out_residual = self.residual(x)
        out_shortcut = self.shortcut(x)

        # Check if padding is needed
        if out_residual.size(2) != out_shortcut.size(2) or out_residual.size(3) != out_shortcut.size(3):
            padding = [0, out_residual.size(3) - out_shortcut.size(3), 0, out_residual.size(2) - out_shortcut.size(2)]
            out_shortcut = nn.functional.pad(out_shortcut, padding)

        out = out_residual + out_shortcut
        out = self.relu(out)
        return out

class DisNetV1_2(nn.Module):
    def __init__(self, config):
        super(DisNetV1_2, self).__init__()
        self.config_dict = config

        out_channels = self.config_dict['out_channels']
        groups = self.config_dict['dim_1'] if self.config_dict['dim_1'] % 32 == 0 else 1  # Ensure num_groups is a divisor of num_channels

        self.conv1 = nn.Conv2d(3, self.config_dict['dim_1'], kernel_size=self.config_dict['kernel_1'], stride=2, padding=self.config_dict['kernel_1']//2, bias=False)
        self.gn1 = nn.GroupNorm(num_groups=groups, num_channels=self.config_dict['dim_1'])
        self.relu = nn.ReLU(inplace=True)
        self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

        # Pass num_blocks parameter when creating layers
        self.layer1 = self._make_layer(self.config_dict['dim_1'], self.config_dict['dim_2'], blocks=self.config_dict['num_blocks_1'], stride=1, kernel_size=self.config_dict['kernel_2'])
        self.layer2 = self._make_layer(self.config_dict['dim_2'], self.config_dict['dim_3'], blocks=self.config_dict['num_blocks_2'], stride=2, kernel_size=self.config_dict['kernel_3'])

        self.global_avg_pool = nn.AdaptiveAvgPool2d((1, 1))
        self.fc = nn.Linear(self.config_dict['dim_3'], out_channels)

    def _make_layer(self, in_channels, out_channels, blocks, stride, kernel_size):
        layers = []
        layers.append(Bottleneck(in_channels, out_channels, stride=stride, kernel_size=kernel_size))
        for _ in range(1, blocks):  # Use blocks parameter to determine the number of Bottleneck blocks
            layers.append(Bottleneck(out_channels, out_channels, kernel_size=kernel_size))
        return nn.Sequential(*layers)

    def forward(self, x):
        x = self.conv1(x)
        x = self.gn1(x)
        x = self.relu(x)
        x = self.maxpool(x)

        x = self.layer1(x)
        x = self.layer2(x)

        w = self.global_avg_pool(x)
        w = torch.flatten(w, 1)

        # x = self.fc(x)

        return x, w

class PositionalEncoding2D(nn.Module):
    def __init__(self, d_model, max_len=5000):
        super(PositionalEncoding2D, self).__init__()
        self.encoding = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * -(torch.log(torch.tensor(10000.0)) / d_model))

        self.encoding[:, 0::2] = torch.sin(position * div_term)

        # Handling the case when d_model is odd
        if d_model % 2 == 0:
            self.encoding[:, 1::2] = torch.cos(position * div_term)
        else:
            self.encoding[:, 1::2] = torch.cos(position * div_term)[:,:-1]

        self.encoding = self.encoding.unsqueeze(0).transpose(0, 1)

    def forward(self, x):
        x = x.flatten(2).permute(2, 0, 1)
        y = self.encoding[:x.size(0), :].to(x.device)
        return x + y

class TransformerDecoderBlock(nn.Module):
    def __init__(self, d_model, nhead):
        super(TransformerDecoderBlock, self).__init__()
        self.self_attn = nn.MultiheadAttention(d_model, nhead)
        self.norm1 = nn.LayerNorm(d_model)
        self.norm2 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(0.1)
        self.fc = nn.Sequential(
            nn.Linear(d_model, d_model),
            nn.ReLU(inplace=True),
            nn.Dropout(0.1),
            nn.Linear(d_model, d_model)
        )

    def forward(self, x, memory):
        attn_output, _ = self.self_attn(x, x, x)  # Self attention
        x = x + self.dropout(attn_output)
        x = self.norm1(x)
        fc_output = self.fc(x)
        x = x + self.dropout(fc_output)
        x = self.norm2(x)
        return x

class TransformerDecoder(nn.Module):
    def __init__(self, feature_dim, num_heads, num_layers, img_size):
        super(TransformerDecoder, self).__init__()
        self.feature_dim = feature_dim
        self.img_size = img_size
        self.positional_encoding = PositionalEncoding2D(feature_dim)
        self.decoder_blocks = nn.ModuleList([TransformerDecoderBlock(feature_dim, num_heads) for _ in range(num_layers)])

        # Upsampling layers
        self.upsample1 = nn.ConvTranspose2d(feature_dim, feature_dim, kernel_size=3, stride=3, padding=1, output_padding=1)
        self.upsample2 = nn.ConvTranspose2d(feature_dim, feature_dim, kernel_size=3, stride=2, padding=1, output_padding=1)
        self.upsample3 = nn.ConvTranspose2d(feature_dim, feature_dim, kernel_size=3, stride=2, padding=1, output_padding=1)

        self.final_conv = nn.Conv2d(feature_dim, 3, kernel_size=1)  # Assuming the output has 3 channels

    def forward(self, x):
        # x is the encoded feature map: [batch_size, feature_dim, height, width]
        batch_size, _, height, width = x.size()
        x = self.positional_encoding(x)

        for decoder_block in self.decoder_blocks:
            x = decoder_block(x, x)  # Pass through each transformer block

        # Upsampling steps
        x = self.upsample1(x.permute(1, 2, 0).view(batch_size, self.feature_dim, height, width))
        x = self.upsample2(x)
        x = self.upsample3(x)

        x = self.final_conv(x)
        return x

class DisNet_SRAutoencoder(nn.Module):
    def __init__(self, config):
        super(DisNet_SRAutoencoder, self).__init__()

        # Initialize the encoder
        self.encoder = DisNetV1_2(config)

        # Extract necessary parameters from config or define them manually
        feature_dim = config['dim_3']
        num_heads = config['num_heads']
        num_layers = config['num_decoder_layers']
        img_size = config['input_size']

        # Initialize the transformer decoder
        self.decoder = TransformerDecoder(feature_dim, num_heads, num_layers, img_size)

    def forward(self, x):

        encoded, encode_pooled  = self.encoder.forward(x)  # Get feature map before the fc layer
        decoded = self.decoder(encoded)

        return encode_pooled, decoded

##################################################

class UNet(nn.Module):
    def __init__(self, in_channels, out_channels):
        super(UNet, self).__init__()

        self.encoder1 = self.conv_block(in_channels, 64)
        self.pool1 = nn.MaxPool2d(kernel_size=2)
        self.encoder2 = self.conv_block(64, 128)
        self.pool2 = nn.MaxPool2d(kernel_size=2)

        self.bottleneck = self.conv_block(128, 256)

        self.upconv2 = nn.ConvTranspose2d(256, 128, kernel_size=2, stride=2)
        self.decoder2 = self.conv_block(256, 128)
        self.upconv1 = nn.ConvTranspose2d(128, 64, kernel_size=3, stride=2)
        self.decoder1 = self.conv_block(128, 64)

        self.final_conv = nn.Conv2d(64, out_channels, kernel_size=1)

    def conv_block(self, in_channels, out_channels):
        return nn.Sequential(
            nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_channels, out_channels, kernel_size=3, padding=1),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        enc1 = self.encoder1(x)
        enc2 = self.encoder2(self.pool1(enc1))
        bottleneck = self.bottleneck(self.pool2(enc2))

        dec2 = self.decoder2(torch.cat([enc2, self.upconv2(bottleneck)], 1))
        dec1 = self.decoder1(torch.cat([enc1, self.upconv1(dec2)], 1))

        return self.final_conv(dec1)

from diffusers import UNet2DModel
from torch.nn import functional as F

class PhytNet_SRAutoencoder(nn.Module):
    def __init__(self, config):
        super(PhytNet_SRAutoencoder, self).__init__()

        # Initialize the encoder (assuming DisNetV1_2 is defined elsewhere)
        self.encoder = DisNetV1_2(config)

        # Extract necessary parameters from config or define them manually
        feature_dim = config['dim_3']
        num_heads = config['num_heads']
        num_layers = config['num_decoder_layers']
        img_size = config['input_size']

        # Initialize the transformer decoder (assuming TransformerDecoder is defined elsewhere)
        self.decoder = TransformerDecoder(feature_dim, num_heads, num_layers, img_size)

        # Diffusion parameters
        self.noise_std = config.get('noise_std', 0.2)  # Default noise standard deviation
        self.num_diffusion_steps = config.get('num_diffusion_steps', 10)  # Default number of diffusion steps

        # Add U-Net for refinement
        # self.refinement_net = UNet(feature_dim, feature_dim)
        self.refinement_net = UNet2DModel(
                                    sample_size=(48, 48),
                                    in_channels=84,
                                    out_channels=84).float()

    def latent_diffusion(self, encoded):
        # Add noise to the encoded representations
        noisy_encoded = encoded + torch.randn_like(encoded) * self.noise_std
        noisy_encoded = F.pad(noisy_encoded, (1, 2, 1, 2))

        # Iteratively refine the noisy representations using the U-Net
        for i in range(self.num_diffusion_steps):
            noisy_encoded = self.refinement_net(sample=noisy_encoded, timestep=i)
            noisy_encoded = noisy_encoded.sample

        noisy_encoded = noisy_encoded[:, :, :45, :45]

        return noisy_encoded

    def forward(self, x):
        encoded, encode_pooled  = self.encoder.forward(x)  # Get feature map before the fc layer

        # Apply latent diffusion to the encoded representations
        diffused_encoded = self.latent_diffusion(encoded)

        decoded = self.decoder(diffused_encoded)
        return encode_pooled, decoded