text:
import collections
import copy
import typing as tp

from satella.coding.typing import T

ITER_KEYS = 0
ITER_VALUES = 1
ITER_ITEMS = 2

class DictDeleter:
    """
    Having problems deleting entries from your dict while iterating on them? No problem. Just swap
    the following:

    >>> keys_to_delete = []
    >>> for key, value in my_dict.items():
    >>>     if value.should_delete():
    >>>         keys_to_delete.append(key)
    >>> for key in keys_to_delete:
    >>>     del my_dict[key]

    With the following:

    >>> with DictDeleter(my_list) as ld:
    >>>     for key, value in ld.items():
    >>>         if value.should_delete():
    >>>             ld.delete()

    Note that a single DictDeleter running from a single context must be iterated on by only a
    single Thread as it keeps the state of iterator in itself, to prevent allocating new objects
    and slowing things down too much.

    This allocates only a single object per a call to delete().
    """
    __slots__ = ('dict_to_process', 'current_iterator', 'keys_to_delete', 'iter_mode',
                 'current_key')

    def __init__(self, dict_to_process: collections.abc.MutableMapping):
        self.dict_to_process = dict_to_process
        self.iter_mode = ITER_KEYS

    def __enter__(self):
        self.keys_to_delete = set()
        return self

    def __iter__(self):
        self.current_iterator = iter(self.dict_to_process.items())
        return self

    def __next__(self):
        key, value = next(self.current_iterator)  # raises StopIteration
        self.current_key = key
        if self.iter_mode == ITER_ITEMS:
            return key, value
        elif self.iter_mode == ITER_VALUES:
            return value
        elif self.iter_mode == ITER_KEYS:
            return key

    def items(self) -> 'DictDeleter':
        self.iter_mode = ITER_ITEMS
        return self

    def keys(self) -> 'DictDeleter':
        self.iter_mode = ITER_KEYS
        return self

    def values(self) -> 'DictDeleter':
        self.iter_mode = ITER_VALUES
        return self

    def delete(self) -> None:
        self.keys_to_delete.add(self.current_key)

    def __exit__(self, exc_type, exc_val, exc_tb):
        for key in self.keys_to_delete:
            del self.dict_to_process[key]
        return False

DIR_FORWARD = 0
DIR_BACKWARD = 1

class ListDeleter(tp.Generic[T]):
    """
    Having problems deleting entries from your list while iterating on them? No problem. Just swap
    the following:

    >>> entries_to_delete = []
    >>> for entry in my_list:
    >>>     if entry.should_delete():
    >>>         entries_to_delete.append(entry)
    >>> for entry in entries_to_delete:
    >>>     my_list.remove(entry)

    With the following:

    >>> with ListDeleter(my_list) as ld:
    >>>     for entry in ld:
    >>>         if entry.should_delete():
    >>>             ld.delete()

    You can also use the alternative syntax of:
    >>> ld = ListDeleter(my_list)
    >>> while True:
    >>>     try:
    >>>         v = ld.next()
    >>>     except StopIteration:
    >>>         break
    >>>     if condition(v):
    >>>         ld.delete()
    >>> ld.remove_items()

    Note that a single ListDeleter running from a single context must be iterated on by only a
    single Thread as it keeps the state of iterator in itself, to prevent allocating new objects
    and slowing things down too much.

    Note that calling reversed() on this will reset the pointer to the end of the list
    or the beginning of the list, respectively.

    This allocates only a single object per a call to delete().

    Calling the list deleter during iteration will yield the element.

    You can pass any type of object here, as long as it supports pop(position) and __getitem__
    """
    __slots__ = 'list_to_process', 'current_index', 'indices_to_delete', 'direction', 'removed'

    def __init__(self, list_to_process: tp.MutableSequence[T], direction: int = DIR_FORWARD):
        self.list_to_process = list_to_process
        # pointer to currently processed element
        self.direction = direction
        self.current_index = -1 if direction == DIR_FORWARD else len(self.list_to_process)
        self.indices_to_delete = set()  # type: tp.Set[int]
        self.removed = False

    def __enter__(self) -> 'ListDeleter':
        return self

    @property
    def value(self) -> T:
        return self()

    def __call__(self) -> T:
        """
        Return current element
        """
        return self.list_to_process[self.current_index]

    def __iter__(self) -> 'ListDeleter':
        self.current_index = -1 if self.direction == DIR_FORWARD else len(self.list_to_process)
        self.indices_to_delete = set()
        return self

    def __next__(self) -> T:
        if self.direction == DIR_BACKWARD and self.current_index == -1:
            raise StopIteration('First element reached')
        if self.direction == DIR_FORWARD and self.current_index == len(self.list_to_process):
            raise StopIteration('Last element reached')
        self.current_index += +1 if self.direction == DIR_FORWARD else -1
        try:
            return self.list_to_process[self.current_index]
        except IndexError:
            raise StopIteration()

    def next(self) -> T:
        """
        :return: the next element
        :raises StopIteration: no more entries
        """
        return next(self)

    def prev(self) -> T:
        """
        Move to previous element, as per ordering.

        :return: the previous element
        :raises StopIteration: list is already at the first element!
        """
        if self.direction == DIR_FORWARD and self.current_index == -1:
            raise StopIteration('Cannot go previous on a first element!')
        if self.direction == DIR_BACKWARD and self.current_index == len(self.list_to_process):
            raise StopIteration('Cannot go previous on a last element!')
        self.current_index += -1 if self.direction == DIR_FORWARD else +1
        try:
            return self.list_to_process[self.current_index]
        except IndexError:
            raise StopIteration()

    def delete(self) -> None:
        self.indices_to_delete.add(self.current_index)

    def __reversed__(self) -> 'ListDeleter':
        """
        This will return a new ListDeleter. Take care that it will break context managers
        such as this:

        >>> with ListDeleter(a):
        >>>     for v in reversed(a):
        >>>         ...

        It is much preferred to do the following in that case:

        >>> with reversed(ListDeleter(a)) as ld:
        >>>     for v in a:
        >>>         if condition(v):
        >>>             ld.delete()
        """
        if self.direction == DIR_BACKWARD:
            return ListDeleter(self.list_to_process)
        else:
            return ListDeleter(self.list_to_process, DIR_BACKWARD)

    def remove_items(self) -> None:
        """
        After all of the items have been marked for deletion, delete them
        """
        if not self.removed:
            index_delta = 0
            for index, element in enumerate(self.list_to_process[:]):
                if index in self.indices_to_delete:
                    self.list_to_process.pop(index - index_delta)
                    index_delta += 1
            self.removed = True

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.remove_items()
        return False

    def __copy__(self) -> 'ListDeleter':
        ld = ListDeleter(self.list_to_process)
        ld.direction = self.direction
        ld.current_index = self.current_index
        ld.indices_to_delete = copy.copy(self.indices_to_delete)
        return ld


text:
angular.module('project').component('project', {
    templateUrl: 'project/project.template.html',

    controller: ['$route', '$routeParams' ,'$firebaseObject', '$firebaseArray', function projectController($route, $routeParams, $firebaseObject, $firebaseArray) {
        var self = this;
        self.project = $routeParams.name;
        self.formattedProjectName =self.project.replace(" ", "%20");
        self.projectRef = firebase.database().ref().child("project-data/"+self.project);
        self.commentsEnabledRef = firebase.database().ref().child("comments-enabled");
        self.commentsEnabled = $firebaseObject(self.commentsEnabledRef);
        self.descriptionRef = (self.projectRef.child("description"));
        self.description = $firebaseObject(self.descriptionRef);
        self.imageRef = (self.projectRef.child("image-url"));
        self.image = $firebaseObject(self.imageRef);
        self.likesRef = (self.projectRef.child("likes"));
        self.likes = $firebaseObject(self.likesRef);
        self.likes.$loaded()
            .then(function(data) {
                if (data.$value == null) {
                    self.likes.$value = 0;
                    self.likes.$save();
                }
            })
            .catch(function(error) {
                console.error("Error:", error);
            });

        self.pushLike = function () {
            self.likes.$value = self.likes.$value + 1;
            self.likes.$save();
        };

        self.comment = "";
        self.commenter = "";

        self.commentsRef = (self.projectRef.child("comments"));

        self.pushComment = function () {
            var key = Date.now();
            if (self.comment && self.comment != "" && self.commenter && self.commenter != "") {
                var commentObj = $firebaseObject(self.commentsRef.child(key));
                commentObj.comment = self.comment;
                commentObj.commenter = self.commenter;
                commentObj.$save();
                self.comment = "";
                self.commenter = "";
            }
        };

        self.comments = $firebaseArray(self.commentsRef);
    }]
});

redpajama_set_name: RedPajamaGithub


text:
created_at: 20 July 2016
title: "Voxxed Days Belgrade statistics"
excerpt: "Voxxed Days Belgrade statistics"
author: Voxxed Team
image: 10/vdb-2015.jpg

layout: '/blog_single.html'
---

Tech vs Business

 Tech (75.4%)

 Business (24.6%)

Talk vs Lightning

 Talk (81%)

 Lightning (19%)

Male vs Female

 Male (91.2%)

 Female (8.8%)

Foreign vs Local

 Foreign (72.5%)

 Local (27.5%)


text:
setfenv(1, require'winapi')
require'winapi.basewindowclass'
require'winapi.comctl'

Control = subclass({
	__defaults = {
		anchors = {
			left = true,
			top = true,
			right = false,
			bottom = false,
		},
	},
}, BaseWindow)

function Control:__before_create(info, args)
	Control.__index.__before_create(self, info, args)
	self.anchors = info.anchors
	args.style = bit.bor(args.style, info.parent and WS_CHILD or WS_POPUP, WS_CLIPSIBLINGS, WS_CLIPCHILDREN)
	args.parent = info.parent and info.parent.hwnd
end

function Control:__init(info)
	Control.__index.__init(self, info)
	--subclass the control to intercept the messages sent to it.
	self.__prev_proc = SetWindowLong(self.hwnd, GWL_WNDPROC, MessageRouter.proc)
end

function Control:get_info() --recreate the info table to serve for duplication of the window
	return update(Control.__index.get_info(self), {
		parent = self.parent,
	})
end

function Control:__default_proc(WM, wParam, lParam)
	return CallWindowProc(self.__prev_proc, self.hwnd, WM, wParam, lParam)
end

function Control:get_parent()
	return Windows:find(GetParent(self.hwnd))
end

function Control:set_parent(parent)
	local old_parent = self.parent
	if parent and not old_parent then --popup windows can become child windows
		SetWindowStyle(self.hwnd, setbits(GetWindowStyle(self.hwnd),
														bit.bor(WS_POPUP, WS_CHILD), WS_CHILD))
	end
	SetParent(self.hwnd, parent and parent.hwnd)
	pin(self, parent)
	if not parent and old_parent then --child windows can become popup windows (diff. from overlapped)
		SetWindowStyle(self.hwnd, setbits(GetWindowStyle(self.hwnd),
														bit.bor(WS_POPUP, WS_CHILD), WS_POPUP))
	end
	if (parent and not old_parent) or (not parent and old_parent) then
		SetWindowPos(self.hwnd, nil, 0, 0, 0, 0, SWP_FRAMECHANGED_ONLY)
	end
	--TODO: find out which of the windows is enough to send this message to.
	ChangeUIState(self.hwnd, UIS_INITIALIZE, bit.bor(UISF_HIDEACCEL, UISF_HIDEFOCUS))
	if old_parent then ChangeUIState(old_parent.hwnd, UIS_INITIALIZE, bit.bor(UISF_HIDEACCEL, UISF_HIDEFOCUS)) end
	if parent then ChangeUIState(parent.hwnd, UIS_INITIALIZE, bit.bor(UISF_HIDEACCEL, UISF_HIDEFOCUS)) end
