text:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Configuration.Logging;
using Stratis.Bitcoin.Configuration.Settings;
using Stratis.Bitcoin.Networks;
using Stratis.Bitcoin.P2P;
using Stratis.Bitcoin.Tests.Common.Logging;
using Stratis.Bitcoin.Utilities;
using Stratis.Bitcoin.Utilities.Extensions;
using Xunit;

namespace Stratis.Bitcoin.Tests.P2P
{
    public sealed class PeerSelectorTests : LogsTestBase
    {
        private readonly ExtendedLoggerFactory extendedLoggerFactory;
        private readonly ConnectionManagerSettings connectionManagerSettings;

        public PeerSelectorTests()
        {
            this.extendedLoggerFactory = new ExtendedLoggerFactory();
            this.extendedLoggerFactory.AddConsoleWithFilters();
            this.connectionManagerSettings = new ConnectionManagerSettings(NodeSettings.Default(new StratisRegTest()));
        }

        /// <summary>
        /// <para>
        /// Ensures that connection data is reset.
        /// </para><para>
        /// Scenario 1:
        /// Add peer, make some attempts and connect.
        /// Find Peer.
        /// </para><para>
        /// Result:
        /// Connection attepts reset to 0.
        /// Last attempt set to null.
        /// Last connection success date set to a value.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_AllConnectionDataGetsReset()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endpoint = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeer(endpoint, IPAddress.Loopback);

            peerAddressManager.PeerConnected(endpoint, DateTime.UtcNow);

            peerAddressManager.PeerAttempted(endpoint, DateTime.UtcNow);
            peerAddressManager.PeerAttempted(endpoint, DateTime.UtcNow);
            peerAddressManager.PeerAttempted(endpoint, DateTime.UtcNow);

            peerAddressManager.PeerConnected(endpoint, DateTime.UtcNow);

            PeerAddress peerOne = peerAddressManager.FindPeer(endpoint);

            Assert.Equal(0, peerOne.ConnectionAttempts);
            Assert.Null(peerOne.LastAttempt);
            Assert.NotNull(peerOne.LastConnectionSuccess);
        }

