def construct(self, bookmark_notes):
     '''
     bookmark_notes: {loc_sort: {color, location, note}…}
     Optionally include <hr> between booknotes
     '''
     soup = None
     if bookmark_notes:
         soup = BeautifulSoup(
             '''<div class="{0}"></div>'''.format('bookmark_notes'))
         dtc = 0
         for i, location_sort in enumerate(sorted(bookmark_notes.keys())):
             soup.div.insert(
                 dtc,
                 self.BOOKMARK_TEMPLATE.format(
                     location_sort, bookmark_notes[location_sort]['color'],
                     self._get_style('Location'),
                     bookmark_notes[location_sort]['location'],
                     self._get_style('Note'),
                     bookmark_notes[location_sort]['note']))
             dtc += 1
             if (i < len(bookmark_notes) - 1
                     and plugin_prefs.get('appearance_hr_checkbox', False)):
                 soup.div.insert(
                     dtc,
                     plugin_prefs.get('HORIZONTAL_RULE',
                                      '<hr width="80%" />'))
                 dtc += 1
     return soup
Пример #2
0
def sort_merged_annotations(merged_soup):
    '''
    Input: a combined group of user annotations
    Output: sorted by location
    '''
    include_hr = plugin_prefs.get('appearance_hr_checkbox', False)
    locations = merged_soup.findAll(location_sort=True)
    locs = [loc['location_sort'] for loc in locations]
    locs.sort()

    sorted_soup = BeautifulSoup(ANNOTATIONS_HEADER)
    dtc = 0
    for i, loc in enumerate(locs):
        next_div = merged_soup.find(attrs={'location_sort': loc})
        sorted_soup.div.insert(dtc, next_div)
        dtc += 1
        if include_hr and i < len(locs) - 1:
            sorted_soup.div.insert(dtc, plugin_prefs.get('HORIZONTAL_RULE', '<hr width="80%" />'))
            dtc += 1

    return sorted_soup
Пример #3
0
    def _log(self, msg=None):
        '''
        Print msg to console
        '''
        from calibre_plugins.marvin_manager.config import plugin_prefs
        if not plugin_prefs.get('debug_plugin', False):
            return

        if msg:
            debug_print(" %s" % str(msg))
        else:
            debug_print()
Пример #4
0
 def _timestamp_to_datestr(self, timestamp):
     '''
     Convert timestamp to
     01 Jan 2011 12:34:56
     '''
     from calibre_plugins.marvin_manager.appearance import default_timestamp
     d = datetime.fromtimestamp(float(timestamp))
     friendly_timestamp_format = plugin_prefs.get('appearance_timestamp_format', default_timestamp)
     try:
         friendly_timestamp = d.strftime(friendly_timestamp_format)
     except:
         friendly_timestamp = d.strftime(default_timestamp)
     return friendly_timestamp
Пример #5
0
def sort_merged_annotations(merged_soup):
    '''
    Input: a combined group of user annotations
    Output: sorted by location
    '''
    include_hr = plugin_prefs.get('appearance_hr_checkbox', False)
    locations = merged_soup.findAll(location_sort=True)
    locs = [loc['location_sort'] for loc in locations]
    locs.sort()

    sorted_soup = BeautifulSoup(ANNOTATIONS_HEADER)
    dtc = 0
    for i, loc in enumerate(locs):
        next_div = merged_soup.find(attrs={'location_sort': loc})
        sorted_soup.div.insert(dtc, next_div)
        dtc += 1
        if include_hr and i < len(locs) - 1:
            sorted_soup.div.insert(
                dtc, plugin_prefs.get('HORIZONTAL_RULE', '<hr width="80%" />'))
            dtc += 1

    return sorted_soup
Пример #6
0
    def _log(self, msg=None):
        """
        Print msg to console
        """
        from calibre_plugins.marvin_manager.config import plugin_prefs

        if not plugin_prefs.get("debug_plugin", False):
            return

        if msg:
            debug_print(" %s" % str(msg))
        else:
            debug_print()
