text:
\chapter{Deployment}
\section{Prerequisites}
\subsection{XMPP server}
\copilot components use XMPP to communicate. We recommend using \indexed{ejabberd} \cite{ejabberd}. Ejabberd sources and documentation can be obtained from
http://www.ejabberd.im/.

\subsection{Chirp server}
Co-Pilot components exchange files using \indexed{Chirp}\cite{chirp}. Chirp sources and documentation can be obtained from http://nd.edu/~ccl/software/chirp/. Chirp server should be running on both Job (or Storage) Manager host.

\lstset{caption=Starting Chirp server}
\begin{lstlisting}
$ mkdir -p JM_CHIRP_WORK_DIR
$ chirp_server -i non_root_user -u - -d all -r JM_CHIRP_WORK_DIR
\end{lstlisting}

Where \emph{JM\_CHIRP\_WORK\_DIR} should coincide with the directory defined as \emph{JM\_CHIRP\_WORK\_DIR} in Manager's configuration file (see ~\ref{sct:jmconfig}).

\subsection{Redis server}
Co-Pilot Generic Job Manager uses \indexed{Redis}\cite{redis} for storing the job queue. Redis sources and documentation can be obtained from http://redis.io/.

\subsection{MongoDB server}
Co-Pilot Monitor and Co-Pilot Dashboard use \indexed{MongoDB}\cite{mongodb} for storing details about Co-Pilot agents and Dashboard settings. Binary distribution of MongoDB can be obtained from http://mongodb.org/downloads.

\subsection{Perl modules}
\copilot is written in Perl programming language and depends on a list of 3rd party Perl modules. On CernVM, the installation of these modules will be triggered automatically during the installation of \copilot. To install all the dependencies on CernVM manually one can trigger the installation of perl-Component-Copilot package (all \copilot components depend on it):

\lstset{caption=Installing \copilot dependencies on CernVM}
\begin{lstlisting}
$ sudo conary update perl-Copilot
\end{lstlisting}

Running \copilot Generic Job Manager requires manual installation of Perl modules for Redis and MongoDB using CPAN:

\lstset{caption=Installing Perl modules}
\begin{lstlisting}
$ sudo cpan CPAN
['no' to manual configuration]
$ sudo cpan
['yes' to automatic configuration]
cpan[1]> reload cpan
cpan[2]> reload index
cpan[3]> install Redis
cpan[4]> install JSON
cpan[5]> notest install MongoDB
\end{lstlisting}

Installation procedure is interactive and depending on target system it might take some time. When asked if dependencies should be installed, answer with yes. Tests won't be performed for the MongoDB library because they require an up-and-running installation of MongoDB.

\subsection{Graphite}
\subsubsection{Installing Graphite}
Graphite is used for collecting statistics and it powers Co-Pilot Monitor.
Graphite sources and documentation can be obtained from http://graphite.wikidot.com/.

