from transformers import AutoModelForSequenceClassification
import numpy as np
import os
from tqdm import tqdm
import torch
from pathlib import Path
import argparse
import distutils.util
import pickle

from core.data_models.em_dataset import EMDataset
from core.data_models.ditto_dataset import DittoDataset
from core.data_models.supcon_dataset import ContrastiveClassificationDataset
from utils.general import get_dataset
from utils.data_collector import DM_USE_CASES, DataCollectorDitto, DataCollectorSupCon
from utils.knowledge import GeneralDKInjector
from core.modeling.ditto import DittoModel
from utils.ditto_utils import tune_threshold, predict
from core.modeling.supcon import ContrastiveClassifierModel
from torch.utils.data import DataLoader
from utils.supcon_utils import DataCollatorContrastiveClassification
from utils.nlp import FastTextModel
from sklearn.metrics import f1_score

PROJECT_DIR = Path(__file__).parent.parent.parent
RESULTS_DIR = os.path.join(PROJECT_DIR, 'results', 'models')

def load_ditto_model(path, lm='roberta'):
    """ Code taken from https://github.com/megagonlabs/ditto/blob/master/matcher.py """
    # load models
    if not os.path.exists(path):
        raise ValueError(f"Model not found: {path}!")

    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    model = DittoModel(device=device, lm=lm)

    saved_state = torch.load(path, map_location=lambda storage, loc: storage)
    model.load_state_dict(saved_state['model'])

    return model

def evaluate(tuned_model, eval_dataset: (EMDataset, DittoDataset, ContrastiveClassificationDataset),
             thr=None, collate_fn=None):
    print("Starting the inference task...")

    eval_dataloader = DataLoader(
        dataset=eval_dataset,
        batch_size=32,
        shuffle=False,
        num_workers=4,
        collate_fn=collate_fn
    )

    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    tuned_model.to(device)
    tuned_model.eval()

    preds = None
    labels = None

    with torch.no_grad():
        for features in tqdm(eval_dataloader):
            input_ids = features['input_ids']

            batch_labels = features['labels'].numpy()

            if isinstance(tuned_model, DittoModel):
                logits = tuned_model(input_ids)
                probs = logits.softmax(dim=1)[:, 1]
                batch_preds = probs.numpy()
                if thr is None:
                    thr = 0.5
                batch_preds[batch_preds >= thr] = 1
                batch_preds[batch_preds < thr] = 0

            elif isinstance(tuned_model, ContrastiveClassifierModel):
                attention_mask = features['attention_mask']
                input_ids_right = features['input_ids_right']
                attention_mask_right = features['attention_mask_right']
                outputs = tuned_model(
                    input_ids=input_ids, attention_mask=attention_mask, labels=features['labels'],
                    input_ids_right=input_ids_right, attention_mask_right=attention_mask_right
                )
                logits = outputs[1]
                logits[logits >= 0.5] = 1
                logits[logits < 0.5] = 0
                batch_preds = logits.numpy().flatten()

            else:
                token_type_ids = features['token_type_ids']
                attention_mask = features['attention_mask']
                outputs = tuned_model(input_ids=input_ids, token_type_ids=token_type_ids, attention_mask=attention_mask)
                logits = outputs['logits']
                batch_preds = torch.argmax(logits, axis=1)

            if labels is None:
                labels = batch_labels
                preds = batch_preds
            else:
                labels = np.concatenate((labels, batch_labels))
                preds = np.concatenate((preds, batch_preds))

    out = {
        'preds': preds,
        'labels': labels,
    }

    return out

def evaluate_simple_bert_model(args, source_uc: str, target_uc: str):
    target_conf = {
        'use_case': target_uc,
        'model_name': args.bert_model,
        'tok': args.tok,
        'label_col': args.label_col,
        'left_prefix': args.left_prefix,
        'right_prefix': args.right_prefix,
        'max_len': args.max_len,
        'permute': args.permute,
        'verbose': args.verbose,
        'typeMask': None,
        'columnMask': None,
        'topk_mask': None
    }

    test_conf = target_conf.copy()
    test_conf['data_type'] = 'test'
    test_dataset = get_dataset(test_conf)

    if args.approach == 'bert':
        model_path = os.path.join(RESULTS_DIR, f"{source_uc}_{args.tok}_tuned")
    elif args.approach == 'sbert':
        model_path = os.path.join(RESULTS_DIR, "sbert", f"{source_uc}_{args.tok}_tuned")
    else:
        raise ValueError("Wrong approach name!")

    tuned_model = AutoModelForSequenceClassification.from_pretrained(model_path)

    res = evaluate(tuned_model, test_dataset, collate_fn=test_dataset.pad)

    f1 = f1_score(res['labels'], res['preds'])
    res['f1'] = f1
    res['source'] = source_uc
    res['target'] = target_uc
    print(f1)

    return res

def evaluate_ditto(source_uc: str, target_uc: str, lm: str, max_len: int):
    ditto_collector = DataCollectorDitto()
    injector = GeneralDKInjector('general')

    valid_path = ditto_collector.get_path(use_case=source_uc, data_type='valid')
    test_path = ditto_collector.get_path(use_case=target_uc, data_type='test')

    valid_path = injector.transform_file(valid_path)
    test_path = injector.transform_file(test_path)

    valid_dataset = DittoDataset(
        path=valid_path, lm=lm, max_len=max_len, verbose=False
    )
    test_dataset = DittoDataset(
        path=test_path, lm=lm, max_len=max_len, verbose=False
    )

    model_path = os.path.join(RESULTS_DIR, "ditto", f"{source_uc}.pt")
    tuned_model = load_ditto_model(model_path)

    # Find the best classifier threshold
    threshold = tune_threshold(valid_dataset, tuned_model)

    res = evaluate(tuned_model, test_dataset, thr=threshold, collate_fn=test_dataset.pad)

    f1 = f1_score(res['labels'], res['preds'])
    res['f1'] = f1
    res['source'] = source_uc
    res['target'] = target_uc
    print(f1)

    return res

