diff --git a/README.md b/README.md index e69de29..6fe6e5a 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,69 @@ +Providers are modules that export: + CONFIG = { + "config key": "optional | required", + ... + } + + update(config, state) -> list[items] + +And optionally: + action_NAME(config, state, item) -> None + + on_create(config, state, item) -> None + + on_delete(config, state, item) -> None + +Sources are folders in the data directory with: + config: user-modified configuration values, drawn from the provider + state: provider-modified data + *.item: feed items + +config reserved keys: + "provider": the provider the source uses + "name": given to sources by default, individuates sources from same provider + +GET /config + Edit subfeed configs + +GET /config/[feedname] + Edit config values for feedname + +GET /feed/[feedname] + Feed view for feedname + + + +CLI: + intake create [provider] [--config KEY VALUE] [-c KEY VALUE] ... + load the provider + verify that the provider's config is satisfied + create the source and set the config values + + intake update [source] [--reset] + load the source's config and state + use config.provider to load the provider + verify the source config against the provider config + call provider.update(config, state) to get current items + merge new items into current items + if --reset, new items overwrite everything, including active + + intake deactivate [source] [--tag TAG] [--title TITLE] + load each item in the source and deactivate it + + intake add [--id ID] [--title TITLE] [--link LINK] [--time TIME] [--author AUTHOR] [--body BODY] [--tags TAGS] [--ttl TTL] [--ttd TTD] [--tts TTS] + create the item and add it to the default source + + intake feed [--json] + Dump active item feed to stdout + + intake action [source] [item] [action] + load config, state, item + verify item supports this action + load config.provider + verify config + verify provider supports this action + action_[action](config, state, item) diff --git a/providers/echo.py b/providers/echo.py new file mode 100644 index 0000000..7462b65 --- /dev/null +++ b/providers/echo.py @@ -0,0 +1,21 @@ +from intake import BaseSettings, Setting + + +class Settings(BaseSettings): + message = Setting(default="Hello, world!") + + +def update(config, state): + pass + + +def on_create(config, state, item): + pass + + +def on_delete(config, state, item): + pass + + +def action_tick(config, state, item): + pass diff --git a/server/intake/cli.py b/server/intake/cli.py index 78168ca..a001f98 100644 --- a/server/intake/cli.py +++ b/server/intake/cli.py @@ -15,6 +15,40 @@ from intake.provider import ( verify_settings, verify_update, ) +from intake.source import initialize_source + + +def command_create(args): + """Create a source.""" + parser = argparse.ArgumentParser( + prog="intake create", + description=command_create.__doc__) + parser.add_argument("provider", + help="Provider to create the source from", + metavar="provider") + parser.add_argument("--setting", "-s", + help="Define a provider setting", + nargs=2, + action="append", + metavar="key value", + dest="settings", + default=[]) + parser.add_argument("--data", + help="Data folder path", + metavar="path", + type=os.path.abspath) + parser.add_argument("--path", + nargs="+", + help="Additional paths to add to INTAKEPATH", + metavar="path", + type=os.path.abspath, + default=[]) + args = parser.parse_args() + + ret = 0 + + settings = {key: value for key, value in args.settings} + initialize_source(args.data, args.path, **settings) def command_test(args): diff --git a/server/intake/provider.py b/server/intake/provider.py index 1402686..25f2bfd 100644 --- a/server/intake/provider.py +++ b/server/intake/provider.py @@ -45,6 +45,10 @@ class BaseSettings: raise SettingMissingError(missing) +def issetting(obj): + return isinstance(obj, Setting) + + class chdir: """ A context manager that changes the working directory inside the context. diff --git a/server/intake/source.py b/server/intake/source.py new file mode 100644 index 0000000..668db5c --- /dev/null +++ b/server/intake/source.py @@ -0,0 +1,50 @@ +import inspect +import json +import os + +from intake.provider import ( + load_provider, + issetting, + SettingMissingError, + verify_settings, +) + + +def initialize_source(data_path, search_paths, **setting_kvs): + """ + Create a source's data folder and store its settings. + """ + # Verify the base required settings + if "name" not in setting_kvs: + raise KeyError("Missing name in settings") + if "provider" not in setting_kvs: + raise KeyError("Missing provider in settings") + # Check if the directory exists already + source_name = setting_kvs.get("name") + source_path = os.path.join(data_path, source_name) + if os.path.exists(source_path): + raise FileExistsError(source_path) + # Load the provider + provider_name = setting_kvs.get("provider") + provider = load_provider(search_paths, provider_name) + if not provider: + raise FileNotFoundError(provider_name) + # Verify that the provider has settings + verify_settings(provider) + # Check that all the required settings are populated + settings = inspect.getmembers(provider.Settings, issetting) + for name, setting in settings: + if setting.required and name not in setting_kvs: + raise SettingMissingError() + + # Create the directory + os.mkdir(source_path) + # Create the settings file + setting_json = {} + for name, setting in settings: + setting_json[name] = setting_kvs.get(name, setting.default) + with open(os.path.join(source_path, ".settings"), 'w') as f: + json.dump(setting_json, f, indent=2) + # Create the state file + with open(os.path.join(source_path, ".state"), 'w') as f: + json.dump({}, f, indent=2)