123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- """
- A registry for content sources that work in terms of the View Model (view_model.py).
- Generally a source returns a CollectionPage or individual items.
- At present many sources return a List of Maps because the design is being discovered and solidified as it makes sense rather than big design up front.
- May end up similar to Android's ContentProvider, found later. I was
- thinking about using content:// URI scheme.
- https://developer.android.com/reference/android/content/ContentProvider
- Could also be similar to a Coccoon Generator
- https://cocoon.apache.org/1363_1_1.html
- Later processing in Python:
- https://www.innuy.com/blog/build-data-pipeline-python/
- https://www.bonobo-project.org/
- """
- import re
- import inspect
- content_sources = {}
- hooks = {}
- def register_content_source (id_prefix, content_source_fn, id_pattern='(\d+)', source_id=None):
- if not source_id:
- source_id=f'{inspect.getmodule(content_source_fn).__name__}:{content_source_fn.__name__}'
-
- print(f'register_content_source: {id_prefix}: {source_id} with ID pattern {id_pattern}')
- content_sources[ id_prefix ] = [content_source_fn, id_pattern, source_id]
- def find_content_id_args (id_pattern, content_id):
- id_args = re.fullmatch(id_pattern, content_id)
- if not id_args:
- return [], {}
-
- args = []
- kwargs = id_args.groupdict()
- if not kwargs:
- args = id_args.groups()
-
- return args, kwargs
-
- def get_content (content_id, *extra_args, **extra_kwargs):
- id_prefixes = list(content_sources.keys())
- id_prefixes.sort(key=lambda id_prefix: len(id_prefix), reverse=True)
-
- for id_prefix in id_prefixes:
- [content_source_fn, id_pattern, source_id] = content_sources[ id_prefix ]
- if not content_id.startswith(id_prefix):
- continue
-
- source_content_id = content_id[len(id_prefix):]
-
- print(f'get_content {content_id} from source {source_id}, resolves to {source_content_id}')
-
- args, kwargs = find_content_id_args(id_pattern, source_content_id)
-
- if id_prefix.endswith(':') and not args and not kwargs:
- continue
-
- if extra_args:
- args += extra_args
-
- if extra_kwargs:
- kwargs = {**extra_kwargs, **kwargs}
-
- content = content_source_fn(*args, **kwargs)
-
- if content:
- invoke_hooks('got_content', content_id, content)
-
- return content
- def get_all_content (content_ids):
- """
- Get content from all sources, using a grouping call if possible.
-
- Returns a map of source_id to to result; the caller needs
- to have the intelligence to merge and paginate.
-
- Native implementation is to juse make one call to get_content per ID,
- but we need to figure out a way to pass a list of IDs and pagination
- per source; for exampe a list of 100+ Tweet IDs and 100+ YT videos
- from a Swipe file.
- """
-
- return get_all_content2(content_ids)
-
- def get_all_content2 (content_collection_ids, content_args = None, max_results = None):
- """
- Takes a list of collection IDs and content_args is a map of (args, kwargs) keyed by collection ID.
-
- We could just use keys from content_args with empty values but that's a little confusing.
-
- Interleaving the next page of a source into existing results is a problem.
-
- Gracefully degraded could simply get the next page at the end of all pages and then
- view older content.
-
- We also need intelligence about content types, meaning perhaps some lambdas pass in.
- Eg. CollectionPage.
-
- See feeds facade for an example of merging one page.
-
- Seems like keeping feed items in a DB is becoming the way to go, serving things in order.
-
- Client side content merging might work to insert nodes above, eg. HTMx.
-
- Might be jarring to reader, so make optional. Append all new or merge.
-
- Cache feed between requests on disk, merge in memory, send merge/append result.
-
- """
-
- result = {}
-
- for content_id in content_collection_ids:
- if content_args and content_id in content_args:
- extra_args, extra_kwargs = content_args[content_id]
-
- result[ content_id ] = get_content(content_id, *extra_args, **extra_kwargs)
-
- return result
- def register_hook (hook_type, hook_fn, *extra_args, **extra_kwargs):
- if not hook_type in hooks:
- hooks[hook_type] = []
-
- hooks[hook_type].append([hook_fn, extra_args, extra_kwargs])
- def invoke_hooks (hook_type, *args, **kwargs):
- if not hook_type in hooks:
- return
-
- for hook, extra_args, extra_kwargs in hooks[hook_type]:
- hook_args = args
- hook_kwargs = kwargs
- if extra_args:
- hook_args = args + extra_args
- if extra_kwargs:
- hook_kwargs = {**extra_kwargs, **hook_kwargs}
-
- hook(*hook_args, **hook_kwargs)
-
- #try:
- # hook(*args, **kwargs)
- #except TypeError as e:
- # print ('tried to call a hook with wrong args. no problem')
- # continue
-
-
|