Пример #7
0
def set_cc_mapping(cc_name, field=None, combobox=None):
    """
    Store element to cc_name in prefs:cc_mappings
    """
    from calibre_plugins.marvin_manager.config import plugin_prefs

    cc_mappings = plugin_prefs.get("cc_mappings", {})
    current_library = current_library_name()
    if current_library in cc_mappings:
        cc_mappings[current_library][cc_name] = {"field": field, "combobox": combobox}
    else:
        cc_mappings[current_library] = {cc_name: {"field": field, "combobox": combobox}}
    plugin_prefs.set("cc_mappings", cc_mappings)
 def construct(self, bookmark_notes):
     '''
     bookmark_notes: {loc_sort: {color, location, note}…}
     Optionally include <hr> between booknotes
     '''
     soup = None
     if bookmark_notes:
         soup = BeautifulSoup('''<div class="{0}"></div>'''.format('bookmark_notes'))
         dtc = 0
         for i, location_sort in enumerate(sorted(bookmark_notes.keys())):
             soup.div.insert(dtc, self.BOOKMARK_TEMPLATE.format(
                 location_sort,
                 bookmark_notes[location_sort]['color'],
                 self._get_style('Location'),
                 bookmark_notes[location_sort]['location'],
                 self._get_style('Note'),
                 bookmark_notes[location_sort]['note']))
             dtc += 1
             if (i < len(bookmark_notes) - 1 and
                 plugin_prefs.get('appearance_hr_checkbox', False)):
                 soup.div.insert(dtc, plugin_prefs.get('HORIZONTAL_RULE', '<hr width="80%" />'))
                 dtc += 1
     return soup
Пример #9
0
 def _timestamp_to_datestr(self, timestamp):
     '''
     Convert timestamp to
     01 Jan 2011 12:34:56
     '''
     from calibre_plugins.marvin_manager.appearance import default_timestamp
     d = datetime.fromtimestamp(float(timestamp))
     friendly_timestamp_format = plugin_prefs.get(
         'appearance_timestamp_format', default_timestamp)
     try:
         friendly_timestamp = d.strftime(friendly_timestamp_format)
     except:
         friendly_timestamp = d.strftime(default_timestamp)
     return friendly_timestamp
    def _get_style(self, target):
        '''
        Get the current CSS for target
        '''
        from calibre_plugins.marvin_manager.appearance import default_elements
        stored_css = plugin_prefs.get('appearance_css', default_elements)

        style = ''
        for element in stored_css:
            if element['name'] == target:
                style = re.sub('\n', '', element['css'])
                break
        else:
            self._log_location("ERROR: Unable to find '{0}' in stored_css".format(target))
        return style
    def _get_style(self, target):
        '''
        Get the current CSS for target
        '''
        from calibre_plugins.marvin_manager.appearance import default_elements
        stored_css = plugin_prefs.get('appearance_css', default_elements)

        style = ''
        for element in stored_css:
            if element['name'] == target:
                style = re.sub('\n', '', element['css'])
                break
        else:
            self._log_location(
                "ERROR: Unable to find '{0}' in stored_css".format(target))
        return style
    def _get_note_style(self):
        '''
        Get the current CSS for book_notes
        '''
        from calibre_plugins.marvin_manager.appearance import default_elements
        stored_css = plugin_prefs.get('appearance_css', default_elements)

        note_style = ''
        for element in stored_css:
            if element['name'] == 'Note':
                note_style = re.sub('\n', '', element['css'])
                break
        else:
            _log_location("ERROR: Unable to find 'Note' in stored_css")

        return note_style
    def _get_note_style(self):
        '''
        Get the current CSS for book_notes
        '''
        from calibre_plugins.marvin_manager.appearance import default_elements
        stored_css = plugin_prefs.get('appearance_css', default_elements)

        note_style = ''
        for element in stored_css:
            if element['name'] == 'Note':
                note_style = re.sub('\n', '', element['css'])
                break
        else:
            _log_location("ERROR: Unable to find 'Note' in stored_css")

        return note_style
