12-16-2020, 02:06 PM | #106 |
Grand Sorcerer
Posts: 11,867
Karma: 7036359
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
@capink: a random thought. I think that you can get a usable form of action branching if you add an option to run an action only if a template returns a value. You would add this option as another column in the chain links table. Doing this would permit creating a chain where actions are run only if they make sense for the current book.
You could simplify things by requiring use of a zero-parameter stored template. That would ensure you only need a name in the table, and as a side effect would make it easier for the user to test the template. You could go a bit further and add a check for what the template returns (another column?). If the template returns that value then the action is run. If it does not, the action isn't. That would permit using the same stored template in multiple places. |
12-16-2020, 03:49 PM | #107 | |
Wizard
Posts: 1,130
Karma: 1954142
Join Date: Aug 2015
Device: Kindle
|
Quote:
The current code even have an instance variable in the chain_loop called step_length (default is 1), that is used by the chain loop to jump to the next action (and is ofcourse reset to default after each jump). I was not sure whether this would be useful or not so I deferred it. This bit about a separate column for a check is not clear to me. |
|
Advert | |
|
12-16-2020, 05:04 PM | #108 |
Grand Sorcerer
Posts: 11,867
Karma: 7036359
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
The idea is to be able to express the following pseudo-code:
Code:
if template() == 'a' then do action 1 fi; if template() == 'b' then do action 2 fi; if template() == 'c' then do action 3 fi; Code:
v = template_action('foo()') if v == X then do action 1 fi if v == X then do action 2 fi if v == Y then do action 3 fi Code:
v = template1_action('foo()') if v == X then do action 1 fi if v == X then do action 2 fi if v == Y then do action 3 fi v = template1_action('bar()') if v == Z then do action 4 fi if v == W then do action 5 fi
|
12-16-2020, 09:29 PM | #109 |
Guru
Posts: 925
Karma: 417282
Join Date: Jun 2015
Device: kobo aura h2o, kobo forma
|
While being able to branch would be very powerful, and calling different chains from the end of the branching chain would work and is something I might use, I'd still rather be able to feed a calculated constant down a chain.
I'm thinking something along the lines of maybe code in a module that sets a variable in a special way, and then be able to use that variable in a template where a constant would go in a form in the chain (like, a tag field for instance). |
12-16-2020, 11:01 PM | #110 | |
Wizard
Posts: 1,130
Karma: 1954142
Join Date: Aug 2015
Device: Kindle
|
Quote:
Actually I was thinking of not (just) using templates on book metadata, since mi objects can accept extra information I can do something like this: Code:
device_uuid = get_device_uuid() cmeta1 = {'datatype': 'text', 'is_multiple': {}, 'name': '#device_uuid'} mi.set_user_metadata('#device_uuid', cmeta1) mi.set('#device_uuid', device_uuid) Code:
{#device_uuid} By the way, the above technique is already used in the plugin. Any action that implements an update_metadata(self, mi) method, is called on every mi object and given the chance to modify it. I have a custom action that inserts a calculated file path, that is not stored in calibre, into the mi object as a #path_to_file field, this field is used by the template feature of the open with action to open the file/folder. Since the file(s) location changes from time to time, I have it mapped into a automatically updated json file (uuid to location mapping). Finally, when fetching mi object in case we have multiple books selected, which one do we use? the current index? would the metadata for that one book provide value for conditional execution of actions? @compurandom: See whether the above point about update_metadata() method satisfies your needs, also the code snippet above it on how to insert arbitrary fields into mi objects |
|
Advert | |
|
12-17-2020, 02:04 AM | #111 |
Hedge Wizard
Posts: 802
Karma: 19999999
Join Date: May 2011
Location: UK/Philippines
Device: Kobo Touch, Nook Simple
|
Problem Installing Plugin
Hi
I spotted the plugin so thought I would have a look at it. I downloaded the file but it will not install. I get the following error. I have Windows 10 Enterprise, Calibre 5.4. Spoiler:
|
12-17-2020, 06:43 AM | #112 | ||||
Grand Sorcerer
Posts: 11,867
Karma: 7036359
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
Another safer way to insert data: implement "globals" in the formatter. I would extend the formatter to accept a dictionary of name:value pairs. A template would access this dict using a new function, perhaps globals(), that acts similarly to the existing arguments() function, copying the globals to local variables. Example: in your plugin you call the formatter with something like Code:
dict = {'a': 2, 'b': 'some string', 'e':'not used'} Code:
globals(a, b='no string', c='default') In addition to the above, you define a new built-in action to set the value of a global. It would be almost identical to "Single Field Edit". That along with the branching idea might solve @compurandom's problem because he could use branching to set variables he can use in templates. In fact, he might not need branching. There might be a way to combine the globals dict with branching. For example, branching could test the global variable '_branch_condition_'. Another way would be to use two columns, one for the global name and one for the value. Quote:
Regarding extra information, perhaps things like the chain name, the action name, the action comment, the value controlling branching, and action-specific data such as the file path you mention below. FWIW: I think automatically-added globals should have a distinctive name to avoid collisions, perhaps one that begins and ends with '_'. Quote:
Quote:
|
||||
12-17-2020, 10:53 AM | #113 |
Wizard
Posts: 1,130
Karma: 1954142
Join Date: Aug 2015
Device: Kindle
|
|
12-17-2020, 11:05 AM | #114 | ||
Wizard
Posts: 1,130
Karma: 1954142
Join Date: Aug 2015
Device: Kindle
|
Quote:
Just to be sure we are on the same page, I will define a global dict for each chain, which have some predefined values and also allows the user to insert his own arbitrary variables Code:
self.global = { _chain_name: ...., _action_name: ...., user_var1: ......, ...... } The user can access this using a template like this: Code:
program: globals('user_var1') Code:
template_output = SafeFormat().safe_format(template, mi, TEMPLATE_ERROR, mi, chain_loop.global) One problem here is that since this is a name/value pair, to test conditions you should have two corresponding columns, one for name and another for value. I much prefer that it is one column whose value is tested against a predefined name in the global dict e.g. _condition. Adding multiple columns is confusing, one column with keywords is more accessible IMO. I am open to feedback from others on this. Edit: striked out since it would not work with one column. Quote:
I think a table widget with two columns is more suitable since number and names of variables are not preset. Somewhat similar to table widgets used for chains and actions, with ability to add/remove rows (except for protected name like _chain_name). I never use devices with calibre (non rootable kindle, only email one book at a time since collections are useless), so my knowledge here is very limited. I only went for device_uuid as way to prevent name clashes if the user has two similar devices. I will defer to others decide whether a device_uuid is needed. Last edited by capink; 12-17-2020 at 11:35 AM. |
||
12-17-2020, 12:01 PM | #115 | ||||
Grand Sorcerer
Posts: 11,867
Karma: 7036359
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
Quote:
Note that Code:
program: globals(user_var1='some_default') Code:
{somefield:'globals(user_var1);'rest of template''} Quote:
Code:
def safe_format(self, fmt, kwargs, error_value, book, column_name=None, template_cache=None, strip_results=True, template_functions=None, global_vars={}): Code:
template_output = SafeFormat().safe_format(template, mi, TEMPLATE_ERROR, mi, global_vars=chain_loop.global) Quote:
If you run from source, attached is a version of calibre.utils.formatter.py that implements globals. For fun, it also implements for loops. Example: Code:
for v in expression: statement [; statement]* rof EDIT: I will submit the formatter changes to Kovid if you think they work for you. EDIT 2: Replaced the attachment. I had left a print statement in the original. EDIT 3: Removed the attachment now that the files are in the official calibre source. Last edited by chaley; 12-19-2020 at 06:06 AM. Reason: Removed the attachment |
||||
12-17-2020, 01:07 PM | #116 | ||
Wizard
Posts: 1,130
Karma: 1954142
Join Date: Aug 2015
Device: Kindle
|
Quote:
Quote:
That is something I wondered about. Will certainly useful for a lot people . |
||
12-17-2020, 02:50 PM | #117 |
Custom User Title
Posts: 9,096
Karma: 62580135
Join Date: Oct 2018
Location: Canada
Device: Kobo Libra H2O, formerly Aura HD
|
|
12-17-2020, 02:58 PM | #118 |
Wizard
Posts: 1,130
Karma: 1954142
Join Date: Aug 2015
Device: Kindle
|
OK, I am testing this now using calibre-debug -e script.py
Code:
def test(): from calibre.library import db as DB from calibre.ebooks.metadata.book.formatter import SafeFormat db = DB().new_api book_id = list(db.all_book_ids())[0] mi = db.get_proxy_metadata(book_id) TEMPLATE_ERROR = 'TEMPLATE_ERROR: ' my_globals = {'path_to_file': '/home/user', 'b': 2} template = "program: globals(path_to_file)" template_output = SafeFormat().safe_format(template, mi, TEMPLATE_ERROR, mi, global_vars=my_globals) print('debug: template_output: ({})'.format(template_output)) test() Code:
debug: template_output: () |
12-17-2020, 03:01 PM | #119 |
Grand Sorcerer
Posts: 11,867
Karma: 7036359
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Your template "program: globals(path_to_file)" didn't do anything with the variable it set. Try "program: globals(path_to_file); path_to_file", which will return the value of the variable (I hope).
EDIT: I suppose I could have "globals" return the last variable it set instead of the empty string but I am not convinced that is a good idea. |
12-17-2020, 03:15 PM | #120 |
Wizard
Posts: 1,130
Karma: 1954142
Join Date: Aug 2015
Device: Kindle
|
Thanks, it is working now. It works for me either way. It is your call.
|
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
[Editor Plugin] Editor Chains | capink | Plugins | 94 | 07-03-2024 07:26 PM |
Action Chains Resources | capink | Plugins | 65 | 07-01-2024 03:12 PM |
[GUI Plugin] Noosfere_util, a companion plugin to noosfere DB | lrpirlet | Plugins | 2 | 08-18-2022 03:15 PM |
[GUI Plugin] Save Virtual Libraries To Column (GUI) | chaley | Plugins | 14 | 04-04-2021 05:25 AM |