        /// <summary>
        /// <para>
        /// Ensures that a particular peer is returned from the fresh peers
        /// set.
        /// </para><para>
        /// Scenario 1:
        /// Peer 1 has had no connection attempts.
        /// Peer 2 has had no connection attempts.
        /// Peer 3 has had no connection attempts.
        /// </para><para>
        /// Result:
        /// All 3 peers are in the Fresh set.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_TestReturnFromPeerFreshSet()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.2");
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.3");
            var endPointThree = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointThree, IPAddress.Loopback);

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.Fresh();
            Assert.Equal(3, peers.Count());
        }

        /// <summary>
        /// <para>
        /// Ensures that a particular peer is returned from the attempted peers
        /// set.
        /// </para><para>
        /// Scenario:
        /// Peer 1 has had a connection attempt (in the last 60 seconds).
        /// Peer 2 has had a connection attempt (more than 1 hour ago).
        /// Peer 3 has had a connection attempt (more than 1 hour ago).
        /// </para><para>
        /// Result:
        /// Peers 2 and 3 are in the attempted set.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_TestReturnFromPeerAttemptedSet_Scenario1()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.2");
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.3");
            var endPointThree = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointThree, IPAddress.Loopback);

            peerAddressManager.PeerAttempted(endPointOne, DateTime.UtcNow);
            peerAddressManager.PeerAttempted(endPointTwo, DateTime.UtcNow.AddHours(-2));
            peerAddressManager.PeerAttempted(endPointThree, DateTime.UtcNow.AddHours(-2));

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.Attempted();
            Assert.Equal(2, peers.Count());
            Assert.DoesNotContain(peers, p => p.Endpoint.Match(endPointOne));
        }

        /// <summary>
        /// <para>
        /// Ensures that a particular peer is returned from the attempted peers
        /// set.
        /// </para><para>
        /// Scenario:
        /// Peer 1 has had a connection attempt (more than 1 hour ago).
        /// Peer 2 has had a connection attempt (more than 1 hour ago).
        /// Peer 3 was attempted unsuccessfully more than 10 times.
        /// </para><para>
        /// Result:
        /// Peers 1 and 2 are in the attempted set.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_TestReturnFromPeerAttemptedSet_Scenario2()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.2");
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.3");
            var endPointThree = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointThree, IPAddress.Loopback);

            peerAddressManager.PeerAttempted(endPointOne, DateTime.UtcNow.AddHours(-2));
            peerAddressManager.PeerAttempted(endPointTwo, DateTime.UtcNow.AddHours(-820));

            for (int i = 0; i < 15; i++)
            {
                peerAddressManager.PeerAttempted(endPointThree, DateTime.UtcNow);
            }

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.Attempted();
            Assert.Equal(2, peers.Count());
            Assert.DoesNotContain(peers, p => p.Endpoint.Match(endPointThree));
        }

        /// <summary>
        /// <para>
        /// Ensures that a particular peer is returned from the connected peers
        /// set.
        /// </para><para>
        /// Scenario 1:
        /// Peer 1 has had a successful connection made to it (in the last 60 seconds).
        /// Peer 2 has had a successful connection made to it (more than 60 seconds ago).
        /// Peer 3 has only had an unsuccessful connection attempt.
        /// </para><para>
        /// Result:
        /// Peer 2 gets returned in the Connected set.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_TestReturnFromPeerConnectedSet()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.2");
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.3");
            var endPointThree = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);

            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointThree, IPAddress.Loopback);

            peerAddressManager.PeerConnected(endPointOne, DateTime.UtcNow);
            peerAddressManager.PeerConnected(endPointTwo, DateTime.UtcNow.AddSeconds(-80));
            peerAddressManager.PeerAttempted(endPointThree, DateTime.UtcNow);

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.Connected();
            Assert.Single(peers);
            Assert.Contains(peers, p => p.Endpoint.Match(endPointTwo));
        }

        /// <summary>
        /// <para>
        /// Ensures that a particular peer is returned from the handshaked peers
        /// set.
        /// </para><para>
        /// Scenario 1:
        /// Peer 1 has had a successful handshake (in the last 60 seconds).
        /// Peer 2 has had a successful handshake (more than 60 seconds ago).
        /// Peer 3 has only had an unsuccessful connection attempt.
        /// </para><para>
        /// Result:
        /// Peer 2 gets returned in the Connected set.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_TestReturnFromPeerHandshakedSet()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.2");
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.3");
            var endPointThree = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);

            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointThree, IPAddress.Loopback);

            peerAddressManager.PeerConnected(endPointOne, DateTime.UtcNow);
            peerAddressManager.PeerHandshaked(endPointOne, DateTime.UtcNow);

            peerAddressManager.PeerConnected(endPointTwo, DateTime.UtcNow.AddSeconds(-80));
            peerAddressManager.PeerHandshaked(endPointTwo, DateTime.UtcNow.AddSeconds(-80));

            peerAddressManager.PeerAttempted(endPointThree, DateTime.UtcNow);

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.Handshaked();
            Assert.Single(peers);
            Assert.Contains(peers, p => p.Endpoint.Match(endPointTwo));
        }

        /// <summary>
        /// <para>
        /// Tests how peers are returned from the selector during GetAddrPayload.
        /// </para><para>
        /// Scenario 1:
        /// PeerAddressManager contains 15 peers.
        /// 7 Peers = Handshaked.
        /// 8 Peers = Fresh.
        /// </para><para>
        /// We ask for 8 peers.
        /// </para><para>
        /// Result:
        /// 4 handshaked peers returned.
        /// 4 fresh peers returned.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerSelector_ReturnPeersForGetAddrPayload_Scenario1()
        {
            var peersToAdd = new List<IPEndPoint>();

            for (int i = 1; i <= 15; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peersToAdd.Add(new IPEndPoint(ipAddress, 80));
            }

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeers(peersToAdd.ToArray(), IPAddress.Loopback);

            for (int i = 1; i <= 7; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peerAddressManager.PeerConnected(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddSeconds(-80));
                peerAddressManager.PeerHandshaked(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddSeconds(-80));
            }

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.SelectPeersForGetAddrPayload(8);
            Assert.Equal(8, peers.Count());
            Assert.Equal(4, peers.Count(p => p.Handshaked));
            Assert.Equal(4, peers.Count(p => p.Fresh));
        }

        /// <summary>
        /// <para>
        /// Tests how peers are returned from the selector during GetAddrPayload.
        /// </para><para>
        /// Scenario 2:
        /// PeerAddressManager contains 15 peers.
        /// 7 Peers = Handshaked.
        /// 8 Peers = Fresh.
        /// </para><para>
        /// We ask for 15 peers.
        /// </para><para>
        /// Result:
        /// 7 handshaked peers returned.
        /// 8 fresh peers returned.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerSelector_ReturnPeersForGetAddrPayload_Scenario2()
        {
            var peersToAdd = new List<IPEndPoint>();

            for (int i = 1; i <= 15; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peersToAdd.Add(new IPEndPoint(ipAddress, 80));
            }

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeers(peersToAdd.ToArray(), IPAddress.Loopback);

            for (int i = 1; i <= 7; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peerAddressManager.PeerConnected(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddSeconds(-80));
                peerAddressManager.PeerHandshaked(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddSeconds(-80));
            }

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.SelectPeersForGetAddrPayload(15);
            Assert.Equal(15, peers.Count());
            Assert.Equal(7, peers.Count(p => p.Handshaked));
            Assert.Equal(8, peers.Count(p => p.Fresh));
        }

        /// <summary>
        /// <para>
        /// Tests how peers are returned from the selector during GetAddrPayload.
        /// </para><para>
        /// Scenario 3:
        /// PeerAddressManager contains 15 peers.
        /// </para><para>
        /// 7 Peers = Attempted.
        /// 8 Peers = Fresh.
        /// </para><para>
        /// We ask for 15 peers.
        /// </para><para>
        /// Result:
        /// 7 attempted peers returned.
        /// 8 fresh peers returned.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerSelector_ReturnPeersForGetAddrPayload_Scenario3()
        {
            var peersToAdd = new List<IPEndPoint>();

            for (int i = 1; i <= 15; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peersToAdd.Add(new IPEndPoint(ipAddress, 80));
            }

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeers(peersToAdd.ToArray(), IPAddress.Loopback);

            for (int i = 1; i <= 7; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peerAddressManager.PeerAttempted(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddHours(-2));
            }

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.SelectPeersForGetAddrPayload(15);
            Assert.Equal(15, peers.Count());
            Assert.Equal(7, peers.Count(p => p.Attempted));
            Assert.Equal(8, peers.Count(p => p.Fresh));
        }

        /// <summary>
        /// <para>
        /// Tests how peers are returned from the selector during GetAddrPayload.
        /// </para><para>
        /// Scenario 4:
        /// PeerAddressManager contains 15 peers.
        /// </para><para>
        /// 2 Peers = Connected
        /// 13 Peers = Fresh
        /// </para><para>
        /// We ask for 15 peers.
        /// </para><para>
        /// Result:
        /// 2 connected peers returned.
        /// 13 fresh peers returned.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerSelector_ReturnPeersForGetAddrPayload_Scenario4()
        {
            var peersToAdd = new List<IPEndPoint>();

            for (int i = 1; i <= 15; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peersToAdd.Add(new IPEndPoint(ipAddress, 80));
            }

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);

            peerAddressManager.AddPeers(peersToAdd.ToArray(), IPAddress.Loopback);

            for (int i = 1; i <= 2; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peerAddressManager.PeerConnected(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddSeconds(-80));
            }

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.SelectPeersForGetAddrPayload(15);
            Assert.Equal(15, peers.Count());
            Assert.Equal(2, peers.Count(p => p.Connected));
            Assert.Equal(13, peers.Count(p => p.Fresh));
        }

        /// <summary>
        /// <para>
        /// Tests how peers are returned from the selector during discovery.
        /// </para><para>
        /// Scenario 1:
        /// PeerAddressManager contains 5 peers.
        /// 3 Peers was recently discovered from in the last 24 hours.
        /// 2 Peers was discovered from more than 24 hours ago.
        /// </para><para>
        /// We ask for 5 peers.
        /// </para><para>
        /// Result:
        /// 2 peers returned.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerSelector_ReturnPeersForDiscovery_Scenario1()
        {
            var peersToAdd = new List<IPEndPoint>();

            for (int i = 1; i <= 5; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peersToAdd.Add(new IPEndPoint(ipAddress, 80));
            }

            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);

            peerAddressManager.AddPeers(peersToAdd.ToArray(), IPAddress.Loopback);

            //These peers were all discovered from in the last 24 hours
            for (int i = 1; i <= 3; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peerAddressManager.PeerDiscoveredFrom(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddHours(-5));
            }

            //These peers were all discovered from more than 24 hours ago
            for (int i = 4; i <= 5; i++)
            {
                IPAddress ipAddress = IPAddress.Parse(string.Format("::ffff:192.168.0.{0}", i));
                peerAddressManager.PeerDiscoveredFrom(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddHours(-25));
            }

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.SelectPeersForDiscovery(5);
            Assert.Equal(2, peers.Count());
        }

        /// <summary>
        /// Ensures a banned peer is ignored from the selector during discovery.
        /// <para>
        /// Scenario :
        /// PeerAddressManager contains two peers.
        /// One banned peer.
        /// One discovered peers.
        /// </para>
        /// <para>
        /// Result:
        /// One peer returned, banned peer ignored.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerSelector_ReturnPeersForDiscovery_IgnoringBannedPeer()
        {
            string discoveredpeer = "::ffff:192.168.0.2";

            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse(discoveredpeer);
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);

            var peerAddressManager = new PeerAddressManager(DateTimeProvider.Default, peerFolder, this.extendedLoggerFactory,
                new SelfEndpointTracker(this.extendedLoggerFactory, this.connectionManagerSettings));
            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);

            // Discovered peer.
            ipAddress = IPAddress.Parse(discoveredpeer);
            peerAddressManager.PeerDiscoveredFrom(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddHours(-25));

            // Banned peer.
            peerAddressManager.FindPeer(endPointOne).BanUntil = DateTime.UtcNow.AddMinutes(1);

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.SelectPeersForDiscovery(2);
            Assert.Single(peers);
            Assert.Contains(peers, p => p.Endpoint.Match(endPointTwo));
        }

        /// <summary>
        /// Ensures that a particular peer is returned from the attempted peers
        /// set and ignores banned peers.
        /// <para>
        /// Scenario:
        /// Peer 1 has had a connection attempt (more than 1 hour ago).
        /// Peer 2 has had a connection attempt (more than 1 hour ago), and is banned.
        /// </para>
        /// <para>
        /// Result:
        /// Peer 1 is in the attempted set, peer 2 is ignored.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_TestReturnFromPeerAttemptedSet_IgnoringBannedPeer()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.2");
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);

            var peerAddressManager = new PeerAddressManager(DateTimeProvider.Default, peerFolder, this.extendedLoggerFactory,
                new SelfEndpointTracker(this.extendedLoggerFactory, this.connectionManagerSettings));
            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);

            peerAddressManager.FindPeer(endPointTwo).BanUntil = DateTime.UtcNow.AddMinutes(1);

            peerAddressManager.PeerAttempted(endPointOne, DateTime.UtcNow.AddHours(-2));
            peerAddressManager.PeerAttempted(endPointTwo, DateTime.UtcNow.AddHours(-2));

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.Attempted();

            Assert.Single(peers);
            Assert.Contains(peers, p => p.Endpoint.Match(endPointOne));
        }

        /// <summary>
        /// Ensures that a particular peer is returned from the connected peers
        /// set and ignores banned peers.
        /// <para>
        /// Scenario :
        /// Peer 1 has had a successful connection made to it (more than 60 seconds ago).
        /// Peer 2 has had a successful connection made to it (more than 60 seconds ago), and is banned.
        /// </para>
        /// <para>
        /// Result:
        /// Peer 1 gets returned in the Connected set.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_TestReturnFromPeerConnectedSet_IgnoringBannedPeer()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.2");
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);

            var peerAddressManager = new PeerAddressManager(DateTimeProvider.Default, peerFolder, this.extendedLoggerFactory,
                new SelfEndpointTracker(this.extendedLoggerFactory, this.connectionManagerSettings));
            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);

            peerAddressManager.FindPeer(endPointTwo).BanUntil = DateTime.UtcNow.AddMinutes(1);

            peerAddressManager.PeerConnected(endPointOne, DateTime.UtcNow.AddSeconds(-80));
            peerAddressManager.PeerConnected(endPointTwo, DateTime.UtcNow.AddSeconds(-80));

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.Connected();

            Assert.Single(peers);
            Assert.Contains(peers, p => p.Endpoint.Match(endPointOne));
        }

        /// <summary>
        /// Ensures that a peer is returned from the fresh peers
        /// set, and the banned peer is ignored.
        /// <para>
        /// Scenario 1:
        /// Peer 1 has had no connection attempts.
        /// Peer 2 has had no connection attempts, and is banned.
        /// </para>
        /// <para>
        /// Result:
        /// Peer 1 is in the Fresh set.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_TestReturnFromPeerFreshSet_IgnoringBannedPeer()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.2");
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);

            var peerAddressManager = new PeerAddressManager(DateTimeProvider.Default, peerFolder, this.extendedLoggerFactory,
                new SelfEndpointTracker(this.extendedLoggerFactory, this.connectionManagerSettings));
            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);

            peerAddressManager.FindPeer(endPointTwo).BanUntil = DateTime.UtcNow.AddMinutes(1);

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.Fresh();
            Assert.Single(peers);
            Assert.Contains(peers, p => p.Endpoint.Match(endPointOne));
        }

        /// <summary>
        /// Ensures that a particular peer is returned from the handshaked peers
        /// set, and the banned peer is ignored.
        /// <para>
        /// Scenario 1:
        /// Peer 1 has had a successful handshake (more than 60 seconds ago).
        /// Peer 2 has had a successful handshake (more than 60 seconds ago), and is banned.
        /// </para>
        /// <para>
        /// Result:
        /// Peer 1 gets returned in the Connected set, and Peer 2 is ignored.
        /// </para>
        /// </summary>
        [Fact]
        public void PeerState_TestReturnFromPeerHandshakedSet_IgnoringBanned()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            var endPointOne = new IPEndPoint(ipAddress, 80);

            ipAddress = IPAddress.Parse("::ffff:192.168.0.2");
            var endPointTwo = new IPEndPoint(ipAddress, 80);

            DataFolder peerFolder = CreateDataFolder(this);

            var peerAddressManager = new PeerAddressManager(DateTimeProvider.Default, peerFolder, this.extendedLoggerFactory,
                new SelfEndpointTracker(this.extendedLoggerFactory, this.connectionManagerSettings));
            peerAddressManager.AddPeer(endPointOne, IPAddress.Loopback);
            peerAddressManager.AddPeer(endPointTwo, IPAddress.Loopback);

            peerAddressManager.FindPeer(endPointTwo).BanUntil = DateTime.UtcNow.AddMinutes(1);

            peerAddressManager.PeerConnected(endPointOne, DateTime.UtcNow.AddSeconds(-80));
            peerAddressManager.PeerHandshaked(endPointOne, DateTime.UtcNow.AddSeconds(-80));

            peerAddressManager.PeerConnected(endPointTwo, DateTime.UtcNow.AddSeconds(-80));
            peerAddressManager.PeerHandshaked(endPointTwo, DateTime.UtcNow.AddSeconds(-80));

            IEnumerable<PeerAddress> peers = peerAddressManager.PeerSelector.Handshaked();
            Assert.Single(peers);
            Assert.Contains(peers, p => p.Endpoint.Match(endPointOne));
        }

        /// <summary>
        /// Tests that own peer address is not returning during SelectPeersForDiscovery and SelectPeer.
        /// <para>
        /// Scenario 1 - SelectPeersForDiscovery:
        /// PeerAddressManager contains 2 peers.
        /// 1 Peer that is within self endpoint tracker.
        /// 1 Peer not withing self endpoint tracker.
        /// </para><para>
        /// We ask for 2 peers.
        /// </para><para>
        /// Result:
        /// 1 peers returned. It is not the one within self endpoint tracker.
        /// </para><para>
        /// Scenario 2 - SelectPeer:
        /// PeerAddressManager contains 2 peers.
        /// 1 Peer that is within self endpoint tracker.
        /// 1 Peer not withing self endpoint tracker.
        /// </para><para>
        /// We ask for a peer.
        /// </para><para>
        /// Result:
        /// Peer returned is not the one within self endpoint tracker.
        /// </para>
        /// </summary>
        [Fact]
        public void SelectPeersForDiscovery_WhenPeerAddressesContainsOwnIPEndoint_DoesNotReturnOwnEndpoint()
        {
            var selfIpEndPoint = new IPEndPoint(new IPAddress(1), 123);
            var selfPeerAddress = new PeerAddress() { Endpoint = selfIpEndPoint };
            selfPeerAddress.SetDiscoveredFrom(DateTime.MinValue);

            var otherIpEndPoint = new IPEndPoint(new IPAddress(2), 345);
            var otherPeerAddress = new PeerAddress() { Endpoint = otherIpEndPoint };
            otherPeerAddress.SetDiscoveredFrom(DateTime.MinValue);

            var peerAddresses = new ConcurrentDictionary<IPEndPoint, PeerAddress>();
            peerAddresses.AddOrUpdate(selfIpEndPoint, selfPeerAddress, (x, y) => selfPeerAddress);
            peerAddresses.AddOrUpdate(otherIpEndPoint, otherPeerAddress, (x, y) => otherPeerAddress);

            var selfEndpointTracker = new SelfEndpointTracker(this.extendedLoggerFactory, this.connectionManagerSettings);
            selfEndpointTracker.Add(selfIpEndPoint);

            var peerSelector = new PeerSelector(new DateTimeProvider(), this.LoggerFactory.Object, peerAddresses, selfEndpointTracker);

            IEnumerable<PeerAddress> peers = peerSelector.SelectPeersForDiscovery(2);

            Assert.Equal(otherPeerAddress, peers.Single());

            // Note: This for loop is because Random is currently a hard dependency rather than using dependency inversion.
            // It is not 100% safe without mocking random, so a workaround of 20 attempts used for now.
            for (int i = 0; i < 20; i++)
            {
                Assert.Equal(otherPeerAddress, peerSelector.SelectPeer());
            }
        }

        [Fact]
        public void PeerSelector_ReturnConnectedPeers_AfterHandshakeFailure_WithAttemptsRemaining()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeer(new IPEndPoint(ipAddress, 80), IPAddress.Loopback);

            peerAddressManager.PeerConnected(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddSeconds(-80));
            DateTime handshakeAttempt = DateTime.UtcNow.AddSeconds(-80);

            PeerAddress peer = peerAddressManager.Peers.First();

            // Peer selected after one handshake failure.
            peer.SetHandshakeAttempted(handshakeAttempt);
            Assert.Equal(1, peer.HandshakedAttempts);
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));

            // Peer selected after two handshake failures.
            peer.SetHandshakeAttempted(handshakeAttempt);
            Assert.Equal(2, peer.HandshakedAttempts);
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));

            // Peer not selected after three handshake failures.
            peer.SetHandshakeAttempted(handshakeAttempt);
            Assert.Equal(3, peer.HandshakedAttempts);
            Assert.DoesNotContain(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));
        }

        [Fact]
        public void PeerSelector_ReturnConnectedPeers_AfterHandshakeFailure_ThresholdExceeded()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeer(new IPEndPoint(ipAddress, 80), IPAddress.Loopback);

            peerAddressManager.PeerConnected(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddSeconds(-80));
            DateTime handshakeAttempt = DateTime.UtcNow.AddSeconds(-80);

            PeerAddress peer = peerAddressManager.Peers.First();

            // Peer selected after one handshake failure.
            peer.SetHandshakeAttempted(handshakeAttempt.AddHours(-(PeerAddress.AttempThresholdHours + 4)));
            Assert.Equal(1, peer.HandshakedAttempts);
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));

            // Peer selected after two handshake failures.
            peer.SetHandshakeAttempted(handshakeAttempt.AddHours(-(PeerAddress.AttempThresholdHours + 3)));
            Assert.Equal(2, peer.HandshakedAttempts);
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));

            // Peer selected after two handshake failures when threshold time has elapsed.
            peer.SetHandshakeAttempted(handshakeAttempt.AddHours(-(PeerAddress.AttempThresholdHours + 2)));
            Assert.Equal(3, peer.HandshakedAttempts);
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));
        }

        [Fact]
        public void PeerSelector_ReturnConnectedPeers_AfterHandshakeFailure_HandshakeSucceeded_ResetsCounters()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeer(new IPEndPoint(ipAddress, 80), IPAddress.Loopback);

            peerAddressManager.PeerConnected(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddSeconds(-80));
            DateTime handshakeAttempt = DateTime.UtcNow.AddSeconds(-80);

            PeerAddress peer = peerAddressManager.Peers.First();

            // Peer selected after one handshake failure.
            peer.SetHandshakeAttempted(handshakeAttempt);
            Assert.Equal(1, peer.HandshakedAttempts);
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));

            // Peer selected after two handshake failures.
            peer.SetHandshakeAttempted(handshakeAttempt);
            Assert.Equal(2, peer.HandshakedAttempts);
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));

            // Peer attempt counter and last attempt reset after successful handshake.
            peer.SetHandshaked(handshakeAttempt);
            Assert.Equal(0, peer.HandshakedAttempts);
            Assert.Null(peer.LastHandshakeAttempt);
        }

        [Fact]
        public void PeerSelector_ReturnConnectedPeers_AfterThresholdExpired_ResetCounters()
        {
            IPAddress ipAddress = IPAddress.Parse("::ffff:192.168.0.1");
            DataFolder peerFolder = CreateDataFolder(this);
            PeerAddressManager peerAddressManager = this.CreatePeerAddressManager(peerFolder);
            peerAddressManager.AddPeer(new IPEndPoint(ipAddress, 80), IPAddress.Loopback);

            peerAddressManager.PeerConnected(new IPEndPoint(ipAddress, 80), DateTime.UtcNow.AddSeconds(-80));
            DateTime firstHandshakeAttemptTime = DateTime.UtcNow.AddSeconds(-80);

            PeerAddress peer = peerAddressManager.Peers.First();

            // Peer selected after one handshake failure.
            peer.SetHandshakeAttempted(firstHandshakeAttemptTime);
            Assert.Equal(1, peer.HandshakedAttempts);
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));

            // Peer selected after two handshake failures.
            peer.SetHandshakeAttempted(firstHandshakeAttemptTime);
            Assert.Equal(2, peer.HandshakedAttempts);
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));

            // Peer attempt counter and last attempt reset when threshold time has elapsed.
            peer.SetHandshakeAttempted(firstHandshakeAttemptTime.AddHours(-(PeerAddress.AttempThresholdHours + 2)));
            Assert.Contains(peer, peerAddressManager.PeerSelector.FilterBadHandshakedPeers(peerAddressManager.Peers));
            Assert.Equal(0, peer.HandshakedAttempts);
        }

        [Fact]
        public void PeerSelector_HasAllPeersReachedConnectionThreshold()
        {
            var peerAddress = new PeerAddress() { Endpoint = new IPEndPoint(new IPAddress(2), 345) };

            var peerAddresses = new ConcurrentDictionary<IPEndPoint, PeerAddress>();
            peerAddresses.AddOrUpdate(peerAddress.Endpoint, peerAddress, (x, y) => peerAddress);

            var peerSelector = new PeerSelector(new DateTimeProvider(), this.LoggerFactory.Object, peerAddresses,
                new SelfEndpointTracker(this.extendedLoggerFactory, this.connectionManagerSettings));

            Assert.False(peerSelector.HasAllPeersReachedConnectionThreshold());

            for (int i = 0; i < 5; i++)
                peerAddress.SetAttempted(DateTime.UtcNow);

            Assert.True(peerSelector.HasAllPeersReachedConnectionThreshold());
        }

        [Fact]
        public void PeerSelector_CanResetAttempts()
        {
            var peerAddress = new PeerAddress() { Endpoint = new IPEndPoint(new IPAddress(2), 345) };

            var bannedPeerAddress = new PeerAddress() { Endpoint = new IPEndPoint(new IPAddress(3), 346) };
            bannedPeerAddress.BanUntil = DateTime.UtcNow.AddHours(1);

            var peerAddresses = new ConcurrentDictionary<IPEndPoint, PeerAddress>();
            peerAddresses.AddOrUpdate(peerAddress.Endpoint, peerAddress, (x, y) => peerAddress);
            peerAddresses.AddOrUpdate(bannedPeerAddress.Endpoint, bannedPeerAddress, (x, y) => bannedPeerAddress);

            var peerSelector = new PeerSelector(new DateTimeProvider(), this.LoggerFactory.Object, peerAddresses,
                new SelfEndpointTracker(this.extendedLoggerFactory, this.connectionManagerSettings));

            for (int i = 0; i < 5; i++)
                peerAddress.SetAttempted(DateTime.UtcNow);

            Assert.Equal(5, peerAddress.ConnectionAttempts);
            Assert.True(peerSelector.HasAllPeersReachedConnectionThreshold());

            peerSelector.ResetConnectionAttemptsOnNotBannedPeers();

            Assert.Equal(0, peerAddress.ConnectionAttempts);
        }

        private PeerAddressManager CreatePeerAddressManager(DataFolder peerFolder)
        {
            var peerAddressManager = new PeerAddressManager(DateTimeProvider.Default, peerFolder, this.extendedLoggerFactory,
                new SelfEndpointTracker(this.extendedLoggerFactory, this.connectionManagerSettings));
            return peerAddressManager;
        }
    }
}


