import time

from datadog import initialize
from datadog import api as dogapi
from datadog.dogstatsd.base import DogStatsd
from datadog.dogstatsd.context import TimedContextManagerDecorator
from flask import g, request

class TimerWrapper(TimedContextManagerDecorator):
    def __init__(self, statsd, *args, **kwargs):
        super(TimerWrapper, self).__init__(statsd, *args, **kwargs)

    def start(self):
        self.__enter__()

    def stop(self):
        self.__exit__(None, None, None)

class StatsD(object):
    def __init__(self, app=None, config=None):
        """
        Constructor for `flask.ext.datadog.StatsD`

        >>> from flask.ext.datadog import StatsD
        >>> app = Flask(__name__)
        >>> statsd = StatsD(app=app)

        :param app: Flask app to configure this client for, if `app` is `None`, then do not
            configure yet (call `init_app` manually instead)
        :type app: flask.Flask or None

        :param config: Configuration for this client to use instead of `app.config`
        :type config: dict or None
        """
        self.config = config
        self.statsd = None

        # If an app was provided, then call `init_app` for them
        if app is not None:
            self.init_app(app)
        else:
            self.app = None

    def init_app(self, app, config=None):
        """
        Initialize Datadog DogStatsd client from Flask app

        >>> from flask.ext.datadog import StatsD
        >>> app = Flask(__name__)
        >>> statsd = StatsD()
        >>> statsd.init_app(app=app)

        Available DogStatsd config settings:

          STATSD_HOST - statsd host to send metrics to (default: 'localhost')
          STATSD_MAX_BUFFER_SIZE - max number of metrics to buffer before sending, only used when batching (default: 50)
          STATSD_NAMESPACE - metric name prefix to use, e.g. 'app_name' (default: None)
          STATSD_PORT - statsd port to send metrics to (default: 8125)
          STATSD_TAGS - list of tags to include by default, e.g. ['env:prod'] (default: None)
          STATSD_USEMS - whether or not to report timing in milliseconds (default: False)

        Available Flask-Datadog config settings:

          DATADOG_CONFIGURE_MIDDLEWARE - whether or not to setup response timing middleware (default: True)
          DATADOG_RESPONSE_METRIC_NAME - the name of the response time metric (default: 'flask.response.time')
          DATADOG_RESPONSE_SIZE_METRIC_NAME - the name of the response time metric (default: 'flask.response.size')
          DATADOG_RESPONSE_SAMPLE_RATE - the sample rate to use for response timing middleware (default: 1)
          DATADOG_RESPONSE_AUTO_TAG - whether to auto-add request/response tags to response metrics (default: True)
          DATADOG_RESPONSE_ENDPOINT_TAG_NAME - tag name to use for request endpoint tag name (default: 'endpoint')
          DATADOG_RESPONSE_METHOD_TAG_NAME - tag name to use for the request method tag name (default: 'method')

        :param app: Flask app to configure this client for
        :type app: flask.Flask

        :param config: optional, dictionary of config values (defaults to `app.config`)
        :type config: dict
        """
        # Used passed in config if provided, otherwise use the config from `app`
        if config is not None:
            self.config = config
        elif self.config is None:
            self.config = app.config

        # Set default values for expected config properties
        self.config.setdefault('STATSD_HOST', 'localhost')
        self.config.setdefault('STATSD_MAX_BUFFER_SIZE', 50)
        self.config.setdefault('STATSD_NAMESPACE', None)
        self.config.setdefault('STATSD_PORT', 8125)
        self.config.setdefault('STATSD_TAGS', None)
        self.config.setdefault('STATSD_USEMS', False)

        self.app = app

        # Configure DogStatsd client
        # https://github.com/DataDog/datadogpy/blob/v0.11.0/datadog/dogstatsd/base.py
        self.statsd = DogStatsd(host=self.config['STATSD_HOST'],
                                port=self.config['STATSD_PORT'],
                                max_buffer_size=self.config['STATSD_MAX_BUFFER_SIZE'],
                                namespace=self.config['STATSD_NAMESPACE'],
                                constant_tags=self.config['STATSD_TAGS'],
                                use_ms=self.config['STATSD_USEMS'])

        # Configure any of our middleware
        self.setup_middleware()

    def timer(self, *args, **kwargs):
        """Helper to get a `flask_datadog.TimerWrapper` for this `DogStatsd` client"""
        return TimerWrapper(self.statsd, *args, **kwargs)

    def incr(self, *args, **kwargs):
        """Helper to expose `self.statsd.increment` under a shorter name"""
        return self.statsd.increment(*args, **kwargs)

    def decr(self, *args, **kwargs):
        """Helper to expose `self.statsd.decrement` under a shorter name"""
        return self.statsd.decrement(*args, **kwargs)

    def setup_middleware(self):
        """Helper to configure/setup any Flask-Datadog middleware"""
        # Configure response time middleware (if desired)
        self.config.setdefault('DATADOG_CONFIGURE_MIDDLEWARE', True)
        self.config.setdefault('DATADOG_RESPONSE_SIZE_METRIC_NAME', 'flask.response.size')
        self.config.setdefault('DATADOG_RESPONSE_METRIC_NAME', 'flask.response.time')
        self.config.setdefault('DATADOG_RESPONSE_SAMPLE_RATE', 1)
        self.config.setdefault('DATADOG_RESPONSE_AUTO_TAG', True)
        self.config.setdefault('DATADOG_RESPONSE_ENDPOINT_TAG_NAME', 'endpoint')
        self.config.setdefault('DATADOG_RESPONSE_METHOD_TAG_NAME', 'method')
        if self.config['DATADOG_CONFIGURE_MIDDLEWARE']:
            self.app.before_request(self.before_request)
            self.app.after_request(self.after_request)

    def before_request(self):
        """
        Flask-Datadog middleware handle for before each request
        """
        # Set the request start time
        g.flask_datadog_start_time = time.time()
        g.flask_datadog_request_tags = []

        # Add some default request tags
        if self.config['DATADOG_RESPONSE_AUTO_TAG']:
            self.add_request_tags([
                # Endpoint tag
                '{tag_name}:{endpoint}'.format(tag_name=self.config['DATADOG_RESPONSE_ENDPOINT_TAG_NAME'],
                                               endpoint=str(request.endpoint).lower()),
                # Method tag
                '{tag_name}:{method}'.format(tag_name=self.config['DATADOG_RESPONSE_METHOD_TAG_NAME'],
                                             method=request.method.lower()),
            ])

    def after_request(self, response):
        """
        Flask-Datadog middleware handler for after each request

        :param response: the response to be sent to the client
        :type response: ``flask.Response``
        :rtype: ``flask.Response``
        """
        # Return early if we don't have the start time
        if not hasattr(g, 'flask_datadog_start_time'):
            return response

        # Get the response time for this request
        elapsed = time.time() - g.flask_datadog_start_time
        # Convert the elapsed time to milliseconds if they want them
        if self.use_ms:
            elapsed = int(round(1000 * elapsed))

        # Add some additional response tags
        if self.config['DATADOG_RESPONSE_AUTO_TAG']:
            self.add_request_tags(['status_code:%s' % (response.status_code, )])

        tags = self.get_request_tags()
        sample_rate = self.config['DATADOG_RESPONSE_SAMPLE_RATE']

        # Emit timing metric
        self.statsd.timing(self.config['DATADOG_RESPONSE_METRIC_NAME'],
                           elapsed,
                           tags,
                           sample_rate)

        # Emit response size metric
        if 'content-length' in response.headers:
            size = int(response.headers['content-length'])
            self.statsd.histogram(self.config['DATADOG_RESPONSE_SIZE_METRIC_NAME'],
                                  size,
                                  tags,
                                  sample_rate)

        # We ALWAYS have to return the original response
        return response

    def get_request_tags(self):
        """
        Get the current list of tags set for this request

        :rtype: list
        """
        return getattr(g, 'flask_datadog_request_tags', [])

    def add_request_tags(self, tags):
        """
        Add the provided list of tags to the tags stored for this request

        :param tags: tags to add to this requests tags
        :type tags: list
        :rtype: list
        """
        # Get the current list of tags to append to
        # DEV: We use this method since ``self.get_request_tags`` will ensure that we get a list back
        current_tags = self.get_request_tags()

        # Append our new tags, and return the new full list of tags for this request
        g.flask_datadog_request_tags = current_tags + tags
        return g.flask_datadog_request_tags

    def __getattr__(self, name):
        """
        Magic method for fetching any underlying attributes from `self.statsd`

        We utilize `__getattr__` to ensure that we are always compatible with
        the `DogStatsd` client.
        """
        # If `self.statsd` has the attribute then return that attribute
        if self.statsd and hasattr(self.statsd, name):
            return getattr(self.statsd, name)
        raise AttributeError('\'StatsD\' has has attribute \'{name}\''.format(name=name))

    def __enter__(self):
        """
        Helper to expose the underlying `DogStatsd` client for context managing

        >>> statsd = StatsD(app=app)
        >>> # Batch any metrics within the `with` block
        >>> with statsd:
        >>>   statsd.increment('metric')
        """
        return self.statsd.__enter__()

    def __exit__(self, *args, **kwargs):
        """Helper to expose the underlying `DogStatsd` client for context managing"""
        return self.statsd.__exit__(*args, **kwargs)