Пример #14
0
def _log_location(*args):
    LOCATION_TEMPLATE = "{cls}:{func}({arg1}) {arg2}"

    from calibre_plugins.marvin_manager.config import plugin_prefs

    if not plugin_prefs.get("debug_plugin", False):
        return

    arg1 = arg2 = ""

    if len(args) > 0:
        arg1 = str(args[0])
    if len(args) > 1:
        arg2 = str(args[1])

    debug_print(
        LOCATION_TEMPLATE.format(cls="common_utils", func=sys._getframe(1).f_code.co_name, arg1=arg1, arg2=arg2)
    )
Пример #15
0
    def _log(self, msg=None):
        '''
        Upon first call, switch to appropriate method
        '''
        from calibre_plugins.marvin_manager.config import plugin_prefs
        if not plugin_prefs.get('debug_plugin', False):
            # Neuter the method
            self._log = self.__null
            self._log_location = self.__null
        else:
            # Log the message, then switch to real method
            if msg:
                debug_print(" {0}".format(str(msg)))
            else:
                debug_print()

            self._log = self.__log
            self._log_location = self.__log_location
Пример #16
0
def get_cc_mapping(cc_name, element, default=None):
    '''
    Return the element mapped to cc_name in prefs
    '''
    from calibre_plugins.marvin_manager.config import plugin_prefs

    if element not in ['field', 'combobox']:
        raise ValueError(
            "invalid element '{0}' requested for custom column '{1}'".format(
                element, cc_name))

    ans = default
    cc_mappings = plugin_prefs.get('cc_mappings', {})
    current_library = current_library_name()
    if (current_library in cc_mappings
            and cc_name in cc_mappings[current_library]
            and element in cc_mappings[current_library][cc_name]):
        ans = cc_mappings[current_library][cc_name][element]
    return ans
Пример #17
0
def _log_location(*args):
    LOCATION_TEMPLATE = "{cls}:{func}({arg1}) {arg2}"

    from calibre_plugins.marvin_manager.config import plugin_prefs
    if not plugin_prefs.get('debug_plugin', False):
        return

    arg1 = arg2 = ''

    if len(args) > 0:
        arg1 = str(args[0])
    if len(args) > 1:
        arg2 = str(args[1])

    debug_print(
        LOCATION_TEMPLATE.format(cls='common_utils',
                                 func=sys._getframe(1).f_code.co_name,
                                 arg1=arg1,
                                 arg2=arg2))
Пример #18
0
def merge_annotations_with_comments(parent, cid, comments_soup, new_soup):
    '''
    comments_soup: comments potentially with user_annotations
    '''

    # Prepare a new COMMENTS_DIVIDER
    comments_divider = '<div class="comments_divider"><p style="text-align:center;margin:1em 0 1em 0">{0}</p></div>'.format(
        plugin_prefs.get(
            'COMMENTS_DIVIDER',
            '&middot;  &middot;  &bull;  &middot;  &#x2726;  &middot;  &bull;  &middot; &middot;'
        ))

    # Remove the old comments_divider
    cds = comments_soup.find('div', 'comments_divider')
    if cds:
        cds.extract()

    # Existing annotations?
    uas = comments_soup.find('div', 'user_annotations')
    if uas:
        # Save the existing annotations to old_soup
        old_soup = BeautifulSoup(unicode(uas))

        # Remove any hrs from old_soup
        hrs = old_soup.findAll('hr')
        if hrs:
            for hr in hrs:
                hr.extract()

        # Remove the existing annotations from comments_soup
        uas.extract()

        # Merge old_soup with new_soup
        merged_soup = unicode(comments_soup) + \
                      unicode(comments_divider) + \
                      unicode(merge_annotations(parent, cid, old_soup, new_soup))
    else:
        # No existing, just merge comments_soup with already sorted new_soup
        merged_soup = unicode(comments_soup) + \
                      unicode(comments_divider) + \
                      unicode(new_soup)

    return merged_soup