Graphite depends on the following Python libraries:
\begin{itemize}
  \item PySQLite2 (comes with Python 2.5 or newer)
  \item ctypes (comes with Python 2.5 or newer)
  \item hashlib (comes with Python 2.5 or newer)
  \item Zope.Interface (3.7.0)
  \item Twisted (11.1.0)
  \item Gevent (0.13.6)
  \item Gunicorn (0.11.0)
  \item Django (1.3)
  \item django-tagging (0.3.1)
  \item PyMongo (2.2)
  \item PyCairo (available in CernVM's package manager)
\end{itemize}

A script is provided in \texttt{copilot-monitor/utils/install-graphite.sh} which downloads and installs all Graphite's prerequisites.
A list of URLs from which the packages can be manually obtained is provided in the file as well.

There are some known problems with Graphite being installed via Python's package manager, which is why you are advised to it from source:

\lstset{caption=Installing Graphite}
\begin{lstlisting}
$ wget http://github.com/downloads/graphite-project/whisper/whisper-0.9.10.tar.gz
$ tar xf whisper-0.9.10.tar.gz && cd whisper-0.9.10
$ sudo python setup.py install
$ wget http://github.com/downloads/graphite-project/carbon/carbon-0.9.10.tar.gz
$ tar xf carbon-0.9.10.tar.gz && cd carbon-0.9.10
$ sudo python setup.py install
$ wget http://github.com/downloads/graphite-project/graphite-web/graphite-web-0.9.10.tar.gz
$ tar xf graphite-web-0.9.10.tar.gz && cd graphite-web-0.9.10
$ sudo python setup.py install
\end{lstlisting}

\subsubsection{Configuration}
Before using Graphite's web interface, an administrator account has to be created:
\lstset{caption=Creating admin users}
\begin{lstlisting}
$ cd /opt/graphite/webapp/graphite
$ sudo python manage.py syncdb
\end{lstlisting}

Recommended configuration files for Graphite and Whisper are provided in \texttt{copilot-monitor/util/config}. Note that you may have to adjust the names of hard disks and network interfaces mentioned in configuration files according to your system.
It is also recommended to change the ownership of \texttt{/opt/graphite} directory to a non-root user.

\subsubsection{Troubleshooting}
In some cases it might happen that not all of the Graphite's files were copied over to the installation directory.
To verify your Graphite installation make sure that both \texttt{carbon} and \texttt{twisted} directories exist in \texttt{/opt/graphite/lib}.
Non-functioning aggregation service (\texttt{carbon-aggregator.py}) can be one of the indicators of the broken installation as well.

To fix the problem, make sure that user account which will run Graphite processes has read and write access to following directories:
\lstset{caption=Adjusting directory permissions}
\begin{lstlisting}
$ chown -R manager:manager /opt/graphite
$ chown -R manager:manager /usr/lib64/python2.4/site-packages/Twisted*
\end{lstlisting}

And as a final step remove generated Python bytecode files if Twisted is still reporting an error.
\lstset{caption=Removing Python bytecode files}
\begin{lstlisting}
$ rm /opt/graphite/lib/carbon/aggregator/*.pyc
\end{lstlisting}

\section{Deployment of \copilot Generic Job and Storage Manager}
\subsection{Installing the Job Manager}
To obtain Job Manager sources and install it do

\lstset{caption=\copilot Generic Job Manager installation}
\begin{lstlisting}
$ svn co https://cernvm.cern.ch/project/svn/copilot/trunk copilot
$ svn co \
https://cernvm.cern.ch/project/svn/copilot-jobmanager-generic/trunk \
copilot-jobmanager-generic
$ cd copilot
$ perl Makefile.PL
$ make
$ sudo make install
$ cd ../copilot-jobmanager-generic
$ perl Makefile.PL
$ make
$ sudo make install-all
\end{lstlisting}

\subsection{Configuring the Job Manager}
\label{sct:jmconfig}

The default location of Job Manager configuration is in \texttt{/etc/copilot/copilot-jobmanager-generic.conf}. Config file has a simple format (VARIABLE\_NAME WHITESPACE VARIABLE\_VALUE).
The value of the following variables must be set before the Job Manager can be started:

\begin{itemize}
  \item \emph{JM\_JABBER\_SERVER} - address of your Jabber/XMPP server (e.g. xmpp.cern.ch)
  \item \emph{JM\_JABBER\_DOMAIN} - domain which your Jabber/XMPP server is configured to serve (e.g. xmpp.cern.ch)
  \item \emph{JM\_JABBER\_ID} - Jabber ID of the Job Manager (ID should be registered on your jabber server)
  \item \emph{JM\_JABBER\_PASSWORD} - password for authenticating as JM\_JABBER\_ID on JM\_JABBER\_SERVER
  \item \emph{JM\_JABBER\_RESOURCE} - Jabber resource id (e.g. firstcopilotjm, required for Co-Pilot Monitor)
  \item \emph{JM\_LOGGER\_CONFIG\_FILE} - log file format configuration (e.g. \texttt{/etc/copilot/loggerConf/jobmanager-generic-logger.conf})
  \item \emph{JM\_CHIRP\_SERVER} - address of the server on which Chirp is running
  \item \emph{JM\_CHIRP\_WORK\_DIR} - directory which is 'exported' by the Chirp server
  \item \emph{JM\_DONE\_JOB\_DIR} - directory where the results of the done jobs are saved
  \item \emph{JM\_REDIS\_SERVER} - machine on which Redis DB is running (e.g. localhost)
  \item \emph{JM\_WAITING\_JOBS\_LIST} - Name of the list in the Redis DB containing waiting jobs' IDs (defaults to 'waiting\_jobs')
  \item \emph{JM\_JOB\_REQUIRE\_FILE} - Name of the file which is required to be present on the Agent machine which receives a job from this Job Manager (optional). For details see \ref{sct:havegetjob}.
  \item \emph{JM\_QUEUE\_ONLY\_MODE\_ON} - Set to '1' if you want the Job Manager to only act as a job queue (i.e. ignore messages related to storing job results)
  \item \emph{JM\_STORAGE\_ONLY\_MODE\_ON} - Set to '1' if you want the Job Manager to only act as a storage service (i.e. ignore messages related to requesting new jobs)
  \item \emph{MON\_JABBER\_ADDRESS} - Jabber address of the Monitor component (e.g mon@xmpp.cern.ch; optional)
  \item \emph{HB\_CHAT\_SERVER} - domain name of the XMPP MUC server (e.g. conference.xmpp.cern.ch; optional)
  \item \emph{HB\_CHAT\_ROOM} - chat room used for Heartbeat service (e.g. pilots-lounge; optional)
 \end{itemize}

You might also want to take a look at the log format definition file (specified by JM\_LOGGER\_CONFIG\_FILE).

\subsection{Starting the Job Manager}

Job manager can be started with \texttt{copilot-jobmanager-generic}. The output of the command should look like this:

\lstset{caption=Starting \copilot Generic Job Manager}
\begin{lstlisting}
$ copilot-jobmanager-generic
[Fri Mar 25 16:18:57 2011] [info] The component does not need to be waken up.
[Fri Mar 25 16:18:57 2011] [info] The security module does not need to be waken up.

\end{lstlisting}

\section{Deployment of Co-Pilot Agent}

\subsection{Installing the Agent}
To obtain Agent sources and install it do:

\lstset{caption=\copilot Agent installation}
\begin{lstlisting}
$ svn co https://cernvm.cern.ch/project/svn/copilot/trunk copilot
$ svn co \
https://cernvm.cern.ch/project/svn/copilot-agent/trunk \
copilot-agent
$ cd copilot-agent
$ perl Makefile.PL
$ make
$ sudo make install-all
\end{lstlisting}

\subsection{Configuring the Agent}
Agent configuration file is located in \texttt{/etc/copilot/copilot-agent.conf}. Configuration file has a simple format (VARIABLE\_NAME WHITESPACE VARIABLE\_VALUE).
The value of the following variables must be set before the Agent can be started:

\begin{itemize}
  \item \emph{JM\_JABBER\_ADDRESS} - Jabber ID of the Job Manager (jobmanager@xmpp.cern.ch)
  \item \emph{AGENT\_JABBER\_SERVER} - address of your Jabber/XMPP server (e.g. xmpp.cern.ch)
  \item \emph{AGENT\_JABBER\_DOMAIN} -  domain which your Jabber/XMPP server is configured to serve (e.g. xmpp.cern.ch)
  \item \emph{AGENT\_JABBER\_ID} - Jabber ID of the Agent (ID should be registered on your jabber server)
  \item \emph{AGENT\_JABBER\_PASSWORD} -  password for authenticating as \texttt{AGENT\_JABBER\_ID} on \texttt{AGENT\_JABBER\_SERVER}
  \item \emph{AGENT\_LOGGER\_CONFIG\_FILE} - log file format configuration (e.g. \texttt{/etc/copilot/loggerConf/agent-logger.conf})
  \item \emph{AGENT\_WORK\_DIR} - working directory of the Agent (should be writeable)
  \item \emph{HB\_CHAT\_SERVER} - domain name of the XMPP MUC server (e.g. conference.xmpp.cern.ch; optional)
  \item \emph{HB\_CHAT\_ROOM} - chat room used for Heartbeat service (e.g. pilots-lounge; optional)
\end{itemize}

You might also want to take a look at the log format definition file (specified by JM\_LOGGER\_CONFIG\_FILE).

\subsection{Starting the Agent}

Agent can be started with copilot-agent. The output of the command should look like this:

\lstset{caption=Starting \copilot Agent}
\begin{lstlisting}
$ copilot-agent
[Fri Mar 25 17:09:02 2011] [info] Waking the component up.
[Fri Mar 25 17:09:03 2011] [info] The security module does not need to be waken up.
[Fri Mar 25 17:09:03 2011] [info] Component was waken up. Asking job manager for a job
[Fri Mar 25 17:09:03 2011] [info] Asking jobmanager@xmpp.cern.ch for an address of the job manager
[Fri Mar 25 17:09:03 2011] [debug] Sending message to jobmanager@xmpp.cern.ch the component (Msg ID:47b06aea-2222-4f41-be1b-507de80de51c)
[Fri Mar 25 17:09:03 2011] [debug] Got ACK for 47b06aea-2222-4f41-be1b-507de80de51c
\end{lstlisting}

\section{Deployment of Co-Pilot Monitor}

\subsection{Installing Co-Pilot Monitor}

To obtain Monitor's source code and install it:
\lstset{caption=Installing the Monitor}
\begin{lstlisting}
$ svn co https://cernvm-copilot-monitor.googlecode.com/svn/trunk/copilot-monitor\
copilot-monitor
$ cd copilot-monitor
$ perl Makefile.PL
$ make
$ sudo make install-all
\end{lstlisting}

\subsection{Configuring Co-Pilot Monitor}

Monitor's configuration file is located in \texttt{/etc/copilot/copilot-monitor.conf} and follows the same format as other Co-Pilot components: (VARIABLE\_NAME WHITESPACE VARIABLE\_VALUE).

\begin{itemize}
  \item \emph{MON\_JABBER\_SERVER} - address of your Jabber/XMPP server (e.g. xmpp.cern.ch)
  \item \emph{MON\_JABBER\_DOMAIN} - domain which your Jabber/XMPP server is configured to serve (e.g. xmpp.cern.ch)
  \item \emph{MON\_JABBER\_ID} - Jabber ID of the Job Manager (ID should be registered on your jabber server)
  \item \emph{MON\_JABBER\_PASSWORD} - password for authenticating as \texttt{MON\_JABBER\_ID} on \texttt{MON\_JABBER\_SERVER}
  \item \emph{CARBON\_SERVER} - address of Carbon database server
  \item \emph{CARBON\_PORT} - port on which Carbon aggregation service is running (default: 2023)
  \item \emph{MONGODB\_SERVER} - machine on which MongoDB is running (e.g. localhost)
  \item \emph{MONGODB\_PORT} - port on which MongoDB is running (default: 27017)
\end{itemize}

\subsection{Starting the Monitor}

Monitor can be started with the \texttt{copilot-monitor} command. The output should look like this:
\lstset{caption=Starting \copilot Monitor}
\begin{lstlisting}
$ copilot-monitor
[Thu Aug 11 19:52:58 2011] [info] The component does not need to be waken up.
[Thu Aug 11 19:52:58 2011] [info] The security module does not need to be waken up.
[Thu Aug 11 19:53:06 2011] [info] Sent 0 updates to Carbon.
\end{lstlisting}

\subsection{Ejabberd Module}

\subsubsection{Installing ejabberd Module}

As a part of the monitoring solution, an ejabberd module is available for collecting the data like number of connected users or their geographical location.
\lstset{caption=Installing ejabberd module}
\begin{lstlisting}
$ cd copilot/src/copilot-ejabberd-module
$ ./configure
$ make build-deps
$ sudo make install-deps
$ make compile
$ sudo make install
\end{lstlisting}

\subsubsection{Configuring ejabberd Module}

In order to get ejabberd to load the module, ejabberd's configuration file needs to be updated. At the end of list of modules in \texttt{/etc/ejabberd/ejabberd.cfg} you need to add:
\lstset{caption=Updated ejabberd.cfg}
\begin{lstlisting}
{modules, [
  ..., % <- add a colon
  {mod_copilot, [
    {monitor_jid, "monitor@localhost"},
  ]}
]}.
\end{lstlisting}

After modifying the configuration file, the server has to be restarted in order to load the module.

\subsection{Installing Co-Pilot Dashboard}

Monitor comes with a simplified dashboard interface for instant overview of the system. The dashboard is meant to augment the features provided by Graphite's web interface.

To obtain the source code and install it:
\lstset{caption=Installing the Dashboard}
\begin{lstlisting}
$ cd copilot/src/copilot-dashboard
$ python setup.py build
$ sudo python setup.py install
\end{lstlisting}

\subsection{Configuring Co-Pilot Dashboard}

Example configuration file, \texttt{copilot-dashboard.conf.example} has been provided in the source tree, the file should be manually copied to \texttt{/etc/copilot/copilot-dashboard.conf}. Configuration file follows the same format as other Co-Pilot components (VARIABLE\_NAME WHITESPACE VARIABLE\_VALUE):

\begin{itemize}
  \item \emph{DASH\_SERVER\_HOST} - address on which the web server will run (e.g. 0.0.0.0 or localhost)
  \item \emph{DASH\_SERVER\_PORT} - port on which the web server will be available (default: 3274)
  \item \emph{DASH\_GRAPHITE\_HOST} - address of Graphite's web interface (e.g. localhost)
  \item \emph{DASH\_GRAPHITE\_PORT} - port on which Graphite's web interface is running (e.g. 8000)
  \item \emph{DASH\_MONGODB\_HOST} - address on which MongoDB is running (eg. localhost)
  \item \emph{DASH\_MONGODB\_PORT} - port on which MongoDB is running (default: 27017)
  \item \emph{DASH\_MONGODB\_DB} - name of the MongoDB database used by the Dashboard (default: copilot)
  \item \emph{DASH\_GMAPS\_API\_KEY} - API key for Google Maps (optional)
\end{itemize}

\subsection{Starting Co-Pilot Dashboard}

Dashboard can be started with the \texttt{copilot-dashboard} command. By default, the web server will be bound to 0.0.0.0 on port 3274.
\lstset{caption=Starting Co-Pilot}
\begin{lstlisting}
$ copilot-dashboard start
Starting Co-Pilot Dashboard on 0.0.0.0:3274...
\end{lstlisting}

\section{Deployment of Co-Pilot Heartbeat}

\subsection{Installing Co-Pilot Heartbeat}

To obtain source code of the utility and to install it:
\lstset{caption=Installing the Monitor}
\begin{lstlisting}
$ svn co https://cernvm-copilot-monitor.googlecode.com/svn/trunk/copilot-heartbeat\
copilot-heartbeat
$ cd copilot-heartbeat
$ perl Makefile.PL
$ make
$ sudo make install-all
\end{lstlisting}

\subsection{Configuring Co-Pilot Heartbeat}

Heartbeat's configuration file is located in \texttt{/etc/copilot/copilot-heartbeat.conf} and follows the same format as other Co-Pilot components: (VARIABLE\_NAME WHITESPACE VARIABLE\_VALUE).

\begin{itemize}
  \item \emph{HB\_JABBER\_SERVER} - address of your Jabber/XMPP server (e.g. xmpp.cern.ch)
  \item \emph{HB\_JABBER\_DOMAIN} - domain which your Jabber/XMPP server is configured to serve (e.g. xmpp.cern.ch)
  \item \emph{HB\_JABBER\_ID} - Jabber ID of the Job Manager (ID should be registered on your jabber server)
  \item \emph{HB\_JABBER\_PASSWORD} - password for authenticating as MON\_JABBER\_ID on MON\_JABBER\_SERVER
  \item \emph{HB\_CHAT\_SERVER} - domain name of the XMPP MUC server (e.g. conference.xmpp.cern.ch)
  \item \emph{HB\_CHAT\_ROOM} - chat room used for Heartbeat service (e.g. pilots-lounge)
\end{itemize}

\subsection{Configuring XMPP server}

\copilot Heartbeat service requires a working MUC (Multi User Chat) server. Example configuration of the MUC module for Ejabberd is provided below.

\lstset{caption=Example configuration of MUC module}
\begin{lstlisting}
{mod_muc, [
          {host, "conference.@HOST@"},
          {access, muc},
          {access_create, muc_create},
          {access_persistent, muc_create},
          {access_admin, muc_admin},
          {history_size, 0},
          {min_presence_interval, 60},
          {logging, false}
         ]},
\end{lstlisting}

\subsection{Using command-line utility}
\label{sct:heartbeatcmd}

The command-line utility sends commands to components that are connected to the Heartbeat chat room and thus allows remote introspection
of the system.

\lstset{caption=Arguments of copilot-heartbeat utility}
\begin{lstlisting}
copilot-heartbeat [OPTIONS] [COMMAND] [JIDs]
\end{lstlisting}

You can use command line flags to override XMPP settings from the configuration file.

\lstset{caption=Overriding name of the chat room}
\begin{lstlisting}
$ copilot-heartbeat --chat-room=dev-env list
\end{lstlisting}

List of supported commands can be seen with \texttt{--help} flag, or refer to section ~\ref{sct:heartbeatgetstatus}.

To send the command to a specific component you can specify its full JID, a room JID or just its room nickname. If no JIDs are specified then the command will be sent to every component connected to the chat room.

\lstset{caption=Requesting system load from several components}
\begin{lstlisting}
$ copilot-heartbeat systemLoad \
jmgeneric@xmpp.cern.ch/alpha \
pilots-lounge@conference.xmpp.cern.ch/e48b-42d9 \
25af-ecf4
Room nick   1min    5min    15min
alpha       0.90    0.65    0.52
e48b-42d9   1.51    1.23    1.13
24af-ecf4   1.50    0.95    0.40
\end{lstlisting}

\section{Test Job submission}

Jobs can be submitted with the \emph{copilot-job-submit} command provided in \emph{copilot-util} package.

\lstset{caption=Obtaining copilot-util package from SVN}
\begin{lstlisting}
$ svn co https://cernvm.cern.ch/project/svn/copilot-util/trunk copilot-util
$ perl Makefile.PL
$ make
$ sudo make install-all
\end{lstlisting}

The \texttt{copilot-job-submit} accepts the following arguments:
\begin{itemize}
  \item \texttt{--command} - file which will be executed by the agents
  \item \texttt{--packages} - list of packages needed for the job to run
  \item \texttt{--chirp-server} - address of the Chirp server used by the job manager
  \item \texttt{--chirp-dir} - path to the directory exposed by local Chirp server
\end{itemize}

Let us set up a simple script which will print a sum of two numbers. Jobs can be executable files, binaries or scripts. In this case we will use a simple shell script:

\lstset{caption=Test job (\texttt{calculator.sh})}
\begin{lstlisting}
#!/bin/bash

echo "2 + 2" | bc
\end{lstlisting}

This script can be submitted to \copilot as a job with \texttt{copilot-job-submit} command:
\lstset{caption=Submitting a job}
\begin{lstlisting}
$ copilot-job-submit \
--command=calculator.sh \
--chirp-server=cervnvma12.cern.ch \
--chirp-dir=JM_CHIRP_WORK_DIR
\end{lstlisting}

The value of the \texttt{--chirp-server} flag will be provided to agents as the address of the machine where the Chirp server is running. By default \texttt{copilot-job-submit} sets it to the hostname of the machine from where it was executed.
\texttt{JM\_CHIRP\_WORK\_DIR} should coincide with the directory defined as \texttt{JM\_CHIRP\_WORK\_DIR} in Manager's configuration file (see~\ref{sct:jmconfig}). After the job is fetched by the \copilot Agent and executed, its results should be available in \texttt{JM\_DONE\_JOB\_DIR} directory (see~\ref{sct:jmconfig}).

\lstset{caption=Inspecting job results}
\begin{lstlisting}
$ cd JM_DONE_JOB_DIR/[Job ID]
$ tar xvf [Job ID].tgz
$ cat [Job ID]/stdout
4
\end{lstlisting}


text:
from datetime import datetime

from kitsune.announcements.models import Announcement
from kitsune.announcements.tests import AnnouncementFactory
from kitsune.sumo.tests import TestCase
from kitsune.sumo.urlresolvers import reverse
from kitsune.users.tests import UserFactory, add_permission
from kitsune.wiki.tests import LocaleFactory

class TestCreateLocaleAnnouncement(TestCase):
    def setUp(self):
        self.locale = LocaleFactory(locale="es")

    def _create_test(self, status, count):
        """Login, or other setup, then call this."""
        url = reverse("announcements.create_for_locale", locale="es")
        resp = self.client.post(
            url,
            {
                "content": "Look at me!",
                "show_after": "2012-01-01",
            },
        )
        self.assertEqual(resp.status_code, status)
        self.assertEqual(Announcement.objects.count(), count)

    def test_create(self):
        u = UserFactory(is_superuser=1)
        self.client.login(username=u.username, password="testpass")
        self._create_test(200, 1)

    def test_leader(self):
        u = UserFactory()
        self.locale.leaders.add(u)
        self.locale.save()
        self.client.login(username=u.username, password="testpass")
        self._create_test(200, 1)

    def test_has_permission(self):
        u = UserFactory()
        add_permission(u, Announcement, "add_announcement")
        self.client.login(username=u.username, password="testpass")
        self._create_test(200, 1)

    def test_no_perms(self):
        u = UserFactory()
        self.client.login(username=u.username, password="testpass")
        self._create_test(403, 0)

    def test_anon(self):
        self._create_test(302, 0)

class TestDeleteAnnouncement(TestCase):
    def setUp(self):
        self.locale = LocaleFactory(locale="es")

        self.u = UserFactory()

        self.locale.leaders.add(self.u)
        self.locale.save()

        self.announcement = AnnouncementFactory(
            creator=self.u,
            locale=self.locale,
            content="Look at me!",
            show_after=datetime(2012, 1, 1, 0, 0, 0),
        )

    def _delete_test(self, id, status, count):
        """Login, or other setup, then call this."""
        url = reverse("announcements.delete", locale="es", args=(id,))
        resp = self.client.post(url)
        self.assertEqual(resp.status_code, status)
        self.assertEqual(Announcement.objects.count(), count)

    def test_delete(self):
        u = UserFactory(is_superuser=1)
        self.client.login(username=u.username, password="testpass")
        self._delete_test(self.announcement.id, 204, 0)

    def test_leader(self):
        # Use the user that was created in setUp.
        self.client.login(username=self.u.username, password="testpass")
        self._delete_test(self.announcement.id, 204, 0)

    def test_has_permission(self):
        u = UserFactory()
        add_permission(u, Announcement, "add_announcement")
        self.client.login(username=u.username, password="testpass")
        self._delete_test(self.announcement.id, 204, 0)

    def test_no_perms(self):
        u = UserFactory()
        self.client.login(username=u.username, password="testpass")
        self._delete_test(self.announcement.id, 403, 1)

    def test_anon(self):
        self._delete_test(self.announcement.id, 302, 1)

redpajama_set_name: RedPajamaGithub


text:
The [ATM90E26 Datasheet](http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-46002-SE-M90E26-Datasheet.pdf) has an extensive list of calibration registers. Of these the ones we are most interested in are:

- **Ugain** for Voltage gain
- **IgainL** for Live current line gain

These also affect the checksum2 stored in the device and have to be updated together. The library provides a `CalcCheckSum` function to help in evaluating the checksums the ASIC needs when the gain values are changed.


text:
package no.kantega.openaksess.search.controller;

import no.kantega.openaksess.search.model.AutocompleteMultimedia;
import no.kantega.openaksess.search.provider.transformer.MultimediaTransformer;
import no.kantega.openaksess.search.query.AksessSearchContextCreator;
import no.kantega.openaksess.search.security.AksessSearchContext;
import no.kantega.publishing.common.data.Multimedia;
import no.kantega.publishing.common.service.MultimediaService;
import no.kantega.publishing.controls.AksessController;
import no.kantega.search.api.search.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

import static java.util.Collections.emptyList;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
import static org.springframework.web.bind.ServletRequestUtils.*;

@Controller
@RequestMapping("/multimediasearch")
public class MultimediaSearchController implements AksessController {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    AksessSearchContextCreator aksessSearchContextCreator;

    @Autowired
    Searcher searcher;

    @Value("${oa.usefuzzysearch:false}")
    private boolean useFuzzySearch;

    /**
     * How many suggestions should autocomplete give?
     */
    @Value("${multimediasearch.autocomplete.suggestions:15}")
    private int multimediaAutocompleteSuggestions;

    private List<String> facetQueries = emptyList();
    private List<String> facetFields = emptyList();

    @RequestMapping("/search")
    public @ResponseBody
    Map<String, Object> handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, Object> model = new HashMap<>();
        String query = getQuery(request);
        int numberOfHits = 0;
        List<Multimedia> mediaList = new ArrayList<>();

        if (isNotEmpty(query)) {
            SearchResponse searchResponse = performSearch(request, query);
            numberOfHits = searchResponse.getNumberOfHits().intValue();
            mediaList = getListFromSearchResponse(request, searchResponse);
        }

        model.put("numberOfHits", numberOfHits);
        model.put("mediaList", mediaList);
        return model;
    }

    @RequestMapping("/autocomplete")
    public @ResponseBody List<AutocompleteMultimedia> autocomplete(HttpServletRequest request) {
        List<AutocompleteMultimedia> autocompleteList = new ArrayList<>(multimediaAutocompleteSuggestions);
        String term = request.getParameter("term");
        if (isNotBlank(term)) {
            AksessSearchContext searchContext = aksessSearchContextCreator.getSearchContext(request);

            SearchResponse searchResponse = searcher.search(getAutocompleteQuery(term, searchContext));
            List<Multimedia> multimediaList = getListFromSearchResponse(request, searchResponse);
            for (Multimedia m : multimediaList) {
                autocompleteList.add(new AutocompleteMultimedia(m, request));
            }
        }

        return autocompleteList;
    }

    private SearchQuery getAutocompleteQuery(String term, AksessSearchContext searchContext) {
        String queryString = "(title_no:" + term + "*) OR (altTitle_no:" + term + "*)";
        SearchQuery query = new SearchQuery(searchContext, queryString, "indexedContentType:" + MultimediaTransformer.HANDLED_DOCUMENT_TYPE);
        query.setResultsPerPage(multimediaAutocompleteSuggestions);
        query.setQueryType(QueryType.Lucene);
        return query;
    }

    private List<Multimedia> getListFromSearchResponse(HttpServletRequest request, SearchResponse searchResponse) {
        List<Multimedia> multimediaList = new ArrayList<>();

        List<GroupResultResponse> groupResultResponses = searchResponse.getGroupResultResponses();
        MultimediaService multimediaService = new MultimediaService(request);
        for (GroupResultResponse groupResultResponse : groupResultResponses) {
            List<SearchResult> searchResults = groupResultResponse.getSearchResults();
            for (SearchResult searchResult : searchResults) {
                int id = searchResult.getId();
                try {
                    multimediaList.add(multimediaService.getMultimedia(id));
                } catch (Exception e) {
                    log.error("Error getting multimedia for id " + id, e);
                }
            }
        }
        return multimediaList;
    }

    private SearchResponse performSearch(HttpServletRequest request, String query) {
        AksessSearchContext searchContext = aksessSearchContextCreator.getSearchContext(request);
        return searcher.search(getSearchQuery(request, query, searchContext));
    }

    private SearchQuery getSearchQuery(HttpServletRequest request, String query, AksessSearchContext searchContext) {
        SearchQuery searchQuery = new SearchQuery(searchContext, query, getFilterQueries(request));
        searchQuery.setFuzzySearch(useFuzzySearch);
        searchQuery.setPageNumber(getIntParameter(request, "page", 0));
        searchQuery.setResultsPerPage(getIntParameter(request, "resultsprpage", SearchQuery.DEFAULT_RESULTS_PER_PAGE));
        searchQuery.setOffset(getIntParameter(request, "offset", 0));

        addFacetFields(request, searchQuery, searchContext);

        searchQuery.setGroupField(getGroupField(request));
        searchQuery.setGroupQueries(getGroupQueries(request));
        searchQuery.setBoostByPublishDate(getBoostByPublishDate(request));
        return customizeQuery(searchQuery, searchContext, request);
    }

    /**
     * @param request - Current HttpServletRequest.
     * @return whether boostByPublishDate should be true or false for the current search query.
     * default returns the value of request parameter boostByPublishDate, or false if absent.
     */
    public boolean getBoostByPublishDate(HttpServletRequest request) {
        return getBooleanParameter(request, "boostByPublishDate", false);
    }

    /**
     * @param request - Current HttpServletRequest.
     * @return the field to group results by.
     */
    public String getGroupField(HttpServletRequest request) {
        return getStringParameter(request, "groupfield", null);
    }

    /**
     * @param request - Current HttpServletRequest.
     * @return the group queries to add to the current search query.
     */
    public String[] getGroupQueries(HttpServletRequest request) {
        return getStringParameters(request, "groupquery");
    }

    private void addFacetFields(HttpServletRequest request, SearchQuery searchQuery, AksessSearchContext searchContext) {
        boolean excludeDefaultFacets = getBooleanParameter(request, "excludedefaultfacets", false);
        searchQuery.setFacetFields(getFacetFields(request, searchContext, excludeDefaultFacets));

        if (!excludeDefaultFacets) {
            searchQuery.setFacetQueries(facetQueries);
        }
    }

    /**
     * Get the facet fields to be used for the current request.
     * @param request - Current HttpServletRequest.
     * @param excludeDefaultFacets true if the fields defined in the field facetFields should be included in
     *                             the facesFields-list.
     * @return all facetfields to be applied.
     */
    public List<String> getFacetFields(HttpServletRequest request, AksessSearchContext searchContext, boolean excludeDefaultFacets) {
        List<String> fields = excludeDefaultFacets? Collections.<String>emptyList() : facetFields;
        String parameterfacetFields = getStringParameter(request, "facetFields", null);
        if(parameterfacetFields != null){
            fields = Arrays.asList(parameterfacetFields.split(","));
        }
        return fields;
    }

    /**
     * Get the filter queries to be used for the current request.
     * @param request - Current HttpServletRequest.
     * @return all filter queries to be applied.
     */
    public List<String> getFilterQueries(HttpServletRequest request) {
        List<String> filterQueries = new ArrayList<>(Arrays.asList(getStringParameters(request, QueryStringGenerator.FILTER_PARAM)));
        filterQueries.add("indexedContentType:" + MultimediaTransformer.HANDLED_DOCUMENT_TYPE);
        return filterQueries;
    }

    public String getQuery(HttpServletRequest request) {
        return getStringParameter(request, QueryStringGenerator.QUERY_PARAM, "");
    }

    public SearchQuery customizeQuery(SearchQuery searchQuery, AksessSearchContext searchContext, HttpServletRequest request){
        return searchQuery;
    }

    public String getDescription() {
        return "Performs search for Aksess content";
    }
}