class API(object):
    def __init__(self, app=None, config=None):
        """
        Constructor for `flask.ext.datadog.API`

        >>> from flask.ext.datadog import API
        >>> app = Flask(__name__)
        >>> dogapi = API(app=app)

        :param app: Flask app to configure this client for, if `app` is `None`, then do not
            configure yet (call `init_app` manually instead)
        :type app: flask.Flask or None

        :param config: Configuration for this client to use instead of `app.config`
        :type config: dict or None
        """
        self.config = config

        # If an app was provided, then call `init_app` for them
        if app is not None:
            self.init_app(app)
        else:
            self.app = None

    def init_app(self, app, config=None):
        """
        Initialize Datadog API client from Flask app

        >>> from flask.ext.datadog import API
        >>> app = Flask(__name__)
        >>> dogapi = API()
        >>> dogapi.init_app(app=app)

        Available config settings:

          DATADOG_API_KEY - Datadog API key from https://app.datadoghq.com/account/settings#api
          DATADOG_APP_KEY - Datadog APP key from https://app.datadoghq.com/account/settings#api

        :param app: Flask app to configure this client for
        :type app: flask.Flask

        :param config: optional, dictionary of config values (defaults to `app.config`)
        :type config: dict
        """
        # Used passed in config if provided, otherwise use the config from `app`
        if config is not None:
            self.config = config
        elif self.config is None:
            self.config = app.config

        # Set default values for expected config properties
        self.config.setdefault('DATADOG_API_KEY', None)
        self.config.setdefault('DATADOG_APP_KEY', None)

        self.app = app

        # Initialize datadog client
        # DEV: Datadog client uses module level variables for storing API keys rather than initializing a
        #   class to manage a connection/and keys
        # https://github.com/DataDog/datadogpy/blob/v0.11.0/datadog/__init__.py
        # https://github.com/DataDog/datadogpy/blob/v0.11.0/datadog/api/__init__.py#L4-L9
        options = {
            'api_key': self.config['DATADOG_API_KEY'],
            'app_key': self.config['DATADOG_APP_KEY'],
        }
        initialize(**options)

    def __getattr__(self, name):
        """
        Magic method for fetching attributes from `datadog.api`

        We utilize `__getattr__` to ensure that we are always compatible with
        the `datadog.api` module.
        """
        # If `self.statsd` has the attribute then return that attribute
        if dogapi and hasattr(dogapi, name):
            return getattr(dogapi, name)
        raise AttributeError('\'API\' has has attribute \'{name}\''.format(name=name))


import jsonlines
import os

from pathlib import Path
from xml_handler import XmlHandler
from google_cloud_storage_client import GoogleCloudStorageClient

def main(event, context):
    # Retrieve file from GCS
    input_filename = event.get("name")
    input_bucket_name = event.get("bucket")
    output_bucket_name = os.environ.get("OUTPUT_BUCKET_NAME")

    gcs_client = GoogleCloudStorageClient()

    local_filename = gcs_client.download_file_from_gcs(bucket_name=input_bucket_name, source_blob=input_filename)

    # Read file and parse to List[dict]
    xml_content_dict = XmlHandler.read_xml_file(file_path=local_filename)
    parsed_row_list = XmlHandler.parse_harvest_xml_to_json(content_dict=xml_content_dict)

    # Write file to jsonlines
    output_filename = Path(input_filename).stem + '.jsonl'

    local_output_file = Path("/tmp") / output_filename
    with jsonlines.open(local_output_file, mode="w") as writer:
        writer.write_all(parsed_row_list)

    # Upload file to GCS
    gcs_client.upload_file_to_gcs(bucket_name=output_bucket_name, destination_blob=Path(output_filename).name,
                                  source_filename=local_output_file, remove_local_file=True)


from kivy.uix.label import Label
from kivy.uix.textinput import TextInput
from kivy.uix.gridlayout import GridLayout
from kivy.uix.scrollview import ScrollView
from app.main import ShowcaseScreen
from app.widgets.heartfelt_hellos_button import HeartfeltHellosButton
from app.widgets.heartfelt_hellos_step_progression_button import HeartfeltHellosStepProgressionButton
from app.data.data_types.idea import Idea

class IdeaCreationScreen(ShowcaseScreen):

    scroll_view = None
    grid_layout = None

    def __init__(self, **kwargs):
        super(IdeaCreationScreen, self).__init__(**kwargs)
        # NOTE: replace tags and ideas with global list so that it can be editted in this class
        self.tags=["books", "movies", "sports"]
        self.ideas=[]
        #self.progress_grid = GridLayout(spacing='10dp', padding='10dp', cols=5, size_hint_y=None)
        self.grid_layout = GridLayout(spacing='10dp', padding='10dp', cols=1, size_hint_y=None)
        self.grid_layout.bind(minimum_height=self.grid_layout.setter("height"))
        self.scroll_view = ScrollView(do_scroll_y=True)
        self.add_widget(self.scroll_view)
        self.scroll_view.add_widget(self.grid_layout)

    def on_pre_enter(self, *args):
        self.stepOne()

    def stepOne(self):
        self.grid_layout.clear_widgets()
        self.grid_layout.add_widget(Label(text="What is your conversation idea?", font_size=24, color=(255,255,255)))
        self.grid_layout.add_widget(Label())

        # text box
        textinput = TextInput(hint_text="Are you still into sports?", font_size=24, size_hint_y=None, multiline=False)
        textinput.bind(text=lambda x, y: print("Hi"))
        self.grid_layout.add_widget(textinput)
        #self.name = textinput.text

        # next and back button rendering
        next_button = HeartfeltHellosStepProgressionButton(text="next",on_press=lambda x: self.stepTwo(textinput.text))
        progress_grid=GridLayout(spacing='10dp', padding='10dp', cols=3, size_hint_y=None)
        progress_grid.add_widget(Label())
        progress_grid.add_widget(Label())
        progress_grid.add_widget(next_button)
        self.grid_layout.add_widget(progress_grid)

    def stepTwo(self, prompt):
        self.grid_layout.clear_widgets()
        self.prompt=prompt
        self.grid_layout.add_widget(Label(text="Search and select the tag(s)\nthat matches with your idea!", height=50, color=(255,255,255), size_hint_y=None))

        # text box
        #NOTE TO SELF: add filtering for search bar
        textinput = TextInput(hint_text="Search Tag here", height=50, font_size=24, size_hint_y=None)
        #textinput.bind(on_text_validate=on_enter(textinput.text))
        self.grid_layout.add_widget(textinput)

        # NOTE TO SELF: add scroll bar to tags section
        #tag_grid=GridLayout(spacing='10dp', padding='10dp', cols=1, size_hint_y=None)
        for tag in self.getTags():
            tag_button = HeartfeltHellosButton(text=tag, height=50, on_press=lambda x: self.pressTag(x.text), size_hint_y=None)
            self.grid_layout.add_widget(tag_button)
        #self.grid_layout.add_widget(tag_grid)

        # next and back button rendering
        create_person_button = HeartfeltHellosStepProgressionButton(text="Create\nIdea", on_press=lambda x: self.createIdea())
        back_button = HeartfeltHellosStepProgressionButton(text="back", on_press=lambda x: self.stepOne())
        progress_grid=GridLayout(spacing='10dp', padding='10dp', cols=3, size_hint_y=None)
        progress_grid.add_widget(back_button)
        progress_grid.add_widget(Label())
        progress_grid.add_widget(create_person_button)
        self.grid_layout.add_widget(progress_grid)

    def pressTag(self, name: str):
        print("pressed " + str)
        if name not in self.tags:
            self.tags.append(name)
        else:
            self.tags.remove(name)

    def createIdea(self):
        print("pressed create idea")
        # NOTE: return to idea screen + add self.idea to global list of ideas as input somehow
        self.ideas.append(Idea(self.prompt, self.tags)) #place holder (something like that maybe?)

    def on_leave(self, *args):
        self.grid_layout.clear_widgets()

    def getTags(self) -> list:
        return ["sports", "books", "movies"]


from __future__ import absolute_import

from .dataset_iter import default_collate, DatasetIter
from .samplers import RandomSampler, SequentialSampler

import torch

import os
import os.path
import warnings
import fnmatch
import math

import numpy as np
try:
    import nibabel
except:
    warnings.warn('Cant import nibabel.. Cant load brain images')

try:
    from PIL import Image
except:
    warnings.warn('Cant import PIL.. Cant load PIL images')

IMG_EXTENSIONS = [
    '.jpg', '.JPG', '.jpeg', '.JPEG',
    '.png', '.PNG', '.ppm', '.PPM', '.bmp', '.BMP',
    '.nii.gz', '.npy'
]

def is_image_file(filename):
    return any(filename.endswith(extension) for extension in IMG_EXTENSIONS)

def find_classes(dir):
    classes = [d for d in os.listdir(dir) if os.path.isdir(os.path.join(dir, d))]
    classes.sort()
    class_to_idx = {classes[i]: i for i in range(len(classes))}
    return classes, class_to_idx

def pil_loader(path):
    return Image.open(path).convert('RGB')

def npy_loader(path):
    return torch.from_numpy(np.load(path).astype('float32'))

def nifti_loader(path):
    return nibabel.load(path)

