Skip to content

Commit

Permalink
docs(new): add sphinx docs
Browse files Browse the repository at this point in the history
  • Loading branch information
idocx committed Oct 25, 2021
1 parent ff83ed9 commit 0829965
Show file tree
Hide file tree
Showing 25 changed files with 502 additions and 28 deletions.
1 change: 1 addition & 0 deletions alab_management/config.default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ task_db = "tasks"

[sample_positions]
sample_db = "sample_positions"
containers = ["crucible", "flask", "vial"]
11 changes: 4 additions & 7 deletions alab_management/lab_status/device_view.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
from enum import Enum, unique, auto

from bson import ObjectId

from ..db import get_collection


@unique
class DeviceStatus(Enum):
Expand All @@ -16,10 +12,11 @@ class DeviceStatus(Enum):


class DeviceView:
device_collection = get_collection("devices")
def get_status(self, device_name: str) -> DeviceStatus:
...

def get_status(self, device_id: ObjectId) -> DeviceStatus:
def set_status(self, device_name: str, status: DeviceStatus):
...

def set_status(self, device_id: ObjectId, status: DeviceStatus):
def get_device(self, name):
...
2 changes: 1 addition & 1 deletion alab_management/op_def/base_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def dest_location(self) -> str:
return self.operation_location

@abstractmethod
def __call__(self):
def run(self):
raise NotImplementedError()

@abstractmethod
Expand Down
5 changes: 4 additions & 1 deletion alab_management/sample_position.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
class SamplePosition:
name: str
description: str = field(compare=False, hash=False)
alias: List[str] = field(default_factory=list, compare=False, hash=False)


@dataclass
Expand All @@ -20,3 +19,7 @@ class SamplePositionPair:
def __post_init__(self):
if self.containers is None:
self.containers = config["sample_positions"]["containers"]

for container in self.containers:
if container not in config["sample_positions"]["containers"]:
raise ValueError(f"Undefined container: {container}, please define it in config file.")
11 changes: 7 additions & 4 deletions alab_management/scripts/cleanup_lab.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
"""
To remove all the device, task, sample position definition from database
"""

from ..config import config
from ..db import get_collection


def clean_up_db():
"""
Drop device, task, sample_position collection from MongoDB
"""
device_collection = get_collection(config["devices"]["device_db"])
task_collection = get_collection(config["tasks"]["task_db"])
sample_position_collection = get_collection(config["sample_positions"]["sample_db"])

device_collection.drop()
task_collection.drop()
sample_position_collection.drop()


def main():
clean_up_db()
2 changes: 1 addition & 1 deletion alab_management/scripts/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def launch_executor():
executor.run()


def main():
def launch_lab():
task_manager_process = Process(target=launch_task_manager)
executor_process = Process(target=launch_executor)

Expand Down
84 changes: 75 additions & 9 deletions alab_management/scripts/setup_lab.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
"""
Setup the lab,
Set up the lab,
Generate device, sample position, task definitions from user defined files (task & device)
and write them to MongoDB
which will make it easier to query
"""

import inspect
from dataclasses import asdict, fields, Field
from typing import Iterable, Type
from typing import Iterable, Type, Any, Dict, List

import pymongo
from bson import ObjectId