text:
/**
Descripcion: Codigo controlador para la pagina grupos.jsp
Autor      : Mauricio Hernandez
Fecha      : 26/10/2009
*/

/**
*Al iniciar la pagina carga los eventos a los controles del formulario
*onClick="
*/

$(document).ready(function() {

	$('#btnGrabar').on('click', function(){
		guardar();
	});

	$('#btnGrabar').on('click', function(){
		limpiar();
	});

	$('#tipo').on('change', function(){
		pintarTablaDetalles();
		// alert( this.value );
	});
});

function limpiar(){

	 $('#descripcion').val('');
	 $('#estatus').prop('checked',true);
	 $('#clave').val('');
}

function guardar(){

	var error="";
	var titulo ='Advertencia - Informacion no válida';
	if ( $('#descripcion').val()=="")  error += 'Descripción
';
	if ( $('#tipo').val()=="")  error += 'Tipo
';
	if ( error=="") {
	var estatus='ACTIVO';
	if (!$('#estatus').prop('checked'))
	   estatus='INACTIVO';
	ShowDelay('Guardando grupo','');
    controladorGruposRemoto.guardarGrupo($('#clave').val(),$('#descripcion').val(),estatus,$('#tipo').val(),{
			 callback:function(items) {
	  			 CloseDelay("Grupo guardado con éxito", 2000, function(){pintarTablaDetalles();});
 		     }
								,errorHandler:function(errorString, exception) {
								   swal("Fallo la operacion:
Error::"+errorString+"-message::"+exception.message+"-JavaClass::"+exception.javaClassName+".
Consulte a su administrador");
								}
			});		}else swal(error,titulo);

}

 function pintarTablaDetalles() {
	quitRow("detallesTabla");
	var estatus='ACTIVO';
	if (!$('#estatus2').attr('checked'))
	   estatus='INACTIVO';
	   ShowDelay('Cargando grupos ' + $('#tipo').val(),'');

	controladorGruposRemoto.getGruposEstatus(estatus,$('#tipo').val(), {
        callback:function(items) {
            jQuery.each(items,function(i) {

 		     	pintaTabla( "detallesTabla", i+1 ,this.ID_GRUPO_CONFIG,this.GRUPO_CONFIG,this.ESTATUS,this.TIPO);

        });
            swal.closeModal();

			//limpiar();
            //swal.hideLoading();
            //swal.disableLoading ()
        }
        ,
        errorHandler:function(errorString, exception) {
           swal("Fallo la operacion:
Error::"+errorString+"-message::"+exception.message+"-JavaClass::"+exception.javaClassName+".
Consulte a su administrador");
        }
    });

 }

  function pintaTabla( table, consecutivo,id,descripcion,estatus,tipo){
 	var tabla = document.getElementById( table ).tBodies[0];
 	var row =   document.createElement( "TR" );
    var htmlCheck = "<input type='checkbox' name='claves' id='claves' value='"+id+"' >";
    var htmlEdit = "";
	row.appendChild( Td("",centro,"",htmlCheck) );
	row.appendChild( Td(descripcion,"","","") );
	row.appendChild( Td(tipo,centro,"","") );
	row.appendChild( Td(estatus,centro,"","") );
    row.appendChild( Td("",centro,"",htmlEdit) );
	tabla.appendChild( row );
 }

 function editar(id,descripcion,estatus,tipo) {
		 $('#descripcion').attr('value',descripcion);
		 if (estatus=='ACTIVO')
		   $('#estatus').attr('checked',true);
		 else
		   $('#estatus').attr('checked',false);
		 $('#clave').attr('value',id);
		 $('#tipo').attr('value',tipo);
 }

  function eliminar(){
	  var checkRetenciones = [];
     $('input[name=claves]:checked').each(function() {checkRetenciones.push($(this).val());	 });
	 if (checkRetenciones.length > 0 ) {
		 jConfirm('¿Confirma que desea eliminar el grupo?','Confirmar', function(r){
			 	if(r){
						ShowDelay('Eliminando el grupo','');
						controladorGruposRemoto.eliminarGrupo(checkRetenciones, {
							callback:function(items) {
							   CloseDelay("Grupo eliminado con éxito", 2000, function(){pintarTablaDetalles();});

							}
							,
							errorHandler:function(errorString, exception) {
							jError("Fallo la operacion:
Error::"+errorString+"-message::"+exception.message+"-JavaClass::"+exception.javaClassName+".
Consulte a su administrador");
							}
						});
					}
		})

	 } else
	    jInformation("Es necesario que seleccione un elemento de la lista");
	 }


text:
/* ###
 * IP: GHIDRA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package agent.dbgeng.jna.dbgeng.dataspaces;

import java.nio.ByteBuffer;

import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT.HRESULT;

import agent.dbgeng.jna.dbgeng.UnknownWithUtils;
import agent.dbgeng.jna.dbgeng.sysobj.WrapIDebugSystemObjects;

public class WrapIDebugDataSpaces extends UnknownWithUtils implements IDebugDataSpaces {
	public static class ByReference extends WrapIDebugSystemObjects
			implements Structure.ByReference {
	}

	public WrapIDebugDataSpaces() {
	}

	public WrapIDebugDataSpaces(Pointer pvInstance) {
		super(pvInstance);
	}

	@Override
	public HRESULT ReadVirtual(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize,
			ULONGByReference BytesRead) {
		return _invokeHR(VTIndices.READ_VIRTUAL, getPointer(), Offset, Buffer, BufferSize,
			BytesRead);
	}

	@Override
	public HRESULT WriteVirtual(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize,
			ULONGByReference BytesWritten) {
		return _invokeHR(VTIndices.WRITE_VIRTUAL, getPointer(), Offset, Buffer, BufferSize,
			BytesWritten);
	}

	@Override
	public HRESULT ReadVirtualUncached(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize,
			ULONGByReference BytesRead) {
		return _invokeHR(VTIndices.READ_VIRTUAL_UNCACHED, getPointer(), Offset, Buffer, BufferSize,
			BytesRead);
	}

	@Override
	public HRESULT WriteVirtualUncached(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize,
			ULONGByReference BytesWritten) {
		return _invokeHR(VTIndices.WRITE_VIRTUAL_UNCACHED, getPointer(), Offset, Buffer, BufferSize,
			BytesWritten);
	}

	@Override
	public HRESULT ReadPhysical(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize,
			ULONGByReference BytesRead) {
		return _invokeHR(VTIndices.READ_PHYSICAL, getPointer(), Offset, Buffer, BufferSize,
			BytesRead);
	}

	@Override
	public HRESULT WritePhysical(ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize,
			ULONGByReference BytesWritten) {
		return _invokeHR(VTIndices.WRITE_PHYSICAL, getPointer(), Offset, Buffer, BufferSize,
			BytesWritten);
	}

	@Override
	public HRESULT ReadControl(ULONG Processor, ULONGLONG Offset, ByteBuffer Buffer,
			ULONG BufferSize, ULONGByReference BytesRead) {
		return _invokeHR(VTIndices.READ_CONTROL, getPointer(), Processor, Processor, Offset, Buffer,
			BufferSize, BytesRead);
	}

	@Override
	public HRESULT WriteControl(ULONG Processor, ULONGLONG Offset, ByteBuffer Buffer,
			ULONG BufferSize, ULONGByReference BytesWritten) {
		return _invokeHR(VTIndices.WRITE_CONTROL, getPointer(), Offset, Buffer, BufferSize,
			BytesWritten);
	}

	@Override
	public HRESULT ReadBusData(ULONG BusDataType, ULONG BusNumber, ULONG SlotNumber,
			ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesRead) {
		return _invokeHR(VTIndices.READ_BUS_DATA, getPointer(), BusDataType, BusNumber, SlotNumber,
			Offset, Buffer, BufferSize, BytesRead);
	}

	@Override
	public HRESULT WriteBusData(ULONG BusDataType, ULONG BusNumber, ULONG SlotNumber,
			ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesWritten) {
		return _invokeHR(VTIndices.WRITE_BUS_DATA, getPointer(), BusDataType, BusNumber, SlotNumber,
			Offset, Buffer, BufferSize, BytesWritten);
	}

	@Override
	public HRESULT ReadIo(ULONG InterfaceType, ULONG BusNumber, ULONG AddressSpace,
			ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesRead) {
		return _invokeHR(VTIndices.READ_IO, getPointer(), InterfaceType, BusNumber, AddressSpace,
			Offset, Buffer, BufferSize, BytesRead);
	}

	@Override
	public HRESULT WriteIo(ULONG InterfaceType, ULONG BusNumber, ULONG AddressSpace,
			ULONGLONG Offset, ByteBuffer Buffer, ULONG BufferSize, ULONGByReference BytesWritten) {
		return _invokeHR(VTIndices.WRITE_IO, getPointer(), InterfaceType, BusNumber, AddressSpace,
			Offset, Buffer, BufferSize, BytesWritten);
	}

	@Override
	public HRESULT ReadMsr(ULONG Msr, ULONGLONGByReference Value) {
		return _invokeHR(VTIndices.READ_MSR, getPointer(), Msr, Value);
	}

	@Override
	public HRESULT WriteMsr(ULONG Msr, ULONGLONG Value) {
		return _invokeHR(VTIndices.WRITE_MSR, getPointer(), Msr, Value);
	}

	@Override
	public HRESULT ReadDebuggerData(ULONG Offset, ByteBuffer Buffer, ULONG BufferSize,
			ULONGByReference BytesRead) {
		return _invokeHR(VTIndices.READ_DEBUGGER_DATA, getPointer(), Offset, Buffer, BufferSize,
			BytesRead);
	}

}


text:
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import withStyles from '../styles/withStyles';
import Typography from '../Typography';

export const styles = {
  /* Styles applied to the root element. */
  root: {
    margin: 0,
    padding: '16px 24px',
    flex: '0 0 auto',
  },
};

const DialogTitle = React.forwardRef(function DialogTitle(props, ref) {
  const { children, classes, className, disableTypography = false, ...other } = props;

  return (
    <div className={clsx(classes.root, className)} ref={ref} {...other}>
      {disableTypography ? (
        children
      ) : (
        <Typography component="h2" variant="h6">
          {children}
        </Typography>
      )}
    </div>
  );
});

DialogTitle.propTypes = {
  /**
   * The content of the component.
   */
  children: PropTypes.node.isRequired,
  /**
   * Override or extend the styles applied to the component.
   * See [CSS API](#css) below for more details.
   */
  classes: PropTypes.object.isRequired,
  /**
   * @ignore
   */
  className: PropTypes.string,
  /**
   * If `true`, the children won't be wrapped by a typography component.
   * For instance, this can be useful to render an h4 instead of the default h2.
   */
  disableTypography: PropTypes.bool,
};

export default withStyles(styles, { name: 'MuiDialogTitle' })(DialogTitle);


text:
---
layout: post
title:  "Jinkens+iOS测试"
category: tech
author: "lucien"
---

#一:安装Jinkens
<http://linjunpop.logdown.com/posts/162202-set-up-jenkins-server-on-the-mac-mini-to-run-ios-tests>

#二:构建iOS工程
```
项目名称:给项目工程起名字.
描述:写一写关于项目的介绍.
GitHub project
        Project url : 项目的github地址
丢弃的构建
    strategy    Log Roraton
    保持构建天数  5     保持最大构建天数  10

源码管理
     Git
  Repositories      Repository URL  https://github.com/guanghetv/YCMath345-iOS.git   (仓库地址)
  Credentials  XXXXXXXXXXX@qq.com    XXXXXXXXX     (github账号密码)
  Branches to build  branch Specifier    */develop-3.5.0   (分支)

构建环境
Provide Node & npm bin/ folder to PATH    node.js     (配置node.js,供测试用例)

构建
    Xcode

    General build setting
    Target  :   YCMath345-iOS
    Clean before build ?    Yes
    Configuration   Debug

Pack application and build .ipa ?

.ipa filename pattern YCMath345-iOS
Output directory $(WORKSPACE)/Release

Advanced Xcode build options
Clean test reports?    Yes
Custom xcodebuild arguments :  -sdk iphonesimulator (这个参数是个坑,配了好长时间,因为appium只能运行iphonesimulator版,不能运行iphoneos版)
Xcode Workspace File    /Users/yulu/.jenkins/workspace/YCMathiOSCI/YCMath345-iOS
Xcode Project File          /Users/yulu/.jenkins/workspace/YCMathiOSCI/YCMath345-iOS
Build output directory     ${WORKSPACE}/build

Execute Shell
Command  mocha ${WORKSPACE}/script/AppiumTest-iOS.js

构建后步骤
Upload  to fir.im
fir.im token   (输入在fir.im的token)
IPA/APK Files (optional)  /Users/yulu/.jenkins/workspace/YCMathiOSCI/Release
ExInclude IPA/APK File Name    **/*unaligned*.apk,**/*unsigned*.apk
Build Notes   Updated by yulu with fir-jenkins

Email Notification
Recipients  yulu@gaunghe.tv    (测试成功或失败都会发邮件通知)
```
#3.总结
根据以上操作,就可以实现Github+Jinkens+Appium+Fir.im+Email.
即每向Github提交一次代码,Jenkins都会拉取代码到本地然后编译,打包,再交由Appium跑测试用例,测试成功后,Jenkins将打包的文件上传到Fir.im上,并发邮件通知开发者.


text:
package de.codesourcery.javr.assembler.elf;

import java.io.IOException;

import de.codesourcery.javr.assembler.elf.ElfWriter.Endianess;
import de.codesourcery.javr.assembler.elf.SectionTableEntry.SpecialSection;
import de.codesourcery.javr.ui.config.ProjectConfiguration.OutputFormat;

public class ElfHeader
{

    // ELF header file types
    private static final int FILE_TYPE_RELOCATABLE = 1;
    private static final int FILE_TYPE_EXECUTABLE = 2;

    public final ElfFile elfFile;

    public ElfHeader(ElfFile file) {
        this.elfFile = file;
    }

    public void write(ElfWriter writer) throws IOException {
        /*

 enum {
00050   EI_MAG0       = 0,          // File identification index.
00051   EI_MAG1       = 1,          // File identification index.
00052   EI_MAG2       = 2,          // File identification index.
00053   EI_MAG3       = 3,          // File identification index.
00054   EI_CLASS      = 4,          // File class.
00055   EI_DATA       = 5,          // Data encoding.
00056   EI_VERSION    = 6,          // File version.
00057   EI_OSABI      = 7,          // OS/ABI identification.
00058   EI_ABIVERSION = 8,          // ABI version.
00059   EI_PAD        = 9,          // Start of padding bytes.
00060   EI_NIDENT     = 16          // Number of bytes in e_ident.
00061 };

typedef struct
{
unsigned char  e_ident [EI_NIDENT] ; 0

Elf32_Half     e_type; // 16
Elf32_Half     e_machine; // 18
Elf32_Word     e_version; // 20
Elf32_Addr     e_entry; // 22
Elf32_Off      e_phoff; // 26
Elf32_Off      e_shoff; // 30
Elf32_Word     e_flags; // 34
Elf32_Half     e_ehsize;// 36

Elf32_Half     e_phentsize; // 38
Elf32_Half     e_phnum;  // 40

Elf32_Half     e_shentsize; // 42
Elf32_Half     e_shnum; // 44

Elf32_Half     e_shstrndx; // 46
// 48
} El32_Ehdr ;
         */

        writer.writeByte( 0x7f ); // eident[EI_MAG0]
        writer.writeByte( 'E' ); // eident[EI_MAG1]
        writer.writeByte( 'L' ); // eident[EI_MAG2]
        writer.writeByte( 'F' ); // eident[EI_MAG3]
        writer.writeByte( 0x01 ); // eident[EI_CLASS] => ELF32
        writer.writeByte( 0x01 ); // eident[EI_DATA] => LSB / little-endian
        writer.writeByte( 0x01 ); // eident[EI_VERSION] => ELF v1
        writer.pad( 9 );

        if ( elfFile.type == OutputFormat.ELF_EXECUTABLE ) {
            writer.writeHalf( FILE_TYPE_EXECUTABLE , Endianess.LITTLE ); // 16: e_type
        } else if ( elfFile.type == OutputFormat.ELF_RELOCATABLE) {
            writer.writeHalf( FILE_TYPE_RELOCATABLE, Endianess.LITTLE ); // 16: e_type
        } else {
            throw new RuntimeException("Internal error,unhandled output type "+elfFile.type);
        }

        writer.writeHalf( 0x53 , Endianess.LITTLE ); // 18: e_machine => AVR
        writer.writeWord( 0x01 , Endianess.LITTLE);  // 20: e_version
        writer.writeWord( 0 , Endianess.LITTLE); // 24: e_entry => address of program entry point

        // 28: e_phoff => program header table offset
        if ( elfFile.type == OutputFormat.ELF_EXECUTABLE ) {
            writer.deferredWriteWord( (w,file) -> w.getMarker( ElfFile.MarkerName.PROGRAM_HEADER_TABLE_START).offset , Endianess.LITTLE);
        } else {
            writer.writeWord( 0 , Endianess.LITTLE );
        }

        writer.deferredWriteWord( ElfFile.MarkerName.SECTION_TABLE_START , Endianess.LITTLE ); // 32: e_shoff => section header table offset
//        writer.writeWord( 0x85 , Endianess.LITTLE ); // 36: e_flags => AVR5 , link-relax
        writer.writeWord( 0x05 , Endianess.LITTLE ); // 36: e_flags => AVR5
        writer.writeHalf( 52 , Endianess.LITTLE);  // 40: e_ehsize => ELF header size
        writer.writeHalf( ProgramTableEntry.SIZE_IN_BYTES , Endianess.LITTLE);  // 42: Elf32_Half     e_phentsize;  // size of ONE entry in the program header table
        writer.deferredWriteHalf( (w,file) -> file.getProgramHeaderCount() , Endianess.LITTLE );  // 44: Elf32_Half     e_phnum;  // number of entries in program header table

        writer.writeHalf( SectionTableEntry.SIZE_IN_BYTES , Endianess.LITTLE);  // 46: Elf32_Half     e_shentsize; // size of ONE entry in the section header table

        writer.deferredWriteHalf( (w,file) -> file.getSectionCount(), Endianess.LITTLE ); // 48: e_shnum => number of section header entries (=number of sections)

        writer.deferredWriteHalf(  (w,file) ->
        file.getTableIndex( file.getSectionByName(  SpecialSection.SHSTRTAB.name ) ) , Endianess.LITTLE);  // 50: Elf32_Half     e_shstrndx; // section header table index of the entry associated with the section name string table
    }
}


text:
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{{ page.title }}</title>
    <link rel="stylesheet" href="stylesheets/normalize.css">
    <link rel="stylesheet" href="stylesheets/styles.css">
    <link rel="stylesheet" href="stylesheets/pygment_trac.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
    <script src="javascripts/index.js"></script>
  </head>
  <body>
    {% for slide in site.data[{{page.slides}}] %}
      <section class="{{ slide.style }}" {% if slide.image %}style="background-image: url({{slide.image}})"{% endif %}>
        {% include elements.html %}
      </section>
    {% endfor %}
  </body>
</html>


text:
// InsertControlDlg.Cpp : implementation file
//

#include "stdafx.h"
#include "DevInc.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CInsertControlDlg dialog

CInsertControlDlg::CInsertControlDlg( CWnd* pParent ) :
   CDialog( CInsertControlDlg::IDD, pParent )
{
	//{{AFX_DATA_INIT(CInsertControlDlg)
	//}}AFX_DATA_INIT
}

void CInsertControlDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CInsertControlDlg)
	DDX_Control(pDX, IDC_SERVERPATH, m_staticServerPath);
	DDX_Control(pDX, IDC_CONTROLS, m_lbControls);
	//}}AFX_DATA_MAP

   if( pDX->m_bSaveAndValidate )
   {
		GetSelectCLSIDs();
   }
}