def make_dataset(directory, class_mode, class_to_idx=None,
            input_regex=None, target_regex=None, ):
    """Map a dataset from a root folder"""
    if class_mode == 'image':
        if not input_regex and not target_regex:
            raise ValueError('must give input_regex and target_regex if'+
                ' class_mode==image')
    inputs = []
    targets = []
    for subdir in sorted(os.listdir(directory)):
        d = os.path.join(directory, subdir)
        if not os.path.isdir(d):
            continue

        for root, _, fnames in sorted(os.walk(d)):
            for fname in fnames:
                if fnmatch.fnmatch(fname, input_regex):
                    path = os.path.join(root, fname)
                    inputs.append(path)
                    if class_mode == 'label':
                        targets.append(class_to_idx[subdir])
                if class_mode == 'image' and fnmatch.fnmatch(fname, target_regex):
                    path = os.path.join(root, fname)
                    targets.append(path)
    if class_mode is None:
        return inputs
    else:
        return inputs, targets

class Dataset(object):
    """An abstract class representing a Dataset.

    All other datasets should subclass it. All subclasses should override
    ``__len__``, that provides the size of the dataset, and ``__getitem__``,
    supporting integer indexing in range from 0 to len(self) exclusive.
    """

    def __getitem__(self, index):
        raise NotImplementedError

    def __len__(self):
        raise NotImplementedError

    def one_epoch(self):
        """Return an iterator that will loop through all the samples one time"""
        return DatasetIter(self)

    def __iter__(self):
        """Return an iterator that will loop through all the samples one time"""
        return DatasetIter(self)

    def __next__(self):
        """Return the next batch in the data. If this batch is the last
        batch in the data, the iterator will be reset -- allowing you
        to loop through the data ad infinitum
        """
        new_batch = next(self._iter)
        self.batches_seen += 1
        if self.batches_seen % self.nb_batches == 0:
            #print('Last Batch of Current Epoch')
            self._iter = DatasetIter(self)
        return new_batch

    next = __next__

class FolderDataset(Dataset):

    def __init__(self,
                 root,
                 class_mode='label',
                 input_regex='*',
                 target_regex=None,
                 transform=None,
                 target_transform=None,
                 co_transform=None,
                 loader='npy',
                 batch_size=1,
                 shuffle=False,
                 sampler=None,
                 num_workers=0,
                 collate_fn=default_collate,
                 pin_memory=False):
        """Dataset class for loading out-of-memory data.

        Arguments
        ---------
        root : string
            path to main directory

        class_mode : string in `{'label', 'image'}`
            type of target sample to look for and return
            `label` = return class folder as target
            `image` = return another image as target as found by 'target_regex'
                NOTE: if class_mode == 'image', you must give an
                input and target regex and the input/target images should
                be in a folder together with no other images in that folder

        input_regex : string (default is any valid image file)
            regular expression to find input images
            e.g. if all your inputs have the word 'input',
            you'd enter something like input_regex='*input*'

        target_regex : string (default is Nothing)
            regular expression to find target images if class_mode == 'image'
            e.g. if all your targets have the word 'segment',
            you'd enter somthing like target_regex='*segment*'

        transform : torch transform
            transform to apply to input sample individually

        target_transform : torch transform
            transform to apply to target sample individually

        loader : string in `{'npy', 'pil', 'nifti'} or function
            defines how to load samples from file
            if a function is provided, it should take in a file path
            as input and return the loaded sample.

        Examples
        --------
        For loading input images and target images (e.g. image and its segmentation):
            >>> data = FolderDataset(root=/path/to/main/dir,
                    class_mode='image', input_regex='*input*',
                    target_regex='*segment*', loader='pil')

        For loading input images with sub-directory as class label:
            >>> data = FolderDataset(root=/path/to/main/dir,
                    class_mode='label', loader='pil')
        """
        if loader == 'npy':
            loader = npy_loader
        elif loader == 'pil':
            loader = pil_loader
        elif loader == 'nifti':
            loader = nifti_loader

        root = os.path.expanduser(root)

        classes, class_to_idx = find_classes(root)
        inputs, targets = make_dataset(root, class_mode,
            class_to_idx, input_regex, target_regex)

        if len(inputs) == 0:
            raise(RuntimeError("Found 0 images in subfolders of: " + root + "\n"
                    "Supported image extensions are: " + ",".join(IMG_EXTENSIONS)))

        self.root = os.path.expanduser(root)
        self.inputs = inputs
        self.targets = targets
        self.classes = classes
        self.class_to_idx = class_to_idx
        self.transform = transform
        self.target_transform = target_transform
        self.co_transform = co_transform
        self.loader = loader
        self.class_mode = class_mode

        self.batch_size = batch_size
        self.num_workers = num_workers
        self.collate_fn = collate_fn
        self.pin_memory = pin_memory

        if sampler is not None:
            self.sampler = sampler
        elif shuffle:
            self.sampler = RandomSampler(nb_samples=len(self.inputs))
        elif not shuffle:
            self.sampler = SequentialSampler(nb_samples=len(self.inputs))

        if class_mode == 'image':
            print('Found %i input images and %i target images' %
                (len(self.inputs), len(self.targets)))
        elif class_mode == 'label':
            print('Found %i input images across %i classes' %
                (len(self.inputs), len(self.classes)))

        self.batches_seen = 0
        self.nb_batches = int(math.ceil(len(self.sampler) / float(self.batch_size)))
        self._iter = DatasetIter(self)

    def __getitem__(self, index):
        # get paths
        input_sample = self.inputs[index]
        target_sample = self.targets[index]

        # load samples into memory
        input_sample = self.loader(os.path.join(self.root, input_sample))
        if self.class_mode == 'image':
            target_sample = self.loader(os.path.join(self.root, target_sample))

        # apply transforms
        if self.transform is not None:
            input_sample = self.transform(input_sample)
        if self.target_transform is not None:
            target_sample = self.target_transform(target_sample)
        if self.co_transform is not None:
            input_sample, target_sample = self.co_transform(input_sample, target_sample)

        return input_sample, target_sample

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

class TensorDataset(Dataset):

    def __init__(self,
                 input_tensor,
                 target_tensor=None,
                 transform=None,
                 target_transform=None,
                 co_transform=None,
                 batch_size=1,
                 shuffle=False,
                 sampler=None,
                 num_workers=0,
                 collate_fn=default_collate,
                 pin_memory=False):
        """Dataset class for loading in-memory data.

        Arguments
        ---------
        input_tensor : torch tensor

        target_tensor : torch tensor

        transform : torch transform
            transform to apply to input sample individually

        target_transform : torch transform
            transform to apply to target sample individually

        loader : string in `{'npy', 'pil', 'nifti'} or function
            defines how to load samples from file
            if a function is provided, it should take in a file path
            as input and return the loaded sample.

        Examples
        --------
        For loading input images and target images (e.g. image and its segmentation):
            >>> data = FolderDataset(root=/path/to/main/dir,
                    class_mode='image', input_regex='*input*',
                    target_regex='*segment*', loader='pil')

        For loading input images with sub-directory as class label:
            >>> data = FolderDataset(root=/path/to/main/dir,
                    class_mode='label', loader='pil')
        """
        self.inputs = input_tensor
        self.targets = target_tensor
        if target_tensor is None:
            self.has_target = False
        else:
            self.has_target = True
        self.transform = transform
        self.target_transform = target_transform
        self.co_transform = co_transform

        self.batch_size = batch_size
        self.num_workers = num_workers
        self.collate_fn = collate_fn
        self.pin_memory = pin_memory

        if sampler is not None:
            self.sampler = sampler
        else:
            if shuffle:
                self.sampler = RandomSampler(nb_samples=len(self.inputs))
            elif not shuffle:
                self.sampler = SequentialSampler(nb_samples=len(self.inputs))

        self.batches_seen = 0
        self.nb_batches = int(math.ceil(len(self.sampler) / float(self.batch_size)))
        self._iter = DatasetIter(self)

    def __getitem__(self, index):
        """Return a (transformed) input and target sample from an integer index"""
        # get paths
        input_sample = self.inputs[index]
        if self.has_target:
            target_sample = self.targets[index]

        # apply transforms
        if self.transform is not None:
            input_sample = self.transform(input_sample)
        if self.has_target and self.target_transform is not None:
            target_sample = self.target_transform(target_sample)
        if self.has_target and self.co_transform is not None:
            input_sample, target_sample = self.co_transform(input_sample, target_sample)

        if self.has_target:
            return input_sample, target_sample
        else:
            return input_sample

    def __len__(self):
        """Number of samples"""
        return self.inputs.size(0)


import os
from abc import ABC

from keras import Model, layers
from keras.layers import Conv2D, BatchNormalization, Add, MaxPool2D, GlobalAveragePooling2D, Flatten, Dense, Rescaling
import tensorflow as tf

class ResnetBlock(Model, ABC):
    """
    A standard resnet block.
    """

    def __init__(self, channels: int, down_sample=False):
        """
        channels: same as number of convolution kernels
        """
        super().__init__()

        self.__channels = channels
        self.__down_sample = down_sample
        self.__strides = [2, 1] if down_sample else [1, 1]

        KERNEL_SIZE = (3, 3)
        # use He initialization, instead of Xavier (a.k.a 'glorot_uniform' in Keras), as suggested in [2]
        INIT_SCHEME = "he_normal"

        self.conv_1 = Conv2D(self.__channels, strides=self.__strides[0],
                             kernel_size=KERNEL_SIZE, padding="same", kernel_initializer=INIT_SCHEME)
        self.bn_1 = BatchNormalization()
        self.conv_2 = Conv2D(self.__channels, strides=self.__strides[1],
                             kernel_size=KERNEL_SIZE, padding="same", kernel_initializer=INIT_SCHEME)
        self.bn_2 = BatchNormalization()
        self.merge = Add()

        if self.__down_sample:
            # perform down sampling using stride of 2, according to [1].
            self.res_conv = Conv2D(
                self.__channels, strides=2, kernel_size=(1, 1), kernel_initializer=INIT_SCHEME, padding="same")
            self.res_bn = BatchNormalization()

    def call(self, inputs, training=None, mask=None):
        res = inputs

        x = self.conv_1(inputs)
        x = self.bn_1(x)
        x = tf.nn.relu(x)
        x = self.conv_2(x)
        x = self.bn_2(x)

        if self.__down_sample:
            res = self.res_conv(res)
            res = self.res_bn(res)

        # if not perform down sample, then add a shortcut directly
        x = self.merge([x, res])
        out = tf.nn.relu(x)
        return out

