Skip to content

Commit

Permalink
docs: More on Python classes and how to get them. Rename to how-to.
Browse files Browse the repository at this point in the history
  • Loading branch information
odscjames committed Feb 4, 2022
1 parent 49efd39 commit 6668929
Show file tree
Hide file tree
Showing 17 changed files with 171 additions and 81 deletions.
10 changes: 0 additions & 10 deletions docs/examples/index.rst

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Example - create aggregate data
===============================
How to create aggregate data
============================

We want to run a survey about how many people like hats.

Expand All @@ -10,7 +10,7 @@ First we'll import the library and make a new store.
.. code-block:: python
from ocdsmetricsanalysis.library import Store
store = Store("example-create-aggregates.sqlite")
store = Store("how-to-create-aggregates.sqlite")
We'll add our own metric.

Expand Down
10 changes: 7 additions & 3 deletions docs/examples/create.rst → docs/how-to/create-export.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Example - create data
=====================
How to create and export data
=============================

Open a python shell in interactive mode (run `python3`) and run the following commands in sequence.

Expand All @@ -9,7 +9,7 @@ First we'll import the library and make a new store.
.. code-block:: python
from ocdsmetricsanalysis.library import Store
store = Store("example-create.sqlite")
store = Store("how-to-create-export.sqlite")
Currently the store is empty, as you can see by getting a list of current metrics.

Expand All @@ -33,6 +33,10 @@ Now we have our metric, but we need to add some observations to it.
metric.add_observation("obs2", measure=15, dimensions={'answer':'neither like or dislike'})
metric.add_observation("obs3", measure=12, dimensions={'answer':'dislike'})
The first parameter is the id of the observation. This must be unique in the metric.

You can also :doc:`pass other information such as financial data or unit information - see the reference for details <../python-api/metric>`.

Now our store contains some data - we can export this as JSON.

.. code-block:: python
Expand Down
6 changes: 3 additions & 3 deletions docs/examples/read.rst → docs/how-to/import.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Example - read data
===================
How to import data
==================


Start by getting a data file:
Expand All @@ -16,7 +16,7 @@ First we'll import the library and make a new store.
.. code-block:: python
from ocdsmetricsanalysis.library import Store
store = Store("example-read.sqlite")
store = Store("how-to-import.sqlite")
Currently the store is empty, as you can see by getting a list of current metrics.

Expand Down
10 changes: 10 additions & 0 deletions docs/how-to/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
How To
======

.. toctree::
:maxdepth: 3

create-export.rst
query.rst
import.rst
create-aggregates.rst
8 changes: 4 additions & 4 deletions docs/examples/query.rst → docs/how-to/query.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Example - query data
====================
How to query data
=================

Setup
-----
Expand All @@ -11,7 +11,7 @@ First we'll import the library, make a new store and add some sample data to que
.. code-block:: python
from ocdsmetricsanalysis.library import Store
store = Store("example-query.sqlite")
store = Store("how-to-query.sqlite")
store.add_metric("HATS", "How many people like hats?", "We ran a survey to find out.")
metric = store.get_metric("HATS")
metric.add_observation("obs1", measure=34, dimensions={'answer':'like'})
Expand Down Expand Up @@ -171,7 +171,7 @@ Now get all data and print it, as before.
12
{'answer': 'dislike'}
This is however a touch inconvenient; you have to know what all the other dimensions are and specifically exclude them. Fortunately there is an easier way to do this - let's get a new Observation List object and try again.
This is however inconvenient; you have to know what all the other dimensions are and specifically exclude them. Fortunately there is an easier way to do this - let's get a new Observation List object and try again.