BEGIN_MESSAGE_MAP(CInsertControlDlg, CDialog)
	//{{AFX_MSG_MAP(CInsertControlDlg)
	ON_WM_HELPINFO()
	ON_WM_CONTEXTMENU()
	ON_LBN_SELCHANGE(IDC_CONTROLS, OnSelchangeControls)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CInsertControlDlg message handlers

BOOL CInsertControlDlg::OnInitDialog()
{
/*	CRect rect;
	GetClientRect(&rect);
	rect.DeflateRect(10,10);
	rect.right = rect.left + rect.Width() * 2 / 3;
	m_lbControls.Create(LBS_SORT | LBS_OWNERDRAWFIXED |WS_TABSTOP | WS_CHILD | WS_VISIBLE | LBS_HASSTRINGS | WS_BORDER, rect, this, 500);
*/
   HRESULT hResult;
   CATID catid;

   hResult = m_pCatInfo.CreateInstance( CLSID_StdComponentCategoriesMgr, NULL,
	  CLSCTX_INPROC_SERVER );
   if( FAILED( hResult ) )
   {
	  TRACE( "Failed to create category manager\n" );
	  EndDialog( IDCANCEL );
	  return( TRUE );
   }

	CDialog::OnInitDialog();

   catid = CATID_Control;
   m_aImplementedCategories.Add( catid );

//   m_butIgnoreRequiredCategories.SetCheck( 0 );

   CFont* pFont = GetFont();
   if(pFont != NULL)
		m_lbControls.SetFont(pFont);
//   m_lbControls.ModifyStyleEx(0, WS_EX_RIGHTSCROLLBAR);
   m_lbControls.ModifyStyle(0, WS_VSCROLL);

   RefreshControlList();

   m_lbControls.SetFocus();

	return TRUE;
}