class ResNet18(Model):

    def __init__(self, num_classes, **kwargs):
        """
            num_classes: number of classes in specific classification task.
        """
        super().__init__(**kwargs)
        self.conv_1 = Conv2D(64, (7, 7), strides=2,
                             padding="same", kernel_initializer="he_normal")
        self.init_bn = BatchNormalization()
        self.pool_2 = MaxPool2D(pool_size=(2, 2), strides=2, padding="same")
        self.res_1_1 = ResnetBlock(64)
        self.res_1_2 = ResnetBlock(64)
        self.res_2_1 = ResnetBlock(128, down_sample=True)
        self.res_2_2 = ResnetBlock(128)
        self.res_3_1 = ResnetBlock(256, down_sample=True)
        self.res_3_2 = ResnetBlock(256)
        self.res_4_1 = ResnetBlock(512, down_sample=True)
        self.res_4_2 = ResnetBlock(512)
        self.avg_pool = GlobalAveragePooling2D()
        self.flat = Flatten()
        self.fc = Dense(num_classes, activation="softmax")
        self.data_augmentation = tf.keras.Sequential(
            [
                layers.RandomFlip("horizontal",
                                  input_shape=(int(os.getenv("img_height")),
                                               int(os.getenv("img_width")),
                                               3)),
                layers.RandomRotation(0.1),
                layers.RandomZoom(0.1),
            ]
        )

    def call(self, inputs, training=None, mask=None):
        out = self.data_augmentation(inputs)
        out = Rescaling(scale=1.0/255)(out)
        out = self.conv_1(out)
        out = self.init_bn(out)
        out = tf.nn.relu(out)
        out = self.pool_2(out)
        for res_block in [self.res_1_1, self.res_1_2, self.res_2_1, self.res_2_2, self.res_3_1, self.res_3_2,
                          self.res_4_1, self.res_4_2]:
            out = res_block(out)
        out = self.avg_pool(out)
        out = self.flat(out)
        out = self.fc(out)
        return out


import cv2
import numpy as np
import os
import sys
import json
import math
import time
import argparse
from enum import Enum
import platform

class Config:
    @classmethod
    def init(cls):
        if platform.system() == "Windows":
            cls.QUIT_KEY = ord("q")
            cls.CONTINUE_KEY = 2555904     #right arrow
            cls.BACK_KEY = 2424832         #left arrow
            cls.REWIND_KEY = ord("r")
            cls.PLAYPAUSE_KEY = 32         #spacebar
        else:
            cls.QUIT_KEY = ord("q")
            cls.CONTINUE_KEY = 65363       #right arrow
            cls.BACK_KEY = 65361           #left arrow
            cls.REWIND_KEY = ord("r")
            cls.PLAYPAUSE_KEY = 32         #spacebar

        if os.path.exists(os.path.join(os.getcwd(), "./manim-presentation.json")):
            json_config = json.load(open(os.path.join(os.getcwd(), "./manim-presentation.json"), "r"))
            for key, value in json_config.items():
                setattr(cls, key, value)

class State(Enum):
    PLAYING = 0
    PAUSED = 1
    WAIT = 2
    END = 3

    def __str__(self):
        if self.value == 0: return "Playing"
        if self.value == 1: return "Paused"
        if self.value == 2: return "Wait"
        if self.value == 3: return "End"
        return "..."

def now():
    return round(time.time() * 1000)

def fix_time(x):
    return x if x > 0 else 1

class Presentation:
    def __init__(self, config, last_frame_next=False):
        self.last_frame_next = last_frame_next
        self.slides = config["slides"]
        self.files = config["files"]

        self.lastframe = []

        self.caps = [None for _ in self.files]
        self.reset()
        self.add_last_slide()

    def add_last_slide(self):
        last_slide_end = self.slides[-1]["end_animation"]
        last_animation = len(self.files)
        self.slides.append(dict(
            start_animation = last_slide_end,
            end_animation = last_animation,
            type = "last",
            number = len(self.slides) + 1,
            terminated = False
        ))

    def reset(self):
        self.current_animation = 0
        self.load_this_cap(0)
        self.current_slide_i = 0
        self.slides[-1]["terminated"] = False

    def next(self):
        if self.current_slide["type"] == "last":
            self.current_slide["terminated"] = True
        else:
            self.current_slide_i = min(len(self.slides) - 1, self.current_slide_i + 1)
            self.rewind_slide()

    def prev(self):
        self.current_slide_i = max(0, self.current_slide_i - 1)
        self.rewind_slide()

    def rewind_slide(self):
        self.current_animation = self.current_slide["start_animation"]
        self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)

    def load_this_cap(self,cap_number):
        if self.caps[cap_number] == None:
            # unload other caps
            for i in range(len(self.caps)):
                if self.caps[i] != None:
                    self.caps[i].release()
                    self.caps[i] = None
            # load this cap
            self.caps[cap_number] = cv2.VideoCapture(self.files[cap_number])

    @property
    def current_slide(self):
        return self.slides[self.current_slide_i]

    @property
    def current_cap(self):
        self.load_this_cap(self.current_animation)
        return self.caps[self.current_animation]

    @property
    def fps(self):
        return self.current_cap.get(cv2.CAP_PROP_FPS)

    # This function updates the state given the previous state.
    # It does this by reading the video information and checking if the state is still correct.
    # It returns the frame to show (lastframe) and the new state.
    def update_state(self, state):
        if state == State.PAUSED:
            if len(self.lastframe) == 0:
                _, self.lastframe = self.current_cap.read()
            return self.lastframe, state
        still_playing, frame = self.current_cap.read()
        if still_playing:
            self.lastframe = frame
        elif state in [state.WAIT, state.PAUSED]:
            return self.lastframe, state
        elif self.current_slide["type"] == "last" and self.current_slide["terminated"]:
            return self.lastframe, State.END

        if not still_playing:
            if self.current_slide["end_animation"] == self.current_animation + 1:
                if self.current_slide["type"] == "slide":
                    # To fix "it always ends one frame before the animation", uncomment this.
                    # But then clears on the next slide will clear the stationary after this slide.
                    if self.last_frame_next:
                        self.load_this_cap(self.next_cap)
                        self.next_cap = self.caps[self.current_animation + 1]

                        self.next_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
                        _, self.lastframe = self.next_cap.read()
                    state = State.WAIT
                elif self.current_slide["type"] == "loop":
                    self.current_animation = self.current_slide["start_animation"]
                    state = State.PLAYING
                    self.rewind_slide()
                elif self.current_slide["type"] == "last":
                    self.current_slide["terminated"] = True
            elif self.current_slide["type"] == "last" and self.current_slide["end_animation"] == self.current_animation:
                state = State.WAIT
            else:
                # Play next video!
                self.current_animation += 1
                self.load_this_cap(self.current_animation)
                # Reset video to position zero if it has been played before
                self.current_cap.set(cv2.CAP_PROP_POS_FRAMES, 0)

        return self.lastframe, state