def evaluate_supcon(source_uc: str, target_uc: str, lm: str, max_len: int):

    supcon_collector = DataCollectorSupCon()
    test_path = supcon_collector.get_path(use_case=target_uc, data_type='test')

    # Load the test data
    test_dataset = ContrastiveClassificationDataset(
        test_path, dataset_type='test', tokenizer=lm, dataset=target_uc
    )

    # Load the model
    device = 'cuda' if torch.cuda.is_available() else 'cpu'
    model_path = os.path.join(RESULTS_DIR, "supcon", f"{source_uc}.bin")
    tuned_model = ContrastiveClassifierModel(
        checkpoint_path=model_path,
        len_tokenizer=len(test_dataset.tokenizer),
        model=lm,
        frozen=True,
        device=device
    )

    data_collator = DataCollatorContrastiveClassification(
        tokenizer=test_dataset.tokenizer, max_length=max_len
    )
    res = evaluate(tuned_model, test_dataset, collate_fn=data_collator)

    f1 = f1_score(res['labels'], res['preds'])
    res['f1'] = f1
    res['source'] = source_uc
    res['target'] = target_uc
    print(f1)

    return res

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='Out-of-distribution performance evaluation')

    # General parameters
    parser.add_argument('-bert_model', '--bert_model', default='bert-base-uncased', type=str, required=True,
                        choices=['bert-base-uncased', 'sentence-transformers/nli-bert-base', 'roberta-base'],
                        help='the version of the BERT model')
    parser.add_argument('-tok', '--tok', default='sent_pair', type=str, choices=['sent_pair', 'attr_pair'],
                        help='the tokenizer for the EM entries')
    parser.add_argument('-label', '--label_col', default='label', type=str,
                        help='the name of the column in the EM dataset that contains the label')
    parser.add_argument('-left', '--left_prefix', default='left_', type=str,
                        help='the prefix used to identify the columns related to the left entity')
    parser.add_argument('-right', '--right_prefix', default='right_', type=str,
                        help='the prefix used to identify the columns related to the right entity')
    parser.add_argument('-max_len', '--max_len', default=128, type=int,
                        help='the maximum BERT sequence length')
    parser.add_argument('-permute', '--permute', default=False, type=lambda x: bool(distutils.util.strtobool(x)),
                        help='boolean flag for permuting dataset attributes')
    parser.add_argument('-v', '--verbose', default=False, type=lambda x: bool(distutils.util.strtobool(x)),
                        help='boolean flag for the dataset verbose modality')
    parser.add_argument('-approach', '--approach', type=str, default='bert', required=True,
                        choices=['bert', 'sbert', 'ditto', 'supcon'],
                        help='the EM approach to use')
    parser.add_argument('-seed', '--seed', default=42, type=int,
                        help='seed')
    parser.add_argument('-out_dir', '--output_dir', type=str,
                        help='the directory where to store the results', required=True)

    args = parser.parse_args()

    use_case_pairs = [
        # Same domain
        ('Structured_Walmart-Amazon', 'Textual_Abt-Buy'),
        ('Textual_Abt-Buy', 'Structured_Walmart-Amazon'),
        ('Structured_DBLP-GoogleScholar', 'Structured_DBLP-ACM'),
        ('Structured_DBLP-ACM', 'Structured_DBLP-GoogleScholar'),
        ('Dirty_DBLP-GoogleScholar', 'Structured_DBLP-ACM'),
        ('Dirty_DBLP-GoogleScholar', 'Dirty_DBLP-ACM'),
        # Different domain
        ('Structured_iTunes-Amazon', 'Structured_DBLP-ACM'),
        ('Structured_iTunes-Amazon', 'Structured_DBLP-GoogleScholar'),
        ('Structured_DBLP-ACM', 'Structured_iTunes-Amazon'),
        ('Structured_DBLP-GoogleScholar', 'Structured_iTunes-Amazon'),
        ('Dirty_iTunes-Amazon', 'Dirty_DBLP-ACM'),
        ('Dirty_DBLP-ACM', 'Dirty_iTunes-Amazon'),
    ]

    for use_case_pair in use_case_pairs:
        source_uc, target_uc = use_case_pair
        print(f'Info. Source dataset={source_uc}, Target dataset={target_uc}')

        # Evaluate
        if args.approach == 'ditto':
            res = evaluate_ditto(
                source_uc=source_uc, target_uc=target_uc, lm=args.bert_model, max_len=args.max_len
            )

        elif args.approach == 'supcon':
            res = evaluate_supcon(
                source_uc=source_uc, target_uc=target_uc, lm=args.bert_model, max_len=args.max_len
            )

        else:
            res = evaluate_simple_bert_model(
                args=args, source_uc=source_uc, target_uc=target_uc,
            )

        # Save the results
        if args.approach == 'bert':
            out_res_name = f"ROB_BERT_{source_uc}_{target_uc}.pickle"
        elif args.approach == 'sbert':
            out_res_name = f"ROB_SBERT_{source_uc}_{target_uc}.pickle"
        elif args.approach == 'ditto':
            out_res_name = f"ROB_DITTO_{source_uc}_{target_uc}.pickle"
        elif args.approach == 'supcon':
            out_res_name = f"ROB_SUPCON_{source_uc}_{target_uc}.pickle"
        else:
            raise ValueError("Wrong approach name!")

        with open(os.path.join(args.output_dir, out_res_name), 'wb') as handle:
            pickle.dump(res, handle, protocol=pickle.HIGHEST_PROTOCOL)


"""
Functions for generating lists of words with various properties.

Functions:

    get_all_words, get_all_dows, get_dows, get_random_sample
"""

from random import shuffle
from itertools import permutations, combinations, product

from .objects import Word
from .ascending_order import convert_to_ascending_order

def get_all_words(max_length, alphabet_size):
    """
    Args:
        max_length: Integer.
        alphabet_size: Integer.
    Returns:
        A sorted list containing all words of length at most max_length
        from the alphabet {1, 2, ..., alphabet_size}.
    """
    if alphabet_size**max_length > 30000000:
        raise ValueError(
            "Too many words to compute without likely exceeding memory limits!")
    elif alphabet_size > 9:
        raise NotImplementedError("Need to expand allowed alphabets!")

    word_list = []
    for length in range(1, max_length+1):
        letter_lists = product(range(1,alphabet_size+1), repeat=length)
        words = [Word("".join(str(i) for i in letters),
                      double_occurrence=False, optimize=True)
                 for letters in letter_lists]
        word_list.extend(words)

    word_list.sort()
    word_list.sort(key=lambda word: len(word))
    return word_list