void GetClassServerPath( REFCLSID clsid, CString& strServerPath )
{
   HKEY hKey;
   HKEY hServerKey;
   OLECHAR szCLSID[64];
   LONG nResult;
   ULONG nBytes;
   DWORD dwType;
   LPTSTR pszServerPath;

   StringFromGUID2( clsid, szCLSID, 64 );

   hKey = NULL;
   hServerKey = NULL;
   try
   {
	  nResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, CString( "CLSID\\" )+CString(
		 szCLSID ), 0, KEY_READ, &hKey );
	  if( nResult != ERROR_SUCCESS )
	  {
		 throw( E_FAIL );
	  }

	  nResult = RegOpenKeyEx( hKey, _T( "InprocServer32" ), 0, KEY_READ,
		 &hServerKey );
	  if( nResult != ERROR_SUCCESS )
	  {
		 nResult = RegOpenKeyEx( hKey, _T( "InprocHandler32" ), 0, KEY_READ,
			&hServerKey );
		 if( nResult != ERROR_SUCCESS )
		 {
			nResult = RegOpenKeyEx( hKey, _T( "LocalServer32" ), 0, KEY_READ,
			   &hServerKey );
			if( nResult != ERROR_SUCCESS )
			{
			   throw( E_FAIL );
			}
		 }
	  }

	  nBytes = 0;
	  nResult = RegQueryValueEx( hServerKey, NULL, NULL, &dwType, NULL,
		 &nBytes );
	  if( (nResult != ERROR_SUCCESS) || (dwType != REG_SZ) )
	  {
		 throw( E_FAIL );
	  }
	  pszServerPath = LPTSTR( _alloca( nBytes ) );
	  nResult = RegQueryValueEx( hServerKey, NULL, NULL, &dwType,
		 LPBYTE( pszServerPath ), &nBytes );
	  if( (nResult != ERROR_SUCCESS) || (dwType != REG_SZ) )
	  {
		 throw( E_FAIL );
	  }

	  strServerPath = pszServerPath;

	  RegCloseKey( hKey );
	  hKey = NULL;
	  RegCloseKey( hServerKey );
	  hServerKey = NULL;
   }
   catch( HRESULT )
   {
	  if( hKey != NULL )
	  {
		 RegCloseKey( hKey );
	  }
	  if( hServerKey != NULL )
	  {
		 RegCloseKey( hServerKey );
	  }

	  strServerPath.LoadString(IDS_SERVERNOTFOUND);

	  return;
   }
}

void CInsertControlDlg::RefreshControlList()
{
   BOOL tDone;
   HRESULT hResult;
   IEnumGUIDPtr pEnum;
   ULONG nImplementedCategories;
   CATID* pcatidImpl;
   ULONG nRequiredCategories;
   CATID* pcatidReq;
   CLSID clsid;
   LPOLESTR pszName;
   CString strName;
   ULONG iCategory;
   int iItem;
   POSITION posControl;
   CString strServerPath;
   CString strString;

   m_lbControls.ResetContent();
   m_lControls.RemoveAll();

   nImplementedCategories = m_aImplementedCategories.GetSize();
   if( nImplementedCategories == 0 )
   {
	  nImplementedCategories = ULONG( -1 );
	  pcatidImpl = NULL;
   }
   else
   {
	  pcatidImpl = (CATID*)_alloca( nImplementedCategories*sizeof( CATID ) );
	  for( iCategory = 0; iCategory < nImplementedCategories; iCategory++ )
	  {
		 pcatidImpl[iCategory] = m_aImplementedCategories[iCategory];
	  }
   }

  nRequiredCategories = m_aRequiredCategories.GetSize();
  if( nRequiredCategories == 0 )
  {
	 pcatidReq = NULL;
  }
  else
  {
	 pcatidReq = (CATID*)_alloca( nRequiredCategories*sizeof( CATID ) );
	 for( iCategory = 0; iCategory < nRequiredCategories; iCategory++ )
	 {
		pcatidReq[iCategory] = m_aRequiredCategories[iCategory];
	 }
  }

   hResult = m_pCatInfo->EnumClassesOfCategories( nImplementedCategories,
	  pcatidImpl, nRequiredCategories, pcatidReq, &pEnum );
   if( FAILED( hResult ) )
   {
	  return;
   }

   tDone = FALSE;
   while( !tDone )
   {
	  hResult = pEnum->Next( 1, &clsid, NULL );
	  if( hResult == S_OK )
	  {
		 pszName = NULL;
		 hResult = OleRegGetUserType( clsid, USERCLASSTYPE_FULL, &pszName );
		 if( SUCCEEDED( hResult ) )
		 {
			strName = pszName;
			CoTaskMemFree( pszName );
			pszName = NULL;
			iItem = m_lbControls.AddString( strName );
			if(IsCLSIDInUse(clsid))
				m_lbControls.SetCheck(iItem, 1);
			posControl = m_lControls.AddTail( clsid );
			m_lbControls.SetItemDataPtr( iItem, posControl );
		 }
	  }
	  else
	  {
		 tDone = TRUE;
	  }
   }
}

static DWORD rgmapCHID[] =
{
/*   IDC_CONTROLS, HIDC_CONTROLS,
   IDC_IMPLEMENTEDCATEGORIES, HIDC_IMPLEMENTEDCATEGORIES,
   IDC_REQUIREDCATEGORIES, HIDC_REQUIREDCATEGORIES,
   IDC_IGNOREREQUIREDCATEGORIES, HIDC_IGNOREREQUIREDCATEGORIES,
*/  0, 0
};

BOOL CInsertControlDlg::OnHelpInfo( HELPINFO* pHelpInfo )
{
	return( ::WinHelp( HWND( pHelpInfo->hItemHandle ),
	  AfxGetApp()->m_pszHelpFilePath, HELP_WM_HELP, DWORD( LPVOID(
	  rgmapCHID ) ) ) );
}

void CInsertControlDlg::OnContextMenu( CWnd* pWnd, CPoint /* point */ )
{
   ::WinHelp( HWND( *pWnd ), AfxGetApp()->m_pszHelpFilePath, HELP_CONTEXTMENU,
	  DWORD( LPVOID( rgmapCHID ) ) );
}