class Display:
    def __init__(self, presentations, start_paused=False, fullscreen=False):
        self.presentations = presentations
        self.start_paused = start_paused

        self.state = State.PLAYING
        self.lastframe = None
        self.current_presentation_i = 0

        self.lag = 0
        self.last_time = now()

        if fullscreen:
            cv2.namedWindow("Video", cv2.WND_PROP_FULLSCREEN)
            cv2.setWindowProperty("Video", cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_FULLSCREEN)

    @property
    def current_presentation(self):
        return self.presentations[self.current_presentation_i]

    def run(self):
        while True:
            self.lastframe, self.state = self.current_presentation.update_state(self.state)
            if self.state == State.PLAYING or self.state == State.PAUSED:
                if self.start_paused:
                    self.state = State.PAUSED
                    self.start_paused = False
            if self.state == State.END:
                if self.current_presentation_i == len(self.presentations) - 1:
                    self.quit()
                else:
                    self.current_presentation_i += 1
                    self.state = State.PLAYING
            self.handle_key()
            self.show_video()
            self.show_info()

    def show_video(self):
        self.lag = now() - self.last_time
        self.last_time = now()
        cv2.imshow("Video", self.lastframe)

    def show_info(self):
        info = np.zeros((130, 420), np.uint8)
        font_args = (cv2.FONT_HERSHEY_SIMPLEX, 0.7, 255)
        grid_x = [30, 230]
        grid_y = [30, 70, 110]

        cv2.putText(
            info,
            f"Animation: {self.current_presentation.current_animation}",
            (grid_x[0], grid_y[0]),
            *font_args
        )
        cv2.putText(
            info,
            f"State: {self.state}",
            (grid_x[1], grid_y[0]),
            *font_args
        )

        cv2.putText(
            info,
            f"Slide {self.current_presentation.current_slide['number']}/{len(self.current_presentation.slides)}",
            (grid_x[0], grid_y[1]),
            *font_args
        )
        cv2.putText(
            info,
            f"Slide Type: {self.current_presentation.current_slide['type']}",
            (grid_x[1], grid_y[1]),
            *font_args
        )

        cv2.putText(
            info,
            f"Scene  {self.current_presentation_i + 1}/{len(self.presentations)}",
            ((grid_x[0]+grid_x[1])//2, grid_y[2]),
            *font_args
        )

        cv2.imshow("Info", info)

    def handle_key(self):
        sleep_time = math.ceil(1000/self.current_presentation.fps)
        key = cv2.waitKeyEx(fix_time(sleep_time - self.lag))

        if key == Config.QUIT_KEY:
            self.quit()
        elif self.state == State.PLAYING and key == Config.PLAYPAUSE_KEY:
            self.state = State.PAUSED
        elif self.state == State.PAUSED and key == Config.PLAYPAUSE_KEY:
            self.state = State.PLAYING
        elif self.state == State.WAIT and (key == Config.CONTINUE_KEY or key == Config.PLAYPAUSE_KEY):
            self.current_presentation.next()
            self.state = State.PLAYING
        elif self.state == State.PLAYING and key == Config.CONTINUE_KEY:
            self.current_presentation.next()
        elif key == Config.BACK_KEY:
            if self.current_presentation.current_slide_i == 0:
                self.current_presentation_i = max(0, self.current_presentation_i - 1)
                self.current_presentation.reset()
                self.state = State.PLAYING
            else:
                self.current_presentation.prev()
                self.state = State.PLAYING
        elif key == Config.REWIND_KEY:
            self.current_presentation.rewind_slide()
            self.state = State.PLAYING

    def quit(self):
        cv2.destroyAllWindows()
        sys.exit()

def main():
    parser = argparse.ArgumentParser()

    parser.add_argument("scenes", metavar="scenes", type=str, nargs="+", help="Scenes to present")
    parser.add_argument("--folder", type=str, default="./presentation", help="Presentation files folder")
    parser.add_argument("--start-paused", action="store_true", help="Start paused")
    parser.add_argument("--fullscreen", action="store_true", help="Fullscreen")
    parser.add_argument("--last-frame-next", action="store_true", help="Show the next animation first frame as last frame (hack)")

    args = parser.parse_args()
    args.folder = os.path.normcase(args.folder)

    Config.init()

    presentations = list()
    for scene in args.scenes:
        config_file = os.path.join(args.folder, f"{scene}.json")
        if not os.path.exists(config_file):
            raise Exception(f"File {config_file} does not exist, check the scene name and make sure to use Slide as your scene base class")
        config = json.load(open(config_file))
        presentations.append(Presentation(config, last_frame_next=args.last_frame_next))

    display = Display(presentations, start_paused=args.start_paused, fullscreen=args.fullscreen)
    display.run()

if __name__ == "__main__":
    main()


#!/usr/bin/env python3

import fnmatch
import os
import re
import ntpath
import sys
import argparse

# handle x64 python clipboard, ref https://forums.autodesk.com/t5/maya-programming/ctypes-bug-cannot-copy-data-to-clipboard-via-python/m-p/9197068/highlight/true#M10992
import ctypes
from ctypes import wintypes
CF_UNICODETEXT = 13

user32 = ctypes.WinDLL('user32')
kernel32 = ctypes.WinDLL('kernel32')

OpenClipboard = user32.OpenClipboard
OpenClipboard.argtypes = wintypes.HWND,
OpenClipboard.restype = wintypes.BOOL
CloseClipboard = user32.CloseClipboard
CloseClipboard.restype = wintypes.BOOL
EmptyClipboard = user32.EmptyClipboard
EmptyClipboard.restype = wintypes.BOOL
GetClipboardData = user32.GetClipboardData
GetClipboardData.argtypes = wintypes.UINT,
GetClipboardData.restype = wintypes.HANDLE
SetClipboardData = user32.SetClipboardData
SetClipboardData.argtypes = (wintypes.UINT, wintypes.HANDLE)
SetClipboardData.restype = wintypes.HANDLE
GlobalLock = kernel32.GlobalLock
GlobalLock.argtypes = wintypes.HGLOBAL,
GlobalLock.restype = wintypes.LPVOID
GlobalUnlock = kernel32.GlobalUnlock
GlobalUnlock.argtypes = wintypes.HGLOBAL,
GlobalUnlock.restype = wintypes.BOOL
GlobalAlloc = kernel32.GlobalAlloc
GlobalAlloc.argtypes = (wintypes.UINT, ctypes.c_size_t)
GlobalAlloc.restype = wintypes.HGLOBAL
GlobalSize = kernel32.GlobalSize
GlobalSize.argtypes = wintypes.HGLOBAL,
GlobalSize.restype = ctypes.c_size_t

GMEM_MOVEABLE = 0x0002
GMEM_ZEROINIT = 0x0040

def Paste( data ):
    data = data.encode('utf-16le')
    OpenClipboard(None)
    EmptyClipboard()
    handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, len(data) + 2)
    pcontents = GlobalLock(handle)
    ctypes.memmove(pcontents, data, len(data))
    GlobalUnlock(handle)
    SetClipboardData(CF_UNICODETEXT, handle)
    CloseClipboard()

def getFunctions(filepath):
    selfmodule = (re.search(r'addons[\W]*([_a-zA-Z0-9]*)', filepath)).group(1)
    # print("Checking {0} from {1}".format(filepath,selfmodule))
    if (selfmodule.startswith("compat")): return []

    with open(filepath, 'r') as file:
        content = file.read()

        srch = re.compile(r'[^E]FUNC\(([_a-zA-Z0-9]*)\)')
        modfuncs = srch.findall(content)
        modfuncs = sorted(set(modfuncs))

        srch = re.compile(r'EFUNC\(([_a-zA-Z0-9]*),([_a-zA-Z0-9]*)\)')
        exfuncs = srch.findall(content)
        exfuncs = sorted(set(exfuncs))

    fileFuncs = []
    for func in modfuncs:
        fileFuncs.append("ace_{0}_fnc_{1}".format(selfmodule,func))

    for exModule,func in exfuncs:
        fileFuncs.append("ace_{0}_fnc_{1}".format(exModule, func))

    return fileFuncs

def getStrings(filepath):
    selfmodule = (re.search(r'addons[\W]*([_a-zA-Z0-9]*)', filepath)).group(1)
    # print("Checking {0} from {1}".format(filepath,selfmodule))
    if (selfmodule.startswith("compat")): return []

    with open(filepath, 'r') as file:
        content = file.read()

        srch = re.compile(r'[^E][CL]STRING\(([_a-zA-Z0-9]*)\)')
        modStrings = srch.findall(content)
        modStrings = sorted(set(modStrings))

        srch = re.compile(r'E[CL]STRING\(([_a-zA-Z0-9]*),([_a-zA-Z0-9]*)\)')
        exStrings = srch.findall(content)
        exStrings = sorted(set(exStrings))

    fileStrings = []
    for localString in modStrings:
        fileStrings.append("STR_ACE_{0}_{1}".format(selfmodule, localString))

    for (exModule, exString) in exStrings:
        fileStrings.append("STR_ACE_{0}_{1}".format(exModule, exString))

    return fileStrings

def main():

    print("#########################")
    print("# All Functions  #")
    print("#########################")

    sqf_list = []

    allFunctions = []
    allStrings = []

    parser = argparse.ArgumentParser()
    parser.add_argument('-m','--module', help='only search specified module addon folder', required=False, default=".")
    args = parser.parse_args()

    addon_base_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
    for root, dirnames, filenames in os.walk(addon_base_path +"/" + 'addons' + '/' + args.module):
      for filename in fnmatch.filter(filenames, '*.sqf'):
        sqf_list.append(os.path.join(root, filename))
      for filename in fnmatch.filter(filenames, '*.cpp'):
        sqf_list.append(os.path.join(root, filename))
      for filename in fnmatch.filter(filenames, '*.hpp'):
        sqf_list.append(os.path.join(root, filename))

    for filename in sqf_list:
        allFunctions = allFunctions + getFunctions(filename)
    for filename in sqf_list:
        allStrings = allStrings + getStrings(filename)

    codeHeader = "diag_log text '*********** Scaning for nil functions [funcs {0} / strings {1}]';".format(len(set(allFunctions)), len(set(allStrings)))
    codeFuncCheck = "{ if (isNil _x) then {systemChat format ['%1 is nil', _x]; diag_log text format ['%1 is nil', _x];}} forEach allFunctions;"
    codeStringCheck = "{ if (!isLocalized _x) then {systemChat format ['%1 is not in stringtable', _x]; diag_log text format ['%1 is not in stringtable', _x];}} forEach allStrings;"

    outputCode = "{0} allFunctions = {1}; allStrings = {2}; {3} {4}".format(codeHeader, list(set(allFunctions)), list(set(allStrings)), codeFuncCheck, codeStringCheck)

    print(outputCode)
    Paste(outputCode)

    print ("")
    print ("Copied to clipboard, [funcs {0} / strings {1}]'".format(len(set(allFunctions)), len(set(allStrings))))

if __name__ == "__main__":
    main()


from datetime import datetime
def unix_to_dt(time):
    return datetime.utcfromtimestamp(time).strftime('%Y-%m-%d %H:%M:%S')