def get_all_dows(max_size):
    """
    Args:
        max_size: Integer.
    Returns:
        A sorted list containing all double occurrence words
        without bijective equivalence.
    """
    word_list = set()
    letter_list = [str(i) for i in range(1, min(max_size,9)+1)]
    if max_size > 9:
        letter_list.extend(chr(i+96) for i in range(10, max_size+1))
    for size in range(1, max_size+1):
        print("generating words of size " + str(size) + "...")
        for letters in combinations(letter_list, size):
            words = set(Word("".join(tupl), double_occurrence=False, optimize=True)
                        for tupl in permutations(letters*2))
            word_list |= words

    word_list = list(word_list)
    word_list.sort()
    word_list.sort(key=lambda word: len(word))
    return word_list

def get_dows(max_size, ascending_order=True, irreducible=False,
             strongly_irreducible=False):
    """
    Args:
        max_size: Integer.
        ascending_order: Boolean, defaults to True.
    Returns:
        A sorted list containing all double occurrence words,
        where each word of size n is constructed from
        the alphabet {1, 2, ..., n}. Bijective equivalence is assumed
        if and only if ascending_order = True.
    """
    word_list = set()
    for size in range(1, max_size+1):
        print("generating words of size " + str(size) + "...")
        letters = [str(i) for i in range(1, min(size,9)+1)]
        if size > 9:
            letters.extend(chr(i+96) for i in range(10, size+1))
        words = set(Word("".join(tupl), double_occurrence=False, optimize=True)
                    for tupl in permutations(letters*2))
        if ascending_order:
            words = convert_to_ascending_order(words)
        word_list |= words

    word_list = list(word_list)
    word_list.sort()
    word_list.sort(key=lambda word: len(word))
    if irreducible:
        word_list = [word for word in word_list if Word.irreducible(word)]
    if strongly_irreducible:
        word_list = [word for word in word_list if Word.strongly_irreducible(word)]

    return word_list

def get_random_sample(word_list):
    """
    Args:
        word_list: List, contains instances of Word.
    Returns:
        A list of words---if word_list contains m words of size n,
        the returned list contains m uniformly sampled words of size n.
    """
    random_sample = []
    for word in word_list:
        letters = [letter for letter in word]
        shuffle(letters)
        random_word = Word(convert_to_ascending_order("".join(letters)), optimize=True)
        random_sample.append(random_word)

    return random_sample


import sys
import pygrib
import warnings
import numpy as np
import datetime as dt
import configparser
import xarray as xr
from dmit import regrot

import logging
log = logging.getLogger("dmic.log")
log.setLevel(logging.DEBUG)