text:
package discovery

import (
	"fmt"
	"regexp"
	"strconv"
)

//
// IP generator
//
func Generate(pattern string) []string {
	re, _ := regexp.Compile(`\[(.+):(.+)\]`)
	submatch := re.FindStringSubmatch(pattern)
	if submatch == nil {
		return []string{pattern}
	}

	from, err := strconv.Atoi(submatch[1])
	if err != nil {
		return []string{pattern}
	}
	to, err := strconv.Atoi(submatch[2])
	if err != nil {
		return []string{pattern}
	}

	template := re.ReplaceAllString(pattern, "%d")

	var result []string
	for val := from; val <= to; val++ {
		entry := fmt.Sprintf(template, val)
		result = append(result, entry)
	}

	return result
}

redpajama_set_name: RedPajamaGithub


text:
require File.dirname(__FILE__) + '/../../spec_helper.rb'

describe Notifier do
  CHARSET = 'utf-8'

  fixtures :users

  include ActionMailer::Quoting

  before :each do
    @ticket = Factory :ticket

    @email = TMail::Mail.new
    @email.set_content_type 'text', 'plain', { 'charset' => CHARSET }
    @email.mime_version = '1.0'
  end

  it "should not send ticket if notifier is not configured" do
    Notifier.stub!(:configured? => false)

    lambda { Notifier.deliver_ticket(@ticket) }.should raise_error(ArgumentError)
  end

  it "should send ticket with a code if notifier is configured" do
    stub_notifier_secrets

    lambda { Notifier.deliver_ticket(@ticket) }.should change(ActionMailer::Base.deliveries, :size).by(1)

    body = ActionMailer::Base.deliveries.last.body
    body.should =~ /#{@ticket.discount_code}/
  end