class event():
    def __init__(self, events):
        self.type = events['type']
        self.empty = False

        if self.type == 'None':
            self.empty = True
        elif self.type == 'membership.nickname_changed':
            self.nickname_changed(events['data'])
        elif self.type == 'membership.announce.joined':
            self.user_joined(events['data'])
        elif self.type == 'membership.notifications.exited':
            self.user_exited(events['data'])
        elif self.type == 'membership.notifications.removed':
            self.user_removed(events['data'])
        elif self.type == 'membership.notifications.autokicked':
            self.user_autokicked(events['data'])
        elif self.type == 'group.avatar_change':
            self.group_avatar_change(events['data'])
        elif self.type == 'group.like_icon_set':
            self.like_icon_change(events['data'])
        elif self.type == 'group.name_change':
            self.group_name_change(events['data'])
        elif self.type == 'poll.created':
            self.poll_created(events['data'])
        elif self.type == 'poll.reminder':
            self.poll_reminder(events['data'])
        elif self.type == 'poll.finished':
            self.poll_finished(events['data'])
        elif self.type == 'message.deleted':
            self.message_deleted(events['data'])
        else:
            print('Unknown event type: ' + self.type)

    def nickname_changed(self, event_data):
        self.user = event_data['user']
        self.new_name = event_data['name']

    def user_joined(self, event_data):
        self.user = event_data['user']

    def user_added(self, event_data):
        self.user = event_data['adder_user']
        self.added_users = event_data['added_users']

    def user_exited(self, event_data):
        self.placeholder = 'X'
        self.charmap = event_data['charmap']

    def user_removed(self, event_data):
        self.user = event_data['remover_user']
        self.removed_user = event_data['removed_user']

    def user_autokicked(self, event_data):
        self.user = event_data['user']

    def group_avatar_change(self, event_data):
        self.user = event_data['user']
        self.url = event_data['avatar_url']

    def like_icon_change(self, event_data):
        self.user = event_data['user']
        self.like_icon = [event_data['pack_id'], event_data['icon_index']]
        # like_icon->type is ignored

    def group_name_change(self, event_data):
        self.user = event_data['user']
        self.new_name = event_data['name']

    '''POLL EVENTS'''

    def poll_created(self, event_data):
        self.user = event_data['user']
        self.conversation = event_data['conversation']
        self.poll = event_data['poll']

    def poll_reminder(self, event_data):
        self.conversation = event_data['conversation']
        self.poll = event_data['poll']
        self.expiration = unix_to_dt(event_data['expiration'])

    def poll_finished(self, event_data):
        self.conversation = event_data['conversation']
        self.raw_options = event_data['options']
        self.options = []
        for opt in self.raw_options:
            temp_option = {
                "id": None,
                "title": None,
                "votes": None,
                "voter_ids": None
            }
            temp_option["id"] = opt["id"]
            temp_option["title"] = opt["title"]
            try:
                temp_option["votes"] = opt["votes"]
                temp_option["voter_ids"] = opt["voter_ids"]
            except:
                temp_option["votes"] = 0
                temp_option["voter_ids"] = []
            self.options.append(temp_option)

    '''MISC EVENTS'''

    def message_deleted(self, event_data):
        self.message_id = event_data['message_id']
        self.deleted_at = unix_to_dt(event_data['deleted_at'])
        self.deleted_at_ts = event_data['deleted_at']
        self.deletion_actor = event_data['deletion_actor']
        self.deleter_id = event_data['deleter_id']


#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Automatic electric field computation
------------------------------------

:download:`examples/auto_efield.py` demonstrates how
drift can be added self-consistently by calculating the
electric field generated from the concentration profile
of charged species.

::

 $ python auto_efield.py --help

.. exec::
   echo "::\\n\\n"
   python examples/examples/auto_efield.py --help | sed "s/^/   /"

Here is an example generated by:

::

 $ python auto_efield.py --plot --savefig auto_efield.png

.. image:: ../_generated/auto_efield.png