class read:

    def __init__(self, leveltype):

        if leveltype=='sf':
            pass
        else:
            log.error('Sorry! But '+leveltype+' is not implemented yet')
            log.error('Possible options for --leveltype is: sf\n exiting')
            sys.exit(0)

        return

    def get_grid(self, gribfile, using_pygrib_derived_coords=False):
        """Get grid from gribfile.
        This function will always try to return regular coordinates.

        Parameters
        ----------
        gribfile : str
            path to gribfile
        using_pygrib_derived_coords : boolean (optional)
            Default: False, if true, force to use pygrib derived coordinates

        Returns
        -------
        lat : 1d-array
            flattened array with latitudes
        lon : 1d-array
            flattened array with longitudes
        """
        gr = pygrib.open(gribfile)

        g = gr[1]

        latdim = g.Nj
        londim = g.Ni

        if not using_pygrib_derived_coords:
            try:
                latFirst = g.latitudeOfFirstGridPointInDegrees
                lonFirst = g.longitudeOfFirstGridPointInDegrees
                latLast = g.latitudeOfLastGridPointInDegrees
                lonLast = g.longitudeOfLastGridPointInDegrees
                dy = g.jDirectionIncrementInDegrees
                dx = g.iDirectionIncrementInDegrees
                latPole = g.latitudeOfSouthernPoleInDegrees
                lonPole = g.longitudeOfSouthernPoleInDegrees

                lons, lats = np.meshgrid(np.linspace(
                    lonFirst, lonLast, londim), np.linspace(latFirst, latLast, latdim))

                if not latPole==0 and not lonPole==0:
                    log.info('Found rotated coordinates - converting to regular coordinates')
                    lons, lats = regrot.rot_to_reg(lonPole,latPole,lons,lats)

            except RuntimeError:
                using_pygrib_derived_coords = True
                warnings.warn('Falling back to pygrib derived coordinates')
                lats, lons = g.latlons()
                using_pygrib_derived_coords=True
        if using_pygrib_derived_coords:
            lats, lons = g.latlons()

        data_date = g.dataDate
        data_time = g.dataTime

        starttime = dt.datetime.strptime(('%i-%.2i')%(data_date,data_time),'%Y%m%d-%H%M')

        gr.close()

        return lats.flatten(), lons.flatten(), latdim, londim

    # def read_cfgrib(self, grib_file):

    #     indicatorOfParameter = 11
    #     typeOfLevel = 'heightAboveGround'
    #     level = 2
    #     ds_grib = xr.open_dataset(grib_file, engine='cfgrib',
    #                         backend_kwargs=dict(read_keys=['indicatorOfParameter'],
    #                         filter_by_keys={'indicatorOfParameter': indicatorOfParameter, 'typeOfLevel': typeOfLevel, 'level': level}))
    #     print(ds_grib)

    #     return

    #def read(self, gribfile, indicatorOfParameters, level, type_of_level, indicator_of_type_of_level):
    def read(self, gribfile, leveltype, ini_grib):
        """Get fields from gribfile.
        This function will always try to return whatever is found in the gribfile.
        Therefore this can fail for very large gribfiles. If so, and until we support large gribfiles
        use the grib_filter tool from eccodes to reduce the input.

        Time, latitude and longitude dimensions are assumed to be the same for all fields. However, the gribfile can
        hold several times, as long as all fields do.

        Parameters
        ----------
        gribfile : str
            path to gribfile

        Returns
        -------
        ds : xarray-dataset
            xarray dataset with the fields found
        """

        gr = pygrib.open(gribfile)

        init = True

        # First find out the number of time dimensions in this file
        timestamps = []
        levels = []
        msg = 0
        for g in gr:
            data_date = g.dataDate
            data_time = g.dataTime
            timestamps.append(dt.datetime.strptime(('%i-%.2i')%(data_date,data_time),'%Y%m%d-%H%M'))
            levels.append(g.level)
            if init:
                latdim = gr[1].Nj
                londim = gr[1].Ni
                init=False

        Nt = len(list(set(timestamps)))
        Nlev = len(list(set(levels)))
        No_msg = gr.tell()

        gr.seek(0) # Resets iterator to beginning of file

        #print(ini_grib)
        found_names = []
        dim_dict = {}
        for g in gr:
            ini_id    = str(g.indicatorOfParameter)+'_' \
                        + str(g.level)+'_'\
                        + str(g.indicatorOfTypeOfLevel)+'_' \
                        + str(g.typeOfLevel)

            #print(g.indicatorOfParameter,g.level,g.indicatorOfTypeOfLevel,g.typeOfLevel)

           # print(ini_id)

            try:
                cfname = ini_grib[ini_id]
                #print(cfname)
                #log.info('Found variable: '+cfname)
            except KeyError:
                #log.warning("parameterId: "+parid+" was not found in grib.ini")
                continue

            if not cfname in dim_dict.keys():
                found_names.append(cfname)
                dim_dict[cfname] = {}
            if not g.indicatorOfTypeOfLevel in dim_dict[cfname].keys():
                dim_dict[cfname][g.indicatorOfTypeOfLevel] = {}
            if not g.typeOfLevel in dim_dict[cfname][g.indicatorOfTypeOfLevel].keys():
                dim_dict[cfname][g.indicatorOfTypeOfLevel][g.typeOfLevel] = {}
                dim_dict[cfname][g.indicatorOfTypeOfLevel][g.typeOfLevel]['level'] = []

            # Make sure levels are only counted once if Nt>1
            if not g.level in dim_dict[cfname][g.indicatorOfTypeOfLevel][g.typeOfLevel]['level']:
                dim_dict[cfname][g.indicatorOfTypeOfLevel][g.typeOfLevel]['level'].append(g.level)

        # Now we have a dictionary with dimensions of the inputs. We can not allocate and actually
        # read the input data. So we rewind the iterator once again.

        gr.seek(0) # Resets iterator to beginning of file

        ds_grib = xr.Dataset()

        if leveltype=='sf':
            indicatorOfTypeOfLevel = 'sfc'
            typeOfLevel            = 'heightAboveGround'
        if leveltype=='ml':
            indicatorOfTypeOfLevel = 'ml'
            typeOfLevel            = 'hybrid'
        if leveltype=='pl':
            indicatorOfTypeOfLevel = '103'
            typeOfLevel            = 'heightAboveSea'

        for name in dim_dict.keys():
            Nlev = len(dim_dict[name][indicatorOfTypeOfLevel][typeOfLevel]['level'])
            ds_grib[name] = (['time', 'level', 'latitude', 'longitude'], np.zeros([Nt, Nlev, latdim, londim]))

        # Done allocating, now we read the data

        index_counter = {}
        for name in found_names:
            index_counter[name] = {'k_nt': 0, 'k_nlev': 0}

        init=True
        coords = {'time': [], 'level': [], 'latitude': [], 'longitude': []}

        for g in gr:
            ini_id    = str(g.indicatorOfParameter)+'_' \
                        + str(g.level)+'_'\
                        + str(g.indicatorOfTypeOfLevel)+'_' \
                        + str(g.typeOfLevel)

            try:
                cfname = ini_grib[ini_id]
            except KeyError:
                continue

            #print(cfname)

            data_time = dt.datetime.strptime(('%i-%.2i')%(g.dataDate,g.dataTime),'%Y%m%d-%H%M')
            coords['time'].append(data_time)
            coords['level'].append(g.level)

            x = xr.DataArray(g.values, dims=[' latitude', 'longitude']) #.expand_dims(['time','level'])

            kt = index_counter[cfname]['k_nt']
            nt = index_counter[cfname]['k_nlev']

            ds_grib[cfname][kt,nt,:,:] = x

            # Increment level
            index_counter[cfname]['k_nlev']+=1

            # Increment time
            if not init and data_time != prev_time:
                index_counter[cfname]['k_nt']+=1

            init=False
            prev_time = data_time

        coords['time'] = list(set(coords['time']))
        coords['level'] = list(set(coords['level']))

        ds_grib = ds_grib.assign_coords( {'time': ('time', coords['time']),
                                'level': ('level', coords['level'])})

        log.info('In total '+str(No_msg)+' grib messages was iterated through')

        gr.close()

        return ds_grib

class cf_grib_definitions:
    # Follows CF v75 (15 september 2020) convention and DMI grib parameters
    # https://cfconventions.org/Data/cf-standard-names/75/build/cf-standard-name-table.html

    gribparameter2cfnaming_sf = { '11_2_sfc_heightAboveGround': 'air_temperature',
                                  '33_10_sfc_heightAboveGround': 'eastward_wind',
                                  '34_10_sfc_heightAboveGround': 'northward_wind'}


from django.contrib import admin
from django.utils.translation import gettext_lazy as _

from ...core.admin import BaseAdmin, ReadOnlyAdmin
from .. import models

@admin.register(models.Stage)
class StageAdmin(BaseAdmin):
    """Django Admin for `Stage` model."""
    list_display = (
        'id',
        'title',
        'attorney',
    )
    list_display_links = ('id', 'title')
    fieldsets = (
        (_('Main info'), {
            'fields': (
                'attorney',
                'title',
            )
        }),
    )
    search_fields = ('title',)
    create_only_fields = (
        'attorney',
    )
    autocomplete_fields = (
        'attorney',
    )
    autocomplete_list_filter = (
        'attorney',
    )
    ordering = ('created', 'modified')
    related_models = (
        models.Matter,
    )

