Startup
-------

The following code snippet illustrates how to import the necessary API
modules (technically, **import requests** is not necessary, but some of
the API calls are simpler with that module available):

.. code-block:: python

  from ansys.dynamicreporting.core.utils import report_remote_server, report_objects
  import requests


Simple Example
--------------

This snippet creates a new database, starts an ADR Nexus server, verifies the
server and shuts it down. It uses the port=None option to allow the
system to find and use a non-conflicting port to run the server on. If
needed, the port is returned as part of the URL returned by:
serverobj.get_URL().

.. code-block:: python

  db_dir = "D:/data/example_database"
  report_remote_server.create_new_local_database(None, directory=db_dir)
  serverobj = report_remote_server.Server()
  report_remote_server.launch_local_database_server(
      None, port=None, directory=db_dir, connect=serverobj
  )
  version_number = serverobj.validate()
  serverobj.stop_local_server()

core.report_remote_server module
--------------------------------

This module includes the interfaces needed to manage ADR Nexus servers.

Module functions
^^^^^^^^^^^^^^^^

Many of these functions support Qt integration. The parent option
specifies the Qt widget that should be the parent if they are to display
any GUI elements. If parent is specified as None, no GUI elements will
be presented to the user and the operation will just return an error
status.

report_remote_server.launch_local_database_server
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python

  bError = launch_local_database_server(
      parent,
      directory="",
      no_directory_prompt=False,
      port=8000,
      connect=None,
      terminate_on_python_exit=False,
      delete_db_on_python_exit=False,
      username="nexus",
      password="cei",
      verbose=True,
      return_info=None,
      raise_exception=False,
      use_system_tray=None,
      server_timeout=180.0,
      **kwargs
  )


This function will try to launch a local ADR Nexus server using the database
found in the specified directory on the specified port. If parent is not
set None, the function will interact with the user via Qt modal dialogs
(parent will be the parent of the dialogs) to select database directory,
ports, username and password. One can bypass the prompt to select the
server directory using the no_directory_prompt keyword argument.

By default, a launched server will continue to run after the current
Python session stops. There is a server method stop_local_server() that
can be used to stop the launched server. Otherwise, the
terminate_on_python_exit keyword can be set to True to cause the server
to be automatically shut down if the current Python interpreter instance
exits cleanly.

If the delete_db_on_python_exit argument is also set to True, then once
the server is automatically shut down the database directory it was
connected to will be deleted.

If the Qt parent is not set to None and the server has not been set to
terminate on exit, then the use_system_tray option can be used to cause
the server to place a menu in the system tray. This menu will allow for
the server to be stopped, the logs to be reviewed and other options.

When launching a server, this function will try to connect to the
launched server for some time before assuming the launch has failed. The
length of this timeout is specified by the server_timeout keyword.

By default, the function will return True on success or False on
failure. If raise_exception is True, the function will raise an
exception instead of returning False.

The username and password for the database should be specified using
those keywords.

If the connect keyword is specified, it should be an instance of the
report_remote_server.Server class. This object will be populated with
the launched server details when the function returns. It is a handy way
to capture a dynamically selected port.

The port keyword can be set to the specific port number the ADR Nexus server
should use. The port keyword may also be set to None, in which case the
system will search for an open port on which to start the ADR Nexus server,
starting at 8000. If this option is selected, it is critical that the
'connect' keyword be set, otherwise there will be no mechanism for one
to know the actual port that was used.

The verbose keyword controls the level of log output generated by the
ADR Nexus server and determines if the user is allowed to modify any
dynamically selected port number.

In addition to the above mentioned arguments, this method supports all
of the options available to the Ansys Dynamic Reporting Launcher CLI (through ``**kwargs``).

report_remote_server.create_new_local_database
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python

  bError = create_new_local_database(
      parent, directory="", return_info={}, raise_exception=False
  )


This function will create a new, empty database with the default
username and password in the directory specified by the directory
keyword. If parent is None, this function will try to create the
database and return True on success without presenting any GUI elements.
It is very important to set the directory keyword if passing None as the
parent. Otherwise, parent should be a Qt widget object that will serve
as the parent of modal dialog used to select the directory to contain
the new database. The target directory should be empty as this method
will insert a media directory, a db.sqlite3 file and a manage.py file.
The return_info argument is optional. If a dictionary is passed,
information about the created database will be stored in it.

If raise_exception is True, method will throw an error instead of
returning the error flag.

report_remote_server.connect_to_server_dialog
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

.. code-block:: python

  bError = connect_to_server_dialog(parent, server, title=None)


This method presents a dialog to the user with a window title specified
by the title keyword that allows the user to enter all of the
information needed to connect to an ADR Nexus server. It then attempts to
connect to the server and returns True if the connection succeeded. The
server argument should be an instance of the report_remote_server.Server
class which will be initialized with the entered values. This method can
only be called from a PyQt application. You can specify None for the
parent, but it will have the same effect as displaying a modal dialog
with no parent specified. Do not use this method if your application
should not display any GUI.

:doc:`report_remote_server.Server <ServerObject>`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

This class provides an interface to the ADR Nexus server. Most of the core
API can be accessed via an instance of this class. The Server object has
methods for creating instances of data item and template objects.


core.report_objects module
--------------------------