"""

from __future__ import print_function, division, absolute_import

from math import log, erf, exp

import argh
import numpy as np
from chempy.einstein_smoluchowski import electrical_mobility_from_D

from chemreac import ReactionDiffusion
from chemreac.integrate import run
from chemreac.util.plotting import save_and_or_show_plot

def sigm(x, lim=150., n=8):
    # Algebraic sigmoid to avoid overflow/underflow of 'double exp(double)'
    return x/((x/lim)**n+1)**(1./n)

sq2 = 2**0.5
pi = np.pi
sqpi = pi**0.5

def _gaussian(x, mu, sigma, logy, logx, geom, use_log2=False):
    # Formula for normalization from derived in following mathematica code:
    # $Assumptions = {(sigma | mu) \[Element] Reals, sigma > 0}
    # 1/Integrate[E^(-1/2*((x - mu)/sigma)^2), {x, -Infinity, Infinity}]
    # 1/Integrate[2*pi*x*E^(-1/2*((x - mu)/sigma)^2), {x, 0, Infinity}]
    # 1/Integrate[4*pi*x^2*E^(-1/2*((x - mu)/sigma)^2), {x, 0, Infinity}]
    if geom == 'f':
        a = 1/sigma/(2*np.pi)**0.5
    elif geom == 'c':
        a = 1/pi/sigma/(2*exp(-mu**2/2/sigma**2)*sigma +
                        mu*sq2*sqpi*(1 + erf(mu/(sq2*sigma))))
    elif geom == 's':
        a = 1/2/pi/sigma/(2*exp(-mu**2/2/sigma**2)*mu*sigma +
                          sq2*sqpi*(mu**2 + sigma**2)*(1 + erf(mu/sq2/sigma)))
    else:
        raise NotImplementedError("Unkown geomtry: %s" % geom)

    b = -0.5*((x-mu)/sigma)**2
    logb = (lambda arg: log(arg)/log(2)) if use_log2 else log
    if logy:
        return logb(a) + b*logb(np.e)
    else:
        return a*np.exp(b)

def pair_of_gaussians(x, offsets, sigma, logy, logx, geom, use_log2=False):
    try:
        sigma0, sigma1 = sigma[0], sigma[1]
    except:
        sigma0 = sigma1 = sigma
    expb = (lambda arg: 2**arg) if use_log2 else np.exp
    x = expb(x) if logx else x
    xspan = (x[-1] - x[0])
    xl = x[0] + offsets[0]*xspan  # lower
    xu = x[0] + offsets[1]*xspan  # upper
    return (
        _gaussian(x, xl, sigma0, logy, logx, geom, use_log2),
        _gaussian(x, xu, sigma1, logy, logx, geom, use_log2)
    )

def integrate_rd(D=-3e-1, t0=0.0, tend=7., x0=0.1, xend=1.0, N=1024,
                 base=0.5, offset=0.25, nt=25, geom='f',
                 logt=False, logy=False, logx=False, random=False,
                 nstencil=3, lrefl=False, rrefl=False,
                 num_jacobian=False, method='bdf', plot=False,
                 savefig='None', atol=1e-6, rtol=1e-6, random_seed=42,
                 surf_chg=(0.0, 0.0), sigma_q=101, sigma_skew=0.5,
                 verbose=False, eps_rel=80.10, use_log2=False):
    """
    A negative D (diffusion coefficent) denotes:
        mobility := -D
        D := 0
    A positive D calculates mobility from Einstein-Smoluchowski relation

    """
    assert 0 <= base and base <= 1
    assert 0 <= offset and offset <= 1
    if random_seed:
        np.random.seed(random_seed)
    n = 2

    if D < 0:
        mobility = -D
        D = 0
    else:
        mobility = electrical_mobility_from_D(D, 1, 298.15)
        print(D, mobility)

    # Setup the grid
    logb = (lambda arg: log(arg)/log(2)) if use_log2 else log

    _x0 = logb(x0) if logx else x0
    _xend = logb(xend) if logx else xend
    x = np.linspace(_x0, _xend, N+1)
    if random:
        x += (np.random.random(N+1)-0.5)*(_xend-_x0)/(N+2)

    # Setup the system
    stoich_active = []
    stoich_prod = []
    k = []

    rd = ReactionDiffusion(
        n, stoich_active, stoich_prod, k, N,
        D=[D, D],
        z_chg=[1, -1],
        mobility=[mobility, -mobility],
        x=x,
        geom=geom,
        logy=logy,
        logt=logt,
        logx=logx,
        nstencil=nstencil,
        lrefl=lrefl,
        rrefl=rrefl,
        auto_efield=True,
        surf_chg=surf_chg,
        eps_rel=eps_rel,  # water at 20 deg C
        faraday_const=1,
        vacuum_permittivity=1,
        use_log2=use_log2
    )

    # Initial conditions
    sigma = (xend-x0)/sigma_q
    sigma = [(1-sigma_skew)*sigma, sigma_skew*sigma]
    y0 = np.vstack(pair_of_gaussians(
        rd.xcenters, [base+offset, base-offset], sigma, logy, logx, geom, use_log2)).transpose()
    if logy:
        y0 = sigm(y0)

    if plot:
        # Plot initial E-field
        import matplotlib.pyplot as plt
        plt.figure(figsize=(6, 10))
        rd.calc_efield((rd.expb(y0) if logy else y0).flatten())
        plt.subplot(4, 1, 3)
        plt.plot(rd.xcenters, rd.efield, label="E at t=t0")
        plt.plot(rd.xcenters, rd.xcenters*0, label="0")

    # Run the integration
    tout = np.linspace(t0, tend, nt)
    integr = run(rd, y0, tout,
                 atol=atol, rtol=rtol, sigm_damp=True,
                 C0_is_log=logy,
                 with_jacobian=(not num_jacobian), method=method)
    Cout = integr.Cout

    if verbose:
        print(integr.info)
    # Plot results
    if plot:
        def _plot(y, ttl=None,  **kwargs):
            plt.plot(rd.xcenters, y, **kwargs)
            plt.xlabel((('log_%s({})' % ('2' if use_log2 else 'e')) if logx else '{}').format('x / m'))
            plt.ylabel('C / M')
            if ttl:
                plt.title(ttl)

        for i in range(nt):
            plt.subplot(4, 1, 1)
            c = 1-tout[i]/tend
            c = (1.0-c, .5-c/2, .5-c/2)
            _plot(Cout[i, :, 0], 'Simulation (N={})'.format(rd.N),
                  c=c, label='$z_A=1$' if i == nt-1 else None)
            _plot(Cout[i, :, 1], c=c[::-1],
                  label='$z_B=-1$' if i == nt-1 else None)
            plt.legend()

            plt.subplot(4, 1, 2)
            delta_y = Cout[i, :, 0] - Cout[i, :, 1]
            _plot(delta_y, 'Diff',
                  c=[c[2], c[0], c[1]],
                  label='A-B (positive excess)' if i == nt-1 else None)
            plt.legend(loc='best')
            plt.xlabel("$x~/~m$")
            plt.ylabel(r'Concentration / M')
        ylim = plt.gca().get_ylim()
        if N < 100:
            plt.vlines(rd.x, ylim[0], ylim[1],
                       linewidth=1.0, alpha=0.2, colors='gray')

        plt.subplot(4, 1, 3)
        plt.plot(rd.xcenters, rd.efield, label="E at t=tend")
        plt.xlabel("$x~/~m$")
        plt.ylabel(r"$E~/~V\cdot m^{-1}$")
        plt.legend()

        for i in range(3):
            plt.subplot(4, 1, i+1)
            ylim = plt.gca().get_ylim()
            for d in (-1, 1):
                center_loc = [x0+(base+d*offset)*(xend-x0)]*2
                plt.plot(rd.logb(center_loc) if logx else center_loc,
                         ylim, '--k')
        plt.subplot(4, 1, 4)
        for i in range(n):
            amount = [rd.integrated_conc(Cout[j, :, i]) for j in range(nt)]
            plt.plot(tout, amount, c=c[::(1, -1)[i]], label=chr(ord('A')+i))
        plt.xlabel('Time / s')
        plt.ylabel('Amount / mol')
        plt.legend(loc='best')
        plt.tight_layout()
        save_and_or_show_plot(savefig=savefig)
    return tout, Cout, integr.info, rd

if __name__ == '__main__':
    argh.dispatch_command(integrate_rd, output_file=None)


from PIL import Image

def brighten_Image(pixelList):

	pix_len = len(pixelList)
	for i in range(pix_len): #assigns each part of tuple to a variable
		current_pixel = pixelList[i] #then will brigthen each by 50
		red = current_pixel[0]
		green = current_pixel[1]
		blue = current_pixel[2]
		#this is not the best way, ex : replace  current_pixel
		#with pixList[i][0] ect
		newPixel = (red+50, green+50,  blue+50)
		pixelList[i] = newPixel #replaces current pixel with one
								#whos R,G,B values are +50

	return pixelList

def main():

	myFile = "/home/h702546919/Desktop/jimmy.jpg"
	my_img_obj = Image.open(myFile)
	#my_img_obj.show()

	#grabs each pixel's tuple
	pixelList = list(my_img_obj.getdata())

	#sends list of tuples to be brightend, gives back newPic
	newPic = brighten_Image(pixelList)
	#pushes new brighter pixels
	my_img_obj.putdata(newPic)

	#displays new pic and old one
	#(learn how to make a canvas and print side by side ? )
	my_img_obj.show()

	return

#---#
main()


### This file is meant to run from pc, *not* from the server. It extracts the
# data from the datafile, posts it to the database and finally runs the day
# command to add the day to the data.
import math

import pandas as pd
import requests

day_of_month = 6

def upload_data(fname):
    data = extract_data(fname)
    data_dict_list = []
    url = create_url()
    for _, row in data.iterrows():
        aaa = row[5]
        if math.isnan(aaa):
            aaa = 'NULL'
        dict = {"SubmitDateTime": str(row[0]),
                "UserId": row[1]+1000,
                "ExerciseId": row[2],
                "LearningObjectiveId": 8025,
                "Correct": min(row[4], 1),
                "AbilityAfterAnswer": aaa}
        data_dict_list.append(dict)
        print(dict, ",")
    # r = requests.post(url=url + "insert/", json=data_dict_list[0],
    #                   auth=("Group2", "Group2-1234"))
    # print(r.status_code, r.reason, url + "insert/")
    r = requests.get(url + "add_days/start=2018-06-04&end=2018-06-0{}".format( str(day_of_month)), auth=("Group2", "Group2-1234"))
    print(r.status_code, r.reason,
          url + "add_days/start=2018-06-04&end=2018-06-0{}".format(
              day_of_month))

def extract_data(fname):
    data = pd.read_excel(fname)
    return data

def create_url():
    url = "http://applab.ai.ru.nl:5000/"
    return url

if __name__ == "__main__":
    day_of_month = 6
    upload_data("C:/Users/Rick "
                "Dijkstra/Documents/Study/Applab/SnappetDataAnoniem/"
                "resultaten-radboud_anoniem 4-6-18.xlsx")


#!/usr/bin/env python3

from bcc import BPF
from http.server import HTTPServer, BaseHTTPRequestHandler
import sys
import threading

clone_ebpf = """
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
#include <linux/fs.h>

#define ARGSIZE  128

BPF_PERF_OUTPUT(events);

struct data_t {
    u32 pid;  // PID as in the userspace term (i.e. task->tgid in kernel)
    u32 ppid; // Parent PID as in the userspace term (i.e task->real_parent->tgid in kernel)
    u32 uid;
    char comm[TASK_COMM_LEN];
};