////////////////////////////////////////////////////////////////
//¹¦ÄÜ:´ÓÓû§µÄÑ¡ÔñÖÐÉú³É¶ÔÓ¦¿Ø¼þµÄCLSIDÁбíÓÚm_aryClsidÖÐ,
//	   ²¢ÔÚm_idCountsÖÐÉèÖÃÁбíÖÐÏîµÄ¸öÊý
////////////////////////////////////////////////////////////////
void CInsertControlDlg::GetSelectCLSIDs()
{
	//È¥µô¾ÉµÄÁбí
	m_aryClsid.RemoveAll();

	int count = m_lbControls.GetCount();
	while(count > 0)
	{
		count--;
		if(m_lbControls.GetCheck(count) == 1)
		{	//¸ÃÏî±»¹´ÖÐ
			POSITION pos = (POSITION)(m_lbControls.GetItemDataPtr(count));
			if(pos != NULL)
			{
				CLSID tmpID = m_lControls.GetAt(pos);
				m_aryClsid.AddTail(tmpID);
			}
		}
	}
}

/////////////////////////////////////////////////////////////////
//¹¦ÄÜ:¶Ô»°¿ò³õ'»¯Ê±¸ù¾Ým_arClsidÔ¤ÉèµÄ²ÎÊý,³õ'»¯Ó¦Ô¤Ñ¡µÄÏî
//²ÎÊý:clsid[in]		ÒªÅж¨µÄCLSID
//·µ»Ø:ÊÇTRUE,·ñFALSE
/////////////////////////////////////////////////////////////////
BOOL CInsertControlDlg::IsCLSIDInUse(CLSID& clsid)
{
	POSITION pos = m_aryClsid.GetHeadPosition();
	while(pos != NULL)
	{
		CLSID usedID = m_aryClsid.GetNext(pos);
		if(usedID == clsid)
			return TRUE;
	}

	return FALSE;
}

void CInsertControlDlg::OnSelchangeControls()
{
	int iItem;
	POSITION posControl;
	CString strServerPath;
	CLSID clsid;
	CDC dc;
	CFont* pFont;
	LPTSTR pszServerPath;
	CRect rect;
	CFont* pOldFont;

	iItem = m_lbControls.GetCurSel();
	if( iItem != LB_ERR )
	{
		posControl = POSITION( m_lbControls.GetItemDataPtr( iItem ) );
		clsid = m_lControls.GetAt( posControl );
		GetClassServerPath( clsid, strServerPath );

		dc.CreateCompatibleDC( NULL );

		pFont = m_staticServerPath.GetFont();
		pOldFont = dc.SelectObject( pFont );

		// Workaround for SHLWAPI bug (in weird cases, PathCompactPath actually
		// expands the pathname)
		pszServerPath = strServerPath.GetBuffer( MAX_PATH+2 );
		m_staticServerPath.GetWindowRect( &rect );
		PathCompactPath( dc, pszServerPath, rect.Width() );
		strServerPath.ReleaseBuffer();

		dc.SelectObject( pOldFont );

		m_staticServerPath.SetWindowText( strServerPath );
	}
	else
	{
		m_staticServerPath.SetWindowText( NULL );
	}
}


text:
/******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId])
/******/ 			return installedModules[moduleId].exports;
/******/
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			exports: {},
/******/ 			id: moduleId,
/******/ 			loaded: false
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.loaded = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

	'use strict';

	var _mortgage = __webpack_require__(1);

	var _mortgage2 = _interopRequireDefault(_mortgage);

	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

	document.getElementById('calcBtn').addEventListener('click', function () {
	    var principal = document.getElementById("principal").value;
	    var years = document.getElementById("years").value;
	    var rate = document.getElementById("rate").value;
	    var mortgage = new _mortgage2.default(principal, years, rate);

	    document.getElementById("monthlyPayment").innerHTML = mortgage.monthlyPayment.toFixed(2);
	    document.getElementById("monthlyRate").innerHTML = (rate / 12).toFixed(2);

	    var html = "";

	    //amortization.forEach(month => console.log(month))

	    mortgage.amortization.forEach(function (year, index) {
	        return html += '\n
\n            ' + (index + 1) + '	\n            ' + Math.round(year.principalY) + '	\n            \n
\n
\n
\n
\n
\n
\n            	\n            ' + Math.round(year.interestY) + '	\n            ' + Math.round(year.balance) + '	\n
\n    ';
	    });

	    document.getElementById("amortization").innerHTML = html;
	});

/***/ },
/* 1 */
/***/ function(module, exports) {

	"use strict";

	var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();

	Object.defineProperty(exports, "__esModule", {
	    value: true
	});

	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

	var Mortgage = (function () {
	    function Mortgage(principal, years, rate) {
	        _classCallCheck(this, Mortgage);

	        this.principal = principal;
	        this.years = years;
	        this.rate = rate;
	    }

	    _createClass(Mortgage, [{
	        key: "monthlyPayment",
	        get: function get() {
	            var monthlyRate = this.rate / 100 / 12;
	            return this.principal * monthlyRate / (1 - Math.pow(1 / (1 + monthlyRate), this.years * 12));
	        }
	    }, {
	        key: "amortization",
	        get: function get() {
	            var monthlyPayment = this.monthlyPayment;
	            var monthlyRate = this.rate / 100 / 12;
	            var balance = this.principal;
	            var amortization = [];

	            for (var y = 0; y < this.years; y++) {
	                var interestY = 0;
	                var principalY = 0;

	                for (var m = 0; m < 12; m++) {
	                    var interestM = balance * monthlyRate;
	                    var principalM = monthlyPayment - interestM;
	                    interestY = interestY + interestM;
	                    principalY = principalY + principalM;
	                    balance = balance - principalM;
	                }
	                amortization.push({ principalY: principalY, interestY: interestY, balance: balance });
	            }
	            return amortization;
	        }
	    }]);

	    return Mortgage;
	})();

	exports.default = Mortgage;

/***/ }
/******/ ]);
//# sourceMappingURL=app.bundle.js.map


text:
namespace skia {
namespace textlayout {
class TestFontCollection : public FontCollection {
public:
    TestFontCollection(const std::string& resourceDir, bool testOnly = false, bool loadFonts = true);

    size_t fontsFound() const { return fFontsFound; }
    bool addFontFromFile(const std::string& path, const std::string& familyName = "");

private:
    std::string fResourceDir;
    size_t fFontsFound;
    sk_sp<TypefaceFontProvider> fFontProvider;
    std::string fDirs;
};
}  // namespace textlayout
}  // namespace skia


text:
.. haproxytool
.. README.rst

haproxytool
===========

    *A tool to manage HAProxy via stats socket.*

It uses `haproxyadmin <https://github.com/unixsurfer/haproxyadmin>`_
Python library to interact with HAProxy and run all commands.
One of the main feature is that can work with HAProxy in multi-process mode (nbproc > 1)

.. contents::

Examples
--------

Usage
~~~~~

The command line interface tries to be friendly and compatible with the rest of
the tools available in the Linux land. The `docopt Python module
<https://pypi.python.org/pypi/docopt>`_ is used to build the CLI interface.

Here is the basic syntax to start with::

    % haproxytool
    Usage: haproxytool [-v | -h] <command> [<args>...]

    % haproxytool -h
    A tool to manage HAProxy via the stats socket.

    Usage: haproxytool [-v | -h] <command> [<args>...]

    Options:
    -h, --help                show this screen.
    -v, --version             show version.

    Available haproxytool commands are:
        haproxy   HAProxy operations
        frontend  Frontend operations
        backend   Backend operations
        server    Server operations
        dump      Dumps all informations
        map       Manage MAPs
        acl       Manage ACLs

    See 'haproxytool help <command>' for more information on a specific command.

Keep reading for more details about each command.

Commands for HAProxy
~~~~~~~~~~~~~~~~~~~~~~

* Usage

::

    % haproxytool haproxy --help
    Manage haproxy

    Usage:
        haproxytool haproxy [-D DIR | -F SOCKET] (-a | -A | -C | -e | -i | -M |
                                                -o | -r | -u | -U | -V | -R | -p)
        haproxytool haproxy [-D DIR | -F SOCKET] -m METRIC
        haproxytool haproxy [-D DIR | -F SOCKET] -w OPTION VALUE
        haproxytool haproxy [-D DIR | -F SOCKET] -c COMMAND

    Arguments:
        DIR     Directory path with socket files
        OPTION  Option name to set a VALUE
        VALUE   Value to set
        METRIC  Name of a metric, use '-M' to get metric names

    Options:
        -a, --all                   clear all statistics counters
        -A, --clear                 clear max values of statistics counters
        -c, --command               send a command to HAProxy
        -C, --maxconn               show configured maximum connection limit
        -e, --errors                show last know request and response errors
        -F SOCKET, --file SOCKET    socket file
        -i, --info                  show haproxy stats
        -m, --metric                show value of a METRIC
        -M, --show-metrics          show all metrics
        -o, --options               show value of options that can be changed with
                                    '-w' option
        -p, --pids                  show PIDs of HAProxy processes
        -r, --requests              show total cumulative number of requests
                                    processed by all processes
        -u, --uptime-secs           show uptime of HAProxy process in seconds
        -U, --uptime                show uptime of HAProxy process
        -V, --hap-version           show version of HAProxy
        -R, --release-date          show release date
        -w, --write                 set VALUE for an OPTION
        -D DIR, --socket-dir=DIR    directory with HAProxy socket files
                                    [default: /var/lib/haproxy]

Commands for frontends
~~~~~~~~~~~~~~~~~~~~~~

* Usage

::

    % haproxytool frontend --help
    Manage frontends

    Usage:
        haproxytool frontend [-D DIR -F SOCKET] (-c | -r | -s | -o | -e | -p | -i)
                             [NAME...]
        haproxytool frontend [-D DIR -F SOCKET] -w OPTION VALUE [NAME...]
        haproxytool frontend [-D DIR -F SOCKET] [-f ] (-d | -t) [NAME...]
        haproxytool frontend [-D DIR -F SOCKET] (-l | -M)
        haproxytool frontend [-D DIR -F SOCKET] -m METRIC [NAME...]

    Arguments:
        DIR     Directory path with socket files
        SOCKET  Socket file
        VALUE   Value to set
        OPTION  Setting name
        METRIC  Name of a metric, use '-M' to get metric names

    Options:
        -c, --showmaxconn         show max sessions
        -d, --disable             disable frontend
        -e, --enable              enable frontend
        -f, --force               force an operation
        -F SOCKET, --file SOCKET  socket file
        -h, --help                show this screen
        -i, --iid                 show proxy ID number
        -l, --show                show all frontends
        -m, --metric              show value of a metric
        -M, --show-metrics        show all metrics
        -o, --options             show value of options that can be changed with
                                '-w' option
        -p, --process             show process number
        -r, --requests            show requests
        -s, --status              show status
        -t, --shutdown            shutdown frontend
        -w, --write               change a frontend option
        -D DIR, --socket-dir=DIR  directory with HAProxy socket files
                                [default: /var/lib/haproxy]

* Show status of frontend(s)

::

    % haproxytool frontend -s
    frontend_proc1 OPEN
    frontend2_proc34 OPEN
    haproxy OPEN
    frontend1_proc34 OPEN
    frontend_proc2 OPEN

    % haproxytool frontend -s frontend2_proc34
    frontend2_proc34 OPEN

* Show requests

::

    % haproxytool frontend -r frontend2_proc34
    frontend2_proc34 10

* Show in which HAProxy process a frontend is used

::

    % haproxytool frontend -p frontend2_proc34
    frontend2_proc34 [4, 3]

* Show option(s) that can be changed

:NOTE: Currently, HAProxy allows only to change the maximum connections option for the frontends.

::

    % haproxytool frontend -o frontend_proc1
    frontend_proc1 maxconn=1000000

* Change an option

::

    % haproxytool frontend -w maxconn 100000 frontend_proc1
    frontend_proc1 set maxconn to 100000

    % haproxytool frontend -o frontend_proc1
    frontend_proc1 maxconn=100000

* Changing an option for a frontend assigned to multiple HAProxy process

::

    % haproxytool frontend -o frontend1_proc34
    frontend1_proc34 maxconn=2000000

    % haproxytool frontend -w maxconn 40000 frontend1_proc34
    frontend1_proc34 set maxconn to 40000

    % haproxytool frontend -o frontend1_proc34
    frontend1_proc34 maxconn=80000

    % haproxytool frontend -p frontend1_proc34
    frontend1_proc34 [4, 3]

:NOTE: It is not supported to change a option only to one of the HAProxy
    process

:NOTE: The return value of the option is the sum of the values across all
    HAProxy processes