This module provides the core data items that can be pushed into
an Ansys Dynamic Reporting
database. This includes the session, dataset and item objects.
Generally, these classes should be created using the Server object
methods create_item, create_layout, default_dataset and default_session
or via one of the Server query methods.

.. _ReportObjectsList:


Class hierarchy
^^^^^^^^^^^^^^^


**BaseRESTObject** - Abstract Ansys Dynamic Reporting REST object interface base class

    - :ref:`DatasetREST <DatasetREST>` - Dataset
      information

    - :ref:`SessionREST <SessionREST>` - Session
      information

    - :ref:`ItemREST <ItemREST>` - Core data items

    - :ref:`TemplateREST <TemplateREST>` - Abstract base class
      for all template (layout and generator) classes

    - :ref:`LayoutREST <LayoutREST>` - Abstract base class for
      all layout classes

        - :ref:`basicREST <basicREST>` - Basic column layout class

        - :ref:`panelREST <panelREST>` - Column layout class with
          support for pullouts and headers

        - :ref:`boxREST <boxREST>` - Explicit child layout class

        - :ref:`tabsREST <tabsREST>` - Layout that organizes
          layout children into tabs

        - :ref:`carouselREST <carouselREST>` - Layout that
          organizes children into a 'carousel' presentation

        - :ref:`sliderREST <sliderREST>` - Specialized image
          layout for interactive image review/comparison

        - :ref:`footerREST <footerREST>` - Page footer layout

        - :ref:`headerREST <headerREST>` - Page header layout

        - :ref:`iteratorREST <iteratorREST>` - Tag-based layout
          replicator

        - :ref:`tagpropsREST <tagpropsREST>` - Map item/layout
          tags into properties

        - :ref:`tocREST <tocREST>` - Table of contents/figure list
          layout

        - :ref:`reportlinkREST <reportlinkREST>` - Cross layout
          linking

    - :ref:`GeneratorREST <GeneratorREST>` - Abstract base
      class for all generator classes

        - :ref:`tablemergeREST <tablemergeREST>` - Table merge
          generator for merging multiple tables into a single table

        - :ref:`tablereduceREST <tablereduceREST>` - Table
          reduction generator that may collapse rows/columns into aggregated
          values

        - :ref:`tablerowcolumnfilterREST <tablerowcolumnfilterREST>` -
          Table row/column filter generator for removing/organizing table rows
          and columns

        - :ref:`tablevaluefilterREST <tablevaluefilterREST>` - Table
          value filter generator for processing table values

        - :ref:`tablesortfilterREST <tablesortfilterREST>` - Table
          sort/filter generator for re-organizing tables

        - :ref:`sqlqueriesREST <sqlqueriesREST>` - SQL query
          generator for pulling data into the report from external SQL databases

        - :ref:`treemergeREST <mergetreeREST>` - tree merge
          generator for merging multiple trees into a single tree


Examples
--------

A simple example of how this API might be used:


.. code-block:: python

  from ansys.dynamicreporting.core.utils import report_remote_server, report_objects

  serverobj = report_remote_server.Server(
      url="http://localhost:8000", username="nexus", password="cei"
  )
  session = serverobj.get_default_session()
  session.application = "My Application"
  session.version = "10.2"
  item = serverobj.create_item(name="Simple header", source="My Python script")
  item.set_payload_html("<h1>An Example Header</h1>")
  error = serverobj.put_objects([item])


This would start a new session that connects to an ADR Nexus server already
running on port 8000 of the local system. The server has a default
dataset and a default session object. We change the name and version of
the session application before creating a new data item. The data item
is populated with some HTML source and then pushed to the server. The
put_objects() call will push the item, the session and the dataset
objects all to the ADR Nexus server.

A more complex example that generates an ASCII table of three columns
representing a username, a version number and a date (as a floating
point value):

.. code-block:: python

  # core Python modules
  from dateutil import parser
  import datetime
  import random
  import requests
  import numpy

  # Ansys Dynamic Reporting modules
  from ansys.dynamicreporting.core.utils import report_remote_server, report_objects

  # time values can be represented as double precision counts of seconds from a standard time_base
  time_base = datetime.datetime(1970, 1, 1)


  def make_time(s):
      dt = parser.parse(s)
      return (dt - time_base).total_seconds()


  # generate a row of random values
  def row_gen(start, end):
      users = ["bob", "fred", "mary", "jill"]
      versions = ["1.1", "2.0", "1.3", "1.0"]
      t0 = make_time(start)
      t1 = make_time(end)
      return [
          users[random.randint(0, 3)],
          versions[random.randint(0, 3)],
          t0 + (t1 - t0) * random.random(),
      ]


  # connect to the default ADR Nexus server (this assumes the server had been started previously

  s = report_remote_server.Server("http://localhost:8000", "nexus", "cei")
  nrows = 40
  ncols = 3
  item = s.create_item(name="Text List Example", source="externalAPI", sequence=0)
  array = numpy.zeros((nrows, ncols), dtype="\|S20")
  for i in range(nrows):
      array[i] = row_gen("1/1/2017", "2/1/2017")

  item.set_payload_table_values(
      array, rowlbls=None, collbls=["User", "Version", "Date"], title="January"
  )
  item.add_tag("month", "Jan")
  item.add_tag("user_version_example")
  if s.put_objects(item) == requests.codes.ok:
      print("Success")