int clone_ebpf(struct pt_regs *ctx) {
    struct data_t data = {};
    struct task_struct *task;

    data.uid = bpf_get_current_uid_gid() & 0xffffffff;
    data.pid = bpf_get_current_pid_tgid() >> 32;

    task = (struct task_struct *)bpf_get_current_task();
    data.ppid = task->real_parent->tgid;

    bpf_get_current_comm(&data.comm, sizeof(data.comm));

    events.perf_submit(ctx, data, sizeof(struct data_t));

    return 0;
}
"""

pid = ""
ppid = ""
uid = ""
command = ""

def clone_ebpf_thread():
    b = BPF(text=clone_ebpf)
    clone_fn_name = b.get_syscall_fnname("clone")
    b.attach_kprobe(event=clone_fn_name, fn_name="clone_ebpf")
    b["events"].open_perf_buffer(collect_events)
    while 1:
      try:
          b.perf_buffer_poll()
      except KeyboardInterrupt:
          exit()

def collect_events():
    event = b["events"].event(data)
    pid = event.pid
    ppid = event.ppid
    uid = event.uid
    command = event.comm

class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        print("received a get message", sys.stdout)
        self.send_response(200)
        self.end_headers()
        response = "pid: " + pid + " ppid: " + ppid + " uid: " + uid + " command: " + command
        self.wfile.write(response)

x = threading.Thread(target=clone_ebpf_thread)
x.start()
httpd = HTTPServer(('0.0.0.0', 8000), SimpleHTTPRequestHandler)
httpd.serve_forever()


from django.urls import path
from . import views

urlpatterns = [
    path('products/', views.product_list, name='product_list'),
    path('product/<int:product_pk>/', views.product_detail, name='product_detail'),
    path('basket/', views.product_basket, name='product_basket'),
    path('product/<int:product_pk>/add_to_basket', views.add_to_basket, name='add_to_basket'),
    path('product_delete/<int:product_pk>', views.remove_from_basket, name='remove_from_basket'),
    path('product_remove/<int:product_pk>', views.remove_all_products, name='remove_all_products'),
    path('delete_all/', views.delete_all, name='delete_all'),
    path('order/', views.order, name='order'),
    path('orders_history/', views.orders_history, name='orders_history'),
    path('add_to_favorites/<int:product_pk>', views.add_to_favorites, name='add_to_favorites')

]


# usually big companies in Hollywood watermark their script with usually an actor's name.
# So if that actor leaks the script well they'll know that that person has their name on the script and
# they're the ones that leaked it.

# so we are going to use this watermark throgh out all the pdf.
import PyPDF2

template = PyPDF2.PdfFileReader(open('super.pdf', 'rb'))
watermark = PyPDF2.PdfFileReader(open('wtr.pdf', 'rb'))
output = PyPDF2.PdfFileWriter()

for i in range(template.getNumPages()):
    page = template.getPage(i)
    page.mergePage(watermark.getPage(0))
    output.addPage(page)
    with open('watermarked.pdf', 'wb') as file:
        output.write(file)
print('watermark merged with pdf')

12.scripting python (projects)/pdf with python/03.pdf watermark.py


import numpy as np
import logging
from pathlib import Path

#Output folder setup
output = Path('./output/log').expanduser()
output.mkdir(parents=True, exist_ok=True)

en_log = logging.getLogger(__name__)
en_log.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s:%(name)s:%(message)s')
file_handler = logging.FileHandler('./output/log/energy.log',mode='w')

file_handler.setFormatter(formatter)
en_log.addHandler(file_handler)

def energy_init(temp,m,n,dX):
    for i in range(1,m-1):
        for j in range (1,n-1):
            temp[j, i] = 1 - j*dX
    return(temp)

def energy_bound(temp,m,n,iter):
    en_log.debug("Iteration No.: {}--------C".format(iter))
    en_log.debug("Temperature at bouondary calculation entry: \n{}".format(temp))
    for j in range(n):
        temp[0,j]      = 1
        temp[m-1,j]    = 0
    for i in range(m):
        temp[i,0]      = (4/3)*( temp[i,1]- (temp[i, 2]/4) )
        temp[i,n-1]    = (4/3)*( temp[i,n-2] - (temp[i, n-3]/4) )
    en_log.debug("Temperature at boundary calculation exit: \n{}".format(temp))
    en_log.debug("____________________________________________________")
    return(temp)

def energy_bound_ur(temp_o,temp_calc,m,n,r,iter):
    temp_n      = np.copy(temp_calc)
    en_log.debug("Iteration No.: {}--------D".format(iter))
    en_log.debug("Temperature at boundary UR calculation entry: \n{}".format(temp_calc))
    for i in range(m):
        temp_n[i,0]      = temp_o[i,0] + r*(temp_calc[i,0] - temp_o[i,0])
        temp_n[i,n-1]    = temp_o[i,n-1] + r*(temp_calc[i,n-1] - temp_o[i,n-1])
    for j in range(n):
        temp_n[0,j]      = temp_o[0,j] + r*(temp_calc[0,j] - temp_o[0,j])
        temp_n[m-1,j]    = temp_o[m-1,j] + r*(temp_calc[m-1,j] - temp_o[m-1,j])
    en_log.debug("Temperature at boundary UR calculation exit: \n{}".format(temp_n))
    en_log.debug("____________________________________________________")
    return(temp_n)

def energy(temp_o,strm,m,n,dX,dY,div,iter):
    en_log.debug("Iteration No.: {}--------A".format(iter))
    en_log.debug("Temperature at calculation entry: \n{}".format(temp_o))
    temp_calc   = np.copy(temp_o)
    mul         = (-1/(4*dX*dY))
    for i in range(1,m-1):
        for j in range (1,n-1):
            strm_i_diff = (strm[i+1,j]-strm[i-1,j])
            strm_j_diff = (strm[i,j+1]-strm[i,j-1])
            temp_i_diff = (temp_o[i+1,j]-temp_o[i-1,j])
            temp_j_diff = (temp_o[i,j+1]-temp_o[i,j-1])
            temp_i_sum  = (temp_o[i+1,j]+temp_o[i-1,j])/(dX*dX)
            temp_j_sum  = (temp_o[i,j+1]+temp_o[i,j-1])/(dY*dY)
            temp_calc[i,j] =  ( (mul*((strm_j_diff*temp_i_diff)-(strm_i_diff*temp_j_diff))) + temp_i_sum + temp_j_sum )/div

    en_log.debug("Temperature at calculation exit: \n{}".format(temp_calc))
    en_log.debug("____________________________________________________")
    return temp_calc

def energy_ur(temp_o,temp_calc,m,n,r,iter):
    en_log.debug("Iteration No.: {}--------B".format(iter))
    en_log.debug("Temperature at UR calculation entry: \n{}".format(temp_calc))
    temp_n      = np.copy(temp_calc)
    for i in range(1,m-1):
        for j in range (1,n-1):
            temp_n[i,j] = temp_o[i,j] + r*(temp_calc[i,j] - temp_o[i,j])
    en_log.debug("Temperature at UR calculation exit: \n{}".format(temp_n))
    en_log.debug("____________________________________________________")
    return(temp_n)

def converge(temp_o,strm,m,n,dX,dY,div,iter):
    temp_residue = np.zeros((m,n))
    mul         = (-1/(4*dX*dY))
    if iter<5:
        temp_max = 1
    else:
        temp_max    = int(np.amax(np.abs(temp_o)))
    en_log.debug("Temperature max value: \n{}".format(temp_max))
    for i in range(1,m-1):
        for j in range (1,n-1):
            strm_i_diff = (strm[i+1,j]-strm[i-1,j])
            strm_j_diff = (strm[i,j+1]-strm[i,j-1])
            temp_i_diff = (temp_o[i+1,j]-temp_o[i-1,j])
            temp_j_diff = (temp_o[i,j+1]-temp_o[i,j-1])
            temp_i_sum  = (temp_o[i+1,j]+temp_o[i-1,j])/(dX*dX)
            temp_j_sum  = (temp_o[i,j+1]+temp_o[i,j-1])/(dY*dY)
            temp_residue[i,j] = (( ( (mul*((strm_j_diff*temp_i_diff)-(strm_i_diff*temp_j_diff))) + temp_i_sum + temp_j_sum )/div )- temp_o[i,j])/temp_max
    en_log.debug("Temperature residue domain: \n{}".format(temp_residue))
    return np.std(temp_residue)


from application import app, db
from flask import redirect, render_template, request, url_for, flash
from application.child.models import Child
from application.quotes.models import Quote
from application.likes.models import Likes
from application.child.forms import ChildForm, MakeSureForm
from datetime import datetime, date
from flask_login import login_required, current_user
from datetime import date
from wtforms import ValidationError

@app.route("/child", methods=["GET"])
def child_index():
    return render_template("child/listchild.html", quotes = Child.query.all())

# Käyttäjän omien lapsien haku kyselyllä
@app.route("/child/userlist/", methods=["GET"])
@login_required
def child_userchildren():
    return render_template("child/ownchildren.html", find_users_children = Child.find_users_children())

@app.route("/child/newchild/")
@login_required
def child_form():
    return render_template("child/newchild.html", form = ChildForm())

@app.route("/child/", methods=["GET","POST"])
@login_required
def child_create():
    form = ChildForm(request.form)

    if not form.validate():
        return render_template("child/newchild.html", form = form)

    # Tarkastetaan, ettei käyttäjällä ole samannimistä lasta
    alreadyExistsChild = Child.query.filter_by(name=form.name.data, account_id=current_user.id).first()
    if alreadyExistsChild:
        form.name.errors.append("Sinulla on jo tämänniminen lapsi olemassa.")
        return render_template("child/newchild.html", form = form)

    c = Child(name = form.name.data, birthday = form.birthday.data)

    c.account_id = current_user.id

    db.session.add(c)
    db.session().commit()

    return  redirect(url_for("child_userchildren"))

@app.route("/child/modifychild/<child_id>/", methods=["GET", "POST"])
@login_required
def child_modifychild(child_id):
    # Asetetaan lomakkeelle valmiiksi olemassaolevat tiedot
    form=ChildForm()
    child = Child.query.get(child_id)
    form.name.data = child.name
    form.birthday.data = child.birthday
    return render_template("child/modifyChild.html", form = form, child_id = child_id)

@app.route("/child/<child_id>/", methods=["POST"])
@login_required
def child_update(child_id):

    child = Child.query.get(child_id)
    form = ChildForm(request.form)

    if not form.validate():
        return render_template("child/modifyChild.html", form = form, child_id=child_id)

    # Tarkastetaan, ettei käyttäjällä ole samannimistä lasta
    alreadyExistsChild = Child.query.filter_by(name=form.name.data, account_id=current_user.id).first()
    if alreadyExistsChild and child != alreadyExistsChild:
        form.name.errors.append("Sinulla on jo tämänniminen lapsi olemassa.")
        return render_template("child/modifyChild.html", form = form, child_id=child_id)

    child.name = form.name.data
    child.birthday =form.birthday.data

    db.session().commit()

    return redirect(url_for("child_userchildren"))

@app.route("/child/<child_id>/delete", methods=["POST","GET"])
@login_required
def child_delete(child_id):

    # Tarkastuslomake, jottei lasta tule poistettua liian helpolla
    form = MakeSureForm()

    return render_template("child/deletechild.html", form = form, child_id=child_id)

@app.route("/child/<child_id>/del", methods=["POST"])
@login_required
def child_deleteConfirm(child_id):

    form = MakeSureForm(request.form)
    ok = form.name.data

    # jos tarkastuslomakkeeseen on syötetty oikea tieto, jolla halutaan varmistaa poisto
    if ok == "x":

        c = Child.query.get(child_id)

        # Etsitään lapsen lapsen sanonnat ja poistataan sanonnat sekä sanonnan tykkäykset
        q = Quote.query.filter(Quote.child_id == child_id)
        for quote in q:

            likes = Likes.query.filter(Likes.quote_id==quote.id)
            for like in likes:
                db.session.delete(like)
                db.session().commit()

            db.session.delete(quote)
            db.session().commit()

        # Poistetaan lapsi
        db.session().delete(c)
        db.session().commit()
        flash("Lapsi poistettu onnistuneesti", category="success")
        return redirect(url_for("child_userchildren"))

    flash("Lasta ei poistettu", category="warning")
    return redirect(url_for("child_userchildren"))

# Yhden lapsen tietojen näyttäminen
@app.route("/child/showchild/<child_id>")
@login_required
def child_showOne(child_id):
    child = Child.query.get(child_id)
    return render_template("child/showchild.html", child_id=child_id, child=child)