from .cleanup_lab import clean_up_db
from ..config import config
from ..db import get_collection
from ..sample_position import SamplePosition
from ..device_def.base_device import get_all_devices, BaseDevice
from ..lab_status.device_view import DeviceStatus
from ..lab_status.sample_view import SamplePositionStatus
from ..op_def.base_operation import BaseOperation, BaseMovingOperation
from ..sample_position import SamplePosition
from ..utils.fake_device import FakeDevice
from ..utils.module_ops import import_module_from_path, get_full_cls_name
from ..utils.typing_ops import is_typing
Expand All @@ -31,14 +33,14 @@ def add_sample_positions_to_db(collection: pymongo.collection.Collection,
Insert sample positions info to db, which includes position name,
number, description and status (default to UNKNOWN)
If one sample position's name has already appeared in the database, we will just skip it.
If one sample position's name has already appeared in the database,
we will just skip it.
Args:
collection: the db collection to store sample position data
sample_positions: some sample position instances
"""
for sample_pos in sample_positions:

sample_pos_ = collection.find_one({"name": sample_pos.name})
if sample_pos_ is None:
collection.insert_one({
Expand Down Expand Up @@ -77,22 +79,46 @@ def add_devices_to_db(collection: pymongo.collection.Collection,


def setup_from_device_def():
"""
Set up sample positions, devices from user's device definition, whose path is
specified by `config["devices"]["device_dir"]`
"""
device_collection = get_collection(config["devices"]["device_db"])
sample_position_collection = get_collection(config["sample_positions"]["sample_db"])

device_collection.create_index([("name", pymongo.HASHED)])
sample_position_collection.create_index([("name", pymongo.HASHED)])

# import all the devices, which will execute `__init__.py` in the device dir
# and call `add_device` function
import_module_from_path(config["devices"]["device_dir"])

# obtain all the devices
devices = get_all_devices().values()

add_devices_to_db(device_collection, devices)
add_sample_positions_to_db(sample_position_collection, [sample_pos for device in devices
for sample_pos in device.sample_positions])


def init_with_fake_parameters(cls: Type[BaseOperation]):
def init_with_fake_parameters(cls: Type[BaseOperation]) -> BaseOperation:
"""
For task class, which need to be initialized to get the occupied positions
and operation locations. We will initialized the task with a set of fake parameters
based on their type annotations.
Possible parameters type:
- BaseDevice -> FakeDevice with name = {{{name}}}
- SamplePosition -> SamplePosition with name = {{{name}}}
- (nested) built-in types: int, float, str, list, dict, set
Notes:
Things like `List[BaseDevice]` and `Dict[str, SamplePosition]` are not
supported up to now.
Args:
cls: the operation class to be initialized
"""
type_hints = fields(cls)

fake_parameters = {}
Expand All @@ -115,6 +141,14 @@ def init_with_fake_parameters(cls: Type[BaseOperation]):

def add_tasks_to_db(collection: pymongo.collection.Collection,
tasks: Iterable[BaseOperation]):
"""
Insert task definitions to db, which includes tasks' name, operation location,
(where the sample should be), occupied positions and accepted_args
Args:
collection: the collection that stores task definition data
tasks: some tasks inherited from :obj:`BaseOperation`
"""
for task in tasks:
if collection.find_one({"name": task.__class__.__name__}) is not None:
raise NameError(f"Duplicated task name: {task.__class__.__name__}")
Expand All @@ -123,8 +157,8 @@ def add_tasks_to_db(collection: pymongo.collection.Collection,
"operation_location": task.operation_location,
"occupied_positions": task.occupied_positions,
"dist_location": task.dest_location,
"accept_args": [{"name": field.name, "type": get_full_cls_name(field.type)} for field in fields(task)
if is_typing(field.type) or not issubclass(field.type, (BaseDevice, ObjectId))]
"accepted_args": [{"name": field.name, "type": get_full_cls_name(field.type)} for field in fields(task)
if is_typing(field.type) or not issubclass(field.type, (BaseDevice, ObjectId))]
}
if isinstance(task, BaseMovingOperation):
task_info.update({
Expand All @@ -138,7 +172,38 @@ def add_tasks_to_db(collection: pymongo.collection.Collection,
collection.insert_one(task_info)


def make_sample_position_graph():
"""
From the moving operation's source and destination pairs,
we can add edges to sample view, which can tell the system
how to move samples between two places
"""
task_collection = get_collection(config["tasks"]["task_db"])
sample_position_collection = get_collection(config["sample_positions"]["sample_db"])

next_positions_dict: Dict[str, List[Dict[str, Any]]] = {}
for task in task_collection.find({"src_dest_pairs": {"$exists": True}}):
for src_dest_pair in task["src_dest_pairs"]:
next_positions_dict.setdefault(src_dest_pair["src"], [])
next_positions_dict[src_dest_pair["src"]].extend([{
"dest": src_dest_pair["dest"],
"container": container,
"task_name": task["name"],
} for container in src_dest_pair["containers"]])

for name, next_positions in next_positions_dict.items():
if sample_position_collection.find_one({"name": name}) is None:
raise ValueError(f"Sample position does not exist: {name}")
sample_position_collection.update_one({"name": name}, {"$set": {
"next_positions": next_positions,
}})


def setup_from_task_def():
"""
Set up sample positions' edges, task definitions from user's task definition, whose path is
specified by `config["tasks"]["task_dir"]`
"""
task_collection = get_collection(config["tasks"]["task_db"])

task_collection.create_index([("name", pymongo.HASHED)])
Expand All @@ -147,9 +212,10 @@ def setup_from_task_def():

tasks = [init_with_fake_parameters(task_cls) for task_cls in tasks_cls]
add_tasks_to_db(task_collection, tasks)
make_sample_position_graph()


def main():
def setup_lab():
clean_up_db()
setup_from_device_def()
setup_from_task_def()
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = source
BUILDDIR = build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Binary file added docs/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions docs/source/alab_management.device_def.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
alab\_management.device\_def package
====================================

Submodules
----------

alab\_management.device\_def.base\_device module
------------------------------------------------

.. automodule:: alab_management.device_def.base_device
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

.. automodule:: alab_management.device_def
:members:
:undoc-members:
:show-inheritance:
29 changes: 29 additions & 0 deletions docs/source/alab_management.executor.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
alab\_management.executor package
=================================

Submodules
----------

alab\_management.executor.executor module
-----------------------------------------

.. automodule:: alab_management.executor.executor
:members:
:undoc-members:
:show-inheritance:

alab\_management.executor.scheduler module
------------------------------------------

.. automodule:: alab_management.executor.scheduler
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

.. automodule:: alab_management.executor
:members:
:undoc-members:
:show-inheritance:
37 changes: 37 additions & 0 deletions docs/source/alab_management.lab_status.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
alab\_management.lab\_status package
====================================

Submodules
----------

alab\_management.lab\_status.device\_view module
------------------------------------------------

.. automodule:: alab_management.lab_status.device_view
:members:
:undoc-members:
:show-inheritance:

alab\_management.lab\_status.sample\_view module
------------------------------------------------

.. automodule:: alab_management.lab_status.sample_view
:members:
:undoc-members:
:show-inheritance:

alab\_management.lab\_status.task\_view module
----------------------------------------------

.. automodule:: alab_management.lab_status.task_view
:members:
:undoc-members:
:show-inheritance:

Module contents
---------------

.. automodule:: alab_management.lab_status
:members:
:undoc-members:
:show-inheritance:
10 changes: 10 additions & 0 deletions docs/source/alab_management.logger.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
alab\_management.logger package
===============================

Module contents
---------------

.. automodule:: alab_management.logger
:members:
:undoc-members:
:show-inheritance:
Loading

0 comments on commit 0829965

Please sign in to comment.