Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
2a6158c
correct doc string typo
phycodurus Jul 24, 2026
f0f4326
explain in settings.py tmpl that INSTALLED_APPS can add facilities
phycodurus Jul 24, 2026
2058c86
add Facilities nav-bar item via TomObaservations integration point
phycodurus Jul 24, 2026
2e86f2b
collect facilities from both settings and installed apps
phycodurus Jul 24, 2026
9ebaf3d
Merge branch 'dev' into 1627-general-facilities-nav-bar-menu
jchate6 Jul 30, 2026
4bce18a
simplify comment
phycodurus Aug 3, 2026
dd5e810
make index_url_name a Facility class attribute
phycodurus Aug 3, 2026
e5e5d5f
update doc for observation_facilities integration point
phycodurus Aug 3, 2026
9cb1399
improve docstring with tom_demoapp reference
phycodurus Aug 3, 2026
0ad385f
update for index_url_name class attribute (not int point config)
phycodurus Aug 4, 2026
ee70d91
clarify what is being tested and why
phycodurus Aug 4, 2026
672b9a4
Merge branch 'dev' into 1627-general-facilities-nav-bar-menu
jchate6 Aug 4, 2026
ede300b
update some doc strings
jchate6 Aug 4, 2026
e0958c5
fix a few more doc strings
jchate6 Aug 4, 2026
42d52b5
rename index_url_name → detail_url_name
phycodurus Aug 4, 2026
b07b1f6
update docs for facility_detail name change
phycodurus Aug 6, 2026
3860480
clean up some doc strings
phycodurus Aug 10, 2026
cd3c0f8
clean up doc strings, comments, and error messages
phycodurus Aug 11, 2026
6f835ea
make Facilities drop-down css consisten with other nav-bar menus
phycodurus Aug 11, 2026
fa03486
add documentation for observation_facilities integration point
phycodurus Aug 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/common/customsettings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ A list of observation facility classes to make available to your TOM. If
you have written or downloaded a custom observation facility you would
add the class to this list to make your TOM load it.

INSTALLED_APPS that implement the ``observation_facilities()`` AppConfig integration
point do not need to be listed here.

`TOM_LATEX_PROCESSORS <#tom-latex-processors>`__
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
63 changes: 63 additions & 0 deletions docs/observing/observation_module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,28 @@ like this:
This means our new observation facility module has been successfully
loaded.

Adding a facility from an app
-----------------------------

A facility implemented as a Django reusable app can advertise itself
to a TOM without the editing ``settings.py``, which simplifies it's installation.
To do this, implement the ``observation_facilities()`` integration point
on the app's ``AppConfig`` subclass:

.. code:: python

class MyAppConfig(AppConfig):
name = 'myapp'

def observation_facilities(self):
return [{'class': f'{self.name}.myfacility.MyObservationFacility'}]

Facilities from both ``settings.TOM_FACILITY_CLASSES`` and the apps implementing
the integration point are merged by ``tom_observations.facility.get_service_classes()``.
So, if your facility implements the ``observation_facilities()`` integration point,
adding the app to ``INSTALLED_APPS`` is all that is required for installation.
See ``tom_demoapp`` for a worked example.

BaseRoboticObservationFacility and BaseRoboticObservationForm
-------------------------------------------------------------

Expand All @@ -126,6 +148,47 @@ use. The ``BaseRoboticObservationForm`` class, just like the previous
super class, contains logic and layout that all observation facility
form classes should contain.

Linking to a facility detail page
---------------------------------

A facility may set ``detail_url_name`` to the namespaced Django URL name of a page
describing the facility. Facilities that set it appear in the navbar **Facilities**
dropdown, linked to that page:

.. code:: python

class MyObservationFacility(BaseRoboticObservationFacility):
name = 'MyFacility'
detail_url_name = 'myapp:facility-detail'

``detail_url_name`` is optional and is omitted from the minimal example above. A facility
that leaves it unset is still fully registered -- it has an observe button and observation
forms -- but gets no menu item. If no facility sets it, the dropdown is not displayed.
See ``tom_demoapp`` for a worked example, including composing the namespace from the
AppConfig's ``url_namespace`` attribute.