@admin.register(models.ChecklistEntry)
class ChecklistEntryAdmin(BaseAdmin):
    """Django Admin for `ChecklistEntry` model."""
    list_display = (
        'id',
        'attorney',
        'description',
    )
    list_display_links = ('id', 'attorney')
    fieldsets = (
        (_('Main info'), {
            'fields': (
                'attorney',
                'description',
            )
        }),
    )
    search_fields = ('description',)
    create_only_fields = ('attorney',)
    autocomplete_fields = ('attorney',)
    autocomplete_list_filter = (
        'attorney',
    )
    ordering = ('created', 'modified')

@admin.register(models.Referral)
class ReferralAdmin(ReadOnlyAdmin, BaseAdmin):
    """Django Admin for `Referral` model."""
    list_display = (
        'id',
        'attorney',
        'message'
    )
    list_display_links = ('id', 'message')
    fieldsets = (
        (_('Main info'), {
            'fields': (
                'attorney',
                'message',
            )
        }),
    )
    search_fields = ('message',)
    create_only_fields = (
        'attorney',
    )
    autocomplete_fields = (
        'attorney',
    )
    autocomplete_list_filter = (
        'attorney',
    )
    related_models = (
        models.Matter,
    )

@admin.register(models.PaymentMethods)
class PaymentMethodsAdmin(BaseAdmin):
    """Django Admin for `PaymentMethods` model."""
    list_display = (
        'id',
        'title',
    )
    list_display_links = ('id', 'title')
    fieldsets = (
        (_('Main info'), {
            'fields': (
                'title',
            )
        }),
    )
    search_fields = ('title',)
    ordering = ('title',)


# WS server example
import asyncio
import websockets
import locationlogger

def saveMessage(message):
    locationlogger.log(message)

async def handler(websocket, path):
    remote_ip = websocket.remote_address[0]
    message = await websocket.recv()

    saveMessage(message)

    print(f"({remote_ip}) wrote: {message}")

start_server = websockets.serve(handler, "localhost", 4444)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()


import sys,time,os
import hashlib

def readfile(fold,outfile):
	fdout={}
	filelist=[]
	for filename in os.listdir(fold):
		print(filename)
		if filename.endswith("_corp"):
			filelist.append(filename)
	filelist.sort()
	print(filelist)
	for filename in filelist:
		print("process "+filename)
		with open(fold+filename,"r") as fd:
			cnt=0
			for line in fd:
				cnt+=1
				if cnt%1000000==0:
					print(str(cnt)+" lines")
				try:
					line=line.strip()
					(user,words)=line.split("\t")
				except:
					print("Error line:",line)
					continue
				bk = bucket(user)
				if bk not in fdout:
					fdout[bk] =open(outfile+"_"+str(bk),"w")
				fdout[bk].write(line+"\n")

	for k in fdout.keys():
		fdout[k].close()

def bucket(user):
	hash_md5 = hashlib.md5(user.encode("utf-8")).hexdigest()
	hash_md5_hex = hash_md5[15:20]
	return int(hash_md5_hex, 16)%16

if __name__=="__main__":
	readfile(sys.argv[1],sys.argv[2])


import sys

def fct(N):
    if N <= 1:
        return 1
    else:
        return N * fct(N-1)

chk = str(fct(int(sys.stdin.readline())))
if chk[-1] == '0':
    ans = 1
    while chk[-(ans+1)] == '0':
        ans +=1
    print(ans)
else:
    print(0)

추문비/단계별/14_정수론 및 조합론/11.5S_팩토리얼 0의 개수.py


from flask import Blueprint, request, jsonify, Response
from flask_jwt_extended import jwt_required
from kluster.helpers import query_one_filtered
from kluster.models.medication import Medication
from datetime import datetime
from typing import Dict, Tuple, Any

patients = Blueprint("patients", __name__, url_prefix="/api/v1/patients")

"""
MEDICATIONS
"""