end


text:
<?php

namespace raptor;

require_once 'FormHelper.php';
require_once 'UserPageHelper.php';
require_once 'ChildEditBasePage.php';

/**
 * This class returns the Admin Information input content
 *
 * @author Frank Font of SAN Business Consultants
 */
class EditUserPage extends \raptor\ChildEditBasePage
{

    private $m_oContext = NULL;
    private $m_oPageHelper = null;
    private $m_nUID = null;

     //Call same function as in EditUserPage here!
    function __construct($nUID, $gobacktooverride=NULL)
    {
        module_load_include('php', 'raptor_datalayer', 'config/Choices');
        module_load_include('php', 'raptor_datalayer', 'core/UserInfo');

        if(!isset($nUID) || !is_numeric($nUID))
        {
            throw new \Exception("Missing or invalid uid value = " . $nUID);
        }
        $this->m_nUID = $nUID;
        $this->m_oPageHelper = new \raptor\UserPageHelper();
        $this->m_oContext = \raptor\Context::getInstance();
        $this->m_oPageHelper->checkAllowedToEditUser($this->m_oContext, $this->m_nUID);

        //Set the default gobackto url now
        global $base_url;
        if($gobacktooverride == NULL)
        {
            $this->setGobacktoURL($base_url.'/raptor/manageusers');
        } else {
            $this->setGobacktoURL($base_url.'/'.$gobacktooverride);
        }
    }

    /**
     * Some checks to validate the data before we try to save it.
     * @return TRUE or FALSE
     */
    function looksValid($form, $myvalues)
    {
        $bGood = TRUE;
        if(!isset($myvalues['username']) || trim($myvalues['username']) == '')
        {
            form_set_error('username','Missing username value.');
            $bGood = FALSE;
        }
        if(isset($myvalues['prefemail']) && trim($myvalues['prefemail']) > ''
                && !valid_email_address($myvalues['prefemail']))
        {
            form_set_error('prefemail','Email address is not valid');
            $bGood = FALSE;
        }

        if(isset($myvalues['newpassword']) && trim($myvalues['newpassword']) > '')
        {
            //We require a current password value
            $username = $myvalues['username'];
            $password = $myvalues['currentpass'];
            if(FALSE === user_authenticate($username, $password))
            {
                form_set_error('currentpass','Wrong current password');
                $bGood = FALSE;
            }
            $newpassword = $myvalues['newpassword'];
            if(strlen($newpassword) < MIN_ADMIN_PASSWORD_LEN)
            {
                form_set_error('newpassword','New password must be at least '.MIN_ADMIN_PASSWORD_LEN.' characters in length');
                $bGood = FALSE;
            }
        }

        if($bGood)
        {
            $bGood = $this->m_oPageHelper->validateModality($myvalues);
        }

        return $bGood;
    }

    /**
     * Get the values to populate the form.
     * @return type result of the queries as an array
     */
    function getFieldValues()
    {
        $myvalues = $this->m_oPageHelper->getFieldValues($this->m_nUID);
        $myvalues['formmode'] = 'E';
        return $myvalues;
    }

    /**
     * Write the values into the database.
     * @param type associative array where 'username' MUST be one of the values so we know what record to update.
     */
    function updateDatabase($form, $myvalues)
    {
        if(!isset($myvalues['username']))
        {
            throw new \Exception("Cannot update user record because missing username in array!\n" . var_dump($myvalues));
        }
        if(!isset($myvalues['uid']))
        {
            throw new \Exception("Cannot update user record because missing uid in array!\n" . var_dump($myvalues));
        }
        $updated_dt = date("Y-m-d H:i:s", time());
        if(!isset($myvalues['accountactive_yn']))
        {
            $myvalues['accountactive_yn'] = 1;  //If not set, then assume because user is NOT allowed to disable the account.
        }

        try
        {
            $nUpdated = db_update('raptor_user_profile')
                    -> fields(['role_nm' => $myvalues['role_nm'],
                        'usernametitle' => $myvalues['usernametitle'],
                        'firstname' => $myvalues['firstname'],
                        'lastname' => $myvalues['lastname'],
                        'suffix' => $myvalues['suffix'],
                        'prefemail' => $myvalues['prefemail'],
                        'prefphone' => $myvalues['prefphone'],
                        'accountactive_yn' => $myvalues['accountactive_yn'],

                        'CEUA1' => $this->m_oContext->valForNullOrMissing($myvalues, 'CEUA1'),
                        'LACE1' => $this->m_oContext->valForNullOrMissing($myvalues, 'LACE1'),

                        'SWI1' => $this->m_oContext->valForNullOrMissing($myvalues, 'SWI1'),
                        'PWI1' => $this->m_oContext->valForNullOrMissing($myvalues, 'PWI1'),
                        'APWI1' => $this->m_oContext->valForNullOrMissing($myvalues, 'APWI1'),
                        'SUWI1' => $this->m_oContext->valForNullOrMissing($myvalues, 'SUWI1'),
                        'CE1' => $this->m_oContext->valForNullOrMissing($myvalues, 'CE1'),
                        'QA1' => $this->m_oContext->valForNullOrMissing($myvalues, 'QA1'),
                        'QA2' => $this->m_oContext->valForNullOrMissing($myvalues, 'QA2'),
                        'QA3' => $this->m_oContext->valForNullOrMissing($myvalues, 'QA3'),
                        'SP1' => $this->m_oContext->valForNullOrMissing($myvalues, 'SP1'),
                        'VREP1' => $this->m_oContext->valForNullOrMissing($myvalues, 'VREP1'),
                        'VREP2' => $this->m_oContext->valForNullOrMissing($myvalues, 'VREP2'),

                        'EBO1' => $this->m_oContext->valForNull($myvalues['EBO1']),
                        'UNP1' => $this->m_oContext->valForNull($myvalues['UNP1']),
                        'REP1' => $this->m_oContext->valForNull($myvalues['REP1']),
                        'DRA1' => $this->m_oContext->valForNull($myvalues['DRA1']),
                        'ELCO1' => $this->m_oContext->valForNull($myvalues['ELCO1']),
                        'ELHO1' => $this->m_oContext->valForNull($myvalues['ELHO1']),
                        'ELSO1' => $this->m_oContext->valForNull($myvalues['ELSO1']),
                        'ELSVO1' => $this->m_oContext->valForNull($myvalues['ELSVO1']),
                        'ELRO1' => $this->m_oContext->valForNull($myvalues['ELRO1']),
                        'ECIR1' => $this->m_oContext->valForNull($myvalues['ECIR1']),
                        'EECC1' => $this->m_oContext->valForNull($myvalues['EECC1']),
                        'EERL1' => $this->m_oContext->valForNull($myvalues['EERL1']),
                        'EARM1' => $this->m_oContext->valForNull($myvalues['EARM1']),
                        'CUT1' => $this->m_oContext->valForNull($myvalues['CUT1']),

                        'updated_dt' => $updated_dt,
                        ])
            ->condition('username',$myvalues['username'],'=')
            ->execute();
            if($nUpdated !== 1)
            {
                throw new \Exception('Expected to update 1 record but updated '
                        .$nUpdated.' instead!');
            }

            //Now write all the child records.
            $this->m_oPageHelper->writeChildRecords($myvalues);

            //Success if we are here
            $msg = 'Saved updates for '.$myvalues['username'].' ('.$myvalues['role_nm'].')';
            drupal_set_message($msg);
        } catch (\Exception $ex) {
            $nUpdated = FALSE;
            $msg = 'Failed saved updates for '.$myvalues['username'].' ('.$myvalues['role_nm'].')';
            drupal_set_message($msg,'error');
            error_log($msg.' because '.$ex->getMessage()
                    ."\nData Details..."
                    .print_r($myvalues,TRUE));
        }

        //Returns 1 if everything was okay.
        return $nUpdated;
    }

    private function writeKeywords($nUID, $weightgroup, $userpref_keywords, $bSpecialist, $updated_dt)
    {
        $this->m_oPageHelper->writeKeywords($nUID, $weightgroup, $userpref_keywords, $bSpecialist, $updated_dt);
    }

    /**
     * @return array of all option values for the form
     */
    public function getAllOptions()
    {
        return $this->m_oPageHelper->getAllOptions();
    }

    private function formatKeywordText($myvalues)
    {
        return $this->m_oPageHelper->formatKeywordText($myvalues);
    }

    /**
     * Get all the form contents for rendering
     * @return type renderable array
     */
    function getForm($form, &$form_state, $disabled, $myvalues)
    {
        $oUserInfo = $this->m_oContext->getUserInfo();
        $form = $this->m_oPageHelper->getForm($form, $form_state, $disabled, $myvalues);

        //Replace the intro blurb
        $form['data_entry_area1']['introblurb'] = NULL;

        if (!$oUserInfo->isSiteAdministrator())
        {
            //Clear the password block.
            $form['data_entry_area1']['leftpart']['password'] = NULL;
        }

        //Do NOT let them change the role or name of an existing user!!!!
        $form['data_entry_area1']['leftpart']['role_nm']['#disabled'] = TRUE;
        $form['data_entry_area1']['leftpart']['username']['#disabled'] =  TRUE;

        $nSelfUID = $oUserInfo->getUserID();
        if(isset($myvalues['uid']))
        {
            $editself = ($nSelfUID == $myvalues['uid']);
        } else {
            $editself = FALSE;
        }

        if($editself)
        {
            //Don't allow a user to disable their own account.
            $form['data_entry_area1']['rightpart']['accountactive_yn']['#disabled'] = TRUE;

            //Just close the dialog
            $cancelbuttonclass = 'raptor-dialog-cancel';
        } else {
            //Go back to management screen.
            $cancelbuttonclass = 'admin-cancel-button';
        }

        $form['data_entry_area1']['action_buttons'] = array(
            '#type' => 'item',
            '#prefix' => '
',
            '#suffix' => '
',
            '#tree' => TRUE,
        );
        $form['data_entry_area1']['action_buttons']['create'] = array('#type' => 'submit'
                , '#attributes' => array('class' => array('admin-action-button'))
                , '#value' => t('Save Profile Updates')
                , '#disabled' => $disabled
            );

        global $base_url;
        $goback = $this->getGobacktoFullURL();
        $form['data_entry_area1']['action_buttons']['cancel'] = FormHelper::getExitButtonMarkup($goback);
        return $form;
    }
}