.. code-block:: python
Expand Down
7 changes: 2 additions & 5 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ It can be used in a `Jupyter <https://jupyter.org/>`_ or `Google Colab <https://
:maxdepth: 3

install.rst
store.rst
metric.rst
observation_list.rst
observation.rst
examples/index.rst
how-to/index.rst
python-api/index.rst

11 changes: 0 additions & 11 deletions docs/metric.rst

This file was deleted.

13 changes: 0 additions & 13 deletions docs/observation.rst

This file was deleted.

12 changes: 0 additions & 12 deletions docs/observation_list.rst

This file was deleted.

11 changes: 11 additions & 0 deletions docs/python-api/index.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Python API
==========

.. toctree::
:maxdepth: 3

store.rst
metric.rst
observation_list.rst
observation.rst

24 changes: 24 additions & 0 deletions docs/python-api/metric.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Metric
======

Getting a metric
----------------

Do not construct a metric directly. Instead, use one of these methods on a store.


.. autofunction:: ocdsmetricsanalysis.library.Store.get_metric
:noindex:


.. autofunction:: ocdsmetricsanalysis.library.Store.get_metrics
:noindex:



Class reference
---------------

.. autoclass:: ocdsmetricsanalysis.library.Metric
:members:
:undoc-members:
24 changes: 24 additions & 0 deletions docs/python-api/observation.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Observation
===========



Getting an observation
----------------------

Do not construct an Observation directly. Instead, use these methods on an observation list.


.. autofunction:: ocdsmetricsanalysis.library.ObservationList.get_data
:noindex:

.. autofunction:: ocdsmetricsanalysis.library.ObservationList.get_data_by_dimension
:noindex:


Class reference
---------------

.. autoclass:: ocdsmetricsanalysis.library.Observation
:members:
:undoc-members:
20 changes: 20 additions & 0 deletions docs/python-api/observation_list.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Observation List
================


Getting an observation list
---------------------------

Do not construct an Observation List directly. Instead, use this method on a metric.


.. autofunction:: ocdsmetricsanalysis.library.Metric.get_observation_list
:noindex:


Class reference
---------------

.. autoclass:: ocdsmetricsanalysis.library.ObservationList
:members:
:undoc-members:
23 changes: 23 additions & 0 deletions docs/python-api/store.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
Store
=====


Getting a store
---------------

Create a new instance of the class.

.. code-block:: python
from ocdsmetricsanalysis.library import Store
store = Store("temp-database.sqlite")
See below for the parameters to pass.


Class reference
---------------

.. autoclass:: ocdsmetricsanalysis.library.Store
:members:
:undoc-members:
11 changes: 0 additions & 11 deletions docs/store.rst

This file was deleted.

46 changes: 40 additions & 6 deletions ocdsmetricsanalysis/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@


class Store:
def __init__(self, database_filename):
"""
Every time you want to work with a set of data, you need to create a store.
A store has methods for adding and querying metrics and observations.
Stores do not persist data automatically; if you want to keep the data there are methods to export it as JSON
that should be used before discarding the store.
Construct: Pass database_filename. This should be a file that does not already exist.
It is not needed after the store is finished with and can be deleted."""

def __init__(self, database_filename: str):
self._database_connection = sqlite3.connect(database_filename)
self._database_connection.row_factory = sqlite3.Row
cur = self._database_connection.cursor()
Expand Down Expand Up @@ -105,6 +115,11 @@ def get_metrics(self):


class Metric:
"""A class representing one metric from a store.
Do not construct directly; instead call other methods on a Store to get a metric.
"""

def __init__(self, store: Store, metric_id: str):
self._store = store
self._metric_id = metric_id
Expand Down Expand Up @@ -300,6 +315,12 @@ def get_dimension_keys(self) -> list:


class ObservationList:
"""A class to get list of observations from a metric.
It has methods to set query filters, and then methods to get the data.
Do not construct directly; instead call `get_observation_list` on a Metric to get an observation list.
"""

def __init__(self, metric: Metric):
self._metric: Metric = metric
self._store: Store = metric._store
Expand All @@ -314,8 +335,10 @@ def filter_by_dimension_not_set(self, dimension_key: str):
"""Filter by dimension - this key must not exist on the observation."""
self._filter_by_dimensions_not_set.append(dimension_key)

def get_data(self):
"""Returns a list of Observations matching the filters set on this object."""
def get_data(self) -> list:
"""Returns a list of Observations.
Observations will match the filters set on this observation list. (Just don't set any filters to get all observations.)"""
cur = self._store._database_connection.cursor()

params: dict = {"metric_id": self._metric._metric_id}
Expand Down Expand Up @@ -366,13 +389,18 @@ def get_data(self):

cur.execute(sql, params)

out = []
out: list = []
for result in cur.fetchall():
out.append(Observation(self._metric, result))
return out

def get_data_by_dimension(self, dimension_key: str):
out = defaultdict(list)
def get_data_by_dimension(self, dimension_key: str) -> dict:
"""Returns Observations grouped by the value of a dimension key.
Observations will match the filters set on this observation list. (Just don't set any filters to get all observations.)
Returns a dict. The key is the value of the dimension, and the value is a list of all observations with that dimension value."""
out: dict = defaultdict(list)
for observation in self.get_data():
dimensions: dict = observation.get_dimensions()
if dimensions.get(dimension_key):
Expand All @@ -381,6 +409,12 @@ def get_data_by_dimension(self, dimension_key: str):


class Observation:
"""A class representing one observation from a store.
It has methods to get information.
Do not construct directly; instead use an ObservationList to get Observations.
"""

def __init__(self, metric: Metric, observation_row_data):
self._metric: Metric = metric
self._store: Store = metric._store
Expand Down

0 comments on commit 6668929

Please sign in to comment.