Commands for backends
~~~~~~~~~~~~~~~~~~~~~

* Usage

::

    % haproxytool backend --help
    Manage backends

    Usage:
        haproxytool backend [-D DIR | -F SOCKET] (-S | -r | -p | -s | -i) [NAME...]
        haproxytool backend [-D DIR | -F SOCKET] (-l | -M)
        haproxytool backend [-D DIR | -F SOCKET] -m METRIC [NAME...]

    Arguments:
        DIR     Directory path with socket files
        SOCKET  Socket file
        METRIC  Name of a metric, use '-M' to get metric names

    Options:
        -F SOCKET, --file SOCKET  socket file
        -h, --help                show this screen
        -i, --iid                 show proxy ID number
        -l, --show                show all backends
        -m, --metric              show value of a metric
        -M, --show-metrics        show all metrics
        -p, --process             show process number
        -r, --requests            show requests
        -s, --status              show status
        -S, --servers             show servers
        -D DIR, --socket-dir=DIR  directory with HAProxy socket files
                                  [default: /var/lib/haproxy]

Commands for servers
~~~~~~~~~~~~~~~~~~~~

* Usage

::

    % haproxytool server --help
    Manage servers

    Usage:
        haproxytool server [-D DIR | -F SOCKET] (-A | -r | -s | -e | -R | -p | -W |
                        -i | -c | -C | -S | -X) [--backend=<name>...] [NAME...]
        haproxytool server [-D DIR | -F SOCKET] -w VALUE [--backend=<name>...]
                        [NAME...]
        haproxytool server [-D DIR | -F SOCKET] -a VALUE [--backend=<name>...] NAME
        haproxytool server [-D DIR | -F SOCKET] -x VALUE [--backend=<name>...] NAME
        haproxytool server [-D DIR | -F SOCKET] [-f ] (-d | -t | -n)
                        [--backend=<name>...] [NAME...]
        haproxytool server [-D DIR | -F SOCKET] (-l | -M)
        haproxytool server [-D DIR | -F SOCKET] -m METRIC [--backend=<name>...]
                        [NAME...]

    Arguments:
        DIR     Directory path with socket files
        SOCKET  Socket file
        VALUE   Value to set
        METRIC  Name of a metric, use '-M' to get metric names

    Options:
        -a, --address             set server's address
        -A, --show-address        show server's address
        -c, --show-check-code     show check code
        -C, --show-check-status   show check status
        -d, --disable             disable server
        -e, --enable              enable server
        -f, --force               force an operation
        -F SOCKET, --file SOCKET  socket file
        -h, --help                show this screen
        -i, --sid                 show server ID
        -l, --show                show all servers
        -m, --metric              show value of a metric
        -M, --show-metrics        show all metrics
        -n, --drain               drain server
        -p, --process             show process number
        -r, --requests            show requests
        -R, --ready               set server in normal mode
        -s, --status              show status
        -S, --show-last-status    show last check status
        -t, --maintenance         set server in maintenance mode
        -w, --weight              change weight for server
        -W, --get-weight          show weight of server
        -x --port                 set servers's port
        -X, --show-port           show servers's port
        -D DIR, --socket-dir=DIR  directory with HAProxy socket files
                                  [default: /var/lib/haproxy]

* List all servers

::

    % haproxytool server -l
    # backendname servername
    backend1_proc34                bck1_proc34_srv1
    backend1_proc34                bck1_proc34_srv2
    backend1_proc34                bck_all_srv1
    backend_proc2                  bck_proc2_srv4_proc2
    backend_proc2                  bck_proc2_srv3_proc2
    backend_proc2                  bck_proc2_srv2_proc2
    backend_proc2                  bck_proc2_srv1_proc2
    backend_proc1                  member1_proc1
    backend_proc1                  member2_proc1
    backend_proc1                  bck_all_srv1
    backend2_proc34                bck2_proc34_srv1
    backend2_proc34                bck_all_srv1
    backend2_proc34                bck2_proc34_srv2

* Show status of servers per backend

::

    % haproxytool server -s --backend=backend_proc1
    # backendname servername
    backend_proc1                  bck_all_srv1                               DOWN
    backend_proc1                  member1_proc1                              no check
    backend_proc1                  member2_proc1                              no check

    % haproxytool server -s --backend=backend_proc1 --backend=backend2_proc34
    # backendname servername
    backend_proc1                  member1_proc1                              no check
    backend_proc1                  bck_all_srv1                               DOWN
    backend_proc1                  member2_proc1                              no check
    backend2_proc34                bck2_proc34_srv2                           UP
    backend2_proc34                bck2_proc34_srv1                           no check
    backend2_proc34                bck_all_srv1                               no check

* Show weight of servers across all backends and per backend

::

    % haproxytool server -W bck_all_srv1
    # backendname servername
    backend1_proc34                bck_all_srv1                               1
    backend2_proc34                bck_all_srv1                               1
    backend_proc1                  bck_all_srv1                               100

    % haproxytool server -W bck_all_srv1 --backend=backend_proc1 --backend=backend2_proc34
    # backendname servername
    backend_proc1                  bck_all_srv1                               100
    backend2_proc34                bck_all_srv1                               1

* Set weight on servers across all backends and per backend

::

    % haproxytool server -w 10 bck_all_srv1
    bck_all_srv1 backend set weight to 10 in backend2_proc34 backend
    bck_all_srv1 backend set weight to 10 in backend1_proc34 backend
    bck_all_srv1 backend set weight to 10 in backend_proc1 backend

    % haproxytool server -w 50 bck_all_srv1 --backend=backend_proc1 --backend=backend2_proc34
    bck_all_srv1 backend set weight to 50 in backend_proc1 backend
    bck_all_srv1 backend set weight to 50 in backend2_proc34 backend

* Show requests

::

    % haproxytool server -r bck_all_srv1
    # backendname servername
    backend_proc1                  bck_all_srv1                               0
    backend2_proc34                bck_all_srv1                               2
    backend1_proc34                bck_all_srv1                               10

* Show/change address of a server

::

    % haproxytool server -D /run/haproxy --show-port srv1
    backend_dynamic                srv1                                       7777
    % haproxytool server -D /run/haproxy --port 8888 srv1
    set port for srv1 server to 8888 in backend_dynamic backend

    % haproxytool server -D /run/haproxy --show-address srv1
    backend_dynamic                srv1                                        127.0.0.9
    % haproxytool server -D /run/haproxy --address 127.0.0.10 srv1
    set address for srv1 server to 127.0.0.10 in backend_dynamic backend

* List metric names available from the statistics

::

    % haproxytool server -M
    qcur
    qmax
    scur
    smax
    stot
    bin
    bout
    dresp
    econ
    eresp
    wretr
    wredis
    weight
    act
    bck
    chkfail
    chkdown
    lastchg
    downtime
    qlimit
    throttle
    lbtot
    rate
    rate_max
    check_duration
    hrsp_1xx
    hrsp_2xx
    hrsp_3xx
    hrsp_4xx
    hrsp_5xx
    hrsp_other
    cli_abrt
    srv_abrt
    lastsess
    qtime
    ctime
    rtime
    ttime

Please consult `CSV format of HAProxy <http://cbonte.github.io/haproxy-dconv/configuration-1.5.html#9.1>`_ for their
description.

* Show the value of a specific metric

::

    % haproxytool server -m bin bck_all_srv1
    # backendname servername
    backend1_proc34                bck_all_srv1                               760
    backend2_proc34                bck_all_srv1                               152
    backend_proc1                  bck_all_srv1                               0

* Show in which HAProxy process a server is used

::

    % haproxytool server -p bck_all_srv1
    # backendname servername
    backend2_proc34                bck_all_srv1                               [4, 3]
    backend_proc1                  bck_all_srv1                               [1]
    backend1_proc34                bck_all_srv1                               [4, 3]

* Enable/disable a server

::

    % haproxytool server -d bck_all_srv1
    Are you sure we want to disable 3 servers y/n?: y
    bck_all_srv1 disabled in backend1_proc34 backend
    bck_all_srv1 disabled in backend_proc1 backend
    bck_all_srv1 disabled in backend2_proc34 backend

:NOTE: When more than one server are about to be disabled, user is prompted to
       confirm the action. Use -f to skip this confirmation.

::

    % haproxytool server -s bck_all_srv1
    # backendname servername
    backend_proc1                  bck_all_srv1                               MAINT
    backend2_proc34                bck_all_srv1                               MAINT
    backend1_proc34                bck_all_srv1                               MAINT

    % haproxytool server -e bck_all_srv1
    bck_all_srv1 enabled in backend2_proc34 backend
    bck_all_srv1 enabled in backend1_proc34 backend
    bck_all_srv1 enabled in backend_proc1 backend

    % haproxytool server -s bck_all_srv1
    # backendname servername
    backend1_proc34                bck_all_srv1                               UP
    backend2_proc34                bck_all_srv1                               no check
    backend_proc1                  bck_all_srv1                               DOWN

Dump command
~~~~~~~~~~~~

* Usage

::

    % haproxytool dump --help
    Dump a collection of information about frontends, backends and servers

    Usage:
        haproxytool dump [-D DIR | -F SOCKET] [-fbsh]

    Arguments:
        SOCKET  Socket file

    Options:
        -f, --frontends           show frontends
        -F SOCKET, --file SOCKET  socket file
        -b, --backends            show backends
        -s, --servers             show servers
        -D DIR, --socket-dir=DIR  directory with HAProxy socket files
                                  [default: /var/lib/haproxy]

Map command
~~~~~~~~~~~~

* Usage

::

    % haproxytool map --help
    Manage MAPs

    Usage:
        haproxytool map [-D DIR | -F SOCKET] -l
        haproxytool map [-D DIR | -F SOCKET] (-s | -c ) MAPID
        haproxytool map [-D DIR | -F SOCKET] -g MAPID KEY
        haproxytool map [-D DIR | -F SOCKET] (-S | -A) MAPID KEY VALUE
        haproxytool map [-D DIR | -F SOCKET] -d MAPID KEY

    Arguments:
        DIR     Directory path with socket files
        MAPID   ID of the map or file returned by show map
        KEY     ID of key
        SOCKET  Socket file
        VALUE   Value to set

    Options:
        -A, --add                 add a <KEY> entry into the map <MAPID>
        -F SOCKET, --file SOCKET  socket file
        -h, --help                show this screen
        -s, --show                show map
        -g, --get                 lookup the value of a key in the map
        -c, --clear               clear all entries for a map
        -l, --list                list all map ids
        -S, --set                 set a new value for a key in a map
        -d, --delete              delete all the map entries from the map <MAPID>
                                  corresponding to the key <KEY>
        -D DIR, --socket-dir=DIR  directory with HAProxy socket files
                                  [default: /var/lib/haproxy]

* List all MAPIDs

::

    % haproxytool map -l
    # id (file) description
    4 (/etc/haproxy/v-m1-bk) pattern loaded from file '/etc/haproxy/v-m1-bk'
    used by map at file '/etc/haproxy/haproxy.cfg' line 87

* Show the content of a map

::

    % haproxytool map -s 4
    0xb743f0 0 www.foo.com-0
    0xb74460 1 www.foo.com-1

* Add a key to a map

::

    % haproxytool map -A 4 3 www.goo.com
    key was added successfully

    % haproxytool map -s 4
    0xb743f0 0 www.foo.com-0
    0xb74460 1 www.foo.com-1
    0x28f0f50 3 www.goo.com

* Delete an entry from a map

::

    % haproxytool map -d 4 3
    key was deleted successfully

    % haproxytool map -s 4
    0xb743f0 0 www.foo.com-0
    0xb74460 1 www.foo.com-1

* Set a value for a key in a map

::

    % haproxytool map -S 4 1 bar.com
    value was set successfully

    % haproxytool map -s 4
    0xb743f0 0 www.foo.com-0
    0xb74460 1 bar.com

* Clear all entries of a map

::

    % haproxytool map -c 4
    all entries of map were cleared successfully

    % haproxytool map -s 4

    %

:NOTE: Currently, HAProxy doesn't allow to create new MAPs via the stats socket.

ACL command
~~~~~~~~~~~

* Usage

