From d32d3653953321ad71240f07a0cb8c90e26e5ffd Mon Sep 17 00:00:00 2001 From: jtyoung84 <104453205+jtyoung84@users.noreply.github.com> Date: Mon, 24 Jun 2024 15:59:13 -0700 Subject: [PATCH] Release docs v1.0.0 (#120) * docs: adds user docs and diagrams * docs: adds UserGuide and example csv file * docs: removes gitchangelog * Update docs/source/Contributing.rst Co-authored-by: Helen Lin <46795546+helen-m-lin@users.noreply.github.com> * docs: adds link in README and adds readthedocs config * docs: update line in User Guide --------- Co-authored-by: Helen Lin <46795546+helen-m-lin@users.noreply.github.com> --- .readthedocs.yaml | 14 + README.md | 192 +------------- {doc_template => docs}/Makefile | 0 docs/diagrams/system_container.png | Bin 0 -> 49474 bytes docs/diagrams/system_container.puml | 26 ++ docs/diagrams/system_context.png | Bin 0 -> 43740 bytes docs/diagrams/system_context.puml | 19 ++ docs/examples/example1.csv | 4 + {doc_template => docs}/make.bat | 0 docs/source/Contributing.rst | 242 ++++++++++++++++++ docs/source/UserGuide.rst | 194 ++++++++++++++ .../source/_static/dark-logo.svg | 0 .../source/_static/favicon.ico | Bin .../source/_static/light-logo.svg | 0 .../aind_data_transfer_service.configs.rst | 37 +++ .../source/aind_data_transfer_service.hpc.rst | 29 +++ docs/source/aind_data_transfer_service.rst | 38 +++ {doc_template => docs}/source/conf.py | 0 {doc_template => docs}/source/index.rst | 2 + docs/source/modules.rst | 7 + pyproject.toml | 5 +- src/aind_data_transfer_service/__init__.py | 2 +- 22 files changed, 618 insertions(+), 193 deletions(-) create mode 100644 .readthedocs.yaml rename {doc_template => docs}/Makefile (100%) create mode 100644 docs/diagrams/system_container.png create mode 100644 docs/diagrams/system_container.puml create mode 100644 docs/diagrams/system_context.png create mode 100644 docs/diagrams/system_context.puml create mode 100644 docs/examples/example1.csv rename {doc_template => docs}/make.bat (100%) create mode 100644 docs/source/Contributing.rst create mode 100644 docs/source/UserGuide.rst rename {doc_template => docs}/source/_static/dark-logo.svg (100%) rename {doc_template => docs}/source/_static/favicon.ico (100%) rename {doc_template => docs}/source/_static/light-logo.svg (100%) create mode 100644 docs/source/aind_data_transfer_service.configs.rst create mode 100644 docs/source/aind_data_transfer_service.hpc.rst create mode 100644 docs/source/aind_data_transfer_service.rst rename {doc_template => docs}/source/conf.py (100%) rename {doc_template => docs}/source/index.rst (94%) create mode 100644 docs/source/modules.rst diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..be755ca --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,14 @@ +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.9" + +python: + install: + - method: pip + path: . + extra_requirements: + - dev + - docs diff --git a/README.md b/README.md index 163116e..daa07cd 100644 --- a/README.md +++ b/README.md @@ -6,194 +6,4 @@ This service can be used to upload data stored in a VAST drive. It uses FastAPI to upload a job submission csv file that will be used to trigger a data transfer job in an on-prem HPC. Based on the information provided in the file, the data upload process fetches the appropriate metadata and starts the upload process. -## Metadata Sources - -The associated metadata files get pulled from different sources. - -- subject from LabTracks -- procedures from NSB Sharepoint, TARS -- instrument/rig from SLIMS - - -## Usage - -There are two options for uploading data: a python API or a browser UI service. - -### Browser UI -You can go to http://aind-data-transfer-service to submit a `.csv` or `.xlsx` file with the necessary parameters needed to launch a data upload job. Click on **Job Submit Template** to download a template which you may use as a reference. - -What each column means in the job submission template: - -- **project_name**: Project name. A full list can be downloaded at [Project Names](http://aind-metadata-service/project_names) -- **process_capsule_id**: Optional Code Ocean capsule or pipeline to run when data is uploaded -- **input_data_mount**: Optional data mount when running a custom pipeline -- **platform**: For a list of platforms click [here](https://github.com/AllenNeuralDynamics/aind-data-schema/blob/main/src/aind_data_schema/models/platforms.py). -- **acq_datetime**: The time that the data was acquired -- **subject_id**: The unique id of the subject -- **modality0**: For a list of modalities, click [here](https://github.com/AllenNeuralDynamics/aind-data-schema/blob/main/src/aind_data_schema/models/modalities.py). -- **modality0.source**: The source (path to file) of **modality0** in VAST drive -- **metadata_dir**: An optional folder for pre-compiled metadata json files - -Modify the job template as needed and click on **Browse** to upload the file. A rendered table with a message **Successfully validated jobs from file** appears to indicate a valid file. If there are errors in the job submit file, a message that says **Error validating jobs from file** appears. - -To launch a data upload job, click on `Submit`. A message that says **Successfuly submitted jobs** should appear. - -After submission, click on `Job Status` to see the status of the data upload job process. - -### Python API -It's also possible to submit a job via a python api. Here is an example script that can be used. - -Assuming that the data on a shared drive is organized as: -``` -/shared_drive/vr_foraging/690165/20240219T112517 - - Behavior - - Behavior videos - - Configs -``` -then a job request can be submitted as: -```python -from aind_data_transfer_service.configs.job_configs import ModalityConfigs, BasicUploadJobConfigs -from pathlib import PurePosixPath -import json -import requests - -from aind_data_transfer_models.core import ModalityConfigs, BasicUploadJobConfigs, SubmitJobRequest -from aind_data_schema_models.modalities import Modality -from aind_data_schema_models.platforms import Platform -from datetime import datetime - -source_dir = PurePosixPath("/shared_drive/vr_foraging/690165/20240219T112517") - -s3_bucket = "private" -subject_id = "690165" -acq_datetime = datetime(2024, 2, 19, 11, 25, 17) -platform = Platform.BEHAVIOR - - -behavior_config = ModalityConfigs(modality=Modality.BEHAVIOR, source=(source_dir / "Behavior")) -behavior_videos_config = ModalityConfigs(modality=Modality.BEHAVIOR_VIDEOS, source=(source_dir / "Behavior videos")) -metadata_dir = source_dir / "Config" # This is an optional folder of pre-compiled metadata json files -project_name="Ephys Platform" - -upload_job_configs = BasicUploadJobConfigs( - project_name=project_name, - s3_bucket = s3_bucket, - platform = platform, - subject_id = subject_id, - acq_datetime=acq_datetime, - modalities = [behavior_config, behavior_videos_config], - metadata_dir = metadata_dir -) - -# Add more to the list if needed -upload_jobs=[upload_job_configs] - -# Optional email address and notification types if desired -user_email = "my_email_address" -email_notification_types = ["fail"] -submit_request = SubmitJobRequest( - upload_jobs=upload_jobs, - user_email=user_email, - email_notification_types=email_notification_types, -) - -post_request_content = json.loads(submit_request.model_dump_json(round_trip=True)) -submit_job_response = requests.post(url="http://aind-data-transfer-service/api/v1/submit_jobs", json=post_request_content) -print(submit_job_response.status_code) -print(submit_job_response.json()) -``` - -## Installation -To use the software, in the root directory, run -```bash -pip install -e . -``` - -To develop the code, run -```bash -pip install -e .[dev] -``` - -## Local Development -Run uvicorn: -```bash -export AIND_METADATA_SERVICE_PROJECT_NAMES_URL='http://aind-metadata-service-dev/project_names' -export AIND_AIRFLOW_SERVICE_URL='http://localhost:8080/api/v1/dags/run_list_of_jobs/dagRuns' -export AIND_AIRFLOW_SERVICE_JOBS_URL='http://localhost:8080/api/v1/dags/transform_and_upload/dagRuns' -export AIND_AIRFLOW_SERVICE_PASSWORD='*****' -export AIND_AIRFLOW_SERVICE_USER='user' -uvicorn aind_data_transfer_service.server:app --host 0.0.0.0 --port 5000 -``` -You can now access `http://localhost:5000`. - -## Contributing - -### Linters and testing - -There are several libraries used to run linters, check documentation, and run tests. - -- Please test your changes using the **coverage** library, which will run the tests and log a coverage report: - -```bash -coverage run -m unittest discover && coverage report -``` - -- Use **interrogate** to check that modules, methods, etc. have been documented thoroughly: - -```bash -interrogate . -``` - -- Use **flake8** to check that code is up to standards (no unused imports, etc.): -```bash -flake8 . -``` - -- Use **black** to automatically format the code into PEP standards: -```bash -black . -``` - -- Use **isort** to automatically sort import statements: -```bash -isort . -``` - -### Pull requests - -For internal members, please create a branch. For external members, please fork the repository and open a pull request from the fork. We'll primarily use [Angular](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit) style for commit messages. Roughly, they should follow the pattern: -```text -(): -``` - -where scope (optional) describes the packages affected by the code changes and type (mandatory) is one of: - -- **build**: Changes that affect build tools or external dependencies (example scopes: pyproject.toml, setup.py) -- **ci**: Changes to our CI configuration files and scripts (examples: .github/workflows/ci.yml) -- **docs**: Documentation only changes -- **feat**: A new feature -- **fix**: A bugfix -- **perf**: A code change that improves performance -- **refactor**: A code change that neither fixes a bug nor adds a feature -- **test**: Adding missing tests or correcting existing tests - -### Semantic Release - -The table below, from [semantic release](https://github.com/semantic-release/semantic-release), shows which commit message gets you which release type when `semantic-release` runs (using the default configuration): - -| Commit message | Release type | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | -| `fix(pencil): stop graphite breaking when too much pressure applied` | ~~Patch~~ Fix Release, Default release | -| `feat(pencil): add 'graphiteWidth' option` | ~~Minor~~ Feature Release | -| `perf(pencil): remove graphiteWidth option`

`BREAKING CHANGE: The graphiteWidth option has been removed.`
`The default graphite width of 10mm is always used for performance reasons.` | ~~Major~~ Breaking Release
(Note that the `BREAKING CHANGE: ` token must be in the footer of the commit) | - -### Documentation -To generate the rst files source files for documentation, run -```bash -sphinx-apidoc -o doc_template/source/ src -``` -Then to create the documentation HTML files, run -```bash -sphinx-build -b html doc_template/source/ doc_template/build/html -``` -More info on sphinx installation can be found [here](https://www.sphinx-doc.org/en/master/usage/installation.html). +More information can be found at [http://aind-data-transfer-service.readthedocs.io](readthedocs). diff --git a/doc_template/Makefile b/docs/Makefile similarity index 100% rename from doc_template/Makefile rename to docs/Makefile diff --git a/docs/diagrams/system_container.png b/docs/diagrams/system_container.png new file mode 100644 index 0000000000000000000000000000000000000000..e891772c996b9b71088537bc5e4a9e5e3a9f62d2 GIT binary patch literal 49474 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%0DbCpmQNQtOQfy=YkNZJQ9tks%n64dF_ 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 literal 0 HcmV?d00001 diff --git a/docs/diagrams/system_container.puml b/docs/diagrams/system_container.puml new file mode 100644 index 0000000..bda393d --- /dev/null +++ b/docs/diagrams/system_container.puml @@ -0,0 +1,26 @@ +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml +' uncomment the following line and comment the first to use locally +' !include C4_Container.puml + +' LAYOUT_TOP_DOWN() +' LAYOUT_AS_SKETCH() +LAYOUT_WITH_LEGEND() + +title Container diagram for AIND Data Transfer Service + +Person(user, "User", "A scientist or engineer that wants to upload data to the cloud.") + +System_Boundary(c1, "AIND Data Transfer Service") { + Container(app, "API Application", "FastAPI, Docker Container", "Validates and submits request to aind-airflow-service. Runs in K8s cluster managed by Central IT.") +} + +System_Ext(aind_airflow_service, "AIND Airflow Service", "Receives job requests, does additional validation checks, submits and monitors jobs.") +System_Ext(slurm, "Slurm", "High performance computing cluster that runs data transformation and data upload jobs.") + +Rel(user, app, "Uses", "HTTP, REST") + +Rel_Back(user, aind_airflow_service, "Sends e-mails to", "SMTP") +Rel(app, aind_airflow_service, "Uses", "REST API") +Rel(aind_airflow_service, slurm, "Uses", "REST API") +@enduml diff --git a/docs/diagrams/system_context.png b/docs/diagrams/system_context.png new file mode 100644 index 0000000000000000000000000000000000000000..040c2cfb062893033706cd5abe5ca52c8f13949d GIT binary patch literal 43740 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

3ReSU&YF?N>8E#e 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 DhE;?^ literal 0 HcmV?d00001 diff --git a/docs/diagrams/system_context.puml b/docs/diagrams/system_context.puml new file mode 100644 index 0000000..fa512ca --- /dev/null +++ b/docs/diagrams/system_context.puml @@ -0,0 +1,19 @@ +@startuml +!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Context.puml +' uncomment the following line and comment the first to use locally +' !include C4_Context.puml + +LAYOUT_WITH_LEGEND() + +title System Context diagram for AIND Data Transfer Service + +Person(user, "User", "A scientist or engineer that wants to upload data to the cloud.") +System(transfer_service, "AIND Data Transfer Service", "Allows people to send job requests to compress (or transform) and upload raw data assets.") +System_Ext(aind_airflow_service, "AIND Airflow Service", "Receives job requests, does additional validation checks, submits and monitors jobs.") +System_Ext(slurm, "Slurm", "High performance computing cluster that runs data transformation and data upload jobs.") + +Rel(user, transfer_service, "Uses", "web portal or REST API") +Rel_Back(user, aind_airflow_service, "Sends e-mails to", "SMTP") +Rel(transfer_service, aind_airflow_service, "Uses", "REST API") +Rel(aind_airflow_service, slurm, "Uses", "REST API") +@enduml diff --git a/docs/examples/example1.csv b/docs/examples/example1.csv new file mode 100644 index 0000000..d62bab1 --- /dev/null +++ b/docs/examples/example1.csv @@ -0,0 +1,4 @@ +project_name, process_capsule_id, modality0, modality0.source, modality1, modality1.source, s3-bucket, subject-id, platform, acq-datetime +Ephys Platform, , ECEPHYS, dir/data_set_1, ,, some_bucket, 123454, ecephys, 2020-10-10 14:10:10 +Behavior Platform, 1f999652-00a0-4c4b-99b5-64c2985ad070, BEHAVIOR_VIDEOS, dir/data_set_2, MRI, dir/data_set_3, open, 123456, BEHAVIOR, 10/13/2020 1:10:10 PM +Behavior Platform, , BEHAVIOR_VIDEOS, dir/data_set_2, BEHAVIOR_VIDEOS, dir/data_set_3, scratch, 123456, BEHAVIOR, 10/13/2020 1:10:10 PM diff --git a/doc_template/make.bat b/docs/make.bat similarity index 100% rename from doc_template/make.bat rename to docs/make.bat diff --git a/docs/source/Contributing.rst b/docs/source/Contributing.rst new file mode 100644 index 0000000..919e367 --- /dev/null +++ b/docs/source/Contributing.rst @@ -0,0 +1,242 @@ +Contributor Guidelines +====================== + +This document will go through best practices for contributing to this +project. We welcome and appreciate contributions or ideas for +improvement. + +- `Bug Reports and Feature + Requests <#bug-reports-and-feature-requests>`__ +- `Local Installation for + Development <#local-installation-for-development>`__ +- `Branches and Pull Requests <#branches-and-pull-requests>`__ +- `Release Cycles <#release-cycles>`__ + +Bug Reports and Feature Requests +-------------------------------- + +Before creating a pull request, we ask contributors to please open a bug +report or feature request first: +`issues `__ + +We will do our best to monitor and maintain the backlog of issues. + +Local Installation for Development +---------------------------------- + +For development, + +- For new features or non-urgent bug fixes, create a branch off of + ``dev`` +- For an urgent hotfix to our production environment, create a branch + off of ``main`` + +Consult the `Branches and Pull Requests <#branches-and-pull-requests>`__ +and `Release Cycles <#release-cycles>`__ for more details. + +Running a local server +~~~~~~~~~~~~~~~~~~~~~~ + +From the root directory, run: + +.. code:: bash + + pip install -e .[dev] + +to install the relevant code for development. + +We will work on setting up a local dev server to mock airflow responses. +In the meantime, it’s assumed that you are able to connect to our +development backend. Please reach out to us if you need an airflow +account. + +To run uvicorn locally: + +.. code:: bash + + export AIND_METADATA_SERVICE_PROJECT_NAMES_URL='http://aind-metadata-service-dev/project_names' + export AIND_AIRFLOW_SERVICE_URL='http://aind-airflow-service-dev:8080/api/v1/dags/run_list_of_jobs/dagRuns' + export AIND_AIRFLOW_SERVICE_JOBS_URL='http://aind-airflow-service-dev:8080/api/v1/dags/transform_and_upload/dagRuns' + export AIND_AIRFLOW_SERVICE_PASSWORD='*****' + export AIND_AIRFLOW_SERVICE_USER='user' + uvicorn aind_data_transfer_service.server:app --host 0.0.0.0 --port 5000 --reload + +You can now access aind-data-transfer-service at +``http://localhost:5000``. + +Branches and Pull Requests +-------------------------- + +Branch naming conventions +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Name your branch using the following format: +``--`` + +where: + +```` is one of: - **build**: Changes that affect the build system +or external dependencies (e.g., pyproject.toml, setup.py) - **ci**: +Changes to our CI configuration files and scripts (examples: +.github/workflows/ci.yml) - **docs**: Changes to our documentation - +**feat**: A new feature - **fix**: A bug fix - **perf**: A code change +that improves performance - **refactor**: A code change that neither +fixes a bug nor adds a feature, but will make the codebase easier to +maintain - **test**: Adding missing tests or correcting existing tests - +**hotfix**: An urgent bug fix to our production code + +```` references the GitHub issue this branch will close + +```` is a brief description that shouldn’t be more than 3 +words. + +Some examples: + +- ``feat-12-adds-email-field`` +- ``fix-27-corrects-endpoint`` +- ``test-43-updates-server-test`` + +We ask that a separate issue and branch are created if code is added +outside the scope of the reference issue. + +Commit Messages +~~~~~~~~~~~~~~~ + +Please format your commit messages as ``: `` where +```` is from the list above and the short summary is one or two +sentences. + +Testing and Docstrings +~~~~~~~~~~~~~~~~~~~~~~ + +We strive for complete code coverage and docstrings, and we also run +code format checks. + +- To run the code format check: + +.. code:: bash + + flake8 . + +- There are some helpful libraries that will automatically format the + code and import statements: + +.. code:: bash + + black . + +and + +.. code:: bash + + isort . + +Strings that exceed the maximum line length may still need to be +formatted manually. + +- To run the docstring coverage check and report: + +.. code:: bash + + interrogate -v . + +This project uses NumPy’s docstring format: `Numpy docstring +standards `__ + +Many IDEs can be configured to automatically format docstrings in the +NumPy convention. + +- To run the unit test coverage check and report: + +.. code:: bash + + coverage run -m unittest discover && coverage report + +- To view a more detailed html version of the report, run: + +.. code:: bash + + coverage run -m unittest discover && coverage report + coverage html + +and then open ``htmlcov/index.html`` in a browser. + +Pull Requests +~~~~~~~~~~~~~ + +Pull requests and reviews are required before merging code into this +project. You may open a ``Draft`` pull request and ask for a preliminary +review on code that is currently a work-in-progress. + +Before requesting a review on a finalized pull request, please verify +that the automated checks have passed first. + +Release Cycles +-------------------------- + +For this project, we have adopted the `Git +Flow `__ system. We will +strive to release new features and bug fixes on a two week cycle. The +rough workflow is: + +Hotfixes +~~~~~~~~ + +- A ``hotfix`` branch is created off of ``main`` +- A Pull Request into is ``main`` is opened, reviewed, and merged into + ``main`` +- A new ``tag`` with a patch bump is created, and a new ``release`` is + deployed +- The ``main`` branch is merged into all other branches + +Feature branches and bug fixes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- A new branch is created off of ``dev`` +- A Pull Request into ``dev`` is opened, reviewed, and merged + +Release branch +~~~~~~~~~~~~~~ + +- A new branch ``release-v{new_tag}`` is created +- Documentation updates and bug fixes are created off of the + ``release-v{new_tag}`` branch. +- Commits added to the ``release-v{new_tag}`` are also merged into + ``dev`` +- Once ready for release, a Pull Request from ``release-v{new_tag}`` + into ``main`` is opened for final review +- A new tag will automatically be generated +- Once merged, a new GitHub Release is created manually + +Pre-release checklist +~~~~~~~~~~~~~~~~~~~~~ + +- ☐ Increment ``__version__`` in + ``aind_data_transfer_service/__init__.py`` file +- ☐ Run linters, unit tests, and integration tests +- ☐ Verify code is deployed and tested in test environment +- ☐ Update examples +- ☐ Update documentation + + - Run: + + .. code:: bash + + sphinx-apidoc -o docs/source/ src + sphinx-build -b html docs/source/ docs/build/html + +- ☐ Update and build UML diagrams + + - To build UML diagrams locally using a docker container: + + .. code:: bash + + docker pull plantuml/plantuml-server + docker run -d -p 8080:8080 plantuml/plantuml-server:jetty + +Post-release checklist +~~~~~~~~~~~~~~~~~~~~~~ + +- ☐ Merge ``main`` into ``dev`` and feature branches +- ☐ Edit release notes if needed +- ☐ Post announcement diff --git a/docs/source/UserGuide.rst b/docs/source/UserGuide.rst new file mode 100644 index 0000000..a19a772 --- /dev/null +++ b/docs/source/UserGuide.rst @@ -0,0 +1,194 @@ +User Guide +========== + +Thank you for using ``aind-data-transfer-service``! This guide is +intended for scientists and engineers in AIND that wish to upload data +from our shared network drives (e.g., VAST) to the cloud. + +Prerequisites +------------- + +- It’s assumed that raw data is already stored and organized on a + shared network drive such as VAST. +- The raw data should be organized by modality. + + - Example 1: + + .. code:: bash + + - /allen/aind/scratch/working_group/session_123456_2024-06-19 + - /ecephys_data + - /behavior_data + - /behavior_videos + - /aind_metadata + + - Example 2: + + .. code:: bash + + - /allen/aind/scratch/ecephys_data/session_123456_2024-06-19 + - /allen/aind/scratch/behavior_data/session_123456_2024-06-19 + - /allen/aind/scratch/behavior_videos/session_123456_2024-06-19 + - /allen/aind/scratch/aind_metadata/session_123456_2024-06-19 + +- The different modalities should not be nested + +Using the web portal +-------------------- + +Access to the web portal is available only through the VPN. The web +portal can accessed at +`http://aind-data-transfer-service/ `__ + +- Download the excel template file by clicking the + ``Job Submit Template`` link. + +- If there are compatibility issues with the excel template, you can + try saving it as a csv file and modifying the csv file + +- Create one row per data acquisition session + +- Required fields + + - project_name: A list of project names can be seen by clicking the + ``Project Names`` link + - subject_id: The LabTracks ID of the mouse + - acq_datetime: The date and time the data was acquired. Should be + in ISO format, for example, 2024-05-27T16:07:59 + - platform: Standardized way of collecting and processing data + (chosen from drop down menu) + - **modalities**: Two columns must be added per modality. A + **modality** (chosen from drop down menu) and a Posix style path + to the data source. For example, + + - modality0 (e.g., ecephys) + - modaltity0.source (e.g., + /allen/aind/scratch/working_group/session_123456_2024-06-19/ecephys_data) + - modality1 (e.g, behavior) + - modality1.source (e.g., + /allen/aind/scratch/working_group/session_123456_2024-06-19/behavior_data) + - modality2 (e.g, behavior_videos) + - modality2.source (e.g., + /allen/aind/scratch/working_group/session_123456_2024-06-19/behavior_videos) + +- Optional fields + + - metadata_dir: If metadata files are pre-compiled and saved to a + directory, you can add the Posix style path to the directory under + this column + - process_capsule_id: If you wish to trigger a custom Code Ocean + Capsule or pipeline, you can add the capsule_id here + - input_data_mount: If you wish to trigger a custom Code Ocean + Pipeline that has been configured with a specific data mount, you + can add that here + - s3_bucket: As default, data will be uploaded to a private bucket + in S3 managed by AIND. Please reach out to the Scientific + Computing department if you wish to upload to a different bucket. + - metadata_dir_force: We will automatically pull subject and + procedures data for a mouse. By setting this ``True``, we will + overwrite any data in the ``metadata_dir`` folder with data + acquired automatically from our service + - force_cloud_sync: We run a check to verify whether there is + already a data asset with this name saved in our S3 bucket. If + this field is set to ``True``, we will sync the data to the + bucket/folder even if it already exists + +Using the REST API +------------------ + +Jobs can also be submitted via a REST API at the endpoint +``http://aind-data-transfer-service/api/v1/submit_jobs`` + +.. code-block:: python + + from aind_data_transfer_service.configs.job_configs import ModalityConfigs, BasicUploadJobConfigs + from pathlib import PurePosixPath + import json + import requests + + from aind_data_transfer_models.core import ModalityConfigs, BasicUploadJobConfigs, SubmitJobRequest + from aind_data_schema_models.modalities import Modality + from aind_data_schema_models.platforms import Platform + from datetime import datetime + + source_dir = PurePosixPath("/shared_drive/vr_foraging/690165/20240219T112517") + + s3_bucket = "private" + subject_id = "690165" + acq_datetime = datetime(2024, 2, 19, 11, 25, 17) + platform = Platform.BEHAVIOR + + behavior_config = ModalityConfigs(modality=Modality.BEHAVIOR, source=(source_dir / "Behavior")) + behavior_videos_config = ModalityConfigs(modality=Modality.BEHAVIOR_VIDEOS, source=(source_dir / "Behavior videos")) + metadata_dir = source_dir / "Config" # This is an optional folder of pre-compiled metadata json files + project_name = "Ephys Platform" + + upload_job_configs = BasicUploadJobConfigs( + project_name=project_name, + s3_bucket=s3_bucket, + platform=platform, + subject_id=subject_id, + acq_datetime=acq_datetime, + modalities=[behavior_config, behavior_videos_config], + metadata_dir=metadata_dir + ) + + # Add more to the list if needed + upload_jobs = [upload_job_configs] + + # Optional email address and notification types if desired + user_email = "my_email_address" + email_notification_types = ["fail"] + submit_request = SubmitJobRequest( + upload_jobs=upload_jobs, + user_email=user_email, + email_notification_types=email_notification_types, + ) + + post_request_content = json.loads(submit_request.model_dump_json(round_trip=True, exclude_none=True)) + # Uncomment the following to submit the request + # submit_job_response = requests.post(url="http://aind-data-transfer-service/api/v1/submit_jobs", json=post_request_content) + # print(submit_job_response.status_code) + # print(submit_job_response.json()) + +Adding a notifications email address +------------------------------------ + +- NOTE: This is currently optional, but may be required in the future + +You can optionally add your email address to receive email notifications +about the jobs you’ve submitted. The notification types are: + +- BEGIN: When a job starts +- END: When a job is finished +- RETRY: When a job step had an issue and was automatically retried +- FAIL: When a job has failed completely +- ALL: To receive a notification if any one of the previous events has + triggered + +Custom Slurm settings +--------------------- + +``aind-data-transfer-service`` is a small service that forwards requests +to run a compression and upload pipeline. The major computation work is +performed on our Slurm cluster. + +We have provided default settings that work in most cases. However, for +very large jobs, such as processing more than a TB of data, you may need +to customize the Slurm settings to avoid timeouts or out-of-memory +errors. + +Please reach out to Scientific Computing if you think you may need to +customize the Slurm settings. + +Viewing the status of submitted jobs +------------------------------------ + +The status of submitted jobs can be viewed at: +http://aind-data-transfer-service/jobs + +Reporting bugs or making feature requests +----------------------------------------- + +Please report any bugs or feature requests here: +`issues `__ diff --git a/doc_template/source/_static/dark-logo.svg b/docs/source/_static/dark-logo.svg similarity index 100% rename from doc_template/source/_static/dark-logo.svg rename to docs/source/_static/dark-logo.svg diff --git a/doc_template/source/_static/favicon.ico b/docs/source/_static/favicon.ico similarity index 100% rename from doc_template/source/_static/favicon.ico rename to docs/source/_static/favicon.ico diff --git a/doc_template/source/_static/light-logo.svg b/docs/source/_static/light-logo.svg similarity index 100% rename from doc_template/source/_static/light-logo.svg rename to docs/source/_static/light-logo.svg diff --git a/docs/source/aind_data_transfer_service.configs.rst b/docs/source/aind_data_transfer_service.configs.rst new file mode 100644 index 0000000..b30b75e --- /dev/null +++ b/docs/source/aind_data_transfer_service.configs.rst @@ -0,0 +1,37 @@ +aind\_data\_transfer\_service.configs package +============================================= + +Submodules +---------- + +aind\_data\_transfer\_service.configs.csv\_handler module +--------------------------------------------------------- + +.. automodule:: aind_data_transfer_service.configs.csv_handler + :members: + :undoc-members: + :show-inheritance: + +aind\_data\_transfer\_service.configs.job\_configs module +--------------------------------------------------------- + +.. automodule:: aind_data_transfer_service.configs.job_configs + :members: + :undoc-members: + :show-inheritance: + +aind\_data\_transfer\_service.configs.job\_upload\_template module +------------------------------------------------------------------ + +.. automodule:: aind_data_transfer_service.configs.job_upload_template + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aind_data_transfer_service.configs + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/aind_data_transfer_service.hpc.rst b/docs/source/aind_data_transfer_service.hpc.rst new file mode 100644 index 0000000..a2091f1 --- /dev/null +++ b/docs/source/aind_data_transfer_service.hpc.rst @@ -0,0 +1,29 @@ +aind\_data\_transfer\_service.hpc package +========================================= + +Submodules +---------- + +aind\_data\_transfer\_service.hpc.client module +----------------------------------------------- + +.. automodule:: aind_data_transfer_service.hpc.client + :members: + :undoc-members: + :show-inheritance: + +aind\_data\_transfer\_service.hpc.models module +----------------------------------------------- + +.. automodule:: aind_data_transfer_service.hpc.models + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aind_data_transfer_service.hpc + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/source/aind_data_transfer_service.rst b/docs/source/aind_data_transfer_service.rst new file mode 100644 index 0000000..c07d6ae --- /dev/null +++ b/docs/source/aind_data_transfer_service.rst @@ -0,0 +1,38 @@ +aind\_data\_transfer\_service package +===================================== + +Subpackages +----------- + +.. toctree:: + :maxdepth: 4 + + aind_data_transfer_service.configs + aind_data_transfer_service.hpc + +Submodules +---------- + +aind\_data\_transfer\_service.models module +------------------------------------------- + +.. automodule:: aind_data_transfer_service.models + :members: + :undoc-members: + :show-inheritance: + +aind\_data\_transfer\_service.server module +------------------------------------------- + +.. automodule:: aind_data_transfer_service.server + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: aind_data_transfer_service + :members: + :undoc-members: + :show-inheritance: diff --git a/doc_template/source/conf.py b/docs/source/conf.py similarity index 100% rename from doc_template/source/conf.py rename to docs/source/conf.py diff --git a/doc_template/source/index.rst b/docs/source/index.rst similarity index 94% rename from doc_template/source/index.rst rename to docs/source/index.rst index 07adcad..d4bfdfb 100644 --- a/doc_template/source/index.rst +++ b/docs/source/index.rst @@ -11,6 +11,8 @@ Welcome to this repository's documentation! :maxdepth: 2 :caption: Contents: + UserGuide + Contributing modules diff --git a/docs/source/modules.rst b/docs/source/modules.rst new file mode 100644 index 0000000..7b6d782 --- /dev/null +++ b/docs/source/modules.rst @@ -0,0 +1,7 @@ +src +=== + +.. toctree:: + :maxdepth: 4 + + aind_data_transfer_service diff --git a/pyproject.toml b/pyproject.toml index fb12d09..fcb1b2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,10 @@ dev = [ 'coverage', 'flake8', 'interrogate', - 'isort', + 'isort' +] + +docs = [ 'Sphinx', 'furo' ] diff --git a/src/aind_data_transfer_service/__init__.py b/src/aind_data_transfer_service/__init__.py index c1b75e0..a7f8953 100644 --- a/src/aind_data_transfer_service/__init__.py +++ b/src/aind_data_transfer_service/__init__.py @@ -1,7 +1,7 @@ """Init package""" import os -__version__ = "0.14.1" +__version__ = "1.0.0" # Global constants OPEN_DATA_BUCKET_NAME = os.getenv("OPEN_DATA_BUCKET_NAME", "open")