From 091a19e25c57306555952dbd479e2aa9551653f3 Mon Sep 17 00:00:00 2001 From: Patrick Robertson Date: Fri, 21 Feb 2025 16:52:30 +0000 Subject: [PATCH] Further docs improvements/tidy ups --- docs/scripts/scripts.py | 27 ++++++-- docs/source/bc.png | Bin 0 -> 43453 bytes docs/source/conf.py | 15 +++- docs/source/index.md | 4 +- docs/source/installation/configurations.md | 15 ++-- docs/source/installation/installation.md | 77 ++++++--------------- docs/source/installation/requirements.md | 14 ++++ docs/source/installation/setup.md | 76 ++++++++++++++++++++ src/auto_archiver/core/config.py | 2 +- 9 files changed, 159 insertions(+), 71 deletions(-) create mode 100644 docs/source/bc.png create mode 100644 docs/source/installation/requirements.md create mode 100644 docs/source/installation/setup.md diff --git a/docs/scripts/scripts.py b/docs/scripts/scripts.py index d6fd392..66ba14d 100644 --- a/docs/scripts/scripts.py +++ b/docs/scripts/scripts.py @@ -3,6 +3,7 @@ from pathlib import Path from auto_archiver.core.module import ModuleFactory from auto_archiver.core.base_module import BaseModule from ruamel.yaml import YAML +from ruamel.yaml.comments import CommentedMap import io MODULES_FOLDER = Path(__file__).parent.parent.parent.parent / "src" / "auto_archiver" / "modules" @@ -30,6 +31,7 @@ steps: ... {config_string} + """ def generate_module_docs(): @@ -38,8 +40,9 @@ def generate_module_docs(): modules_by_type = {} header_row = "| " + " | ".join(TABLE_HEADER) + "|\n" + "| --- " * len(TABLE_HEADER) + "|\n" - configs_cheatsheet = "\n## Configuration Options\n" - configs_cheatsheet += header_row + global_table = "\n## Configuration Options\n" + header_row + + global_yaml = yaml.load("""\n# Module configuration\nplaceholder: {}""") for module in sorted(ModuleFactory().available_modules(), key=lambda x: (x.requires_setup, x.name)): # generate the markdown file from the __manifest__.py file. @@ -66,6 +69,11 @@ def generate_module_docs(): config_table = header_row config_yaml = {} + + global_yaml[module.name] = CommentedMap() + global_yaml.yaml_set_comment_before_after_key(module.name, f"\n\n{module.display_name} configuration options") + + for key, value in manifest['configs'].items(): type = value.get('type', 'string') if type == 'json_loader': @@ -75,10 +83,16 @@ def generate_module_docs(): default = value.get('default', '') config_yaml[key] = default + + global_yaml[module.name][key] = default + + if value.get('help', ''): + global_yaml[module.name].yaml_add_eol_comment(value.get('help', ''), key) + help = "**Required**. " if value.get('required', False) else "Optional. " help += value.get('help', '') config_table += f"| `{module.name}.{key}` | {help} | {value.get('default', '')} | {type} |\n" - configs_cheatsheet += f"| `{module.name}.{key}` | {help} | {default} | {type} |\n" + global_table += f"| `{module.name}.{key}` | {help} | {default} | {type} |\n" readme_str += "\n## Configuration Options\n" readme_str += "\n### YAML\n" @@ -103,8 +117,13 @@ def generate_module_docs(): f.write(readme_str) generate_index(modules_by_type) + del global_yaml['placeholder'] + global_string = io.BytesIO() + global_yaml = yaml.dump(global_yaml, global_string) + global_string = global_string.getvalue().decode('utf-8') + global_yaml = f"```yaml\n{global_string}\n```" with open(SAVE_FOLDER / "configs_cheatsheet.md", "w") as f: - f.write(configs_cheatsheet) + f.write("### Configuration File\n" + global_yaml + "\n### Command Line\n" + global_table) def generate_index(modules_by_type): diff --git a/docs/source/bc.png b/docs/source/bc.png new file mode 100644 index 0000000000000000000000000000000000000000..766529b5ddd3a1db2a2eebbddbab6b0700434b62 GIT binary patch literal 43453 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 nR}b;8(O!X?|3{rwG3Qz{kqc?pU$;ndtujxF!3R literal 0 HcmV?d00001 diff --git a/docs/source/conf.py b/docs/source/conf.py index 5b1ad9b..ee6416e 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -3,9 +3,11 @@ import sys import os from importlib.metadata import metadata +from datetime import datetime sys.path.append(os.path.abspath('../scripts')) from scripts import generate_module_docs +from auto_archiver.version import __version__ # -- Project Hooks ----------------------------------------------------------- # convert the module __manifest__.py files into markdown files @@ -15,7 +17,8 @@ generate_module_docs() # -- Project information ----------------------------------------------------- package_metadata = metadata("auto-archiver") project = package_metadata["name"] -authors = "Bellingcat" +copyright = str(datetime.now().year) +author = "Bellingcat" release = package_metadata["version"] language = 'en' @@ -32,7 +35,7 @@ extensions = [ ] templates_path = ['_templates'] -exclude_patterns = [] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ""] # -- AutoAPI Configuration --------------------------------------------------- @@ -76,6 +79,14 @@ source_suffix = { html_theme = 'sphinx_book_theme' html_static_path = ["../_static"] html_css_files = ["custom.css"] +html_title = f"Auto Archiver v{__version__}" +html_logo = "bc.png" +html_theme_options = { + "repository_url": "https://github.com/bellingcat/auto-archiver", + "use_repository_button": True, +} + + copybutton_prompt_text = r">>> |\.\.\." copybutton_prompt_is_regexp = True diff --git a/docs/source/index.md b/docs/source/index.md index 53185ee..74b7969 100644 --- a/docs/source/index.md +++ b/docs/source/index.md @@ -8,10 +8,10 @@ :caption: Contents: Overview -contributing -installation/installation +installation/setup core_modules.md how_to +contributing development/developer_guidelines autoapi/index.rst ``` \ No newline at end of file diff --git a/docs/source/installation/configurations.md b/docs/source/installation/configurations.md index 3e9cd08..e3aa76e 100644 --- a/docs/source/installation/configurations.md +++ b/docs/source/installation/configurations.md @@ -1,13 +1,18 @@ # Configuration -This section of the documentation provides guidelines for configuring the tool. +The recommended way to configure auto-archiver for first-time users is to [run the Auto Archiver](setup.md#running) and have it auto-generate a default configuration for you. Then, if needed, you can edit the configuration file using one of the following methods. -## Configuring using a file -The recommended way to configure auto-archiver for long-term and deployed projects is a configuration file, typically called `orchestration.yaml`. This is a YAML file containing all the settings for your entire workflow. +## 1. Configuration file -The structure of orchestration file is split into 2 parts: `steps` (what [steps](../flow_overview.md) to use) and `configurations` (settings for different modules), here's a simplification: +The configuration file is typically called `orchestration.yaml` and stored in the `secrets` folder on your desktop. The configuration file contains all the settings for your entire Auto Archiver workflow in one easy-to-find place. + +If you want to have Auto Archiver run with the recommended 'basic' setup, + +### Advanced Configuration + +The structure of orchestration file is split into 2 parts: `steps` (what [steps](../flow_overview.md) to use) and `configurations` (settings for individual modules). A default `orchestration.yaml` will be created for you the first time you run auto-archiver (without any arguments). Here's what it looks like: @@ -21,7 +26,7 @@ A default `orchestration.yaml` will be created for you the first time you run au -## Configuring from the Command Line +## 2. Command Line configuration You can run auto-archiver directly from the command line, without the need for a configuration file, command line arguments are parsed using the format `module_name.config_value`. For example, a config value of `api_key` in the `instagram_extractor` module would be passed on the command line with the flag `--instagram_extractor.api_key=API_KEY`. diff --git a/docs/source/installation/installation.md b/docs/source/installation/installation.md index bd03e7e..eff0720 100644 --- a/docs/source/installation/installation.md +++ b/docs/source/installation/installation.md @@ -1,81 +1,44 @@ -# Installing Auto Archiver +# Installation -```{toctree} -:maxdepth: 1 -:hidden: +There are 3 main ways to use the auto-archiver. We recommend the 'docker' method for most uses. This installs all the requirements in one command. -configurations.md -authentication.md -config_cheatsheet.md -``` - -There are 3 main ways to use the auto-archiver: -1. Easiest: [via docker](#installing-with-docker) +1. Easiest (recommended): [via docker](#installing-with-docker) 2. Local Install: [using pip](#installing-locally-with-pip) 3. Developer Install: [see the developer guidelines](../development/developer_guidelines) - -But **you always need a configuration/orchestration file**, which is where you'll configure where/what/how to archive. Make sure you read the [](configurations) page. - - -## Installing with Docker +## 1. Installing with Docker [![dockeri.co](https://dockerico.blankenship.io/image/bellingcat/auto-archiver)](https://hub.docker.com/r/bellingcat/auto-archiver) -Docker works like a virtual machine running inside your computer, it isolates everything and makes installation simple. Since it is an isolated environment when you need to pass it your orchestration file or get downloaded media out of docker you will need to connect folders on your machine with folders inside docker with the `-v` volume flag. +Docker works like a virtual machine running inside your computer, making installation simple. You'll need to first set up Docker, and then download the Auto Archiver 'image': -1. Install [docker](https://docs.docker.com/get-docker/) -2. Pull the auto-archiver docker [image](https://hub.docker.com/r/bellingcat/auto-archiver) with `docker pull bellingcat/auto-archiver` -3. Run the docker image locally in a container: `docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --config secrets/orchestration.yaml` breaking this command down: - 1. `docker run` tells docker to start a new container (an instance of the image) - 2. `--rm` makes sure this container is removed after execution (less garbage locally) - 3. `-v $PWD/secrets:/app/secrets` - your secrets folder - 1. `-v` is a volume flag which means a folder that you have on your computer will be connected to a folder inside the docker container - 2. `$PWD/secrets` points to a `secrets/` folder in your current working directory (where your console points to), we use this folder as a best practice to hold all the secrets/tokens/passwords/... you use - 3. `/app/secrets` points to the path the docker container where this image can be found - 4. `-v $PWD/local_archive:/app/local_archive` - (optional) if you use local_storage - 1. `-v` same as above, this is a volume instruction - 2. `$PWD/local_archive` is a folder `local_archive/` in case you want to archive locally and have the files accessible outside docker - 3. `/app/local_archive` is a folder inside docker that you can reference in your orchestration.yml file +**a) Download and install docker** -### Example invocations +Go to the [Docker website](https://docs.docker.com/get-docker/) and download right version for your operating system. -The invocations below will run the auto-archiver Docker image using a configuration file that you have specified +**b) Pull the Auto Archiver docker image** + +Open your command line terminal, and copy-paste / type: ```bash -# all the configurations come from ./secrets/orchestration.yaml -docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --config secrets/orchestration.yaml -# uses the same configurations but for another google docs sheet -# with a header on row 2 and with some different column names -# notice that columns is a dictionary so you need to pass it as JSON and it will override only the values provided -docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --config secrets/orchestration.yaml --gsheet_feeder.sheet="use it on another sheets doc" --gsheet_feeder.header=2 --gsheet_feeder.columns='{"url": "link"}' -# all the configurations come from orchestration.yaml and specifies that s3 files should be private -docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --config secrets/orchestration.yaml --s3_storage.private=1 +docker pull bellingcat/auto-archiver ``` -## Installing Locally with Pip +This will download the docker image, which may take a while. + +That's it, all done! You're now ready to set up [your configuration file](configurations.md). Or, if you want to use the recommended defaults, then you can [run Auto Archiver immediately](setup.md#running-a-docker-install). + +------------ + +## 2. Installing Locally with Pip 1. Make sure you have python 3.10 or higher installed 2. Install the package with your preferred package manager: `pip/pipenv/conda install auto-archiver` or `poetry add auto-archiver` 3. Test it's installed with `auto-archiver --help` -4. Install other local dependency requirements (for ) -5. Run it with your orchestration file and pass any flags you want in the command line `auto-archiver --config secrets/orchestration.yaml` if your orchestration file is inside a `secrets/`, which we advise +4. Install other local dependency requirements (for example `ffmpeg`, `firefox`) -### Example invocations - -Once all your [local requirements](#installing-local-requirements) are correctly installed, the - -```bash -# all the configurations come from ./secrets/orchestration.yaml -auto-archiver --config secrets/orchestration.yaml -# uses the same configurations but for another google docs sheet -# with a header on row 2 and with some different column names -# notice that columns is a dictionary so you need to pass it as JSON and it will override only the values provided -auto-archiver --config secrets/orchestration.yaml --gsheet_feeder.sheet="use it on another sheets doc" --gsheet_feeder.header=2 --gsheet_feeder.columns='{"url": "link"}' -# all the configurations come from orchestration.yaml and specifies that s3 files should be private -auto-archiver --config secrets/orchestration.yaml --s3_storage.private=1 -``` +After this, you're ready to set up your [your configuration file](configurations.md), or if you want to use the recommended defaults, then you can [run Auto Archiver immediately](setup.md#running-a-local-install). ### Installing Local Requirements diff --git a/docs/source/installation/requirements.md b/docs/source/installation/requirements.md new file mode 100644 index 0000000..b820272 --- /dev/null +++ b/docs/source/installation/requirements.md @@ -0,0 +1,14 @@ +# Requirements + +Using the Auto Archiver is very simple, but ideally you have some familiarity with using the command line to run programs. ([Command line crash course](https://developer.mozilla.org/en-US/docs/Learn_web_development/Getting_started/Environment_setup/Command_line)). + +### System Requirements + +* Auto Archiver works on any Windows, macOS and Linux computer +* If you're using the **local install** method, then you should make sure to have python3.10+ installed + +### Storage Requirements + +By default, Auto Archiver uses your local computer storage for any downloaded media (videos, images etc.). If you're downloading large files, this may take up a lot of your local computer's space (more than 5GB of space). + +If your storage space is limited, then you may want to set up an [alternative storage method](../modules/storage.md) for your media. \ No newline at end of file diff --git a/docs/source/installation/setup.md b/docs/source/installation/setup.md new file mode 100644 index 0000000..8d1a6f5 --- /dev/null +++ b/docs/source/installation/setup.md @@ -0,0 +1,76 @@ +# Getting Started + +```{toctree} +:maxdepth: 1 +:hidden: + +installation.md +configurations.md +authentication.md +requirements.md +config_cheatsheet.md +``` + +## Getting Started + +To get started with Auto Archiver, there are 3 main steps you need to complete. + +1. [Install Auto Archiver](installation.md) +2. [Setup up your configuration](configurations.md) (if you are ok with the default settings, you can skip this step) +3. Run the archiving process + +The way you run the Auto Archiver depends on how you installed it (docker install or local install) + +### Running a Docker Install + +If you installed Auto Archiver using docker, open up your terminal, and copy-paste / type the following command: + +```bash +docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver + ``` + +breaking this command down: + 1. `docker run` tells docker to start a new container (an instance of the image) + 2. `--rm` makes sure this container is removed after execution (less garbage locally) + 3. `-v $PWD/secrets:/app/secrets` - your secrets folder with settings + 1. `-v` is a volume flag which means a folder that you have on your computer will be connected to a folder inside the docker container + 2. `$PWD/secrets` points to a `secrets/` folder in your current working directory (where your console points to), we use this folder as a best practice to hold all the secrets/tokens/passwords/... you use + 3. `/app/secrets` points to the path the docker container where this image can be found + 4. `-v $PWD/local_archive:/app/local_archive` - (optional) if you use local_storage + 1. `-v` same as above, this is a volume instruction + 2. `$PWD/local_archive` is a folder `local_archive/` in case you want to archive locally and have the files accessible outside docker + 3. `/app/local_archive` is a folder inside docker that you can reference in your orchestration.yml file + +### Example invocations + +The invocations below will run the auto-archiver Docker image using a configuration file that you have specified + +```bash +# Have auto-archiver run with the default settings, generating a settings file in ./secrets/orchestration.yaml +docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver + +# uses the same configuration, but with the `gsheet_feeder`, a header on row 2 and with some different column names +# notice that columns is a dictionary so you need to pass it as JSON and it will override only the values provided +docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --feeders=gsheet_feeder --gsheet_feeder.sheet="use it on another sheets doc" --gsheet_feeder.header=2 --gsheet_feeder.columns='{"url": "link"}' +# Runs auto-archiver for the first time, but in 'full' mode, enabling all modules to get a full settings file +docker run --rm -v $PWD/secrets:/app/secrets -v $PWD/local_archive:/app/local_archive bellingcat/auto-archiver --mode full +``` + +------------ + +### Running a Local Install + +### Example invocations + +Once all your [local requirements](#installing-local-requirements) are correctly installed, the + +```bash +# all the configurations come from ./secrets/orchestration.yaml +auto-archiver --config secrets/orchestration.yaml +# uses the same configurations but for another google docs sheet +# with a header on row 2 and with some different column names +# notice that columns is a dictionary so you need to pass it as JSON and it will override only the values provided +auto-archiver --config secrets/orchestration.yaml --gsheet_feeder.sheet="use it on another sheets doc" --gsheet_feeder.header=2 --gsheet_feeder.columns='{"url": "link"}' +# all the configurations come from orchestration.yaml and specifies that s3 files should be private +auto-archiver --config secrets/orchestration.yaml --s3_storage.private=1 +``` diff --git a/src/auto_archiver/core/config.py b/src/auto_archiver/core/config.py index 425f96c..66d2ffb 100644 --- a/src/auto_archiver/core/config.py +++ b/src/auto_archiver/core/config.py @@ -18,7 +18,7 @@ from typing import Any, List, Type, Tuple _yaml: YAML = YAML() -DEFAULT_CONFIG_FILE = "orchestration.yaml" +DEFAULT_CONFIG_FILE = "secrets/orchestration.yaml" EMPTY_CONFIG = _yaml.load(""" # Auto Archiver Configuration