::

    % haproxytool acl --help
    Manage ACLs

    Usage:
        haproxytool acl [-D DIR | -F SOCKET] -l
        haproxytool acl [-D DIR | -F SOCKET] (-c | -s) ACLID
        haproxytool acl [-D DIR | -F SOCKET] (-A | -g ) ACLID VALUE
        haproxytool acl [-D DIR | -F SOCKET] -d ACLID KEY

    Arguments:
        DIR     Directory path with socket files
        ACLID   ID of the acl or file returned by show acl
        SOCKET  Socket file
        VALUE   Value to set
        KEY     Key ID of ACL value/pattern

    Options:
        -h, --help                show this screen
        -A, --add                 add a <KEY> entry into the acl <ACLID>
        -F SOCKET, --file SOCKET  socket file
        -s, --show                show acl
        -g, --get                 lookup the value of a key in the acl
        -c, --clear               clear all entries for a acl
        -l, --list                list all acl ids
        -d, --delete              delete all the acl entries from the acl <ACLID>
                                  corresponding to the key <KEY>
        -D DIR, --socket-dir=DIR  directory with HAProxy socket files
                                  [default: /var/lib/haproxy]

* List all ACLIDs

::

    % haproxytool acl -D /run/haproxy -l
    # id (file) description
    0 (/etc/haproxy/wl_stats) pattern loaded from file '/etc/haproxy/wl_stats' used by acl at file '/etc/haproxy/haproxy.cfg' line 55
    1 () acl 'src' file '/etc/haproxy/haproxy.cfg' line 55
    2 (/etc/haproxy/bl_frontend) pattern loaded from file '/etc/haproxy/bl_frontend' used by acl at file '/etc/haproxy/haproxy.cfg' line 85
    3 () acl 'src' file '/etc/haproxy/haproxy.cfg' line 85
    5 () acl 'ssl_fc' file '/etc/haproxy/haproxy.cfg' line 88
    6 () acl 'req.cook' file '/etc/haproxy/haproxy.cfg' line 101
    7 () acl 'req.cook' file '/etc/haproxy/haproxy.cfg' line 102
    8 () acl 'req.cook' file '/etc/haproxy/haproxy.cfg' line 103

* Show the content of a acl

::

    % haproxytool acl -D /run/haproxy -s 2
    0x2115c90 11.155.183.41
    0x3e92f80 10.10.10.10

* Add an entry in a acl

::

    % haproxytool acl -D /run/haproxy --add 2 12.12.12.0
    value was added successfully

    % haproxytool acl -D /run/haproxy -s 2
    0x2115c90 11.155.183.41
    0x3e92f80 10.10.10.10
    0x21341e0 12.12.12.0

* Delete an entry from acl

::

    % haproxytool acl -D /run/haproxy --delete 2 12.12.12.0
    key was deleted successfully

    % haproxytool acl -D /run/haproxy -s 2
    0x2115c90 11.155.183.41
    0x3e92f80 10.10.10.10

* Lookup entries in acl for debugging purposes

::

    % haproxytool acl -D /run/haproxy -g 2 11.155.183.41
    type=ip, case=sensitive, match=yes, idx=tree, pattern="11.155.183.41"

    %haproxytool acl -D /run/haproxy -g 2 11.155.183.0
    type=ip, case=sensitive, match=no

* Clear all entries from a acl

::

    % haproxytool acl -D /run/haproxy -s 2
    0x2115c90 11.155.183.41
    0x3e92f80 10.10.10.10

    % haproxytool acl -D /run/haproxy -c 2
    all entries of acl were cleared successfully

    % haproxytool acl -D /run/haproxy -s 2

    %

Release
-------

#. Bump version in haproxytool/__init__.py

#. Commit above change with::

      git commit -av -m'RELEASE 0.1.3 version'

#. Create a signed tag, pbr will use this for the version number::

      git tag -s 0.1.3 -m 'bump release'

#. Create the source distribution archive (the archive will be placed in the
   **dist** directory)::

      python setup.py sdist

#. pbr updates ChangeLog file and we want to squeeze this change to the
   previous commit, thus run::

      git commit -av --amend

#. Move current tag to the last commit::

      git tag -fs 0.1.3 -m 'bump release'

#. Push changes::

      git push;git push --tags

Installation
------------

Use pip::

    pip install haproxytool

From Source::

   python setup.py install

Build (source) RPMs::

   python setup.py clean --all; python setup.py bdist_rpm

Build a source archive for manual installation::

   python setup.py sdist

Licensing
---------

Apache 2.0

Acknowledgement
---------------
This program was originally developed for Booking.com.  With approval
from Booking.com, the code was generalised and published as Open Source
on github, for which the author would like to express his gratitude.

Contacts
--------

**Project website**: https://github.com/unixsurfer/haproxytool

**Author**: Pavlos Parissis <pavlos.parissis@gmail.com>


text:
import unittest

from pyvdp.globalatmlocator.localatms.models import AbstractInquiryModel

class TestAbstractInquiryModel(unittest.TestCase):
    def setUp(self):
        self.attrs = {
            'wsRequestHeaderV2': object,
            'requestData': object
        }

class TestAbstractRequestData(unittest.TestCase):
    def setUp(self):
        self.attrs = {
            'culture': str,
            'distance': int,
            'distanceUnit': str,
            'location': object,
            'metaDataOptions': int,
            'options': object
        }

class TestAbstractWsRequestHeaderV2(unittest.TestCase):
    def setUp(self):
        self.attrs = {
            'applicationId': str,
            'correlationId': int,
            'requestMessageId': str,
            'requestTs': object,
            'userBid': int,
            'userId': object
        }

class TestAbstractLocation(unittest.TestCase):
    def setUp(self):
        self.attrs = {
            'placeName': str,
            'geocodes': object
        }

class TestAbstractGeocodes(unittest.TestCase):
    def setUp(self):
        self.attrs = {
            'latitude': int,
            'longitude': int
        }

class TestAbstractOptions(unittest.TestCase):
    def setUp(self):
        self.attrs = {
            'range': object,
            'sort': object,
            'operationName': object,
            'findFilters': [object],
            'useFirstAmbiguous': bool
        }

class TestAbstractRange(unittest.TestCase):
    def setUp(self):
        self.attrs = {
            'count': int,
            'start': int
        }

class TestAbstractFilter(unittest.TestCase):
    def setUp(self):
        self.attrs = {
            'filterName': str,
            'filterValue': str,
        }

class TestAbstractSort(unittest.TestCase):
    def setUp(self):
        self.attrs = {
            'primary': str,
            'direction': str,
            'secondary': str
        }


text:
var tweetbase = (function ($, options) {

	options = options || {};

	var my = {};
	my.server = options.server || '127.0.0.1';
	my.database = options.database || 'tw_test';
	my.live = options.live || true;
	my.max_old_tweets = options.max_old_tweets || 10;
	my.update_interval = options.update_interval || 3000;
	my.display_interval = options.display_interval || 300;
	my.updateCallback = options.updateCallback || function(item) { console.log(item) };

	COUCH_VIEW_PATH = 'http://' + my.server + ':5984/' + my.database + '/_design/twitter/_view/';
	DB_GETTWEETS = COUCH_VIEW_PATH + 'get_tweets';
	DB_GETUSERS = COUCH_VIEW_PATH + 'get_users';
	DB_GETCOUNT = COUCH_VIEW_PATH + 'count_type?group=true&group_level=1&startkey=["TWITTER_STATUS"]&endkey=["TWITTER_STATUS",{}]';

	last_id = null;

	my.startFeed = function() {
		my.getCount(function(count) {
			var nskip = Math.max(0, count - my.max_old_tweets);
			my.getTweets({ skip:nskip });
		});
	};

	my.getCount = function(callback) {
		queryDatabase(DB_GETCOUNT, null, function(data) {
			if (data.rows != null && data.rows.length == 1) {
				count = data.rows[0].value;
				callback(count);
			}
		});
	};

	my.getTweets = function(params) {
		queryDatabase(DB_GETTWEETS, params, function(data) {
			if (data.rows != null && data.rows.length > 0) {
				last_id = data.rows[data.rows.length-1].key;
				displayTweets(data.rows, my.updateCallback);
			}
			setTimeout(function() { my.getTweets({ startkey:'"'+incrID(last_id)+'"' }); }, my.update_interval);
		});
	};

	my.getTweetByID = function(id, callback) {
		queryDatabase(DB_GETTWEETS, {key:id}, function(data) {
			if (data.rows != null && data.rows.length > 0)
				callback(data.rows[0].value);
		});
	};

	my.getUser = function(status, callback) {
		queryDatabase(DB_GETUSERS, { key:'"'+status.user_id+'"' }, function(data) {
			if (data.rows != null && data.rows.length == 1) {
				user = data.rows[0].value;
				callback(status, user);
			}
		});
	};

	function queryDatabase(view, params, callback) {
		$.ajax({
			url: view,
			data: params,
			dataType: 'jsonp',
			success: callback,
			error: function(status) {
				error(JSON.stringify(status));
			}
		});
	};

	function displayTweets(rows, callback) {
		if (rows.length > 0) {
			var tw = rows.pop().value;
			setTimeout(function() {
				my.getUser(tw, function(status, user) {
					callback({'status':status, 'user':user});
				});
				displayTweets(rows, callback);
			}, my.display_interval);
		}
	}

	function incrID(id) {
		// INCREMENT STRING REPRESENTATION OF 64-BIT INTEGER
		var digits = id.toString().split('');
		var i = digits.length - 1;
		while (digits[i] == 9 && i > 0) {
			digits[i] = 0;
			i--;
		}
		digits[i] = 1 + parseInt(digits[i]);
		return digits.join('');
	}

	function error(msg) {
		alert('tweetbase error: ' + msg);
	}

	return my;
});


text:
using System;
using System.Collections.Generic;
using System.Text;

namespace FlatRedBall.Gui
{
    public class RoutedEventArgs
    {
        public bool Handled { get; set; }
    }
}


text:
var React = require('react'),
    App = require('./app');

React.render(<App />, document.getElementById('render-spot'));


text:
export interface IConfig {
  port?: number;
  secret?: string;
  env?: string;
  fileLog?: string | boolean;
}


text:
require "active_support/concern"

module AWS
  require "aws/options"

  class Account
    require "aws/account/endpoint"

    module Region
      extend ActiveSupport::Concern

      include Options

      included do
        option_reader :region
      end

      def initialize(*)
        super
        options[:endpoint] = endpoint % { region: region } if respond_to?(:endpoint) && options.key?(:endpoint)
      end
    end
  end
end


text:
<?php

namespace Neuron\Model\Menu;

use Nette\Environment;
use Nette\Web\HttpRequest, Nette\Web\UriScript;
use Nette\String;

/**
 * Menu item
 *
 * @author Jan Marek
 *
 * @Entity
 * @Table(name="menuitem")
 */
class MenuItem extends \Neuron\Model\BaseEntity implements \DoctrineExtensions\NestedSet\Node
{
	/** @Column(nullable=true) */
	private $name;

	/** @Column(nullable=true) */
	private $destination;

	/** @Column(type="array") */
	private $params = array();

	/** @Column(nullable=true) */
	private $url;

	/** @Column(type="integer") */
	private $lft;

	/** @Column(type="integer") */
	private $rgt;

	public function setName($name)
	{
		$this->name = $name;
	}

	public function getName()
	{
		return $this->name;
	}

	public function isCurrent()
	{
		if (empty($this->destination)) {
			return false;
		}

		$presenter = Environment::getApplication()->getPresenter();
		$presenter->link(":$this->destination", $this->params);
		return $presenter->getLastCreatedRequestFlag("current");
	}

	public function isExternal()
	{
		return isset($this->url) && empty($this->destination);
	}

	public function getUrl()
	{
		if (isset($this->destination)) {
			return Environment::getApplication()->getPresenter()->link("//:$this->destination", $this->params);
		} else {
			return $this->url;
		}
	}

	public function setUrl($url)
	{
		$uriScript = new UriScript($url);
		$uriScript->setScriptPath(Environment::getHttpRequest()->getUri()->getScriptPath());

		$httpRequest = new HttpRequest($uriScript);
		$presenterRequest = Environment::getApplication()->getRouter()->match($httpRequest);

		if ($presenterRequest === null || !String::startsWith($url, Environment::getVariable("baseUri"))) {
			$this->url = $url ?: null;
			$this->destination = null;
			$this->params = array();

		} else {
			$presenter = $presenterRequest->getPresenterName();
			$params = $presenterRequest->getParams();
			$action = isset($params["action"]) ? $params["action"] : "default";
			$module = isset($params["module"]) ? $params["module"] . ":" : "";
			unset($params["action"]);

			$this->destination = "$module$presenter:$action";
			$this->params = $params;
			$this->url = null;
		}
	}

	// Node interface

	public function __toString()
	{
		return $this->name;
	}

	public function getLeftValue()
	{
		return $this->lft;
	}

	public function getRightValue()
	{
		return $this->rgt;
	}

	public function setLeftValue($lft)
	{
		$this->lft = $lft;
	}

	public function setRightValue($rgt)
	{
		$this->rgt = $rgt;
	}

}