text:
namespace blink {

CallbackInterfaceBase::CallbackInterfaceBase(
    v8::Local<v8::Object> callback_object,
    SingleOperationOrNot single_op_or_not) {
  DCHECK(!callback_object.IsEmpty());

  v8::Isolate* isolate = callback_object->GetIsolate();
  callback_object_.Set(isolate, callback_object);

  incumbent_script_state_ = ScriptState::From(isolate->GetIncumbentContext());
  is_callback_object_callable_ =
      (single_op_or_not == kSingleOperation) && callback_object->IsCallable();

  // Set |callback_relevant_script_state_| iff the creation context and the
  // incumbent context are the same origin-domain. Otherwise, leave it as
  // nullptr.
  v8::Local<v8::Context> creation_context = callback_object->CreationContext();
  if (BindingSecurityForPlatform::ShouldAllowAccessToV8Context(
          incumbent_script_state_->GetContext(), creation_context,
          BindingSecurityForPlatform::ErrorReportOption::kDoNotReport)) {
    callback_relevant_script_state_ = ScriptState::From(creation_context);
  }
}

void CallbackInterfaceBase::Trace(Visitor* visitor) {
  visitor->Trace(callback_object_);
  visitor->Trace(callback_relevant_script_state_);
  visitor->Trace(incumbent_script_state_);
}

ScriptState* CallbackInterfaceBase::CallbackRelevantScriptStateOrReportError(
    const char* interface_name,
    const char* operation_name) {
  if (callback_relevant_script_state_)
    return callback_relevant_script_state_;

  // Report a SecurityError due to a cross origin callback object.
  ScriptState::Scope incumbent_scope(incumbent_script_state_);
  v8::TryCatch try_catch(GetIsolate());
  try_catch.SetVerbose(true);
  ExceptionState exception_state(GetIsolate(),
                                 ExceptionState::kExecutionContext,
                                 interface_name, operation_name);
  exception_state.ThrowSecurityError(
      "An invocation of the provided callback failed due to cross origin "
      "access.");
  return nullptr;
}

ScriptState* CallbackInterfaceBase::CallbackRelevantScriptStateOrThrowException(
    const char* interface_name,
    const char* operation_name) {
  if (callback_relevant_script_state_)
    return callback_relevant_script_state_;

  // Throw a SecurityError due to a cross origin callback object.
  ScriptState::Scope incumbent_scope(incumbent_script_state_);
  ExceptionState exception_state(GetIsolate(),
                                 ExceptionState::kExecutionContext,
                                 interface_name, operation_name);
  exception_state.ThrowSecurityError(
      "An invocation of the provided callback failed due to cross origin "
      "access.");
  return nullptr;
}

}  // namespace blink


text:
function getCheckCondition( data , headerStr){
   var res = {};
   length = data.length;
   for( i = 0 ; i < length ; ++ i ){
     var t = data[i].val;
     try{
       res[ t ] = $("#" + headerStr + convertCheckBoxValue(t) ).prop('checked');
     }catch( e ){ window.alert( e ); }
   }
   return res;
}

