10-07-2013, 09:32 PM | #1 |
Member
Posts: 11
Karma: 10
Join Date: Oct 2013
Device: Kindle
|
series_index manipulation
Hello,
I want to use/create a function that returns the series_index as follows: If the series_index is an integer (or better said the numbers after the decimal point are 00) return it as 0>2s, if it is a float then return it as 0>5.2f e.g.: Code:
INPUT OUTPUT 2.00 02 2.50 02.50 3.01 03.01 04.00 04 |
10-08-2013, 04:09 AM | #2 |
Grand Sorcerer
Posts: 11,845
Karma: 7035877
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
You don't give a context for where you want to use this value. I am assuming you want it in some save- or send- template.
One way using general program mode: Code:
program: re(format_number(field('series_index'), '{0:05.2f}'), '\.00', '') Another way using template program mode: Code:
{:'template("program: re(format_number(field('series_index'), '[[0:05.2f]]'), '\.00', '')")'} Yet another way: write your own user defined template function to do what you want. |
Advert | |
|
10-08-2013, 05:44 AM | #3 | |
Member
Posts: 11
Karma: 10
Join Date: Oct 2013
Device: Kindle
|
Thank you very much for your reply.
Quote:
Can I use a program right in the custom column template, or do I have to create a user defined template function? I am new to calibre and python, so please forgive my dumb questions. |
|
10-08-2013, 07:50 AM | #4 | ||
Grand Sorcerer
Posts: 11,845
Karma: 7035877
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
Quote:
If you mean a real python program then no. To use python you must create a user defined function, then call that function from a template just as you would any other template function. |
||
10-08-2013, 06:02 PM | #5 | |||
Member
Posts: 11
Karma: 10
Join Date: Oct 2013
Device: Kindle
|
Quote:
Quote:
Quote:
Cheers. |
|||
Advert | |
|
10-09-2013, 04:06 AM | #6 | ||
Grand Sorcerer
Posts: 11,845
Karma: 7035877
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
Quote:
|
||
10-10-2013, 03:51 PM | #7 | |
Member
Posts: 11
Karma: 10
Join Date: Oct 2013
Device: Kindle
|
Quote:
Code:
def evaluate(self, formatter, kwargs, mi, locals, val): return re.sub('\.00', '', format_number(field(val), '[[0:05.2f]]'), flags=re.I) {series_index:format_index()} Unfortunately I get the following error: TEMPLATE ERROR global name 're' is not defined Any idea what the problem is? |
|
10-10-2013, 06:05 PM | #8 | |
Grand Sorcerer
Posts: 11,845
Karma: 7035877
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
One version of your function in python is Code:
def evaluate(self, formatter, kwargs, mi, locals, val): import math # Check if the argument is empty. If so, return the empty string if val == '': return '' # Not empty. Must be a number. If it isn't, bad things will happen. v = float(val) # If it is an integer, return an 2-digit string zero padded if math.floor(v) == v: return '{:02.0f}'.format(v) # Not an integer. Return a 2.2 digit string. return '{:05.2f}'.format(v) |
|
10-10-2013, 09:42 PM | #9 | |
Member
Posts: 11
Karma: 10
Join Date: Oct 2013
Device: Kindle
|
Quote:
e.g. look at the re function. Very strange. On my installation the re function looks like this: Code:
def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement): return re.sub(pattern, replacement, val, flags=re.I) There are so many things that do not make any sense to me. Let's say I want to return either an integer or a float depending on the occurrences within a series. e.g. let's say there are 3 books in series x and the 2nd book has 1.50 as the index value, so a function should return a float in the format aa.bb for all the books in the series. technically you would write a function which loops through all the books in a series and if one index is not an integer then return the current index as aa.bb (even if the current one is an integer like 2.00) series x: book one - 1.00, book 2 - 1.50, book 3 - 3.00, so the function would return 01.00, 01.50, 03.00. If the second book had an index of 2.00, then the function would return 01, 02, 03. (the funcion is called once for every book in the series) But I have no idea how to even reference the books in a series in python. Maybe this is not even possible. I'm used to code in C and thus would access the data via dynamic sql, ran the loop once and stored the result that one or more values are a float in shared memory. In that case I had a map which told me series x had a float or not. whenever I call the function, it would check if an entry in the map existed and if not it'd use above routine to generate the entry in the map. |
|
10-11-2013, 04:25 AM | #10 | |||||
Grand Sorcerer
Posts: 11,845
Karma: 7035877
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
Quote:
It is an accident that the template function and the python module are both named "re". If you look at the template function, its parameter profile is totally different from the one in the python module. Quote:
In your example, you must return strings '1', '1.50', and '2', or some such. You would use python expressions to accomplish this. Quote:
Quote:
Doing what you want requires writing a plugin. These can see the database and can do completely arbitrary things such as writing values into custom columns. Alternatively you could change calibre itself to do what you want. |
|||||
10-11-2013, 06:57 PM | #11 | |||
Member
Posts: 11
Karma: 10
Join Date: Oct 2013
Device: Kindle
|
Thanks again for the detailed information. Your explanations help a lot.
Quote:
The format should depend on the: - max number of digits of the integer part - max number of digits of the fractional part e.g. one book in the series has the index 13.203 (13 and 203 are the biggest numbers for the respective floating number parts), in which case the function should return the index for every book in this series as xx.yyy (it's just an example. it is rather unlikely that book 13 has 203 sub books ) If another series has only numbers with fractional parts of .00 and the biggest index number is 13.00, then it should be formatted as xx Quote:
Quote:
The manual also does not tell me how to use 'hooks' within calibre. There are several ways how someone could accomplish this formatting function: 1) create a function to be used by the template language This function would calculate the 2 info points (as described above) every time it is called. Rather ineffecient, since it would have to loop through all books in the series to find the data. How do I even write a plugin that provides a function for the template language? 2) create a function plus a meta data table for the series A new table with 3 columns (string series, int max_int, int max_fract). In this case calibre would have to update this table, every time a series index is changed/added/deleted. Are there any hooks in calibre or would I have to write this part as well? In this case the function would just access this table and format the current index accordingly. The latter is much more performant, but also more complex. In any case, the documentation did not answer any of my questions and I am afraid I do not have the time to get familiar with the calibre code base and the plugin interfaces, which I would had to do to be actually able to implement my formatting function. I hoped that calibre would already provide a functionality like this, since it seems logical to me that someone would like to have aligned indexes in a filename without having to hardcode the padding. e.g. who wants a filename like 'seriesname [007.000] bookname', if the biggest index is 7.00 and the series has only integers? I just hope that I will find the time to implement something like this in the future. |
|||
10-12-2013, 02:42 AM | #12 | ||||||
Grand Sorcerer
Posts: 11,845
Karma: 7035877
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
Quote:
Quote:
Quote:
You could do it by creating a text custom column and having the UI plugin store the format string into that for each book. That would permit the template language to see the data. Quote:
Quote:
If I were you, I would just solve the problem by hand. Create a custom text column to hold the format string for a book and manually set its content using the mark-1 eyeball to determine the format you want. Hmmm... the above leads me down another path. You could use the command line interface to dump the series information to a CSV table (calibredb), write a script to process that table and compute the resulting format strings, then use the command line interface to set the value of the custom column for the books in a series (calibredb search to get the ids for the books in a series then calibredb set_custom for each id). |
||||||
10-12-2013, 03:05 AM | #13 | |
Member
Posts: 11
Karma: 10
Join Date: Oct 2013
Device: Kindle
|
Quote:
In this case I can write a script in perl, bash or whatnot and run the script every time I modify a series. Thank you so much. I guess I didn't see the forrest for the trees. |
|
10-12-2013, 03:43 AM | #14 | ||
Grand Sorcerer
Posts: 11,845
Karma: 7035877
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
Quote:
1) iterate through all the book ids in the db (there is a method for that), building a map of series -> desired number format and series -> existing number format. 2) iterate through the maps. If desired != existing: 2.1) use the db-level search method to get all ids for a series. 2.2) use the db-level update field multiple to set the custom column for all the books in the series to the "desired" value. Quote:
|
||
10-13-2013, 04:38 AM | #15 |
Grand Sorcerer
Posts: 11,845
Karma: 7035877
Join Date: Jan 2010
Location: Notts, England
Device: Kobo Libra 2
|
I decided to give it a try because I need more experience with the new calibre db layer.
Here is a standalone python program built on calibre's db layer that does what I think you want. It fills in a custom text column with the proper format for a series, computed by looking at all the books in the series. Code:
def init_cache(library_path): from calibre.db.backend import DB from calibre.db.cache import Cache backend = DB(library_path) cache = Cache(backend) cache.init() return cache import math from collections import defaultdict cache = init_cache(library_path = 'd:/cbh_data/calibre.git/library.test_small') series_info = defaultdict(dict) for id_ in cache.all_book_ids(): series = cache.field_for('series', id_) if series: sidx = cache.field_for('series_index', id_) str_sidx = str(sidx) components = str_sidx.split('.') if components[1] == '0': fract_part = 0 else: fract_part = len(components[1]) val_part = len(components[0]) series_info[series]['val_part'] = max(series_info[series].get('val_part', 0), val_part) series_info[series]['fract_part'] = max(series_info[series].get('fract_part', 0), fract_part) for series,sinfo in series_info.iteritems(): fract_part = sinfo['fract_part'] format_str = '{0:0%d.%df}'%(sinfo['val_part'] + fract_part + (1 if fract_part > 0 else 0), fract_part) sids = cache.search('series:"=' + series + '"') dct = {book_id:format_str for book_id in sids} cache.set_field('#text', dct) |
|
Similar Threads | ||||
Thread | Thread Starter | Forum | Replies | Last Post |
Customize series_index value | luoman | Calibre | 3 | 08-23-2013 04:11 AM |
dynamic content manipulation | maze | ePub | 1 | 02-15-2013 11:01 PM |
[Kindle Touch] LocalStorage manipulation | tommytomtom | Kindle Developer's Corner | 1 | 05-12-2012 11:16 AM |
PDF annotation and manipulation? | mr_ed | enTourage eDGe | 10 | 06-27-2011 05:23 PM |
Section for content manipulation and issues? POLL | Alexander Turcic | iRex | 3 | 09-14-2006 04:43 AM |