Пример #19
0
def get_cc_mapping(cc_name, element, default=None):
    """
    Return the element mapped to cc_name in prefs
    """
    from calibre_plugins.marvin_manager.config import plugin_prefs

    if element not in ["field", "combobox"]:
        raise ValueError("invalid element '{0}' requested for custom column '{1}'".format(element, cc_name))

    ans = default
    cc_mappings = plugin_prefs.get("cc_mappings", {})
    current_library = current_library_name()
    if (
        current_library in cc_mappings
        and cc_name in cc_mappings[current_library]
        and element in cc_mappings[current_library][cc_name]
    ):
        ans = cc_mappings[current_library][cc_name][element]
    return ans
Пример #20
0
    def _log_location(self, *args):
        '''
        Print location, args to console
        '''
        from calibre_plugins.marvin_manager.config import plugin_prefs
        if not plugin_prefs.get('debug_plugin', False):
            return

        arg1 = arg2 = ''

        if len(args) > 0:
            arg1 = str(args[0])
        if len(args) > 1:
            arg2 = str(args[1])

        debug_print(
            self.LOCATION_TEMPLATE.format(cls=self.__class__.__name__,
                                          func=sys._getframe(1).f_code.co_name,
                                          arg1=arg1,
                                          arg2=arg2))
Пример #21
0
def set_cc_mapping(cc_name, field=None, combobox=None):
    '''
    Store element to cc_name in prefs:cc_mappings
    '''
    from calibre_plugins.marvin_manager.config import plugin_prefs

    cc_mappings = plugin_prefs.get('cc_mappings', {})
    current_library = current_library_name()
    if current_library in cc_mappings:
        cc_mappings[current_library][cc_name] = {
            'field': field,
            'combobox': combobox
        }
    else:
        cc_mappings[current_library] = {
            cc_name: {
                'field': field,
                'combobox': combobox
            }
        }
    plugin_prefs.set('cc_mappings', cc_mappings)
Пример #22
0
    def _log_location(self, *args):
        """
        Print location, args to console
        """
        from calibre_plugins.marvin_manager.config import plugin_prefs

        if not plugin_prefs.get("debug_plugin", False):
            return

        arg1 = arg2 = ""

        if len(args) > 0:
            arg1 = str(args[0])
        if len(args) > 1:
            arg2 = str(args[1])

        debug_print(
            self.LOCATION_TEMPLATE.format(
                cls=self.__class__.__name__, func=sys._getframe(1).f_code.co_name, arg1=arg1, arg2=arg2
            )
        )
Пример #23
0
def merge_annotations_with_comments(parent, cid, comments_soup, new_soup):
    '''
    comments_soup: comments potentially with user_annotations
    '''

    # Prepare a new COMMENTS_DIVIDER
    comments_divider = '<div class="comments_divider"><p style="text-align:center;margin:1em 0 1em 0">{0}</p></div>'.format(
        plugin_prefs.get('COMMENTS_DIVIDER', '&middot;  &middot;  &bull;  &middot;  &#x2726;  &middot;  &bull;  &middot; &middot;'))

    # Remove the old comments_divider
    cds = comments_soup.find('div', 'comments_divider')
    if cds:
        cds.extract()

    # Existing annotations?
    uas = comments_soup.find('div', 'user_annotations')
    if uas:
        # Save the existing annotations to old_soup
        old_soup = BeautifulSoup(unicode(uas))

        # Remove any hrs from old_soup
        hrs = old_soup.findAll('hr')
        if hrs:
            for hr in hrs:
                hr.extract()

        # Remove the existing annotations from comments_soup
        uas.extract()

        # Merge old_soup with new_soup
        merged_soup = unicode(comments_soup) + \
                      unicode(comments_divider) + \
                      unicode(merge_annotations(parent, cid, old_soup, new_soup))
    else:
        # No existing, just merge comments_soup with already sorted new_soup
        merged_soup = unicode(comments_soup) + \
                      unicode(comments_divider) + \
                      unicode(new_soup)

    return merged_soup