For ``detail_url_name`` to resolve, the app's URLs must be mounted in the TOM under that
namespace. A reusable app mounts its own ``urls.py`` through the ``include_url_paths()``
AppConfig integration point -- ``tom_common`` collects these paths from every installed
app, so no TOM ``urls.py`` edits are required:

.. code:: python

class MyAppConfig(AppConfig):
name = 'myapp'
url_prefix = 'myapp' # URL path prefix for this app's pages: HOST/myapp/...
url_namespace = 'myapp' # the namespace half of detail_url_name

def include_url_paths(self):
return [
path(f'{self.url_prefix}/', include(f'{self.name}.urls', namespace=self.url_namespace)),
]

(``url_prefix`` and ``url_namespace`` are TOM plugin conventions, not Django AppConfig
attributes.) The included ``urls.py`` must set ``app_name`` -- Django requires it when
``include()`` is called with ``namespace=`` -- and must contain a ``path()`` whose
``name=`` is the second half of ``detail_url_name``.

Implementing observation submission
-----------------------------------

Expand Down
17 changes: 10 additions & 7 deletions tom_dataservices/dataservices.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def get_data_service_classes():
"""
Imports the Dataservice class from relevant apps and generates a list of data service names.

Each dataservice class should be contained in a list of dictionaries in an app's apps.py `dataservices` method.
Each dataservice class should be contained in a list of dictionaries in an app's apps.py `data_services` method.
Each dataservice dictionary should contain a 'class' key with the dot separated path to the dataservice class
(typically an extension of DataService).

Expand Down Expand Up @@ -84,8 +84,9 @@ class QueryServiceError(Exception):


class DataService(ABC):
"""
Base class for all Data Services. Data Services are classes that are responsible for querying external services
"""Base class for all Data Services.

Data Services are classes that are responsible for querying external services
and returning data.
"""
# Recognizable name for the DataService (Gaia, TNS, etc)
Expand Down Expand Up @@ -123,11 +124,11 @@ def query_service(self, query_parameters, **kwargs):
"""Takes in the serialized data from the query form and actually submits the query to the service"""

def pre_query_validation(self, query_parameters):
"""Same thing as query_service, but a dry run"""
"""Same thing as query_service, but a dry run."""
raise NotImplementedError(f'pre_query_validation method has not been implemented for {self.name}')

def build_query_parameters(self, parameters, **kwargs):
"""Builds the query parameters from the form data"""
"""Builds the query parameters from the form data."""
raise NotImplementedError(f'build_query_parameters method has not been implemented for {self.name}')

# Include this method if you wish for the TOM to be able to query data for an individual Target.
Expand Down Expand Up @@ -200,7 +201,7 @@ def get_credentials(cls, **kwargs):

@classmethod
def urls(cls, **kwargs) -> dict:
"""Dictionary of URLS for the DataService"""
"""Dictionary of URLS for the DataService."""
return {'base_url': cls.base_url, 'info_url': cls.info_url}

@classmethod
Expand Down Expand Up @@ -387,6 +388,7 @@ def to_target(self, target_result=None, **kwargs):

def create_target_from_query(self, target_result, **kwargs):
"""Create a new target from a single instance of the target results.

:param target_result: dictionary describing target details based on query result
:returns: target object
:rtype: `Target`
Expand Down Expand Up @@ -420,7 +422,8 @@ def to_aliases(self, target, alias_results: List, **kwargs) -> List:
return new_aliases

def create_aliases_from_query(self, alias_results: List, **kwargs) -> List:
"""Create a new target name from the query results
"""Create a new target name from the query results.

This method should be over ridden with a method that creates a list of TargetName objects:
`TargetName(name=alias)` that will be saved as part of the `Target.save(extras=extras, names=aliases)` call.
:param query_result: list of dictionaries describing target details based on query result
Expand Down
11 changes: 11 additions & 0 deletions tom_observations/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,14 @@

class TomObservationsConfig(AppConfig):
name = 'tom_observations'

def nav_items(self):
"""Integration point for adding items to the navbar.

This method should return a list of partial templates to be included in the navbar.

Here, the "Facilities" dropdown menu, listing the facilities contributed by installed
apps via the observation_facilities() integration point (see ``tom_demoapp`` for example).
"""
return [{'partial': 'tom_observations/partials/navbar_facilities_list.html',
'context': 'tom_observations.templatetags.observation_extras.observation_facilities_list'}]
Loading
Loading