function convertCheckBoxValue( val ){
  return val.replace(/ /g,'_').replace(/\./g,'_').replace(/\//g,'_').replace(/\(/g,'_').replace(/\)/g,'_');
}

function setupCheckBox( data,thStr ,headStr,writeTo ){
  var tplData = GetCurrentTemplateData();

  var length = data.length;
  var i = 0;
  var html = "
";
  if( tplData == null ){
    html += "
" + thStr + '
';
  }else{
    html += '
' + thStr + '	' + tplData.tplCheck + '	' + tplData.tplHitNum + '
';
  }
  for( i = 0; i < length ; ++ i ){
    html += '
';
    html += ''+ data[i].val + '	';
    html += '' + '<input type="checkbox" id="'+ headStr + convertCheckBoxValue( data[i].val ) + '" checked/>'+ '	';
    html += ''+ data[i].cnt + '	';
    html += '
';
  }
  html += "
";
  $(writeTo).html(html);
}

function getConditionList( idstr ){
   var list = $("#" +idstr + " input.cond_value");
   var idx = 0;
   var ret = [];
   list.each( function(){
     var tmp = $(this).val();
     if( tmp ){
       ret[idx] = tmp; ++ idx;
     }
   } );

   return ret;
}

function isIncludeFileName( path , cond_list ){
   if( !cond_list || cond_list.length == 0 ){ return true; }
   var length = cond_list.length;
   for( var i = 0; i < length; ++ i ){
     if( path.indexOf(cond_list[i] ) >= 0 ){
       return true;
     }
   }
   return false;
}

function isExcludeFileName( path , cond_list ){
   if( !cond_list || cond_list.length == 0 ){ return false; }
   var length = cond_list.length;
   for( var i = 0; i < length; ++ i ){
     if( path.indexOf(cond_list[i] ) >= 0 ){
       return true;
     }
   }
   return false;
}

function getWarningSummaryData( data , check ){
  var data_length = data.length;
  var check_length = check.length;
  var check_result = {};

  for( var i = 0 ; i < data_length ; ++ i ){
    for( var j = 0 ; j < check_length ; ++ j ){
      if( check[j].check_func(data[i]) ){
        if(check_result[ check[j].message ] == undefined){
          check_result[ check[j].message ] = 1;
        }else{
          ++ check_result[ check[j].message ];
        }
      }
    }
  }
  return check_result;
}
function writeToWarningField( result ){
  // report
  if( result ){
    var str = '
Warning
';
    str += '
';
    str += '
Item	HitNum</ht>
';
    for( var idx in result ){
      str += '
' + idx + "	" + result[idx] + '
';
    }
    str += '
';
    str += '
';
    $("#warning_field").html(str);
  }
}

$(document).ready( function(){
  try{
    var date_str = "report : " + g_report_at;
    var platform_str = g_report_platform;
    if( !g_report_platform ){
       platform_str = "platform : none";
    }else{
       platform_str = "platform : " + platform_str;
    }
    $("#report_at").html(date_str);
    $("#platform").html(platform_str);
  }catch(e){}
});

$(document).on("click",".add_cond",function() {
  var tplData = GetCurrentTemplateData();
  var btnValue = 'delete';
  if( tplData ){ btnValue = tplData.tplDelConditionBtn ;}
  var str = '
<input class="cond_value" type="text" size="80"><input type="button" class="del_cond" value="' + tplData.tplDelConditionBtn + '">
';
  $(this).before(str);
});
$(document).on("click",".del_cond",function() {
  $(this).parent('div').remove();
});

function SetTemplateToHtml( dict )
{
  for( var idx in dict ){
    $("." + idx ).text( dict[ idx] );
    $("input." + idx ).val( dict[ idx] );
  }
}

g_current_template_data = null;
function GetCurrentTemplateData()
{
   return g_current_template_data;
}

function LoadLanguageJavascript( lang , set ,completeCallback=null)
{
  var idstr = "dynamic_js_" + lang +"_" + set;
  if( !document.getElementById( idstr )  ){
    var script = document.createElement( 'script' );
    script.type = 'text/javascript';
    script.src = "template/" + lang + "/template_" + set + ".js";
    script.setAttribute( "id" , idstr );
    script.onload = function(){
      g_current_template_data = eval( "g_" + lang + "_template_" + set );
      SetTemplateToHtml( g_current_template_data  );
      if( completeCallback ){
         completeCallback();
      }
    };
    document.head.appendChild( script );
  }else{
    g_current_template_data = eval( "g_" + lang + "_template_" + set );
    SetTemplateToHtml( g_current_template_data  );
  }
}

function GetLanguageConfig(){
  try{
   if( !g_language ){g_language = "en";}
   return g_language;
  }catch(e){ return "en"; }
}


text:
As a concrete example of extending astx-redux-util, let's explore a
real-world logging extension.  This example is taken directly from my
[GeekU](https://github.com/KevinAst/GeekU) app *(a sandbox project I use
to study various technologies and frameworks)*.

As it turns out, {{book.api.reducerHash}} is a **very central
component to most state changes**.  An overwhelming majority of time
that state changes, it is accomplished by a handler that passes
through {{book.api.reducerHash}}.  *In other words the
{{book.api.reducerHash}} is a clearing house for most state changes*.
In addition, {{book.api.reducerHash}} has a distinct advantage of
knowing several unique operational aspects, such as:

 - whether a handler wishes to operate on the state
 - and the before/after state of a handler operation

As a result, {{book.api.reducerHash}} can play a key roll in implementing a
centralized reducer-based logging utility.  The logging extension
shown here achieves the following advanced characteristics:
 - a central location to log reduction-based probes
 - promotes a standardized format for reduction-based probes
 - dynamically alters the logging probe level based on whether
   the state has changed or not

This last point is a **key feature**.  The probe's logging level is
dynamically determined, based on whether the state has changed, and/or
whether a reducer-specific handler wishes to communicate some
information.  The **advantage** of this dynamic is that it **allows logging
filters to be configured at a very high-level** (i.e. for ALL reducers)
**with minimal output**, because the INSPECT level focuses exclusively on
reducers that have changed state.  This is a VERY USEFUL feature!

Logging levels that are used are:

```
INSPECT: monitor state changes only
DEBUG:   includes explicit reducer logic regardless of state changes
         ... monitors WHICH reducer reasoned about an action
TRACE:   includes ALL reducer enter/exit points
         ... NOT much value: shows ALL state reducer
```

## SideBar: Logging

While logging may not be as fashionable as it once was, I believe that
logging can provide valuable insight into both development and
production diagnostics.

When it comes to state changing diagnostic tools, utilities such as
[redux-devtools](https://github.com/gaearon/redux-devtools) **provide
invaluable insight into state changes**, and can be used without any
tooling in your application code.  With that in mind, **logging** (when
done right) can **provide additional insight into "why" a state change
occurred**.

There are many logging utilities available.  A few features to
consider in your evaluation:

 - filterable probes, allowing each module to have an identity that is filterable
 - interactive configuration (out-of-the-box)
 - efficient pruning (minimizing probe overhead)
 - configurable
   * filter levels
   * output format
   * output destination
 - etc.

## Extending reducerHash

Our logging extension wraps {{book.api.reducerHash}} as follows
([astx-redux-util_loggerPolyfill.js](https://github.com/KevinAst/GeekU/blob/master/src/client/startup/astx-redux-util_loggerPolyfill.js)):

```js
// value-added reducerHash.withLogging()
// ... a wrapper TO reducerHash() - a higher-order function (HOF)
//     - WITH an additional parameter:
//        * log: {Log} the log to use in emitting reducer-based probes
//                     NOTE: the log's filterName is assumed to be the state element name
AstxReduxUtil.reducerHash.withLogging = (log, ...reducerHashArgs) => {

  // validate the supplied log parameter
  assert(log.reducerProbe,
         'AstxReduxUtil.reducerHash.withLogging() requires a log parameter with a log.reducerProbe() method.');

  // invoke the original reducerHash()
  const reducerFn = AstxReduxUtil.reducerHash(...reducerHashArgs);

  // expose actionHandlers ... used in additional logging characteristics
  const actionHandlers = reducerHashArgs[0];

  // wrap the resultant reducerFn to apply our value-added logging
  return (...reducerArgs) => {

    const state  = reducerArgs[0];
    const action = reducerArgs[1];

    // invoke the original reducerFn()
    // NOTE1: The es6 rest syntax accommodates astx-redux-util's
    //        additional "originalReducerState" argument
    // NOTE2: Result (via this extension) is expected to be:
    //        [nextState, logMsgFn|null]
    const reducerResult   = reducerFn(...reducerArgs);
    const handlerInvolved = actionHandlers[action.type] ? true : false;
    const [nextState, logMsgFn] = handlerInvolved
                                   ? reducerResult
                                   : [reducerResult, null]; // pass-throughs return the same state
                                                            // (i.e. NOT wrapped in array)
    // emit our standardized reducer-based logging probe
    log.reducerProbe(action, state!==nextState, logMsgFn);

    return nextState;
  };

};
```

The inline comments should be self-explanatory, but here are some
high-level points of interest:

- The `withLogging` extension is cataloged on the {{book.api.reducerHash}}
  function itself, making it directly available from the
  'astx-redux-util' import.  You may choose to make this it's own
  separate module.

- The logging process requires addition information (i.e. the log
  object) which is passed as a parameter, along with the
  {{book.api.reducerHash}} parameters.  The log's filterName is
  assumed to be the state element name.

- The wrapper invokes the underlying {{book.api.reducerHash}}
  function, and in turn, wraps the created reducer function.

- The **reducer wrapper** is where the **real enhancement is implemented**.

  * The **expectations of each handler is enhanced** by allowing it to
    promote BOTH the **nextState**, along with a **logging probe** (which
    makes up part of the overall probe).  An array is used to
    accumulate both these pieces of information.

  * The logging probe is generated through the enhanced
    log.reducerProbe() method *(detailed next)*.

## log.reducerProbe()

We also polyfill our Log object with the `log.reducerProbe()` method
that standardizes ALL reducer-related logging probes
([astx-redux-util_loggerPolyfill.js](https://github.com/KevinAst/GeekU/blob/master/src/client/startup/astx-redux-util_loggerPolyfill.js)):

```js
Log.prototype.reducerProbe = function(action, stateChanged, reducerMsgFn) {
  const logLevel = (stateChanged)
                     ? Log.INSPECT   // state change
                     : (reducerMsgFn)
                        ? Log.DEBUG  // no state change, but app-specific logic
                        : Log.TRACE; // no state change, and no app-logic

  // emit the "dynamic" logging probe
  this.log(logLevel, ()=> {
    const stateChangedMsg = stateChanged
                             ? 'STATE CHANGED'
                             : 'STATE UN-CHANGED';
    const reducerMsg      = reducerMsgFn
                             ? `,  Reducer Msg: ${reducerMsgFn()}`
                             : '';
    // ex: STATE CHANGED: appState.editSelCrit.extra.fieldOptions,  Action: 'selCrit.edit',  Reducer Msg: set fieldOptions from action
    return `${stateChangedMsg}: ${this.filterName},  Action: '${action.type}'${reducerMsg}`;
  });
};
```

A **key aspect** passed to this method is **an indicator of whether the
state has changed**, which dynamically alters the logging level, as
follows:

```
INSPECT: monitor state changes only
DEBUG:   includes explicit reducer logic regardless of state changes
         ... i.e. monitor WHICH reducer reasoned about an action
TRACE:   includes ALL reducer enter/exit points
         (NOT much value, shows ALL state reducer)
```

## Usage

From a usage perspective, simply replace {{book.api.reducerHash}}
invocations with `reducerHash.withLogging()` and pass the appropriate
log parameter in addition to the reducerHash arguments.

The following reducer is the real code that our
{{book.guide.fullExample}} was derived from *(conceptually replace
`widget` with `selCrit`)*.  The actual code can be found at
[appState.editSelCrit.selCrit.js](https://github.com/KevinAst/GeekU/tree/master/src/client/state/appState.editSelCrit.selCrit.js)
*(all reducers are found at
[GeekU/src/client/state](https://github.com/KevinAst/GeekU/tree/master/src/client/state))*.

```js
import * as Redux       from 'redux';
import * as AstxRedux   from 'astx-redux-util';
import {AT}             from '../actions';
import SelCrit          from '../../shared/domain/SelCrit';
import Log              from '../../shared/util/Log';

import placebo  from './placeboReducer';
import name     from './appState.editSelCrit.selCrit.name';
import desc     from './appState.editSelCrit.selCrit.desc';
import fields   from './appState.editSelCrit.selCrit.fields';
import sort     from './appState.editSelCrit.selCrit.sort';
import filter   from './appState.editSelCrit.selCrit.filter';
import distinguishMajorSortField from './appState.editSelCrit.selCrit.distinguishMajorSortField';

const log         = new Log('appState.editSelCrit.selCrit');
const log4curHash = new Log('appState.editSelCrit.selCrit.curHash');

export default AstxRedux.joinReducers(
  // FIRST: determine content shape (i.e. {} or null)
  AstxRedux.reducerHash.withLogging(log, {
    [AT.selCrit.edit]:       (selCrit, action) => [action.selCrit, ()=>`set selCrit from action.selCrit: ${FMT(action.selCrit)}`],
    [AT.selCrit.edit.close]: (selCrit, action) => [null,           ()=>'set selCrit to null'],
  }),
  AstxRedux.conditionalReducer(
    // NEXT: maintain individual selCrit fields
    //       ONLY when selCrit has content (i.e. is being edited)
    (selCrit, action, originalReducerState) => selCrit !== null,
    AstxRedux.joinReducers(
      Redux.combineReducers({
        _id:      placebo,
        key:      placebo,
        userId:   placebo,
        itemType: placebo,
        lastDbModDate: placebo,

        name,
        desc,

        fields,
        sort,
        distinguishMajorSortField,
        filter,

        dbHash:  placebo,
        curHash: placebo,
      }),
      AstxRedux.conditionalReducer(
        // LAST: maintain curHash
        //       ONLY when selCrit has content (see condition above) -AND- has changed
        (selCrit, action, originalReducerState) => originalReducerState !== selCrit,
        (selCrit, action) => {
          const priorHash = selCrit.curHash;

          selCrit.curHash = SelCrit.hash(selCrit); // OK to mutate (because of changed instance)

          log4curHash.reducerProbe(action,
                                   priorHash !== selCrit.curHash,
                                   ()=>`resetting selCrit.curHash (because selCrit changed) FROM: '${priorHash}' TO: '${selCrit.curHash}'`);
          return selCrit;
        })
    )
  ), null); // initialState
```

Notice:

- By using `reducerHash.withLogging()` we tap into our centralized
  reducer-based logging utility.

- Each reducerHash handler, now returns both the **nextState** along
  with a **logging probe** *(wrapped in an array)*.

- The `log4curHash.reducerProbe()` can also be used outside of
  {{book.api.reducerHash}} control *(promoting standardized
  reducer-based logging probes anywhere)*.

## Demo

By way of demonstration, a picture is worth a thousand words.  Our
logger has a **hidden easter egg** that when invoked, **activates an
interactive logging configuration dialog**.

By setting our appState root filter to INSPECT, our logging probes are
limited to **just the reducers that have changed state**:


text:
package cn.guoyukun.spring.jpa.entity;

import java.io.Serializable;

import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

import org.hibernate.annotations.GenericGenerator;

/**
 *
 抽象实体基类,提供统一的ID,和相关的基本功能方法
 * 如果是oracle请参考{@link BaseOracleEntity}
 *
User: 郭玉昆
 *
Date: 13-1-12 下午4:05
 *
Version: 1.0
 */
@SuppressWarnings("serial")
@MappedSuperclass
public abstract class BaseUUIDEntity<ID extends Serializable> extends AbstractEntity<ID> {

    @Id
    @GeneratedValue(generator = "uuidGenerator")
	@GenericGenerator(name = "uuidGenerator", strategy = "uuid2")
    private ID id;

    @Override
    public ID getId() {
        return id;
    }

    @Override
    public void setId(ID id) {
        this.id = id;
    }

}


text:
module DwcaHunter
  class ResourceFreebase < DwcaHunter::Resource
    def initialize(opts = {})
      @command = "freebase"
      @title = "Freebase"
      @uuid = "bacd21f0-44e0-43e2-914c-70929916f257"
      @download_path = File.join(Dir.tmpdir,
                                 "dwca_hunter",
                                 "freebase",
                                 "data.json")
      @data = []
      @all_taxa = {}
      @cleaned_taxa = {}
      @extensions = []
      super
    end

    def needs_unpack?
      false
    end

    def make_dwca
      organize_data
      generate_dwca
    end

    def download
      DwcaHunter.logger_write(object_id,
                              "Querying freebase for species information...")
      q = {
        query: [{
          type: "/biology/organism_classification",
          id: nil,
          guid: nil,
          name: nil,
          scientific_name: nil,
          synonym_scientific_name: [],
          higher_classification: {
            id: nil,
            guid: nil,
            scientific_name: nil,
            optional: true
          }
        }],
        cursor: true
      }

      run_query(q)

      data = JSON.pretty_generate @data
      f = open(@download_path, "w:utf-8")
      f.write(data)
      f.close
    end

    private

    def run_query(q)
      count = 0
      requests_num = 0
      loop do
        freebase_url = "http://api.freebase.com/api/service/mqlread?query=%s" %
                       URI.encode(q.to_json)
        res = JSON.load RestClient.get(freebase_url)
        requests_num += 1
        break if res["result"].nil? || res["result"].empty?

        if requests_num % 10 == 0
          DwcaHunter.logger_write(object_id,
                                  "Received %s names" % count)
        end
        count += res["result"].size
        res["result"].each { |d| @data << d }
        q[:cursor] = res["cursor"]
      end
    end

    def organize_data
      @data = JSON.load(open(@download_path, "r:utf-8").read)
      @data.each do |d|
        scientific_name = d["scientific_name"].to_s
        id = d["id"]
        parent_id = d["higher_classification"] ?
                    d["higher_classification"]["id"] :
                    nil
        synonyms = d["synonym_scientific_name"]
        @all_taxa[id] = { id: id,
                          parent_id: parent_id,
                          scientific_name: scientific_name,
                          synonyms: synonyms }
      end

      @all_taxa.each do |k, v|
        next unless v[:scientific_name] && v[:scientific_name].strip != ""

        parent_id = v[:parent_id]
        until (@all_taxa[parent_id] &&
                @all_taxa[parent_id][:scientific_name]) || parent_id.nil?
          puts "did not find parent %s" % parent_id
          parent_id = @all_taxa[parent_id]
        end
        parent_id = nil if v[:id] == parent_id
        v[:parent_id] = parent_id
        @cleaned_taxa[k] = v
      end
    end

    def generate_dwca
      DwcaHunter.logger_write(object_id,
                              "Creating DarwinCore Archive file")
      @core = [["http://rs.tdwg.org/dwc/terms/taxonID",
                "http://rs.tdwg.org/dwc/terms/scientificName",
                "http://rs.tdwg.org/dwc/terms/parentNameUsageID"]]

      @extensions << { data: [[
        "http://rs.tdwg.org/dwc/terms/TaxonID",
        "http://rs.tdwg.org/dwc/terms/scientificName"
      ]], file_name: "synonyms.txt" }
      DwcaHunter.logger_write(object_id,
                              "Creating synonyms extension for DarwinCore Archive file")
      count = 0
      @cleaned_taxa.each do |_key, taxon|
        count += 1
        @core << [taxon[:id], taxon[:scientific_name], taxon[:parent_id]]
        if count % BATCH_SIZE == 0
          DwcaHunter.logger_write(object_id,
                                  "Traversing %s extension data record" % count)
        end
        taxon[:synonyms].each do |name|
          @extensions[-1][:data] << [taxon[:id], name]
        end
      end
      @eml = {
        id: @uuid,
        title: @title,
        license: "http://creativecommons.org/licenses/by-sa/3.0/",
        authors: [
          { url: "http://www.freebase.com/home" }
        ],
        abstract: "An entity graph of people, places and things, " \
                  "built by a community that loves open data.",
        metadata_providers: [
          { first_name: "Dmitry",
            last_name: "Mozzherin",
            email: "dmozzherin@mbl.edu" }
        ],
        url: "http://www.freebase.com/home"
      }
      super
    end
  end
end


text:
layout: post
title: BusyBot - Chat Bot for Skype for Business
comments: true
tags: [Microsoft Bot Framework, LUIS, Bots, Chat Bots, Conversational Apps, Skype For Business, Lync]
description: A chatbot for Skype for business
---

We use Skype for Business in our organization which is a fairly common IM applications used in enterprises. The most common distraction while working is popping up of Skype message. And then it takes even more time to reply and finish the conversation, because not replying to colleagues is just rude. So I thought why not create a bot that replies to the messages for me. Unfortunately, Microsoft Bot Framework does not support Skype for Business as one of the channels so I had to find another way to make it works.

Skype for Business has set of APIs called [Unified Communications Web API](https://msdn.microsoft.com/en-us/skype/ucwa/unifiedcommunicationswebapi2_0) which can enable us to integrate it with a bot, however it is unnecessarily complicated (it requires 5 HTTP calls to just send 1 message). So after searching a bit, I found that Lync 2013 SDK still works with Skype for Business (courtesy to my friend Om) and found an excellent starter code at Taha Amin's Github Repo [BotConnectorSkypeForBusiness](https://github.com/tahazayed/BotConnectorSkypeForBusiness).

Lync SDK is fairly straightforward to use. It is event-based and integrates easily with Bot Framework. The only limitation is that Lync 2013/Skype for Business should be already running. Using this I created a simple bot that would let me work in peace. Source code is over [here](https://github.com/ankitbko/SkypeForBusinessBot)

### Dependency
The bot has an **optional** dependency on Redis server. Since the bot will not be talking to Microsoft Bot Connector in any way, we would need to store bot's context somewhere ourself. I had earlier used locally running instance of Redis. However now I have commented out `RedisStore` and used `InMemoryStore`. To use Redis store uncomment the *region* in `Program.cs` and comment InMemory *region*.

You would also need Skype For Business running and signed in.

### Features

So what does the bot do as of now? It accepts the incoming IM and -

* Responds to greetings - Hi, Hello, Good Morning etc.
* In case the person wants to call me or asks whether I am free - respond that I am busy and will talk later and set my status to Busy.
* Ignore any other messages - *Pretend I am busy*
* Exception Filter - Bot does not reply anything if sender is present in **Exception List**. I don't want to reply to my manager that I am busy if he pings me. :)

### How to use

The bot is just a console application. The bot service is not hosted as Web Api, but runs within the console applications.
First create a new [LUIS](https://www.luis.ai/) application by importing the model json from `LuisModel` directory. Copy your LUIS model id and subscription key and paste it in `LuisModel` attribute in `LyncLuisDialog.cs`.

The exception list is located in `App.config` in the console project. Values are *;* separated.

```xml
<add key="ManagerList" value="sip:name1@domain.com;sip:name2@domain.com"/>
```

Make sure your Skype for Business client is running and you are signed in and just start the console project. Ask your friend to ping you and see what happens.

### How it works

Lync 2013 SDK is based on event driven programming. We just subscribe to right event `instantMessageModality.InstantMessageReceived += InstantMessageReceived;` and any messages will come to our `InstantMessageReceived` method.

```csharp
private void InstantMessageReceived(object sender, MessageSentEventArgs e)
{
    var text = e.Text.Replace(Environment.NewLine, string.Empty);
    var conversationService = new ConversationService((InstantMessageModality)sender);
    SendToBot(conversationService, text);
}
```

Once we get the message text, we bootstrap our bot and pass the text as properly formatted `Activity` message.

```csharp
private async void SendToBot(ConversationService conversationService, string text)
{
    Activity activity = new Activity()
    {
        From = new ChannelAccount { Id = conversationService.ParticipantId, Name = conversationService.ParticipantName },
        Conversation = new ConversationAccount { Id = conversationService.ConversationId },
        Recipient = new ChannelAccount { Id = "Bot" },
        ServiceUrl = "https://skype.botframework.com",
        ChannelId = "skype",
    };

    activity.Text = text;

    using (var scope = Microsoft.Bot.Builder.Dialogs.Conversation
        .Container.BeginLifetimeScope(DialogModule.LifetimeScopeTag, builder => Configure(builder, conversationService)))
    {
        scope.Resolve<IMessageActivity>
            (TypedParameter.From((IMessageActivity)activity));
        DialogModule_MakeRoot.Register
            (scope, () => new Dialogs.LyncLuisDialog(scope.Resolve<PresenceService>()));
        var postToBot = scope.Resolve<IPostToBot>();
        await postToBot.PostAsync(activity, CancellationToken.None);
    }
}
```

The bot then follows usual flow of sending the text to LUIS and determining the intent. Based on the context, it will then send it response back to - `BotToUserLync` class which implements `IBotToUser`. This allows us to catch the bot response and instead of sending it to the Bot Connector, we use Lync SDK once again to reply it to our counterpart.

The **Exception Filter** is managed in `ManagerScorable` which implements `IScorable<IActivity, double>`. Scorables are the way to intercept the bot pipeline and branch off with another logic based on requirements. In our case, we check if the incoming message was sent from anyone on the filter list and if it is then we just do nothing. I may write another post on Scorables and discuss about it a little more later.

### Conclusion

That's it. It took me a day to get it all done. The bot is very rudimentary but gets the job done. I now no longer have to reply toe very conversation when I am working. In any case, Skype for Business already saves all the conversation history so I can go over them once I get free. One day of work and lifetime of peace. :)

I hope this article was helpful. If you have any questions, please post a comment below.


text:
<?php

namespace AlphaRPC\Manager\ClientHandler;

/**
 * This is the ClientHandler representation of a Client.
 *
 * It contains the current (and previous) request.
 *
 * @author Reen Lokum <reen@alphacomm.nl>
 * @package AlphaRPC
 * @subpackage ClientHandler
 */
class Client
{
    /**
     *
     * @var ClientBucket
     */
    protected $bucket;

    /**
     * Id for the client
     *
     * @var string
     */
    protected $id;

    /**
     * Last activity in seconds including microtime.
     *
     * @var float
     */
    protected $time;

    /**
     * The id of the request the client is interested in.
     *
     * @var string
     */
    protected $request = null;

    /**
     * The id of the previous request the client was interested in.
     *
     * @var string
     */
    protected $previousRequest = null;

    /**
     * Is the client willing to wait for the worker to execute the job.
     *
     * When true the client will block untill a result is given.
     *
     * @var boolean
     */
    protected $waitForResult;

   /**
    * Creates a new Client instance.
    *
    * @param \AlphaRPC\Manager\ClientHandler\ClientBucket $bucket
    * @param string $id
    */
    public function __construct(ClientBucket $bucket, $id)
    {
        $this->bucket = $bucket;
        $this->id = $id;
        $this->time = microtime(true);
        $this->bucket->refresh($this);
    }

    /**
     * Id for the client
     *
     * @return string
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Last activity in seconds including microseconds.
     *
     * @return float
     */
    public function getTime()
    {
        return $this->time;
    }

    /**
     * The id of the Request the client is interested in.
     *
     * @return string
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * The id of the previous Request the client was interested in.
     *
     * @return string
     */
    public function getPreviousRequest()
    {
        return $this->previousRequest;
    }

    /**
     * Is the client willing to wait for the worker to execute the job.
     *
     * When true the client will block untill a result is given.
     *
     * @return boolean
     */
    public function getWaitForResult()
    {
        return $this->waitForResult;
    }

    /**
     * Sets the request the client is interested in.
     *
     * @param string  $request
     * @param boolean $waitForResult
     *
     * @return \AlphaRPC\Manager\ClientHandler\Client
     */
    public function setRequest($request, $waitForResult = true)
    {
        $this->previousRequest = $this->request;
        $this->request = $request;
        $this->waitForResult = $waitForResult;
        $this->bucket->refresh($this);

        return $this;
    }
}


text:
/**
 * Created by rick on 15/10/6.
 */

function rowStyleOvertime(row, index) {
    if(row.godowndate == ""){
        godownDate = moment()
    }else{
        godownDate = moment(row.godowndate, "YYYY.MM.DD")
    }
    requiredDate = moment(row.requireddate, "YYYY.MM.DD")
    if (requiredDate.diff(godownDate, "days") < 0){
        return {classes: "danger"};
    }
    if (requiredDate.diff(godownDate, "days") < 2 ){
        return {classes: "warning"};
    }
    return {}
}

function supplierFormatter(value, row){
    return wbSprintf('%s', value, row.suppliername, value)
}
function modelFormatter(value, row){
    if (row.product == ""){
        return value
    }
    return wbSprintf('%s', row.product, value)
}


text:
<?php
namespace Skalda\TestLinkAPI\Entities;

use Skalda\TestLinkAPI\Client;

class PlanTestCase extends BaseEntity
{

	protected $instances = [];
	protected $testPlan;
	protected $testCase;
	protected $fullPath = [];

	public $id;
	public $name;
	public $versionId;
	public $version;
	public $externalId;
	public $executionType;
	public $status;
	public $executionOrder;
	public $fullExternalId;

	public function __construct(Client $client, TestPlan $plan, Build $build = null, array $values)
	{
		parent::__construct($client);

		$platforms = [];
		foreach($plan->getPlatforms() as $platform) {
			$platforms[$platform->id] = $platform;
		}

		$tc = null;

		foreach($values as $platformId => $testCase) {
			$instance = new PlanTestCaseInstance($client);
			$instance->featureId = $testCase['feature_id'];
			$instance->platformId = $testCase['platform_id'];
			$instance->platformName = $testCase['platform_name'];
			$instance->execStatus = $testCase['exec_status'];
			$instance->executionDuration = $testCase['execution_duration'];
			$instance->execId = $testCase['exec_id'];
			$instance->tcversionId = $testCase['tcversion_number'];
			$instance->execOnBuild = $testCase['exec_on_build'];
			$instance->execOnTPlan = $testCase['exec_on_tplan'];
			$instance->setTestCase($this);
			if($build) {
				$instance->setBuild($build);
			}

			if(is_array($platforms) && count($platforms) > 0 && isset($platforms[$instance->platformId])) {
				$instance->setPlatform($platforms[$instance->platformId]);
			}

			$this->instances[] = $instance;

			$tc = $testCase;
		}

		if($tc) {
			$this->id = $tc['tcase_id'];
			$this->name = $tc['tcase_name'];
			$this->versionId = $tc['tcversion_id'];
			$this->version = $tc['version'];
			$this->externalId = $tc['external_id'];
			$this->executionType = $tc['execution_type'];
			$this->status = $tc['status'];
			$this->executionOrder = $tc['execution_order'];
			$this->fullExternalId = $tc['full_external_id'];
			$this->testPlan = $plan;
		}
	}

	public function getInstances()
	{
		return $this->instances;
	}

	public function getFullPath()
	{
		if($this->fullPath === null) {
			$this->fullPath = $this->client->getFullPath($this);
		}

		return $this->fullPath;
	}

	public function setFullPath(array $path)
	{
		$this->fullPath = $path;
	}

	public function getTestCase()
	{
		if($this->testCase === null) {
			$this->testCase = $this->client->getTestCaseByPlanTestCase($this);
		}
		return $this->testCase;
	}

	public function setTestCase(TestCase $testCase)
	{
		$this->testCase = $testCase;
	}

	public function getTestPlan()
	{
		return $this->testPlan;
	}

}


text:
namespace content {

using BlockListedFeatures = blink::scheduler::WebSchedulerTrackedFeatures;
using ChromeTrackEvent = perfetto::protos::pbzero::ChromeTrackEvent;

// Represents the result whether the page could be stored in the back-forward
// cache with the reasons.
// TODO(rakina): Rename this to use "Page" instead of "Document", to follow
// the naming of BackForwardCacheImpl::CanStorePageNow().
class CONTENT_EXPORT BackForwardCacheCanStoreDocumentResult {
 public:
  using NotRestoredReasons =
      base::EnumSet<BackForwardCacheMetrics::NotRestoredReason,
                    BackForwardCacheMetrics::NotRestoredReason::kMinValue,
                    BackForwardCacheMetrics::NotRestoredReason::kMaxValue>;

  BackForwardCacheCanStoreDocumentResult();
  BackForwardCacheCanStoreDocumentResult(
      BackForwardCacheCanStoreDocumentResult&);
  BackForwardCacheCanStoreDocumentResult(
      BackForwardCacheCanStoreDocumentResult&&);
  ~BackForwardCacheCanStoreDocumentResult();

  bool operator==(const BackForwardCacheCanStoreDocumentResult& other) const;

  // Add reasons contained in the |other| to |this|.
  void AddReasonsFrom(const BackForwardCacheCanStoreDocumentResult& other);
  bool HasNotRestoredReason(
      BackForwardCacheMetrics::NotRestoredReason reason) const;

  void No(BackForwardCacheMetrics::NotRestoredReason reason);
  void NoDueToFeatures(BlockListedFeatures features);

  void NoDueToRelatedActiveContents(
      absl::optional<ShouldSwapBrowsingInstance> browsing_instance_swap_result);

  // TODO(hajimehoshi): Replace the arbitrary strings with base::Location /
  // FROM_HERE for privacy reasons.
  void NoDueToDisableForRenderFrameHostCalled(
      const std::set<BackForwardCache::DisabledReason>& reasons);
  void NoDueToDisallowActivation(uint64_t reason);
  // TODO(crbug.com/1341507): Remove this function.
  void NoDueToAXEvents(const std::vector<ui::AXEvent>& events);

  // The conditions for storing and restoring the pages are different in that
  // pages with cache-control:no-store can enter back/forward cache depending on
  // the experiment flag, but can never be restored.
  bool CanStore() const;
  bool CanRestore() const;

  const NotRestoredReasons& not_restored_reasons() const {
    return not_restored_reasons_;
  }
  BlockListedFeatures blocklisted_features() const {
    return blocklisted_features_;
  }

  const std::set<BackForwardCache::DisabledReason>& disabled_reasons() const {
    return disabled_reasons_;
  }

  const absl::optional<ShouldSwapBrowsingInstance>
  browsing_instance_swap_result() const {
    return browsing_instance_swap_result_;
  }

  const std::set<uint64_t>& disallow_activation_reasons() const {
    return disallow_activation_reasons_;
  }

  const std::set<ax::mojom::Event>& ax_events() const { return ax_events_; }

  std::string ToString() const;
  std::vector<std::string> GetStringReasons() const;

  void WriteIntoTrace(
      perfetto::TracedProto<
          perfetto::protos::pbzero::BackForwardCacheCanStoreDocumentResult>
          result) const;

 private:
  void AddNotRestoredReason(BackForwardCacheMetrics::NotRestoredReason reason);
  // Returns a one-sentence of explanation for a NotRestoredReason.
  std::string NotRestoredReasonToString(
      BackForwardCacheMetrics::NotRestoredReason reason) const;
  // Returns a name in string for a NotRestoredReason.
  std::string NotRestoredReasonToReportString(
      BackForwardCacheMetrics::NotRestoredReason reason) const;

  NotRestoredReasons not_restored_reasons_;
  BlockListedFeatures blocklisted_features_;
  std::set<BackForwardCache::DisabledReason> disabled_reasons_;
  absl::optional<ShouldSwapBrowsingInstance> browsing_instance_swap_result_;
  std::set<uint64_t> disallow_activation_reasons_;
  // The list of the accessibility events that made the page bfcache ineligible.
  std::set<ax::mojom::Event> ax_events_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_RENDERER_HOST_BACK_FORWARD_CACHE_CAN_STORE_DOCUMENT_RESULT_H_


text:
NSString * const MGCCircleMarkAttributeName = @"MGCCircleMarkAttributeName";

@implementation MGCCircleMark

- (instancetype)init
{
    if (self = [super init]) {
        _color = [UIColor redColor];
        _borderColor = [UIColor clearColor];
        _margin = 5.;
    }
    return self;
}

@end

@implementation NSMutableAttributedString (MGCAdditions)

- (void)processCircleMarksInRange:(NSRange)range
{
    [self enumerateAttribute:MGCCircleMarkAttributeName inRange:range options:0 usingBlock:^(id  _Nullable value, NSRange subrange, BOOL * _Nonnull stop) {

        if (value) {
            MGCCircleMark *circleMark = (MGCCircleMark*)value;

            NSAttributedString *subAttrStr = [self attributedSubstringFromRange:subrange];
            UIImage *image = [subAttrStr imageWithCircleMark:circleMark];

            NSTextAttachment *attachment = [NSTextAttachment new];
            attachment.image = image;
            attachment.bounds = CGRectMake(circleMark.margin, circleMark.yOffset, attachment.image.size.width, attachment.image.size.height);
            NSAttributedString *imgStr = [NSAttributedString attributedStringWithAttachment:attachment];

            [self replaceCharactersInRange:subrange withAttributedString:imgStr];
        }
    }];
}

@end

@implementation NSAttributedString (MGCAdditions)

- (UIImage*)imageWithCircleMark:(MGCCircleMark*)mark
{
    CGSize maxSize = CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX);
    CGRect strRect = [self boundingRectWithSize:maxSize options:NSStringDrawingUsesLineFragmentOrigin context:nil];
    CGFloat markWidth = fmaxf(strRect.size.width, strRect.size.height) + 2. * mark.margin;
    CGRect markRect = CGRectMake(0, 0, markWidth, markWidth);

    UIGraphicsBeginImageContextWithOptions(markRect.size, NO, 0.0f);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    CGContextSaveGState(ctx);

    CGContextSetStrokeColorWithColor(ctx, mark.borderColor.CGColor);
    CGContextSetFillColorWithColor(ctx, mark.color.CGColor);

    CGContextAddEllipseInRect(ctx, CGRectInset(markRect, 1, 1));
    CGContextDrawPath(ctx, kCGPathFillStroke);

    strRect.origin = CGPointMake(markWidth/2. - strRect.size.width/2., markWidth/2. - strRect.size.height/2.);
    [self drawWithRect:strRect options:NSStringDrawingUsesLineFragmentOrigin context:nil];

    CGContextRestoreGState(ctx);
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return img;
}

- (NSAttributedString*)attributedStringWithProcessedCircleMarksInRange:(NSRange)range
{
    NSMutableAttributedString *attrStr = [self mutableCopy];
    [attrStr processCircleMarksInRange:range];
    return attrStr;
}

@end