Пример #24
0
    def to_HTML(self, header=''):
        '''
        Generate HTML with user-specified CSS, element order
        '''
        # Retrieve CSS prefs
        from calibre_plugins.marvin_manager.appearance import default_elements
        stored_css = plugin_prefs.get('appearance_css', default_elements)

        elements = []
        for element in stored_css:
            elements.append(element['name'])
            if element['name'] == 'Note':
                note_style = re.sub('\n', '', element['css'])
            elif element['name'] == 'Text':
                text_style = re.sub('\n', '', element['css'])
            elif element['name'] == 'Timestamp':
                ts_style = re.sub('\n', '', element['css'])

        # Additional CSS for timestamp color and bg to be formatted
        datetime_style = ("background-color:{0};color:{1};" + ts_style)

        # Order the elements according to stored preferences
        comments_body = ''
        for element in elements:
            if element == 'Text':
                comments_body += '{text}'
            elif element == 'Note':
                comments_body += '{note}'
            elif element == 'Timestamp':
                ts_css = '''<table cellpadding="0" width="100%" style="{ts_style}" color="{color}">
                                <tr>
                                    <td class="location" style="text-align:left">{location}</td>
                                    <td class="timestamp" uts="{unix_timestamp}" style="text-align:right">{friendly_timestamp}</td>
                                </tr>
                            </table>'''
                comments_body += re.sub(r'>\s+<', r'><', ts_css)

        if self.annotations:
            soup = BeautifulSoup(ANNOTATIONS_HEADER)
            dtc = 0

            # Add the annotations
            for i, agroup in enumerate(
                    sorted(self.annotations, key=self._annotation_sorter)):
                location = agroup.location
                if location is None:
                    location = ''

                friendly_timestamp = self._timestamp_to_datestr(
                    agroup.timestamp)

                text = ''
                if agroup.text:
                    for agt in agroup.text:
                        text += '<p class="highlight" style="{0}">{1}</p>'.format(
                            text_style, agt)

                note = ''
                if agroup.note:
                    for agn in agroup.note:
                        note += '<p class="note" style="{0}">{1}</p>'.format(
                            note_style, agn)

                try:
                    dt_bgcolor = COLOR_MAP[agroup.highlightcolor]['bg']
                    dt_fgcolor = COLOR_MAP[agroup.highlightcolor]['fg']
                except:
                    if agroup.highlightcolor is None:
                        msg = "No highlight color specified, using Default"
                    else:
                        msg = "Unknown color '%s' specified" % agroup.highlightcolor
                    self._log_location(msg)
                    dt_bgcolor = COLOR_MAP['Default']['bg']
                    dt_fgcolor = COLOR_MAP['Default']['fg']

                if agroup.hash is not None:
                    # Use existing hash when re-rendering
                    hash = agroup.hash
                else:
                    m = hashlib.md5()
                    m.update(text)
                    m.update(note)
                    hash = m.hexdigest()

                divTag = Tag(BeautifulSoup(), 'div')
                content_args = {
                    'color': agroup.highlightcolor,
                    'friendly_timestamp': friendly_timestamp,
                    'location': location,
                    'note': note,
                    'text': text,
                    'ts_style': datetime_style.format(dt_bgcolor, dt_fgcolor),
                    'unix_timestamp': agroup.timestamp,
                }
                divTag.insert(0, comments_body.format(**content_args))
                divTag['class'] = "annotation"
                divTag['genre'] = ''
                if agroup.genre:
                    divTag['genre'] = escape(agroup.genre)
                divTag['hash'] = hash
                divTag['location_sort'] = agroup.location_sort
                divTag['reader'] = agroup.reader_app
                divTag['style'] = ANNOTATION_DIV_STYLE
                soup.div.insert(dtc, divTag)
                dtc += 1
                if i < len(self.annotations) - 1 and \
                    plugin_prefs.get('appearance_hr_checkbox', False):
                    soup.div.insert(
                        dtc,
                        plugin_prefs.get('HORIZONTAL_RULE',
                                         '<hr width="80%" />'))
                    dtc += 1

        else:
            soup = BeautifulSoup(ANNOTATIONS_HEADER)
        return unicode(soup.renderContents())