end

--delphi-style anchors: resize when parent is resized based on anchors spec

local function anchor_dim(self, left, right, enlargement, x, w, adjustment)
	local xofs, wofs
	if right then
		if left then --left and right: resize
			wofs = enlargement + (adjustment or 0)
		else --only right: move
			xofs = enlargement
		end
	end --only left: do nothing; no left/no right: undefined, we choose to do nothing.
	x = xofs and x + xofs
	w = wofs and w + wofs
	return x,w
end

function Control:__parent_resizing(wp)
	Control.__index.__parent_resizing(self, wp)
	local pr, r = self.parent.screen_rect, self.rect
	local x, w = anchor_dim(self, self.anchors.left, self.anchors.right,
									pr.x1 + wp.w - pr.x2, r.x1, r.w, self.__anchor_w)
	local y, h = anchor_dim(self, self.anchors.top,  self.anchors.bottom,
									pr.y1 + wp.h - pr.y2, r.y1, r.h, self.__anchor_h)
	self:move(x, y, w, h)
	--real size might end up different than wanted size in which case we store the difference
	local rw, rh = self.w, self.h
	if w and rw ~= w then self.__anchor_w = w - rw else self.__anchor_w = nil end
	if h and rh ~= h then self.__anchor_h = h - rh else self.__anchor_h = nil end
end


text:
package com.cibuddy.project.configuration.impl;

import com.cibuddy.core.build.configuration.IProjectSetup;
import com.cibuddy.project.configuration.impl.installer.ExtremeFeedbackDeviceSetupListener;
import java.io.File;
import org.apache.felix.fileinstall.ArtifactInstaller;
import org.junit.Assert;
import org.junit.Test;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;

/**
 * Basic test for the XFDs configuration load.
 *
 * General tests to verify that loading the xfd configurations actually works.
 * This includes parsing, as well as service exposure in the OSGi service registry.
 *
 * @author Mirko Jahn <mirkojahn@gmail.com>
 * @version 1.0
 * @since 1.0
 */
public class ExtremeFeedbackDeviceSetupListenerTest extends AbstractOSGIEnvBaseTest {
    private static String filterString = "(&(" + Constants.OBJECTCLASS + "=" + ArtifactInstaller.class.getName() + ")(installerType="+ExtremeFeedbackDeviceSetupListener.class.getName()+"))";
    private File projectsConfigFile = new File("src/test/resources/project-setup.xml");

    /**
     * First test to see that everything is setup correctly.
     *
     * The goal of this test is to verify that the exposure of a well defined
     * XML configuration actually works and that the exposed configuration object
     * could be retrieved from the OSGi registry.
     *
     * @throws Exception indicating problems.
     * @since 1.0
     */
    @Test
    public void test() throws Exception {
        Assert.assertNotNull("BundleContext is not set.", Activator.getBundleContext());

        ServiceReference[] srs = getRegistry().getServiceReferences(ArtifactInstaller.class.getName(), filterString);
        Assert.assertNotNull(srs);
        // there should be just one in our test (Travis fails here, no idea why, but we're good anyway)
//        Assert.assertEquals("The expectation is to find only the service we just registered", 1, srs.length);
        // get the service
        ArtifactInstaller ai = (ArtifactInstaller)getRegistry().getService(srs[0]);
        Assert.assertTrue("File does not exist! Check path and setup!", projectsConfigFile.exists());

        if(ai instanceof ExtremeFeedbackDeviceSetupListener){
            // check if the ArtifactInstaller could handle a configuration file
            boolean canHandle = ai.canHandle(projectsConfigFile);
            Assert.assertTrue("The file failed the verification and appears to be not valid.", canHandle);

            // now check exposure
            ai.install(projectsConfigFile);
            ServiceReference sr2 = getRegistry().getServiceReference(IProjectSetup.class.getName());
            // after registration, there should be at least one...
            if(sr2 == null){
                Assert.fail("No IProjectSetup service found when expected! Check registration...");
            }
            // now see, if we could cast it to the correct class
            IProjectSetup setup = (IProjectSetup)getRegistry().getService(sr2);
            Assert.assertNotNull("Setup object of IProjectSetup service couldn't be retrieved", setup);

        } else {
            Assert.fail("Expected only one service exposed coming from this bundle.");
        }
    }
}


text:
"""oem-client setup script (powered by pbr)."""

from __future__ import absolute_import, division, print_function

from setuptools import setup
import sys

NEEDS_PYTEST = set(['pytest', 'test', 'ptr']).intersection(sys.argv)
PYTEST_RUNNER = ['pytest-runner>=2.0.0'] if NEEDS_PYTEST else []

setup(
    setup_requires=PYTEST_RUNNER + [
        'pbr>=1.9',
        'setuptools>=17.1'
    ],
    pbr=True,
)


text:
import React from 'react';
import StyleSheet from 'react-inline';
import cx from 'classnames';

const { oneOf, bool } = React.PropTypes;

class Button extends React.Component {

	render() {

		const { size, busy, block, className } = this.props;
		const classes = cx( styles.default ,styles[ size ], block && styles.block, className );

		return <button {...this.props} className={classes} disabled={busy}>hih33</button>;
	}
}

Button.propTypes = {
	size: oneOf(['large', 'small']),
	block: bool,
	busy: bool
};

export default Button;

const styles = StyleSheet.create({

	default: {
		padding: '6px 12px',
		fontSize: 14,
		lineHeight: 1.5,
		cursor: 'pointer',
		border: '1px solid #2e6da4',
		borderRadius: 4,
		//color: '#fff',
		color: '#fff',
		width: 300,
		//backgroundColor: '#337ab7',
		//backgroundColor: 'ornage',

'@media only screen and (max-width: 640px)': {
			display: 'block',
			width: '100%'
		},

		':hover': {

			color: 'gray',
			backgroundColor: '#286090',
			borderColor: '#122b40'
		},

		':focus': {
			color: '#fff',
			backgroundColor: '#286090',
			borderColor: '#122b40'
		},

		'[disabled]': {
			backgroundColor: '#337ab7',
			backgroundColor: 'red',
			borderColor: '#2e6da4',
			cursor: 'not-allowed',
			boxShadow: 'none',
			opacity: .65,
			pointerEvents: 'none'
		}
	},

	large: {
		padding: '10px 16px',
		fontSize: 18,
		lineHeight: 1.33,
		borderRadius: 6
	},

	small: {
		padding: '5px 10px',
		fontSize: 12,
		lineHeight: 1.5,
		borderRadius: 3
	},

	block: {
		display: 'block',
		width: '100%'
	}

});


text:
function output = F_sparse_affine_transform(input, transform, bias, singlePrecision)

[D,M,N] = size(input);
if N>1
    input = reshape(input, D,M*N);
end

visible_nonzero_idx = find(sum(abs(input),2)>0);
visible_nonzero = full(input(visible_nonzero_idx,:));
if singlePrecision==1
    visible_nonzero = single(visible_nonzero);
end
output = bsxfun(@plus, transform(:,visible_nonzero_idx) * visible_nonzero, bias);    % only compute the nonzero elements

if N>1
    output = reshape(output, size(output,1), M,N);
end

end


text:
<?php

/* push_out_data_source_custom_data - pushes out the "custom data" associated with a data
	template to all of its children. this includes all fields inhereted from the host
	and the data template
   @arg $data_template_id - the id of the data template to push out values for */