@patients.route("/<user_id>/medications", methods=["GET"])
def medications(user_id: str) -> "tuple[Response, int] | Response | dict[Any, Any]":
    """
    Retrieves medication information for a specific patient based on the provided user ID and date.

    Args:
        user_id (str): The ID of the patient for whom to retrieve medication information.

    Returns:
        tuple[Response, int] | Response | dict[Any, Any]: If successful, returns a JSON response with a success message and a list of medication dictionaries.
            If the user_id or date is invalid, returns a JSON response with an error message and a 400 Bad Request status code.
            If an internal server error occurs, returns a JSON response with an error message and a 500 Internal Server Error status code.
    """
    date = request.args.get("date", datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'))
    if not isinstance(user_id, str):
        return jsonify(
            {
                "message": "couldn't Process Date or id",
                "Error": "Bad Request",
            }
        ), 400
    try:
        date_val = datetime.strptime(date, '%Y-%m-%d %H:%M:%S')
    except ValueError:
        return jsonify({
            "message": "Invalid date format",
            "Error": "Bad Request",
        }), 400

    try:
        # Change the return of the prescribed by to show the name of the doctor
        medications_obj = Medication.query.filter(
            Medication.patient_id == user_id,
            Medication.start_date <= date,
            date <= Medication.end_date
        ).all()
        medication_dict = [meds.to_dict() for meds in medications_obj]
        return jsonify(
            {
                "message": "successful",
                "date": medication_dict
            }
        ), 200
    except Exception as exc:
        print(exc)
        return jsonify(
            {
                "message": "something went wrong",
                "error": "Internal server error"
            }
        ), 500

@patients.route("/<user_id>/medications/<medication_id>", methods=["GET", "DELETE"])
def medication(user_id: str, medication_id: str) -> "tuple[Response, int] | Response | dict[Any, Any]":
    """Gets a users medication by an indicated medication id

    Args:
        user_id (str): The ID of the  Patient
        medication_id (str): The ID of the medication to be gotten

    Returns:
        tuple[Response, int] | Response | dict[Any, Any]: _description_
    """
    if not isinstance(user_id, str) or not isinstance(medication_id, str):
        return jsonify(
            {
                "message": "Invalid ID type passed, expecting UUID",
                "error": "Bad Request"
            }
        )
    if request.method == "GET":
        try:
            medication_obj = Medication.query.filter(
                Medication.id == medication_id,
                Medication.patient_id == user_id,
                ).first_or_404("The Requested Medication was not found.")
            return jsonify({
                "data": medication_obj.to_dict(),
                "message": "successful"
                }
                ), 200
        except Exception as exc:
            print(exc)
            return jsonify(
                {
                    "error": "Internal Server Error",
                    "message": "Something went wrong"
                }
            ), 500

    try:
        medication_obj = Medication.query.filter(
            Medication.id == medication_id,
            Medication.patient_id == user_id,
        ).first_or_404("Not Found")
        print(medication_obj.to_dict())
        print(type(medication_obj))
        medication_obj.delete()
        return jsonify(
            {
                "message": "deleted"
            }
        ), 204
    except Exception as e:
        print(e)
        return jsonify(
            {
                "error": "Something went wrong",
                "message": "Internal Server Error"
             }
            ), 500

from kluster.routes.schedule_medication import *


import numpy as np
from decision_tree import DecisionTree
from decision_tree_plotter import DecisionTreePloter

if __name__=="__main__":
    dataset_url = 'https://archive.ics.uci.edu/ml/machine-learning-databases/lenses/lenses.data'
    dataset = np.genfromtxt(dataset_url, dtype=np.int_)
    X = dataset[:, 1:-1]
    y = dataset[:, -1]
    dt = DecisionTree()
    dt.train(X, y)

    fractions_dict = {
        0: {'name': 'age',
            'value_names': {1: 'yang',
                            2: 'pre-presbyopic',
                            3: 'presbyopic'}
            },
        1: {'name': 'prescript',
            'value_names': {1: 'myope',
                            2: 'hypermetrope'}
            },
        2: {'name': 'astigmatic',
            'value_names': {1: 'no',
                            2: 'yes'}
            },
        3: {'name': 'tear rate',
            'value_names': {1: 'reduced',
                            2: 'normal'}
            }
    }

    label_dict = {
        1: 'head',
        2: 'soft',
        3: 'no_lenses',
    }

    dtp = DecisionTreePloter(dt.tree_,
                             feature_names=fractions_dict,
                             label_names=label_dict)
    dtp.plot()

决策树/分类树/隐形眼镜.py


import sys
sys.path.append("..")

import Parser.can_parser as can_parser
import lexer.can_lexer as can_lexer
import os
import re

from libraries.can_lib import cantonese_lib_import, cantonese_model_new,\
    cantonese_turtle_init

class CanPyCompile(object):
    def __init__(self):
        pass

    @staticmethod
    def cantonese_lib_run(lib_name : str, path : str) -> str:
        pa = os.path.dirname(path) # Return the last file Path
        tokens = []
        code = ""
        found = False
        for dirpath,dirnames,files in os.walk(pa):
            if lib_name + '.cantonese' in files:
                code = open(pa + '/' + lib_name + '.cantonese', encoding = 'utf-8').read()
                code = re.sub(re.compile(r'/\*.*?\*/', re.S), ' ', code)
                found = True
        if found == False:
            raise ImportError(lib_name + '.cantonese not found.')

        tokens = can_lexer.cantonese_token(code, can_lexer.keywords)
        stats = can_parser.StatParser(tokens).parse_stats()
        code_gen = Codegen(stats, path)
        code = ''
        for stat in stats:
            code += code_gen.codegen_stat(stat)

        return code

class Codegen(object):
    def __init__(self, nodes : list, path : str):
        self.nodes = nodes
        self.path = path
        self.tab = ''

    def codegen_expr(self, exp) -> str:
        if isinstance(exp, can_parser.can_ast.StringExp):
            return exp.s

        elif isinstance(exp, can_parser.can_ast.NumeralExp):
            return exp.val

        elif isinstance(exp, can_parser.can_ast.IdExp):
            return exp.name

        elif isinstance(exp, can_parser.can_ast.FalseExp):
            return "False"

        elif isinstance(exp, can_parser.can_ast.TrueExp):
            return "True"

        elif isinstance(exp, can_parser.can_ast.NullExp):
            return "None"

        elif isinstance(exp, can_parser.can_ast.BinopExp):
            return '(' + self.codegen_expr(exp.exp1) + exp.op + self.codegen_expr(exp.exp2) + ')'

        elif isinstance(exp, can_parser.can_ast.MappingExp):
            return self.codegen_expr(exp.exp1) + ':' + self.codegen_expr(exp.exp2)

        elif isinstance(exp, can_parser.can_ast.ObjectAccessExp):
            return self.codegen_expr(exp.prefix_exp) + '.' + self.codegen_expr(exp.key_exp)

        elif isinstance(exp, can_parser.can_ast.ListAccessExp):
            return self.codegen_expr(exp.prefix_exp) + '[' + self.codegen_expr(exp.key_exp) + ']'

        elif isinstance(exp, can_parser.can_ast.UnopExp):
            return '(' + exp.op + ' ' + self.codegen_expr(exp.exp) + ')'

        elif isinstance(exp, can_parser.can_ast.FuncCallExp):
            return self.codegen_expr(exp.prefix_exp) + '(' + self.codegen_args(exp.args) + ')'

        elif isinstance(exp, can_parser.can_ast.LambdaExp):
            return ' lambda ' + self.codegen_args(exp.id_list) + ' : ' + self.codegen_args(exp.blocks)

        elif isinstance(exp, can_parser.can_ast.IfElseExp):
            return '(' + self.codegen_expr(exp.if_exp) + ' if ' + self.codegen_expr(exp.if_cond_exp) + ' else ' + self.codegen_expr(exp.else_exp) + ')'

        elif isinstance(exp, can_parser.can_ast.ListExp):
            s = '['
            if len(exp.elem_exps):
                for elem in exp.elem_exps:
                    s += self.codegen_expr(elem) + ', '
                s = s[ : -2] + ']'
                return s
            else:
                return s + ']'

        elif isinstance(exp, can_parser.can_ast.MapExp):
            s = '{'
            if len(exp.elem_exps):
                for elem in exp.elem_exps:
                    s += self.codegen_expr(elem) + ', '
                s = s[ : -2] + '}'
                return s
            else:
                return s + '}'

        elif isinstance(exp, can_parser.can_ast.ClassSelfExp):
            s = 'self.' + self.codegen_expr(exp.exp)
            return s

        elif isinstance(exp, can_parser.can_ast.AssignExp):
            s = self.codegen_expr(exp.exp1) + ' = ' + self.codegen_expr(exp.exp2)
            return s

        elif isinstance(exp, can_parser.can_ast.SpecificIdExp):
            s = ''
            p_corr = re.match(r'(.*)同(.*)有几衬', exp.id, re.M|re.I)
            if p_corr:
                s = " corr(" + p_corr.group(1) +", " + p_corr.group(2) + ") "
            return s
        else:
            return ''

    def codegen_args(self, args : list) -> str:
        s = ''
        for arg in args:
            s += ', ' + self.codegen_expr(arg)
        return s[2 : ]

    def codegen_method_args(self, args : list) -> str:
        s = ''
        for arg in args:
            s += ', ' + self.codegen_expr(arg)
        return "self, " + s[2 : ]

    def codegen_lib_list(self, lib_list : list) -> str:
        s = ''
        user_lib = []
        for lib in lib_list:
            import_res = cantonese_lib_import(self.codegen_expr(lib))
            if import_res != "Not found":
                s += ', ' + import_res
            else:
                user_lib.append(self.codegen_expr(lib))

        return s[2 : ], user_lib
    def codegen_build_in_method_or_id(self, exp : can_parser.can_ast) -> str:
        list_build_in_method = {
            "加啲" : "append",
            "摞走" : "remove",
            "散水" : "clear"
        }

        if (isinstance(exp, can_parser.can_ast.IdExp)):
            if exp.name in list_build_in_method:
                return list_build_in_method[exp.name]
            else:
                return exp.name
        else:
            return self.codegen_expr(exp)

    def codegen_varlist(self, lst : list) -> str:
        s = ''
        for l in lst:
            s += ', ' + self.codegen_expr(l)
        return s[2 : ]

    def codegen_stat(self, stat):
        if isinstance(stat, can_parser.can_ast.PrintStat):
            return self.tab + 'print(' + self.codegen_args(stat.args) + ')\n'

        elif isinstance(stat, can_parser.can_ast.AssignStat):
            s = ''
            s += self.tab + self.codegen_args(stat.var_list) + ' = ' + self.codegen_args(stat.exp_list) + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.AssignBlockStat):
            s = ''
            for i in range(len(stat.var_list)):
                s += self.tab + self.codegen_expr(stat.var_list[i]) + ' = ' + self.codegen_expr(stat.exp_list[i]) + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.ExitStat):
            return self.tab + 'exit()\n'

        elif isinstance(stat, can_parser.can_ast.PassStat):
            return self.tab + 'pass\n'

        elif isinstance(stat, can_parser.can_ast.BreakStat):
            return self.tab + 'break\n'

        elif isinstance(stat, can_parser.can_ast.IfStat):
            s = ''
            s += self.tab + 'if ' + self.codegen_expr(stat.if_exp) + ':\n'
            s += self.codegen_block(stat.if_block)

            for i in range(len(stat.elif_exps)):
                s += self.tab + 'elif ' + self.codegen_expr(stat.elif_exps[i]) + ':\n'
                s += self.codegen_block(stat.elif_blocks[i])

            if len(stat.else_blocks):
                s += self.tab + 'else:\n'
                s += self.codegen_block(stat.else_blocks)

            return s

        elif isinstance(stat, can_parser.can_ast.TryStat):
            s = ''
            s += self.tab + 'try: \n'
            s += self.codegen_block(stat.try_blocks)

            for i in range(len(stat.except_exps)):
                s += self.tab + 'except ' + self.codegen_expr(stat.except_exps[i]) + ':\n'
                s += self.codegen_block(stat.except_blocks[i])

            if len(stat.finally_blocks):
                s += self.tab + 'finally:\n'
                s += self.codegen_block(stat.finally_blocks)

            return s

        elif isinstance(stat, can_parser.can_ast.RaiseStat):
            s = ''
            s += self.tab + 'raise ' + self.codegen_expr(stat.name_exp) + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.WhileStat):
            s = ''
            s += self.tab + 'while ' + self.codegen_expr(stat.cond_exp) + ':\n'
            s += self.codegen_block(stat.blocks)
            return s

        elif isinstance(stat, can_parser.can_ast.ForStat):
            s = ''
            s += self.tab + 'for ' + self.codegen_expr(stat.var) + ' in range('+ self.codegen_expr(stat.from_exp) \
                        + ', ' + self.codegen_expr(stat.to_exp) + '):\n'
            s += self.codegen_block(stat.blocks)
            return s

        elif isinstance(stat, can_parser.can_ast.FunctionDefStat):
            if stat.args_type != [] and\
                stat.ret_type != []:
                # remove the '<*>'
                stat.args_type = list(filter(lambda x : not isinstance(x, can_parser.can_ast.VarArgExp), stat.args_type))
                assert stat.args.__len__() == stat.args_type.__len__()
                s = ''
                arg_decl = ''
                for arg, arg_ty in zip(stat.args, stat.args_type):
                    arg_decl += self.codegen_expr(arg) + " : " + '"'  + self.codegen_expr(arg_ty) + '"' + ','
                s += self.tab + 'def ' + self.codegen_expr(stat.name_exp) + '(' + arg_decl + ') -> ' + '"'  + self.codegen_args(stat.ret_type) + '"' \
                     + ':\n'
                s += self.codegen_block(stat.blocks)
                return s

            else:
                s = ''
                s += self.tab + 'def ' + self.codegen_expr(stat.name_exp) + '(' + self.codegen_args(stat.args) + '):\n'
                s += self.codegen_block(stat.blocks)
                return s

        elif isinstance(stat, can_parser.can_ast.MatchModeFuncDefStat):
            s = ''
            s += self.tab + 'def ' + self.codegen_expr(stat.func_name) + '(*args):\n'
            for args, blocks in zip(stat.args_list, stat.block_list):
                # TODO: support more args
                if len(args) == 1:
                    if isinstance(args[0], (can_parser.can_ast.NumeralExp, can_parser.can_ast.StringExp,
                                        can_parser.can_ast.ListExp)):
                        s += self.tab + '\t' + 'if args[0] == '+ self.codegen_expr(args[0])  + ':\n';
                        s += self.tab + '\t\t' + 'return ' + self.codegen_expr(blocks) + '\n'
                    elif isinstance(args[0], can_parser.can_ast.IdExp):
                        # TODO: use re.sub here
                        s += self.tab + '\t' +'return ' + self.codegen_expr(blocks).replace(args[0].name, "args[0]") + '\n'

            return s

        elif isinstance(stat, can_parser.can_ast.FuncCallStat):
            s = ''
            s += self.tab + self.codegen_expr(stat.func_name) + '(' + self.codegen_args(stat.args) + ')' + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.ImportStat):
            s = ''
            libs, user_lib = self.codegen_lib_list(stat.idlist)
            if len(libs):
                s += self.tab + 'import ' + libs + '\n'
            if len(user_lib):
                for l in user_lib:
                    s += self.tab + CanPyCompile.cantonese_lib_run(l, self.path)
            return s

        elif isinstance(stat, can_parser.can_ast.ReturnStat):
            s = ''
            s += self.tab + 'return ' + self.codegen_args(stat.exps) + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.DelStat):
            s = ''
            s += self.tab + 'del ' + self.codegen_args(stat.exps) + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.TypeStat):
            s = ''
            s += self.tab + 'print(type(' + self.codegen_expr(stat.exps) + '))\n'
            return s

        elif isinstance(stat, can_parser.can_ast.AssertStat):
            s = ''
            s += self.tab + 'assert ' + self.codegen_expr(stat.exps) + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.ClassDefStat):
            s = ''
            s += self.tab + 'class ' + self.codegen_expr(stat.class_name) + '(' + self.codegen_args(stat.class_extend) + '):\n'
            s += self.codegen_block(stat.class_blocks)
            return s

        elif isinstance(stat, can_parser.can_ast.MethodDefStat):
            s = ''
            s += self.tab + 'def ' + self.codegen_expr(stat.name_exp) + '(' + self.codegen_method_args(stat.args) + '):\n'
            s += self.codegen_block(stat.class_blocks)
            return s

        elif isinstance(stat, can_parser.can_ast.MethodCallStat):
            s = ''
            s += self.tab + self.codegen_expr(stat.name_exp) + '.' + self.codegen_build_in_method_or_id(stat.method) + \
                 '(' + self.codegen_args(stat.args) + ')\n'
            return s

        elif isinstance(stat, can_parser.can_ast.CmdStat):
            s = ''
            s += self.tab + 'os.system(' + self.codegen_args(stat.args) + ')\n'
            return s

        elif isinstance(stat, can_parser.can_ast.CallStat):
            s = ''
            s += self.tab + self.codegen_expr(stat.exp) + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.GlobalStat):
            s = ''
            s += self.tab + 'global ' + self.codegen_args(stat.idlist) + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.ExtendStat):
            s = ''
            s += self.tab + stat.code + '\n'
            return s

        elif isinstance(stat, can_parser.can_ast.MatchStat):
            s = ''
            enum = ['if ', 'elif ']
            for i in range(len(stat.match_val)):
                if i == 0:
                    elif_or_if = 'if '
                else:
                    elif_or_if = 'elif '
                s += self.tab + elif_or_if + self.codegen_expr(stat.match_id) + ' == ' + \
                     self.codegen_expr(stat.match_val[i]) + ':\n'
                s += self.codegen_block(stat.match_block_exp[i])

            if len(stat.default_match_block):
                s += self.tab + 'else:\n'
                s += self.codegen_block(stat.default_match_block)

            return s

        elif isinstance(stat, can_parser.can_ast.ForEachStat):
            s = ''
            s += self.tab + 'for ' + self.codegen_args(stat.id_list) + ' in ' + self.codegen_args(stat.exp_list) + ':\n'
            s += self.codegen_block(stat.blocks)

            return s

        elif isinstance(stat, can_parser.can_ast.ModelNewStat):
            s = ''
            model = self.codegen_expr(stat.model)
            dataset = self.codegen_expr(stat.dataset)
            s += cantonese_model_new(model, dataset, self.tab, s)
            return s

        elif isinstance(stat, can_parser.can_ast.TurtleStat):
            s = ''
            cantonese_turtle_init()
            for item in stat.exp_blocks:
                s += self.tab + self.codegen_expr(item) + '\n'
            return s

    def codegen_block(self, blocks):
        save = self.tab
        self.tab += '\t'
        s = ''
        for block in blocks:
            s += self.codegen_stat(block)
        self.tab = save
        return s