Пример #25
0
    def to_HTML(self, header=''):
        '''
        Generate HTML with user-specified CSS, element order
        '''
        # Retrieve CSS prefs
        from calibre_plugins.marvin_manager.appearance import default_elements
        stored_css = plugin_prefs.get('appearance_css', default_elements)

        elements = []
        for element in stored_css:
            elements.append(element['name'])
            if element['name'] == 'Note':
                note_style = re.sub('\n', '', element['css'])
            elif element['name'] == 'Text':
                text_style = re.sub('\n', '', element['css'])
            elif element['name'] == 'Timestamp':
                ts_style = re.sub('\n', '', element['css'])

        # Additional CSS for timestamp color and bg to be formatted
        datetime_style = ("background-color:{0};color:{1};" + ts_style)

        # Order the elements according to stored preferences
        comments_body = ''
        for element in elements:
            if element == 'Text':
                comments_body += '{text}'
            elif element == 'Note':
                comments_body += '{note}'
            elif element == 'Timestamp':
                ts_css = '''<table cellpadding="0" width="100%" style="{ts_style}" color="{color}">
                                <tr>
                                    <td class="location" style="text-align:left">{location}</td>
                                    <td class="timestamp" uts="{unix_timestamp}" style="text-align:right">{friendly_timestamp}</td>
                                </tr>
                            </table>'''
                comments_body += re.sub(r'>\s+<', r'><', ts_css)

        if self.annotations:
            soup = BeautifulSoup(ANNOTATIONS_HEADER)
            dtc = 0

            # Add the annotations
            for i, agroup in enumerate(sorted(self.annotations, key=self._annotation_sorter)):
                location = agroup.location
                if location is None:
                    location = ''

                friendly_timestamp = self._timestamp_to_datestr(agroup.timestamp)

                text = ''
                if agroup.text:
                    for agt in agroup.text:
                        text += '<p class="highlight" style="{0}">{1}</p>'.format(text_style, agt)

                note = ''
                if agroup.note:
                    for agn in agroup.note:
                        note += '<p class="note" style="{0}">{1}</p>'.format(note_style, agn)

                try:
                    dt_bgcolor = COLOR_MAP[agroup.highlightcolor]['bg']
                    dt_fgcolor = COLOR_MAP[agroup.highlightcolor]['fg']
                except:
                    if agroup.highlightcolor is None:
                        msg = "No highlight color specified, using Default"
                    else:
                        msg = "Unknown color '%s' specified" % agroup.highlightcolor
                    self._log_location(msg)
                    dt_bgcolor = COLOR_MAP['Default']['bg']
                    dt_fgcolor = COLOR_MAP['Default']['fg']

                if agroup.hash is not None:
                    # Use existing hash when re-rendering
                    hash = agroup.hash
                else:
                    m = hashlib.md5()
                    m.update(text)
                    m.update(note)
                    hash = m.hexdigest()

                divTag = Tag(BeautifulSoup(), 'div')
                content_args = {
                            'color': agroup.highlightcolor,
                            'friendly_timestamp': friendly_timestamp,
                            'location': location,
                            'note': note,
                            'text': text,
                            'ts_style': datetime_style.format(dt_bgcolor, dt_fgcolor),
                            'unix_timestamp': agroup.timestamp,
                            }
                divTag.insert(0, comments_body.format(**content_args))
                divTag['class'] = "annotation"
                divTag['genre'] = ''
                if agroup.genre:
                    divTag['genre'] = escape(agroup.genre)
                divTag['hash'] = hash
                divTag['location_sort'] = agroup.location_sort
                divTag['reader'] = agroup.reader_app
                divTag['style'] = ANNOTATION_DIV_STYLE
                soup.div.insert(dtc, divTag)
                dtc += 1
                if i < len(self.annotations) - 1 and \
                    plugin_prefs.get('appearance_hr_checkbox', False):
                    soup.div.insert(dtc, plugin_prefs.get('HORIZONTAL_RULE', '<hr width="80%" />'))
                    dtc += 1

        else:
            soup = BeautifulSoup(ANNOTATIONS_HEADER)
        return unicode(soup.renderContents())