function push_out_data_source_custom_data($data_template_id) {

	/* valid data template id? */
	if (empty($data_template_id)) { return 0; }

	/* get data_input_id from template */
	$data_template_data = db_fetch_row("SELECT " .
			"id, " .
			"data_input_id " .
			"FROM data_template_data " .
			"WHERE data_template_id=$data_template_id " .
			"AND local_data_id=0");

	/* must be a data template */
	if (empty($data_template_data["data_input_id"])) { return 0; }

	/* get a list of data sources using this template */
	$data_sources = db_fetch_assoc("SELECT " .
			"data_template_data.id " .
			"FROM data_template_data " .
			"WHERE data_template_id=$data_template_id " .
			"AND local_data_id>0");

	/* pull out all custom templated 'input' values from the template itself
	 * templated items are selected by querying t_value = '' OR t_value = NULL */
	$template_input_fields = array_rekey(db_fetch_assoc("SELECT " .
			"data_input_fields.id, " .
			"data_input_fields.type_code, " .
			"data_input_data.value, " .
			"data_input_data.t_value " .
			"FROM data_input_fields " .
			"INNER JOIN (data_template_data " .
			"INNER JOIN data_input_data " .
			"ON data_template_data.id = data_input_data.data_template_data_id) " .
			"ON data_input_fields.id = data_input_data.data_input_field_id " .
			"WHERE (data_input_fields.input_output='in') " .
			"AND (data_input_data.t_value='' OR data_input_data.t_value IS NULL) " .
			"AND (data_input_data.data_template_data_id=" . $data_template_data["id"] . ") " .
			"AND (data_template_data.local_data_template_data_id=0)"), "id", array("type_code", "value", "t_value"));

	/* which data_input_fields are templated? */
	$dif_ct = 0;
	$dif_in_str = "";
	if (sizeof($template_input_fields)) {
		$dif_in_str .= "AND data_input_fields.id IN (";
		foreach ($template_input_fields as $key => $value) {
			$dif_in_str .= ($dif_ct == 0 ? "":",") . $key;
			$dif_ct++;
		}
		$dif_in_str .= ") ";
	}

	/* pull out all templated 'input' values from all related data sources
	 * unfortunately, you can't simply provide the same test as above
	 * all input fields not related to a template ALWAYS are marked with t_value = NULL
	 * so we will verify against the list of data_input_field id's taken from above */
	$input_fields = db_fetch_assoc("SELECT " .
			"data_template_data.id AS data_template_data_id, " .
			"data_input_fields.id, " .
			"data_input_fields.type_code, " .
			"data_input_data.value, " .
			"data_input_data.t_value " .
			"FROM data_input_fields " .
			"INNER JOIN (data_template_data " .
			"INNER JOIN data_input_data " .
			"ON data_template_data.id = data_input_data.data_template_data_id) " .
			"ON data_input_fields.id = data_input_data.data_input_field_id " .
			"WHERE (data_input_fields.input_output='in') " .
			$dif_in_str .
			"AND (data_input_data.t_value='' OR data_input_data.t_value IS NULL) " .
			"AND (data_template_data.local_data_template_data_id=" . $data_template_data["id"] . ")");

	$data_rra = db_fetch_assoc("SELECT rra_id
		FROM data_template_data_rra
		WHERE data_template_data_id=" . $data_template_data["id"]);

	/* perform bulk update of rra associations */
	$rra_ct = 0;
	$rra_in_str = "";
	if (sizeof($data_rra)) {
		foreach($data_rra as $rra) {
			$rra_in_str .= ($rra_ct == 0 ? "(":",") . $rra["rra_id"];
			$rra_ct++;
		}
		$rra_in_str .= ")";
	}

	$ds_cnt    = 0;
	$did_cnt   = 0;
	$rra_cnt   = 0;
	$ds_in_str = "";
	$did_vals  = "";
	$rra_vals  = "";
	if (sizeof($data_sources)) {
		foreach ($data_sources as $data_source) {
			reset($input_fields);

			if (sizeof($input_fields)) {
				foreach ($input_fields as $input_field) {
					if ($data_source["id"] == $input_field["data_template_data_id"] &&
						isset($template_input_fields[$input_field["id"]])) {
						/* do not push out "host fields" */
						if (!preg_match('/^' . VALID_HOST_FIELDS . '$/i', $input_field["type_code"])) {
							/* this is not a "host field", so we should either push out the value if it is templated */
							$did_vals .= ($did_cnt == 0 ? "":",") . "(" . $input_field["id"] . ", " . $data_source["id"] . ", '" . addslashes($template_input_fields[$input_field["id"]]["value"]) . "')";
							$did_cnt++;
						}elseif ($template_input_fields[$input_field["id"]]["value"] != $input_field["value"]) { # templated input field deviates from currenmt data source, so update required
							$did_vals .= ($did_cnt == 0 ? "":",") . "(" . $input_field["id"] . ", " . $data_source["id"] . ", '" . addslashes($template_input_fields[$input_field["id"]]["value"]) . "')";
							$did_cnt++;
						}
					}
				}
			}

			/* create large inserts to reduce turns */
			$ds_in_str .= ($ds_cnt == 0 ? "(":",") . $data_source["id"];
			if (sizeof($data_rra)) {
				foreach ($data_rra as $rra) {
					$rra_vals .= ($rra_cnt == 0 ? "":",") . "(" . $data_source["id"] . "," . $rra["rra_id"] . ")";
					$rra_cnt++;
				}
			}
			$ds_cnt++;

			/* per 1000 data source, update rows */
			if ($ds_cnt % 1000 == 0) {
				$ds_in_str .= ")";
				push_out_data_source_templates($did_vals, $ds_in_str, $rra_vals, $rra_in_str);
				$ds_cnt    = 0;
				$did_cnt   = 0;
				$rra_cnt   = 0;
				$ds_in_str = "";
				$did_vals  = "";
				$rra_vals  = "";
			}
		}
	}

	if ($ds_cnt > 0) {
		$ds_in_str .= ")";
		push_out_data_source_templates($did_vals, $ds_in_str, $rra_vals, $rra_in_str);
	}
}

/* push out changed data template fields to related data sources
 * @parm string $did_vals	- data input data fields
 * @parm string $ds_in_str	- all data sources, formatted as SQL "IN" clause
 * @parm string $rra_vals	- new set of rra associations
 * @parm string $rra_in_str	- all rra associations, formatted as SQL "IN" clause
 */
function push_out_data_source_templates($did_vals, $ds_in_str, $rra_vals, $rra_in_str) {

	/* update all templated input fields */
	if ($did_vals != "") {
		db_execute("INSERT INTO data_input_data
			(data_input_field_id,data_template_data_id,value)
			VALUES $did_vals
			ON DUPLICATE KEY UPDATE value=VALUES(value)");
	}

	/* remove old RRA associations */
	if ($ds_in_str != "" && $rra_in_str != "") {
		db_execute("DELETE
			FROM data_template_data_rra
			WHERE data_template_data_id IN $ds_in_str
			AND rra_id NOT IN $rra_in_str");
	}

	/* ... and add new ones */
	if ($rra_vals != "") {
			db_execute("INSERT INTO data_template_data_rra
				(data_template_data_id,rra_id)
			VALUES $rra_vals
			ON DUPLICATE KEY UPDATE rra_id=VALUES(rra_id)");
	}
}

/* push_out_data_source_item - pushes out templated data template item fields to all matching
	children
   @arg $data_template_rrd_id - the id of the data template item to push out values for */
function push_out_data_source_item($data_template_rrd_id) {
	global $struct_data_source_item;

	/* get information about this data template */
	$data_template_rrd = db_fetch_row("select * from data_template_rrd where id=$data_template_rrd_id");

	/* must be a data template */
	if (empty($data_template_rrd["data_template_id"])) { return 0; }

	/* loop through each data source column name (from the above array) */
	reset($struct_data_source_item);
	while (list($field_name, $field_array) = each($struct_data_source_item)) {
		/* are we allowed to push out the column? */
		if (((empty($data_template_rrd{"t_" . $field_name})) || (preg_match("/FORCE:/", $field_name))) && ((isset($data_template_rrd{"t_" . $field_name})) && (isset($data_template_rrd[$field_name])))) {
			db_execute("update data_template_rrd set $field_name='" . addslashes($data_template_rrd[$field_name]) . "' where local_data_template_rrd_id=" . $data_template_rrd["id"]);
		}
	}
}

/* push_out_data_source - pushes out templated data template fields to all matching children
   @arg $data_template_data_id - the id of the data template to push out values for */
function push_out_data_source($data_template_data_id) {
	global $struct_data_source;

	/* get information about this data template */
	$data_template_data = db_fetch_row("select * from data_template_data where id=$data_template_data_id");

	/* must be a data template */
	if (empty($data_template_data["data_template_id"])) { return 0; }

	/* loop through each data source column name (from the above array) */
	reset($struct_data_source);
	while (list($field_name, $field_array) = each($struct_data_source)) {
		/* are we allowed to push out the column? */
		if (((empty($data_template_data{"t_" . $field_name})) || (preg_match("/FORCE:/", $field_name))) && ((isset($data_template_data{"t_" . $field_name})) && (isset($data_template_data[$field_name])))) {
			db_execute("update data_template_data set $field_name='" . addslashes($data_template_data[$field_name]) . "' where local_data_template_data_id=" . $data_template_data["id"]);

			/* update the title cache */
			if ($field_name == "name") {
				update_data_source_title_cache_from_template($data_template_data["data_template_id"]);
			}
		}
	}
}

/* change_data_template - changes the data template for a particular data source to
	$data_template_id
   @arg $local_data_id - the id of the data source to change the data template for
   @arg $data_template_id - id the of the data template to change to. specify '0' for no
	data template */
function change_data_template($local_data_id, $data_template_id) {
	global $struct_data_source, $struct_data_source_item;

	/* always update tables to new data template (or no data template) */
	db_execute("UPDATE data_local SET data_template_id=$data_template_id WHERE id=$local_data_id");

	/* get data about the template and the data source */
	$data = db_fetch_row("SELECT * FROM data_template_data WHERE local_data_id=$local_data_id");
	$template_data = (($data_template_id == "0") ? $data : db_fetch_row("select * from data_template_data where local_data_id=0 and data_template_id=$data_template_id"));

	/* determine if we are here for the first time, or coming back */
	if ((db_fetch_cell("select local_data_template_data_id from data_template_data where local_data_id=$local_data_id") == "0") ||
		(db_fetch_cell("select local_data_template_data_id from data_template_data where local_data_id=$local_data_id") == "")) {
		$new_save = true;
	}else{
		$new_save = false;
	}

	/* some basic field values that ALL data sources should have */
	$save["id"] = (isset($data["id"]) ? $data["id"] : 0);
	$save["local_data_template_data_id"] = $template_data["id"];
	$save["local_data_id"] = $local_data_id;
	$save["data_template_id"] = $data_template_id;

	/* loop through the "templated field names" to find to the rest... */
	reset($struct_data_source);
	while (list($field_name, $field_array) = each($struct_data_source)) {
		if ((isset($data[$field_name])) || (isset($template_data[$field_name]))) {
			if ((!empty($template_data{"t_" . $field_name})) && ($new_save == false)) {
				$save[$field_name] = $data[$field_name];
			}else{
				$save[$field_name] = $template_data[$field_name];
			}
		}
	}

	/* these fields should never be overwritten by the template */
	$save["data_source_path"] = $data["data_source_path"];

	$data_template_data_id = sql_save($save, "data_template_data");
	$data_rrds_list = db_fetch_assoc("SELECT * FROM data_template_rrd WHERE local_data_id=$local_data_id");
	$template_rrds_list = (($data_template_id == "0") ? $data_rrds_list : db_fetch_assoc("SELECT * FROM data_template_rrd WHERE local_data_id=0 AND data_template_id=$data_template_id"));

	if (sizeof($data_rrds_list) > 0) {
		/* this data source already has "child" items */
	}else{
		/* this data source does NOT have "child" items; loop through each item in the template
		and write it exactly to each item */
		if (sizeof($template_rrds_list) > 0) {
		foreach ($template_rrds_list as $template_rrd) {
			unset($save);
			reset($struct_data_source_item);

			$save["id"] = 0;
			$save["local_data_template_rrd_id"] = $template_rrd["id"];
			$save["local_data_id"] = $local_data_id;
			$save["data_template_id"] = $template_rrd["data_template_id"];

			while (list($field_name, $field_array) = each($struct_data_source_item)) {
				$save[$field_name] = $template_rrd[$field_name];
			}

			sql_save($save, "data_template_rrd");
		}
		}
	}

	/* make sure to copy down script data (data_input_data) as well */
	$data_input_data = db_fetch_assoc("SELECT data_input_field_id, t_value, value
		FROM data_input_data
		WHERE data_template_data_id=" . $template_data["id"]);

	/* this section is before most everthing else so we can determine if this is a new save, by checking
	the status of the 'local_data_template_data_id' column */
	if (sizeof($data_input_data) > 0) {
	foreach ($data_input_data as $item) {
		/* always propagate on a new save, only propagate templated fields thereafter */
		if (($new_save == true) || (empty($item["t_value"]))) {
			db_execute("REPLACE INTO data_input_data
				(data_input_field_id,data_template_data_id,t_value,value)
				VALUES (" . $item["data_input_field_id"] . ", $data_template_data_id, '" . $item["t_value"] . "', '" . $item["value"] . "')");
		}
	}
	}

	/* make sure to update the 'data_template_data_rra' table for each data source */
	$data_rra = db_fetch_assoc("SELECT rra_id
		FROM data_template_data_rra
		WHERE data_template_data_id=" . $template_data["id"]);

	db_execute("DELETE FROM data_template_data_rra
		WHERE data_template_data_id=$data_template_data_id");

	if (sizeof($data_rra) > 0) {
	foreach ($data_rra as $rra) {
		db_execute("INSERT INTO data_template_data_rra
			(data_template_data_id,rra_id)
			VALUES ($data_template_data_id," . $rra["rra_id"] . ")");
	}
	}
}

/* push_out_graph - pushes out templated graph template fields to all matching children
   @arg $graph_template_graph_id - the id of the graph template to push out values for */
function push_out_graph($graph_template_graph_id) {
	global $struct_graph;

	/* get information about this graph template */
	$graph_template_graph = db_fetch_row("select * from graph_templates_graph where id=$graph_template_graph_id");

	/* must be a graph template */
	if ($graph_template_graph["graph_template_id"] == 0) { return 0; }

	/* loop through each graph column name (from the above array) */
	reset($struct_graph);
	while (list($field_name, $field_array) = each($struct_graph)) {
		/* are we allowed to push out the column? */
		if (empty($graph_template_graph{"t_" . $field_name})) {
			db_execute("update graph_templates_graph set $field_name='" . addslashes($graph_template_graph[$field_name]) . "' where local_graph_template_graph_id=" . $graph_template_graph["id"]);

			/* update the title cache */
			if ($field_name == "title") {
				update_graph_title_cache_from_template($graph_template_graph["graph_template_id"]);
			}
		}
	}
}

/* push_out_graph_input - pushes out the value of a graph input to a single child item. this function
	differs from other push_out_* functions in that it does not push out the value of this element to
	all attached children. instead, it obtains the current value of the graph input based on other
	graph items and pushes out the "active" value
   @arg $graph_template_input_id - the id of the graph input to push out values for
   @arg $graph_template_item_id - the id the graph template item to push out
   @arg $session_members - when looking for the "active" value of the graph input, ignore these graph
	template items. typically you want to ignore all items that were just selected and have yet to be
	saved to the database. this is because these items most likely contain incorrect data */
function push_out_graph_input($graph_template_input_id, $graph_template_item_id, $session_members) {
	$graph_input = db_fetch_row("select graph_template_id,column_name from graph_template_input where id=$graph_template_input_id");
	$graph_input_items = db_fetch_assoc("select graph_template_item_id from graph_template_input_defs where graph_template_input_id=$graph_template_input_id");

	$i = 0;
	if (sizeof($graph_input_items) > 0) {
	foreach ($graph_input_items as $item) {
		$include_items[$i] = $item["graph_template_item_id"];
		$i++;
	}
	}

	/* we always want to make sure to stay within the same graph item input, so make a list of each
	item included in this input to be included in the sql query */
	if (isset($include_items)) {
		$sql_include_items = "and " . array_to_sql_or($include_items, "local_graph_template_item_id");
	}else{
		$sql_include_items = "and 0=1";
	}

	if (sizeof($session_members) == 0) {
		$values_to_apply = db_fetch_assoc("select local_graph_id," . $graph_input["column_name"] . " from graph_templates_item where graph_template_id=" . $graph_input["graph_template_id"] . " $sql_include_items and local_graph_id>0 group by local_graph_id");
	}else{
		$i = 0;
		while (list($item_id, $item_id) = each($session_members)) {
			$new_session_members[$i] = $item_id;
			$i++;
		}

		$values_to_apply = db_fetch_assoc("select local_graph_id," . $graph_input["column_name"] . " from graph_templates_item where graph_template_id=" . $graph_input["graph_template_id"] . " and local_graph_id>0 and !(" . array_to_sql_or($new_session_members, "local_graph_template_item_id") . ") $sql_include_items group by local_graph_id");
	}

	if (sizeof($values_to_apply) > 0) {
	foreach ($values_to_apply as $value) {
		/* this is just an extra check that i threw in to prevent users' graphs from getting really messed up */
		if (!(($graph_input["column_name"] == "task_item_id") && (empty($value{$graph_input["column_name"]})))) {
			db_execute("update graph_templates_item set " . $graph_input["column_name"] . "='" . addslashes($value{$graph_input["column_name"]}) . "' where local_graph_id=" . $value["local_graph_id"] . " and local_graph_template_item_id=$graph_template_item_id");
		}
	}
	}
}

/* push_out_graph_item - pushes out templated graph template item fields to all matching
	children. if the graph template item is part of a graph input, the field will not be
	pushed out
   @arg $graph_template_item_id - the id of the graph template item to push out values for */
function push_out_graph_item($graph_template_item_id) {
	global $struct_graph_item;

	/* get information about this graph template */
	$graph_template_item = db_fetch_row("select * from graph_templates_item where id=$graph_template_item_id");

	/* must be a graph template */
	if ($graph_template_item["graph_template_id"] == 0) { return 0; }

	/* find out if any graphs actual contain this item */
	if (sizeof(db_fetch_assoc("select id from graph_templates_item where local_graph_template_item_id=$graph_template_item_id")) == 0) {
		/* if not, reapply the template to push out the new item */
		$attached_graphs = db_fetch_assoc("select local_graph_id from graph_templates_graph where graph_template_id=" . $graph_template_item["graph_template_id"] . " and local_graph_id>0");

		if (sizeof($attached_graphs) > 0) {
		foreach ($attached_graphs as $item) {
			change_graph_template($item["local_graph_id"], $graph_template_item["graph_template_id"], true);
		}
		}
	}

	/* this is trickier with graph_items than with the actual graph... we have to make sure not to
	overwrite any items covered in the "graph item inputs". the same thing applies to graphs, but
	is easier to detect there (t_* columns). */
	$graph_item_inputs = db_fetch_assoc("select
		graph_template_input.column_name,
		graph_template_input_defs.graph_template_item_id
		from (graph_template_input, graph_template_input_defs)
		where graph_template_input.graph_template_id=" . $graph_template_item["graph_template_id"] . "
		and graph_template_input.id=graph_template_input_defs.graph_template_input_id
		and graph_template_input_defs.graph_template_item_id=$graph_template_item_id");

	$graph_item_inputs = array_rekey($graph_item_inputs, "column_name", "graph_template_item_id");

	/* loop through each graph item column name (from the above array) */
	reset($struct_graph_item);
	while (list($field_name, $field_array) = each($struct_graph_item)) {
		/* are we allowed to push out the column? */
		if (!isset($graph_item_inputs[$field_name])) {
			db_execute("update graph_templates_item set $field_name='" . addslashes($graph_template_item[$field_name]) . "' where local_graph_template_item_id=" . $graph_template_item["id"]);
		}
	}
}

/* change_graph_template - changes the graph template for a particular graph to
	$graph_template_id
   @arg $local_graph_id - the id of the graph to change the graph template for
   @arg $graph_template_id - id the of the graph template to change to. specify '0' for no
	graph template
   @arg $intrusive - (true) if the target graph template has more or less graph items than
	the current graph, remove or add the items from the current graph to make them equal.
	(false) leave the graph item count alone */
function change_graph_template($local_graph_id, $graph_template_id, $intrusive) {
	global $struct_graph, $struct_graph_item;

	/* always update tables to new graph template (or no graph template) */
	db_execute("update graph_local set graph_template_id=$graph_template_id where id=$local_graph_id");

	/* get information about both the graph and the graph template we're using */
	$graph_list = db_fetch_row("select * from graph_templates_graph where local_graph_id=$local_graph_id");
	$template_graph_list = (($graph_template_id == "0") ? $graph_list : db_fetch_row("select * from graph_templates_graph where local_graph_id=0 and graph_template_id=$graph_template_id"));

	/* determine if we are here for the first time, or coming back */
	if ((db_fetch_cell("select local_graph_template_graph_id from graph_templates_graph where local_graph_id=$local_graph_id") == "0") ||
	(db_fetch_cell("select local_graph_template_graph_id from graph_templates_graph where local_graph_id=$local_graph_id") == "")) {
		$new_save = true;
	}else{
		$new_save = false;
	}

	/* some basic field values that ALL graphs should have */
	$save["id"] = (isset($graph_list["id"]) ? $graph_list["id"] : 0);
	$save["local_graph_template_graph_id"] = $template_graph_list["id"];
	$save["local_graph_id"] = $local_graph_id;
	$save["graph_template_id"] = $graph_template_id;

	/* loop through the "templated field names" to find to the rest... */
	reset($struct_graph);
	while (list($field_name, $field_array) = each($struct_graph)) {
		$value_type = "t_$field_name";

		if ((!empty($template_graph_list[$value_type])) && ($new_save == false)) {
			$save[$field_name] = $graph_list[$field_name];
		}else{
			$save[$field_name] = $template_graph_list[$field_name];
		}
	}

	sql_save($save, "graph_templates_graph");

	$graph_items_list = db_fetch_assoc("select * from graph_templates_item where local_graph_id=$local_graph_id order by sequence");
	$template_items_list = (($graph_template_id == "0") ? $graph_items_list : db_fetch_assoc("select * from graph_templates_item where local_graph_id=0 and graph_template_id=$graph_template_id order by sequence"));

	$graph_template_inputs = db_fetch_assoc("select
		graph_template_input.column_name,
		graph_template_input_defs.graph_template_item_id
		from (graph_template_input,graph_template_input_defs)
		where graph_template_input.id=graph_template_input_defs.graph_template_input_id
		and graph_template_input.graph_template_id=$graph_template_id");

	$k=0;
	if (sizeof($template_items_list) > 0) {
	foreach ($template_items_list as $template_item) {
		unset($save);
		reset($struct_graph_item);

		$save["local_graph_template_item_id"] = $template_item["id"];
		$save["local_graph_id"] = $local_graph_id;
		$save["graph_template_id"] = $template_item["graph_template_id"];

		if (isset($graph_items_list[$k])) {
			/* graph item at this position, "mesh" it in */
			$save["id"] = $graph_items_list[$k]["id"];

			/* make a first pass filling in ALL values from template */
			while (list($field_name, $field_array) = each($struct_graph_item)) {
				$save[$field_name] = $template_item[$field_name];
			}

			/* go back a second time and fill in the INPUT values from the graph */
			for ($j=0; ($j < count($graph_template_inputs)); $j++) {
				if ($graph_template_inputs[$j]["graph_template_item_id"] == $template_items_list[$k]["id"]) {
					/* if we find out that there is an "input" covering this field/item, use the
					value from the graph, not the template */
					$graph_item_field_name = $graph_template_inputs[$j]["column_name"];
					$save[$graph_item_field_name] = $graph_items_list[$k][$graph_item_field_name];
				}
			}
		}else{
			/* no graph item at this position, tack it on */
			$save["id"] = 0;
			$save["task_item_id"] = 0;

			if ($intrusive == true) {
				while (list($field_name, $field_array) = each($struct_graph_item)) {
					$save[$field_name] = $template_item[$field_name];
				}
			}else{
				unset($save);
			}
		}

		if (isset($save)) {
			sql_save($save, "graph_templates_item");
		}

		$k++;
	}
	}

	/* if there are more graph items then there are items in the template, delete the difference */
	if ((sizeof($graph_items_list) > sizeof($template_items_list)) && ($intrusive == true)) {
		for ($i=(sizeof($graph_items_list) - (sizeof($graph_items_list) - sizeof($template_items_list))); ($i < count($graph_items_list)); $i++) {
			db_execute("delete from graph_templates_item where id=" . $graph_items_list[$i]["id"]);
		}
	}

	return true;
}

/* graph_to_graph_template - converts a graph to a graph template
   @arg $local_graph_id - the id of the graph to be converted
   @arg $graph_title - the graph title to use for the new graph template. the variable
	<graph_title> will be substituted for the current graph title */
function graph_to_graph_template($local_graph_id, $graph_title) {
	/* create a new graph template entry */
	db_execute("insert into graph_templates (id,name,hash) values (0,'" . str_replace("<graph_title>", db_fetch_cell("select title from graph_templates_graph where local_graph_id=$local_graph_id"), $graph_title) . "','" . get_hash_graph_template(0) . "')");
	$graph_template_id = db_fetch_insert_id();

	/* update graph to point to the new template */
	db_execute("update graph_templates_graph set local_graph_id=0,local_graph_template_graph_id=0,graph_template_id=$graph_template_id where local_graph_id=$local_graph_id");
	db_execute("update graph_templates_item set local_graph_id=0,local_graph_template_item_id=0,graph_template_id=$graph_template_id,task_item_id=0 where local_graph_id=$local_graph_id");

	/* create hashes for the graph template items */
	$items = db_fetch_assoc("select id from graph_templates_item where graph_template_id='$graph_template_id' and local_graph_id=0");
	for ($j=0; $j<count($items); $j++) {
		db_execute("update graph_templates_item set hash='" . get_hash_graph_template($items[$j]["id"], "graph_template_item") . "' where id=" . $items[$j]["id"]);
	}

	/* delete the old graph local entry */
	db_execute("delete from graph_local where id=$local_graph_id");
	db_execute("delete from graph_tree_items where local_graph_id=$local_graph_id");
}

/* data_source_to_data_template - converts a data source to a data template
   @arg $local_data_id - the id of the data source to be converted
   @arg $data_source_title - the data source title to use for the new data template. the variable
	<ds_title> will be substituted for the current data source title */
function data_source_to_data_template($local_data_id, $data_source_title) {
	/* create a new graph template entry */
	db_execute("insert into data_template (id,name,hash) values (0,'" . str_replace("<ds_title>", db_fetch_cell("select name from data_template_data where local_data_id=$local_data_id"), $data_source_title) . "','" .  get_hash_data_template(0) . "')");
	$data_template_id = db_fetch_insert_id();

	/* update graph to point to the new template */
	db_execute("update data_template_data set local_data_id=0,local_data_template_data_id=0,data_template_id=$data_template_id where local_data_id=$local_data_id");
	db_execute("update data_template_rrd set local_data_id=0,local_data_template_rrd_id=0,data_template_id=$data_template_id where local_data_id=$local_data_id");

	/* create hashes for the data template items */
	$items = db_fetch_assoc("select id from data_template_rrd where data_template_id='$data_template_id' and local_data_id=0");
	for ($j=0; $j<count($items); $j++) {
		db_execute("update data_template_rrd set hash='" . get_hash_data_template($items[$j]["id"], "data_template_item") . "' where id=" . $items[$j]["id"]);
	}

	/* delete the old graph local entry */
	db_execute("delete from data_local where id=$local_data_id");
	db_execute("delete from poller_item where local_data_id=$local_data_id");
}

/* create_complete_graph_from_template - creates a graph and all necessary data sources based on a
	graph template
   @arg $graph_template_id - the id of the graph template that will be used to create the new
	graph
   @arg $host_id - the id of the host to associate the new graph and data sources with
   @arg $snmp_query_array - if the new data sources are to be based on a data query, specify the
	necessary data query information here. it must contain the following information:
	  $snmp_query_array["snmp_query_id"]
	  $snmp_query_array["snmp_index_on"]
	  $snmp_query_array["snmp_query_graph_id"]
	  $snmp_query_array["snmp_index"]
   @arg $suggested_values_array - any additional information to be included in the new graphs or
	data sources must be included in the array. data is to be included in the following format:
	  $values["cg"][graph_template_id]["graph_template"][field_name] = $value  // graph template
	  $values["cg"][graph_template_id]["graph_template_item"][graph_template_item_id][field_name] = $value  // graph template item
	  $values["cg"][data_template_id]["data_template"][field_name] = $value  // data template
	  $values["cg"][data_template_id]["data_template_item"][data_template_item_id][field_name] = $value  // data template item
	  $values["sg"][data_query_id][graph_template_id]["graph_template"][field_name] = $value  // graph template (w/ data query)
	  $values["sg"][data_query_id][graph_template_id]["graph_template_item"][graph_template_item_id][field_name] = $value  // graph template item (w/ data query)
	  $values["sg"][data_query_id][data_template_id]["data_template"][field_name] = $value  // data template (w/ data query)
	  $values["sg"][data_query_id][data_template_id]["data_template_item"][data_template_item_id][field_name] = $value  // data template item (w/ data query) */
function create_complete_graph_from_template($graph_template_id, $host_id, $snmp_query_array, &$suggested_values_array) {
	global $config;

	include_once($config["library_path"] . "/data_query.php");

	/* create the graph */
	$save["id"] = 0;
	$save["graph_template_id"] = $graph_template_id;
	$save["host_id"] = $host_id;

	$cache_array["local_graph_id"] = sql_save($save, "graph_local");
	change_graph_template($cache_array["local_graph_id"], $graph_template_id, true);

	if (is_array($snmp_query_array)) {
		/* suggested values for snmp query code */
		$suggested_values = db_fetch_assoc("select text,field_name from snmp_query_graph_sv where snmp_query_graph_id=" . $snmp_query_array["snmp_query_graph_id"] . " order by sequence");

		$suggested_values_graph = array();
		if (sizeof($suggested_values) > 0) {
		foreach ($suggested_values as $suggested_value) {
			/* once we find a match; don't try to find more */
			if (!isset($suggested_values_graph[$graph_template_id]{$suggested_value["field_name"]})) {
				$subs_string = substitute_snmp_query_data($suggested_value["text"], $host_id, $snmp_query_array["snmp_query_id"], $snmp_query_array["snmp_index"], read_config_option("max_data_query_field_length"));
				/* if there are no '|' characters, all of the substitutions were successful */
				if (!strstr($subs_string, "|query")) {
					db_execute("UPDATE graph_templates_graph SET " . $suggested_value["field_name"] . "='" . $suggested_value["text"] . "' WHERE local_graph_id=" . $cache_array["local_graph_id"]);

					/* once we find a working value, stop */
					$suggested_values_graph[$graph_template_id]{$suggested_value["field_name"]} = true;
				}
			}
		}
		}
	}

	/* suggested values: graph */
	if (isset($suggested_values_array[$graph_template_id]["graph_template"])) {
		while (list($field_name, $field_value) = each($suggested_values_array[$graph_template_id]["graph_template"])) {
			db_execute("update graph_templates_graph set $field_name='$field_value' where local_graph_id=" . $cache_array["local_graph_id"]);
		}
	}

	/* suggested values: graph item */
	if (isset($suggested_values_array[$graph_template_id]["graph_template_item"])) {
		while (list($graph_template_item_id, $field_array) = each($suggested_values_array[$graph_template_id]["graph_template_item"])) {
			while (list($field_name, $field_value) = each($field_array)) {
				$graph_item_id = db_fetch_cell("select id from graph_templates_item where local_graph_template_item_id=$graph_template_item_id and local_graph_id=" . $cache_array["local_graph_id"]);
				db_execute("update graph_templates_item set $field_name='$field_value' where id=$graph_item_id");
			}
		}
	}

	update_graph_title_cache($cache_array["local_graph_id"]);

	/* create each data source */
	$data_templates = db_fetch_assoc("select
		data_template.id,
		data_template.name,
		data_template_rrd.data_source_name
		from (data_template, data_template_rrd, graph_templates_item)
		where graph_templates_item.task_item_id=data_template_rrd.id
		and data_template_rrd.data_template_id=data_template.id
		and data_template_rrd.local_data_id=0
		and graph_templates_item.local_graph_id=0
		and graph_templates_item.graph_template_id=" . $graph_template_id . "
		group by data_template.id
		order by data_template.name");

	if (sizeof($data_templates) > 0) {
	foreach ($data_templates as $data_template) {
		unset($save);

		$save["id"] = 0;
		$save["data_template_id"] = $data_template["id"];
		$save["host_id"] = $host_id;

		$cache_array["local_data_id"]{$data_template["id"]} = sql_save($save, "data_local");
		change_data_template($cache_array["local_data_id"]{$data_template["id"]}, $data_template["id"]);

		$data_template_data_id = db_fetch_cell("select id from data_template_data where local_data_id=" . $cache_array["local_data_id"]{$data_template["id"]});

		if (is_array($snmp_query_array)) {
			/* suggested values for snmp query code */
			$suggested_values = db_fetch_assoc("select text,field_name from snmp_query_graph_rrd_sv where snmp_query_graph_id=" . $snmp_query_array["snmp_query_graph_id"] . " and data_template_id=" . $data_template["id"] . " order by sequence");

			$suggested_values_ds = array();
			if (sizeof($suggested_values) > 0) {
			foreach ($suggested_values as $suggested_value) {
				/* once we find a match; don't try to find more */
				if (!isset($suggested_values_ds{$data_template["id"]}{$suggested_value["field_name"]})) {
					$subs_string = substitute_snmp_query_data($suggested_value["text"], $host_id, $snmp_query_array["snmp_query_id"], $snmp_query_array["snmp_index"], read_config_option("max_data_query_field_length"));

					/* if there are no '|' characters, all of the substitutions were successful */
					if (!strstr($subs_string, "|query")) {
						if (sizeof(db_fetch_row("show columns from data_template_data like '" . $suggested_value["field_name"] . "'"))) {
							db_execute("UPDATE data_template_data SET " . $suggested_value["field_name"] . "='" . $suggested_value["text"] . "' WHERE local_data_id=" . $cache_array["local_data_id"]{$data_template["id"]});
						}

						/* once we find a working value, stop */
						$suggested_values_ds{$data_template["id"]}{$suggested_value["field_name"]} = true;

						if ((sizeof(db_fetch_row("show columns from data_template_rrd like '" . $suggested_value["field_name"] . "'"))) &&
							(!substr_count($subs_string, "|"))) {
							db_execute("UPDATE data_template_rrd SET " . $suggested_value["field_name"] . "='" . $suggested_value["text"] . "' WHERE local_data_id=" . $cache_array["local_data_id"]{$data_template["id"]});
						}
					}
				}
			}
			}
		}

		if (is_array($snmp_query_array)) {
			$data_input_field = array_rekey(db_fetch_assoc("SELECT
				data_input_fields.id,
				data_input_fields.type_code
				FROM (snmp_query,data_input,data_input_fields)
				WHERE snmp_query.data_input_id=data_input.id
				AND data_input.id=data_input_fields.data_input_id
				AND (data_input_fields.type_code='index_type'
					OR data_input_fields.type_code='index_value'
					OR data_input_fields.type_code='output_type')
				AND snmp_query.id=" . $snmp_query_array["snmp_query_id"]), "type_code", "id");

			$snmp_cache_value = db_fetch_cell("SELECT field_value
				FROM host_snmp_cache
				WHERE host_id='$host_id'
				AND snmp_query_id='" . $snmp_query_array["snmp_query_id"] . "'
				AND field_name='" . $snmp_query_array["snmp_index_on"] . "'
				AND snmp_index='" . $snmp_query_array["snmp_index"] . "'");

			/* save the value to index on (ie. ifindex, ifip, etc) */
			db_execute("REPLACE INTO data_input_data
				(data_input_field_id, data_template_data_id, t_value, value)
				VALUES (" . $data_input_field["index_type"] . ", $data_template_data_id, '', '" . $snmp_query_array["snmp_index_on"] . "')");

			/* save the actual value (ie. 3, 192.168.1.101, etc) */
			db_execute("REPLACE INTO data_input_data
				(data_input_field_id,data_template_data_id,t_value,value)
				VALUES (" . $data_input_field["index_value"] . ",$data_template_data_id,'','" . addslashes($snmp_cache_value) . "')");

			/* set the expected output type (ie. bytes, errors, packets) */
			db_execute("REPLACE INTO data_input_data
				(data_input_field_id,data_template_data_id,t_value,value)
				VALUES (" . $data_input_field["output_type"] . ",$data_template_data_id,'','" . $snmp_query_array["snmp_query_graph_id"] . "')");

			/* now that we have put data into the 'data_input_data' table, update the snmp cache for ds's */
			update_data_source_data_query_cache($cache_array["local_data_id"]{$data_template["id"]});
		}

		/* suggested values: data source */
		if (isset($suggested_values_array[$graph_template_id]["data_template"]{$data_template["id"]})) {
			reset($suggested_values_array[$graph_template_id]["data_template"]{$data_template["id"]});
			while (list($field_name, $field_value) = each($suggested_values_array[$graph_template_id]["data_template"]{$data_template["id"]})) {
				db_execute("UPDATE data_template_data
					SET $field_name='$field_value'
					WHERE local_data_id=" . $cache_array["local_data_id"]{$data_template["id"]});
			}
		}

		/* suggested values: data source item */
		if (isset($suggested_values_array[$graph_template_id]["data_template_item"])) {
			reset($suggested_values_array[$graph_template_id]["data_template_item"]);
			while (list($data_template_item_id, $field_array) = each($suggested_values_array[$graph_template_id]["data_template_item"])) {
				while (list($field_name, $field_value) = each($field_array)) {
					$data_source_item_id = db_fetch_cell("select id from data_template_rrd where local_data_template_rrd_id=$data_template_item_id and local_data_id=" . $cache_array["local_data_id"]{$data_template["id"]});
					db_execute("UPDATE data_template_rrd
						SET $field_name='$field_value'
						WHERE id=$data_source_item_id");
				}
			}
		}

		/* suggested values: custom data */
		if (isset($suggested_values_array[$graph_template_id]["custom_data"]{$data_template["id"]})) {
			reset($suggested_values_array[$graph_template_id]["custom_data"]{$data_template["id"]});
			while (list($data_input_field_id, $field_value) = each($suggested_values_array[$graph_template_id]["custom_data"]{$data_template["id"]})) {
				db_execute("replace into data_input_data (data_input_field_id,data_template_data_id,t_value,value) values ($data_input_field_id,$data_template_data_id,'','$field_value')");
			}
		}

		update_data_source_title_cache($cache_array["local_data_id"]{$data_template["id"]});
	}
	}

	/* connect the dots: graph -> data source(s) */
	$template_item_list = db_fetch_assoc("select
		graph_templates_item.id,
		data_template_rrd.id as data_template_rrd_id,
		data_template_rrd.data_template_id
		from (graph_templates_item,data_template_rrd)
		where graph_templates_item.task_item_id=data_template_rrd.id
		and graph_templates_item.graph_template_id=$graph_template_id
		and local_graph_id=0
		and task_item_id>0");

	/* loop through each item affected and update column data */
	if (sizeof($template_item_list) > 0) {
	foreach ($template_item_list as $template_item) {
		$local_data_id = $cache_array["local_data_id"]{$template_item["data_template_id"]};

		$graph_template_item_id = db_fetch_cell("select id from graph_templates_item where local_graph_template_item_id=" . $template_item["id"] . " and local_graph_id=" . $cache_array["local_graph_id"]);
		$data_template_rrd_id = db_fetch_cell("select id from data_template_rrd where local_data_template_rrd_id=" . $template_item["data_template_rrd_id"] . " and local_data_id=$local_data_id");

		if (!empty($data_template_rrd_id)) {
			db_execute("update graph_templates_item set task_item_id='$data_template_rrd_id' where id=$graph_template_item_id");
		}
	}
	}

	/* this will not work until the ds->graph dots are connected */
	if (is_array($snmp_query_array)) {
		update_graph_data_query_cache($cache_array["local_graph_id"]);
	}

	# now that we have the id of the new host, we may plugin postprocessing code
	$save["id"] = $cache_array["local_graph_id"];
	$save["graph_template_id"] = $graph_template_id;	// attention: unset!
	if (is_array($snmp_query_array)) {
		$save["snmp_query_id"] = $snmp_query_array["snmp_query_id"];
		$save["snmp_index"] = $snmp_query_array["snmp_index"];
	} else {
		$save["snmp_query_id"] = 0;
		$save["snmp_index"] = 0;
	}
	api_plugin_hook_function('create_complete_graph_from_template', $save);

	return $cache_array;
}

?>


text:
layout:     post
title:      "My rookie year"
subtitle:   "Hockey Pool Part 2: From the draft through the playoffs"
article-type: "long"
categories: ["hockey pool"]
description: " My season turned around when I figured out that I really wasn't playing the game. I wasn't taking advantage of simple tactical moves that would help me win categories, at no cost. When I awoke to this and started playing, along with making a couple of trades to balance out my roster, things started looking up."
tags:       ["hockey", "hockey pool", "analytics"]
date:       2015-08-25 12:00:00
author:     "RDJ"
header-img: "img/renewal.jpg"
header-img-title: "A decaying wharf in Liverpool"
header-img-link: "https://www.flickr.com/photos/neilsingapore/7980556368"
header-img-author: "Neil Howard"
header-img-author-link: "https://www.flickr.com/photos/neilsingapore/"
header-img-license: "CC BY-NC"
header-img-license-link: "https://creativecommons.org/licenses/by-nc/2.0/"
---

[leafs1]: http://torontoist.com/2012/05/the-saga-of-the-maple-leafs-futility-part-one/ "Torontoist: The Saga of the Maple Leafs' Futility"
[leafs2]: https://en.wikipedia.org/wiki/List_of_NHL_franchise_post-season_droughts#Stanley_Cup_Final_droughts "Wikipedia: Longest Finals Droughts"
[leafs3]: http://mapleleafs.nhl.com/club/page.htm?id=42188 "The 1980's"
[degroot]: https://en.wikipedia.org/wiki/Adriaan_de_Groot "Wikipedia: Adriaan de Groot"
[kramnik]: http://en.chessbase.com/post/how-could-kramnik-overlook-the-mate- "Looking at the date on this ChessBase article, I feel incredibly old"
[rumsfeld]: https://en.wikipedia.org/wiki/There_are_known_knowns "Wikipedia: 'There are known knowns'"
[ageing]: http://www.sbnation.com/nhl/2014/3/13/5500522/nhl-scoring-stats-rates-age-analysis "SBNation: NHL age curve"
[brodeur]: http://www.rantsports.com/nhl/2015/01/25/martin-brodeurs-stint-with-st-louis-blues-was-pointless/ "RankSports: Martin Brodeur's Stint With St. Louis Blues Was Pointless"
[goalies]: http://wagesofwins.com/2011/07/19/your-nhl-goalie-doesnt-matter/ "Wages of Wins: Your NHL Goalie doesn't matter (much)!"

*You might be interested in the prior article,
[{{page.previous.subtitle}}]({{page.previous.url}}). Feel free to go give that a read.*

When we first started the pool, our teams didn't have any players. They didn't have anything but names. That was the first decision I had to make: what to name my team. It's absolutely crucial (and utterly inconsequential to winning the pool) to pick a good name. I went with *Bring Back Gilmour*, echoing the combination of nostalgia and desperation that any Leafs fan [knows][leafs1] [too][leafs2] [well][leafs3].

The 12 teams were assigned an order, 1 through 12. The first team got to pick a player for their team out of all the players in the NHL. Then the second team picked a player, out of all (minus 1) players in the NHL. And so on. After the 12th team gets to pick, the order reverses, such that the 12th team gets two picks in a row. Otherwise, it would be very unfair to the 12th team who would always be disadvantaged. This is called a snake draft. If there were only four teams, the draft would go 1 2 3 4 4 3 2 1 1 2 3 4, etc., until all of the teams are full. This happens over the course of one evening, with a short amount of time allowed between picks for each team to decide who to take.

For the second season this became more complicated, because of two more choices:

* We were allowed to trade draft picks. So now you're not just using trades to optimize your current roster based on immediate performance, but you can strengthen or weaken your hand for the season after.
* We played in a keeper league, with three keepers. This means that at the end of the year I get to pick three of my players who don't leave my team and don't enter the draft. Then in the draft we all have three less roster spots to fill.

Most of my writing will be about draft strategy, even though there are so many other facets to the game. You've got to start somewhere.

## Futile Beginnings

Now I can fill in a few more details about my season. How did I, a novice, approach the draft? In perhaps the least interesting way possible: I didn't. I didn't even show up. I had a good reason not to – I was travelling for work.

![Peaches]({{ site.baseurl }}/img/sakura_peach.jpg){: .center-image }

Excuses are like peaches, everyone has one

Image from sakura_chihaya+ used under licence CC BY-ND

If you're not present during the draft, you can either set a ranking beforehand (I didn't) or let the website (in this case, Yahoo) autodraft for you. Yahoo has a default set of player rankings. It's common knowledge that autodrafting is disastrous.

![Disaster]({{ site.baseurl }}/img/hindenburg.jpg){: .center-image }
Utter and complete disaster.

This isn't actually always true: there are some players who would be better off with an autodrafted roster than by their own horrible picks.

At the start of the season, I wasn't even setting line-ups. This meant I was leaving a lot of free points on the table, by not having my players play the most games. My bench players weren't playing any games, they were just a permanent bench whose only benefit was that other teams couldn't claim them. I didn't know any better. I didn't realize that I could move players around my roster without limitation. I didn't know that I could set them at the start of the week and then not have to worry about it.

![Spud]({{ site.baseurl }}/img/spud.jpg){: .center-image }
There's a lot I didn't know.

I also had a weird allocation of positions on my team. This is a consequence of autodrafting, not that I knew any better. I could drop a goalie and add a forward and end up with many more points. I hadn't figured out that I could drop and add my worst players throughout the week to get more games in (although I did not do this much until the very end of the season).

My season turned around when I figured out that I really wasn't playing the game. I wasn't taking advantage of simple tactical moves that would help me win categories, at no cost. When I awoke to this and started playing, along with making a couple of trades to balance out my roster, things started looking up.

## Beginnings of Success

A younger version of myself might have entered immediately into optimizing. There's a bit of a formula here:

* Find a decision choice that you have (e.g. the draft)
* Gather data, build a model, and optimize
* ???
* Profit!

It's not quite that simple, because the game isn't that simple. For starters, I didn't know what I needed to optimize. It could be line-up management (the marginal adds/drops, picking a line-up for each day, trade values), or it could be optimal draft strategy. In any case there isn't always a simple answer for what the right choice is. I needed to learn the game first.

This is a lesson I learnt from working in a few companies. Nuances matter. People learn the little details and become very good at making decisions. As a new entrant I might have higher technical and statistical sophistication, but I could apply them sub-optimally because I don't understand the intricate details that add up to big differences.

People who do something for a long time build up this knowledge. Some of it is knowledge of "gotchas", the rules and tactics that are critical to the game. Here just some of these include:

* Moving players in and out of your line-up
* That you need a certain amount of goalie starts for your goalie stats to count in a week
* That when players are dropped they go on waivers, and there's an order to who is allowed to pick them

If I started optimizing without internalizing the rules, I could make big mistakes.

Some of the knowledge isn't just the rules, but key heuristics towards optimal strategy. When the world's top chess players compete, victory is not exclusively determined by who can see the most layers of moves ahead (at risk of scaring away my entire audience here, the term for this is *ply*). These players play deep strategies and see a lot of moves as [patterns][kramnik]. There's a feel to their position, they understand it intuitively. There's a lot of research on this front (see: [Adriaan de Groot][degroot]).

Comparing elite chess players to casual hockey pool players might be a bit of a stretch, but there's a point there. Players build up intuition. They learn things such as:

* What the right distribution of positions to have is. They don't need to look at data or run an optimizer to know I don't need three goalies
* The relative value of different players
* That the playoffs occur during the NHL's regular season, not its playoffs. This means you don't lose players if their teams are bad, and don't have to factor team playoff predictions into your roster decisions

My first step had to be to learn the game. When I started, I made a list of notes of what I knew I didn't know. Word for word, here is that list:

* How other players draft
* The value of a goalie vis-a-vis a F or D
* Threshold of C vs LW vs RW
* Consistency of stats
* Value of different stats
* Value of likely keepers
* Value of draft picks

Of course, to borrow Donald Rumsfeld's terminology, that's just the [known unknowns][rumsfeld]. There were a lot of unknown unknowns, hopefully many of which I have now turned into known unknowns or even known knowns.

My first instinct was to jump into predicting, because prediction is my safe place. I could predict how many goals, assists, hits, blocks, etc., each player would have in the season. Ultimately (spoiler alert!) I would do this. But that's not enough. Will predicting these things truly tell me which players I should have on my team? I still needed to understand line-ups. I needed to figure out the relative value of different point categories. For example, a goal is worth more than a hit because there are less of them – but then again, hits are more consistent and predictable...

## Winning Year 1 by Playing for Year 2

I decided to improve my position for year 2. I was halfway through the first season and in the bottom half of the league. I didn't want to entirely tank, because the rewards are so small and playing with intent to lose would be boring. The fun is in winning, at least in winning match-ups. So I resolved to play to win match-ups, which would force me to learn the game, but to try to improve my position for the next year simultaneously. This required either improving the quality of my three keepers, or acquiring draft picks. Improving keepers was tough, because everyone wants to guard their keepers, and players don't degrade enough through one year of [ageing][ageing] to make for a lot of trade-offs of better older players for slightly worse younger ones. Improving my set of draft picks was easier.

I did this through two trades.

In the first, I traded Pekka Rinne, Evander Kane, and a 5th round pick for Patrice Bergeron, Martin Brodeur, and a 3rd round pick. I might just be the only person who ever traded for Martin Brodeur in a year where he played [just 7 games][brodeur] as a 42-year old joining the Blues mid-season. Needless to say (despite the ribbing I got for that), Brodeur was not the point of that trade. The point was upgrading my pick while picking up a most excellent Patrice Bergeron in the process. Losing Pekka Rinne was tough given how well he played, but [goaltender variability is high][goalies].

In the second trade, I traded John Tavares and a 7th round pick for P.K. Subban and a 1st round pick. Not only did I appreciate the pick improvement, but I traded a keeper at the relatively replaceable centre position for a top defenceman. Although Tavares is a great centre, we only get two centre spots and he cannot be used at either left-wing or right-wing. I'm expecting that the drop off from Tavares to another forward is not substantially worse than from Subban to another defenceman.

My opponents aren't perfect in their game play either, which is why early season futility did not doom me. Which is why by the end of the first season, I had moved up to 6th of 12 in the standings, with a feasible play-off match-up against the 3rd seed. I got there through key roster improvements at the margin, combined with active play. After winning that match-up, I beat the 2nd seed in the following week, before losing the final by the narrowest of margins. That final match-up went down the wire, with me losing by a single close category.

Somehow I finished second, winning a bit of money, while picking up extra first and third round draft picks.  I was fortunate to win match-ups against quality teams, and came quite close to beating one more. There was also good play and careful choices in those match-ups. All in all, good recovery, and not bad for my first time!

To see how I'm upping my game for next year, go to the next article in this series,
[{{page.next.subtitle}}]({{page.next.url}}).


text:
<?php

namespace ZendTest\View;

use Zend\View\HelperBroker,
    Zend\View\PhpRenderer;

/**
 * @category   Zend
 * @package    Zend_View
 * @subpackage UnitTests
 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 * @group      Zend_View
 */
class ViewTest extends \PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        $this->broker = new HelperBroker();
    }

    public function testUsesHelperLoaderAsDefaultClassLoader()
    {
        $this->assertType('Zend\View\HelperLoader', $this->broker->getClassLoader());
    }

    public function testViewIsNullByDefault()
    {
        $this->assertNull($this->broker->getView());
    }

    public function testAllowsPassingRendererForView()
    {
        $renderer = new PhpRenderer();
        $this->broker->setView($renderer);
        $this->assertSame($renderer, $this->broker->getView());
    }

    public function testInjectsRendererToHelperWhenRendererIsPresent()
    {
        $renderer = new PhpRenderer();
        $this->broker->setView($renderer);
        $helper = $this->broker->load('doctype');
        $this->assertSame($renderer, $helper->getView());
    }

    public function testNoRendererInjectedInHelperWhenRendererIsNotPresent()
    {
        $helper = $this->broker->load('doctype');
        $this->assertNull($helper->getView());
    }

    public function testRegisteringInvalidHelperRaisesException()
    {
        $this->setExpectedException('Zend\View\InvalidHelperException');
        $this->broker->register('test', $this);
    }

    public function testLoadingInvalidHelperRaisesException()
    {
        $this->broker->getClassLoader()->registerPlugin('test', get_class($this));
        $this->setExpectedException('Zend\View\InvalidHelperException');
        $this->broker->register('test', $this);
    }
}


text:
# Developer Guide

## Table of Content
1. [Introduction](#1-introduction)
2. [Setting Up](#2-setting-up)
    1. [Prerequisites](#21-prerequisites)
    2. [Importing the project into Eclipse](#22-importing-the-project-into-eclipse)
3. [Design](#3-design)
    1. [Architecture](#31-architecture)
    2. [UI component](#32-ui-component)
    3. [Logic component](#33-logic-component)
    4. [Model component](#34-model-component)
    5. [Storage component](#35-storage-component)
    6. [Common classes](#36-common-classes)
4. [Implementation](#4-implementation)
    1. [Logging](#41-logging)
    2. [Configuration](#42-configuration)
5. [Testing](#5-testing)
6. [Dev Ops](#6-dev-ops)
    1. [Build Automation](#61-build-automation)
    2. [Continuous Integration](#62-continuous-integration)
    3. [Making a Release](#63-making-a-release)
    4. [Managing Depedencies](#64-managing-dependencies)
7. [Appendix](#7-appendix)
    1. [Appendix A: User Stories](#71-appendix-a--user-stories)
    2. [Appendix B: Use Cases](#72-appendix-b--use-cases)
    3. [Appendix C: Non Functional Requirements](#73-appendix-c--non-functional-requirements)
    4. [Appendix D: Glossary](#74-appendix-d--glossary)
    5. [Appendix E : Product Survey](#75-appendix-e--product-survey)

## 1. Introduction
Welcome to the developer guide for SmartyDo. This guide is meant to enable budding developers like yourself to better understand the implementation of our program. Through this guide, we hope that you will be able to learn not only about how SmartyDo is implemented, but about different parts of the application that you are able to improve yourself.

## 2. Setting up

#### 2.1 Prerequisites
To ensure that you are able to run SmartyDo smoothly, do ensure that you have met the following prerequisites:

1. Installed **JDK `1.8.0_60`**  or later

    > This app may not work as intended with earlier versions of Java 8.

    This app will not work with earlier versions of Java.

2. Installed **Eclipse** IDE
3. Installed **e(fx)clipse** plugin for Eclipse (Follow the instructions given on
   [this page](http://www.eclipse.org/efxclipse/install.html#for-the-ambitious))
4. Installed **Buildship Gradle Integration** plugin from the Eclipse Marketplace

#### 2.2 Importing the project into Eclipse
To import the lastest version of this project into Eclipse, follow the instructions as given below:

0. Fork this repo, and clone the fork to your computer
1. Open Eclipse (Note: Ensure you have installed the **e(fx)clipse** and **buildship** plugins as given
   in the prerequisites above)
2. Click `File` > `Import`
3. Click `Gradle` > `Gradle Project` > `Next` > `Next`
4. Click `Browse`, then locate the project's directory
5. Click `Finish`

  > * If you are asked whether to 'keep' or 'overwrite' config files, choose to 'keep'.
  > * Depending on your connection speed and server load, it can even take up to 30 minutes for the set up to finish
      (This is because Gradle downloads library files from servers during the project set up process)
  > * If Eclipse auto-changed any settings files during the import process, you can discard those changes.

## 3. Design

### 3.1 Architecture
The **_Architecture Diagram_** given above will explain to you the high-level design of the App. Below, we will give you a quick overview of each component.

Figure 1. Overview of Main

`Main` has only one class called [`MainApp`](../src/main/java/seedu/address/MainApp.java). It is responsible for,
* At app launch: `Main` will initialize the components in the correct sequence, and connect them up with each other.
* At shut down: `Main` will shut down the components and invoke cleanup method where necessary.

[**`Commons`**](#36-common-classes) represents a collection of classes used by multiple other components.
Two of those classes play important roles at the architecture level.
* `EventsCentre`: This class (written using [Google's Event Bus library](https://github.com/google/guava/wiki/EventBusExplained))
  is used by components to communicate with other components using events (i.e. a form of _Event Driven_ design)
* `LogsCenter`: Used by many classes to write log messages to the App's log file.

The rest of the App consists four components.
* [**`UI`**](#ui-component): The UI of the App.
* [**`Logic`**](#logic-component): Executes commands given by the user.
* [**`Model`**](#model-component): Holds the data of the App in-memory.
* [**`Storage`**](#storage-component): Reads data from, and writes data to the hard disk.

Each of the four components will
* Define its _API_ in an `interface` with the same name as the Component.
* Expose its functionality using a `{Component Name}Manager` class.

For example, the `Logic` component (see the class diagram given below) defines it's API in the `Logic.java`
interface and exposes its functionality using the `LogicManager.java` class.

Figure 2. Overview of Logic

The _Sequence Diagram_ below will show you how the components interact for the scenario where the user issues the
command `delete 3`.

Figure 3. Sequence Diagram: Delete 1

>Note how the `Model` simply raises a `ToDoChangedEvent` when the To-Do data are changed,
 instead of asking the `Storage` to save the updates to the hard disk.

The diagram below will show you how the `EventsCenter` reacts to that event, which eventually results in the updates
being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time.

Figure 4. Sequence Diagram: ToDoEventChange

> Note how the event is propagated through the `EventsCenter` to the `Storage` and `UI` without `Model` having
  to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct
  coupling between components.

The follwing sections will give you more details about each component.

### 3.2 UI component

The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `TaskListPanel`,
`StatusBarFooter`, `BrowserPanel` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class
and they can be loaded using the `UiPartLoader`.

Figure 5. Overview of UI

**API** : [`Ui.java`](../src/main/java/seedu/address/ui/Ui.java)

The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files
 that are in the `src/main/resources/view` folder.

 For example, the layout of the [`MainWindow`](../src/main/java/seedu/address/ui/MainWindow.java) is specified in
 [`MainWindow.fxml`](../src/main/resources/view/MainWindow.fxml)

The `UI` component will
* Execute user commands using the `Logic` component.
* Bind itself to some data in the `Model` so that the UI can auto-update when data in the `Model` change.
* Respond to events raised from various parts of the App and updates the UI accordingly.

### 3.3 Logic component
Logic is in charge of reading user input and executing the correct commands. It is also in charge of give the user feedback on their input.

Figure 6. Overview of Logic

**API** : [`Logic.java`](../src/main/java/seedu/address/logic/Logic.java)

1. `Logic` uses the `Parser` class to parse the user command.
2. This results in a `Command` object which is executed by the `LogicManager`.
3. The command execution can affect the `Model` (e.g. adding a task) and/or raise events.
4. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`.
5. `Logic` loads the `undo/redo Manager` which is initially an empty stack. If the command that is recently executed successfully belongs to a undoable command, the undo/redo manager will record it.

Below, you will find the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")`
 API call.

Figure 7. Sequence Diagram: Delete in Logic

### 3.4 Model component
Model is in charge of the structure of the to-do list, and serves as the manager of the abstraction layer between Logic and the actual list of tasks.

Figure 8. Overview of Model

**API** : [`Model.java`](../src/main/java/seedu/address/model/Model.java)

The `Model`,
* stores a `UserPref` object that represents the user's preferences.
* stores the To-Do data.
* exposes a `UnmodifiableObservableList<ReadOnlyTask>` that can be 'observed' e.g. the UI can be bound to this list
  so that the UI automatically updates when the data in the list change.
* does not depend on any of the other three components.

### 3.5 Storage component
Storage is in charge of saving and retrieving data from files stored on the user's device.

Figure 9. Overview of Storage

**API** : [`Storage.java`](../src/main/java/seedu/address/storage/Storage.java)

The `Storage` component,
* can save `UserPref` objects in json format and read it back.
* can save the SmartyDo data in xml format and read it back.

### 3.6 Common classes

You may find classes used by multiple components are in the `seedu.addressbook.commons` package.

## 4. Implementation

### 4.1 Logging

We are using `java.util.logging` package for logging. You can use `LogsCenter` class to manage the logging levels
and logging destinations.

* You can control the logging level by using the `logLevel` setting in the configuration file
  (See [Configuration](#configuration))
* You can obtain the `Logger` for a class by using `LogsCenter.getLogger(Class)` which will log messages according to
  the specified logging level
* Currently log messages are output through: `Console` and to a `.log` file.

**Logging Levels**
|Level|Details|
|---|---|
| `SEVERE` | Critical problem detected which may possibly cause the termination of the application.
| `WARNING` | Can continue, but with caution.
| `INFO` | Information showing the noteworthy actions by the App.
| `FINE` | Details that are not usually noteworthy but may be useful in debugging e.g. printout of the actual list instead of just its size

### 4.2 Configuration

You can control certain properties of the application (e.g App name, logging level) through the configuration file
(default: `config.json`):

## 5. Testing

You can find tests in the `./src/test/java` folder.

**In Eclipse**:
> If you are not using a recent Eclipse version (i.e. _Neon_ or later), you will need to enable assertions in JUnit tests
  as described [here](http://stackoverflow.com/questions/2522897/eclipse-junit-ea-vm-option).

* You can run all tests by right-clicking on the `src/test/java` folder and choose
  `Run as` > `JUnit Test`
* You can also run a subset of tests by right-clicking on a test package, test class, or a test and choose
  to run as a JUnit test.

**Using Gradle**:
* You may refer to [UsingGradle.md](UsingGradle.md) to see how to run tests using Gradle.

We have two types of tests:

1. **GUI Tests** - These are _System Tests_ that test the entire App by simulating user actions on the GUI.
   These are in the `guitests` package.

2. **Non-GUI Tests** - These are tests not involving the GUI. They include,
   1. _Unit tests_ targeting the lowest level methods/classes.

      e.g. `seedu.address.commons.UrlUtilTest`
   2. _Integration tests_ that are checking the integration of multiple code units
     (those code units are assumed to be working).

      e.g. `seedu.address.storage.StorageManagerTest`
   3. _Hybrids of unit and integration tests._ These test are checking multiple code units as well as
      how they are connected together.

      e.g. `seedu.address.logic.LogicManagerTest`

**Headless GUI Testing** :
Thanks to the [TestFX](https://github.com/TestFX/TestFX) library we use,
 our GUI tests can be run in the _headless_ mode.
 In the headless mode, GUI tests do not show up on the screen.
 That means the developer can do other things on the Computer while the tests are running.

 See [UsingGradle.md](UsingGradle.md#running-tests) to learn how to run tests in headless mode.

## 6. Dev Ops

### 6.1 Build Automation

You may read [UsingGradle.md](UsingGradle.md) to learn how to use Gradle for build automation.

### 6.2 Continuous Integration

We use [Travis CI](https://travis-ci.org/) to perform _Continuous Integration_ on our projects.
You may read [UsingTravis.md](UsingTravis.md) for more details.

### 6.3 Making a Release

Here are the steps to create a new release.

 1. Generate a JAR file [using Gradle](UsingGradle.md#creating-the-jar-file).
 2. Tag the repo with the version number. e.g. `v0.1`
 2. [Create a new release using GitHub](https://help.github.com/articles/creating-releases/)
    and upload the JAR file your created.

### 6.4 Managing Dependencies

A project often depends on third-party libraries. For example, SmartyDo depends on the
[Jackson library](http://wiki.fasterxml.com/JacksonHome) for XML parsing. Managing these _dependencies_
can be automated using Gradle. For example, Gradle can download the dependencies automatically, which
is better than these alternatives.

a. Include those libraries in the repo (this bloats the repo size)

b. Require developers to download those libraries manually (this creates extra work for developers)

## 7. Appendix

## 7.1 Appendix A : User Stories

Priorities: High (must have) - `* * *`, Medium (nice to have)  - `* *`,  Low (unlikely to have) - `*`

Priority | As a ... | I want to ... | So that I can...
-------- | :-------- | :--------- | :-----------
`* * *` | new user | see usage instructions | refer to instructions when I forget how to use the App
`* * *` | user | add a task by specifying a task description only | record tasks that need to be done
`* * *` | user | delete a task | remove entries that I no longer need
`* * *` | user | find a task by name | locate details of tasks without having to go through the entire list
`* * *` | user |view list of completed and pending tasks | keep track of what needs to be done
`* *` | user with many tasks at a time | sort my tasks by different criteria | view tasks easily
`* *` | user with large projects/ tasks | add subtasks to main task | break down larger task into smaller tasks
`* *` | user with many uncofirmed events | allocate timeslots for tentative meetings/tasks | avoid having plans that might conflict with unconfirmed plans
`* *` | user | undo  1 previous operation | remove commands executed by accident
`* *` | user | specify a target folder as the data storage location | synchronise file with other applications

## 7.2 Appendix B : Use Cases

(For all use cases below, the **System** is the `SmartyDo` and the **Actor** is the `user`, unless specified otherwise)

### Use case: Add task

**MSS**

1. User requests to add new task
2. SmartyDo shows list of upcoming tasks with new task added

Use case ends.

**Extensions**

1a. The given index is invalid

> Use case ends

### Use case: Edit task

**MSS**

1. User requests to view upcoming tasks
2. SmartyDo shows a list of upcoming tasks
3. User requests to edit a specific task in the list
4. SmartyDo edits the task

Use case ends.

**Extensions**

2a. The list is empty

> Use case ends

3a. The given index is invalid

> 3a1. SmartyDo shows an error message

  Use case resumes at step 2

### Use case: Undo task

**MSS**

1. User requests to undo the previous command
2. SmartyDo performs undo and shows updated list of upcoming tasks

Use case ends.

**Extensions**

1a. There is no previous command

> Use case ends

### Use case: Redo task

**MSS**

1. User requests to redo the command reversed by the undo command
2. SmartyDo performs redo and shows updated list of upcoming tasks

Use case ends.

**Extensions**

1a. There is no previous undo command

> Use case ends

### Use case: View task

**MSS**

1. User requests to view upcoming tasks that matches specific `string`
2. SmartyDo shows a list of upcoming tasks

Use case ends.

**Extensions**

1a. The given `string` is invalid

> Use case ends

### Use case: Mark task

**MSS**

1. User requests to view upcoming tasks
2. SmartyDo shows a list of upcoming tasks
3. User requests to mark a specific task in the list
4. SmartyDo marks the task

Use case ends.

**Extensions**

2a. The list is empty

> Use case ends

3a. The given index is invalid

> 3a1. SmartyDo shows an error message

  Use case resumes at step 2

### Use case: Delete task

**MSS**

1. User requests to view upcoming tasks
2. SmartyDo shows a list of upcoming tasks
3. User requests to delete a specific task in the list
4. SmartyDo deletes the task

Use case ends.

**Extensions**

2a. The list is empty

> Use case ends

3a. The given index is invalid

> 3a1. SmartyDo shows an error message

  Use case resumes at step 2

### Use case: Locate task

**MSS**

1. User requests to view upcoming tasks
2. SmartyDo shows a list of upcoming tasks
3. User requests to locate a specific task in the list
4. SmartyDo shows location of the task

Use case ends.

**Extensions**

2a. The list is empty

> Use case ends

3a. The given index is invalid

> 3a1. SmartyDo shows an error message

  Use case resumes at step 2

### Use case: Save file

**MSS**

1. User requests to save file to specific `file path`
2. SmartyDo saves to `file path`

Use case ends.

**Extensions**

1a. The `file path` is invalid

> Use case ends

### Use case: Load file

**MSS**

1. User requests to load file from specific `file path`
2. SmartyDo loads from `file path`

Use case ends.

**Extensions**

1a. The `file path` is invalid

> Use case ends

## 7.3 Appendix C : Non Functional Requirements

1. Should work on any [mainstream OS](#mainstream-os) as long as it has Java `1.8.0_60` or higher installed.
2. Should be able to hold up to 2 years of entries estimated to be 8000 entries.
3. Should come with automated unit tests and open source code.
4. Should favor DOS style commands over Unix-style commands.

## 7.4 Appendix D : Glossary

##### Mainstream OS

> Windows, Linux, Unix, OS-X

## 7.5 Appendix E : Product Survey

| Existing Product | Pros | Cons |
| :---: | :---: | :---: |
| Google Calendar | Allows creation of task and events and reside them in the same view. Free to use. Synchronises with gmail account. Allows conversion of email invites into events | Does not have blockout slots |
| Sticky Notes | Free on Windows Store. Always open. Easy to bring up. Shows all items, always. Easy addition/editing/removal of tasks.  Can store notes/weblinks. Can store handwritten notes. Supports basic text formatting. |  No backup mechanism. Unable to change font. Manual sorting. Resets to default settings on restart. No "calendar view". Takes up desktop space. Unable to minimise. Can be quite cluttered and messy|
| Todo.txt | Does not rely on network access to operate. Able to synchronise with cloud storage. Allows priority scheduling. Breaks difficult objectives into small steps to reach the goal. Records date of completion for tasks. Simple GUI and lightweight Application | No support for recurring tasks. No reminder for upcoming due dates |


text:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:shape="rectangle">
	<solid android:color="#D0D0D0" />
	<padding android:left="7dp" android:top="7dp" android:right="7dp"
		android:bottom="7dp" />
	<corners android:radius="8dp" />
</shape>