#done using classifiers
#classifiers are trained with 10s of 1000s of images
#opencv comes with some classifiers
#Local Binary PAtterns, and Haar Cascades are examples.
import cv2 as cv

videoFeed = cv.VideoCapture(0, cv.CAP_DSHOW)

while True:
    isReceiving, frame = videoFeed.read()
    cv.imshow("Frame", frame)

    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    cv.imshow("Gray", gray)

    blurred = cv.GaussianBlur(gray, (13,13), 0)
    cv.imshow("Blur", blurred)

    haarCascade = cv.CascadeClassifier("haar_face.xml")
    faceRect = haarCascade.detectMultiScale(blurred, scaleFactor=1.1, minNeighbors=7)

    for(x1, y1, x2, y2) in faceRect:
        cv.rectangle(frame, (x1,y1), (x1+x2, y1+y2), (0, 255, 0), 2)

    cv.imshow("Detection", frame)
    if cv.waitKey(20) & 0xFF==ord('d'):
        break

videoFeed.release()
cv.destroyAllWindows()

""" img = cv.imread("Python/Opencv/face.jpg")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
cv.imshow("Original Image", img)
cv.imshow("Grayscale Image", gray)

haarCascade = cv.CascadeClassifier("Python/Opencv/haar_face.xml")

faceRect = haarCascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=3)
print(len(faceRect), "faces found")

for(x1, y1, x2, y2) in faceRect:
    cv.rectangle(img, (x1,y1), (x1+x2, y1+y2), (0, 255, 0), 2)

cv.imshow("faces detected", img)
cv.waitKey(0)
cv.destroyAllWindows() """