def generate_css_by_element_group(self): ''' css is defined by class, which same as group name in compo_df ''' self.compos_css['ul'] = CSS('ul', list_style='None') compos = self.compos_df groups = compos.groupby('group').groups backgrounds = {'Compo': 'grey', 'Text': 'green'} for i in groups: self.compos_css['.' + i] = CSS('.' + i, width=str(int(compos.loc[groups[i], 'width'].mean())) + 'px', height=str(int(compos.loc[groups[i], 'height'].mean())) + 'px', background=backgrounds[compos.loc[groups[i][0], 'class']]) self.assembly_css()
def generate_css_list_item(self): def sort_list_item(): ''' from top to bottom for vertical list groups / from left to right for horizontal groups :return: [(group name, compo ids in the group, top/left, bottom/right)] ''' groups = compos.groupby('list_item').groups s_groups = [] if self.list_alignment == 'v': for i in groups: s_groups.append((compos.loc[groups[i][0], 'group'], groups[i], compos.loc[groups[i], 'row_min'].min(), compos.loc[groups[i], 'row_max'].max())) elif self.list_alignment == 'h': for i in groups: s_groups.append((compos.loc[groups[i][0], 'group'], groups[i], compos.loc[groups[i], 'column_min'].min(), compos.loc[groups[i], 'column_max'].max())) s_groups = sorted(s_groups, key=lambda k: k[2]) return s_groups compos = self.compos_df name = '.li-' + str(self.list_id) if self.list_type == 'multiple': sorted_groups = sort_list_item() gaps = [] for i in range(1, len(sorted_groups)): gaps.append(sorted_groups[i][2] - sorted_groups[i-1][3]) # set css attrs margin = int(max(gaps)) # set the margin as the max gap among lis if self.list_alignment == 'v': height = max([compos.loc[g[1], 'height'].max() for g in sorted_groups]) # set the height of the li as the highest element self.compos_css[name] = CSS(name, margin_top=str(margin) + 'px', height=str(height) + 'px') elif self.list_alignment == 'h': height = max([sum(compos.loc[g[1], 'height']) for g in sorted_groups]) self.compos_css[name] = CSS(name, margin_left=str(margin) + 'px', height=str(height) + 'px', float='left') elif self.list_type == 'single': if self.list_alignment == 'v': margin = 0 for i in range(1, len(compos)): margin = max(margin, compos.iloc[i]['row_min'] - compos.iloc[i - 1]['row_max']) self.compos_css[name] = CSS(name, margin_top=str(margin) + 'px') elif self.list_alignment == 'h': margin = 0 for i in range(1, len(compos)): margin = max(margin, compos.iloc[i]['column_min'] - compos.iloc[i - 1]['column_max']) self.compos_css[name] = CSS(name, margin_left=str(margin) + 'px', float='left') self.assembly_css()
def generate_css_list_item(self): def sort_list_item(): ''' from top to bottom for vertical list groups / from left to right for horizontal groups :return: [(group name, compo ids in the group, top/left, bottom/right)] ''' groups = compos.groupby('list_item').groups s_groups = [] if self.list_alignment == 'v': for i in groups: s_groups.append((compos.loc[groups[i][0], 'group'], groups[i], compos.loc[groups[i], 'row_min'].min(), compos.loc[groups[i], 'row_max'].max())) elif self.list_alignment == 'h': for i in groups: s_groups.append((compos.loc[groups[i][0], 'group'], groups[i], compos.loc[groups[i], 'column_min'].min(), compos.loc[groups[i], 'column_max'].max())) s_groups = sorted(s_groups, key=lambda k: k[2]) return s_groups compos = self.compos_df if self.list_type == 'multiple': sorted_groups = sort_list_item() gaps = [] for i in range(1, len(sorted_groups)): gaps.append(sorted_groups[i][2] - sorted_groups[i-1][3]) margin_top = int(min(gaps)) height = int(max([compos.loc[g[1], 'height'].max() for g in sorted_groups])) name = '.li-' + str(self.list_id) self.compos_css[name] = CSS(name, margin_top=str(margin_top) + 'px', height=str(height) + 'px') self.assembly_css()
def gather_lists_by_pair_and_group(compos): ''' :param compos: type of dataframe :return: lists: [list_obj] non_list_compos: [compoHTML] ''' lists = [] non_list_compos = [] # list type of multiple (multiple compos in each list item) for paired groups groups = compos.groupby('group_pair').groups list_id = 0 for i in groups: if i == -1 or len(groups[i]) == 1: continue lists.append( List(list_id, compos.loc[groups[i]], 'multiple', compos.loc[groups[i][0]]['alignment_in_group'])) list_id += 1 # remove selected compos compos = compos.drop(list(groups[i])) # list type of single (single compo in each list item) for non-paired groups groups = compos.groupby('group').groups for i in groups: if i == -1 or len(groups[i]) == 1: continue lists.append( List(list_id, compos.loc[groups[i]], 'single', compos.loc[groups[i][0]]['alignment_in_group'])) list_id += 1 # remove selected compos compos = compos.drop(list(groups[i])) # not count as list for non-grouped compos for i in range(len(compos)): compo = compos.iloc[i] html_id = tag_map[compo['class']] + '-' + str(compo['id']) # fake compo presented by colored div css = CSS(name='#' + html_id, background=backgrounds[compo['class']], width=str(compo['width']) + 'px', height=str(compo['height']) + 'px') compo_html = CompoHTML(compo_class=compo['class'], compo_id=compo['id'], compo_df=compo, html_tag=tag_map[compo['class']], html_id=html_id, css={css.name: css}) non_list_compos.append(compo_html) return lists, non_list_compos
def generate_css_by_element_group(self): ''' set css style for each group css is defined by class, which same as group name in compo_df ''' # self.compos_css['ul'] = CSS('ul', list_style='None', padding_left='0', clear='left') compos = self.compos_df groups = compos.groupby('group').groups for i in groups: self.compos_css['.' + i] = CSS('.' + i, width=str(int(compos.loc[groups[i], 'width'].mean())) + 'px', height=str(int(compos.loc[groups[i], 'height'].mean())) + 'px', background=backgrounds[compos.loc[groups[i][0], 'class']]) self.assembly_css()
def build_layout_blocks(compos_html, border='none'): global block_id blocks, single_compos = slice_blocks(compos_html, 'v') for compo in single_compos: block_id += 1 css_name = '#block-' + str(block_id) css = CSS(css_name, clear='left', border=border, height=str(compo.height) + 'px') blocks.append(Block(id=block_id, compos=[compo], slice_sub_block_direction='h', html_id='block-' + str(block_id), css={css_name: css})) blocks.sort(key=lambda x: x.top) blocks[0].update_css('#' + blocks[0].html_id, margin_top=str(blocks[0].top) + 'px') for i in range(1, len(blocks)): gap = blocks[i].top - blocks[i - 1].bottom blocks[i].update_css('#' + blocks[i].html_id, margin_top=str(gap) + 'px', margin_left=str(blocks[i].left) + 'px') return blocks
def generate_css_by_element(self): ''' css is defined by class, which same as group name in compo_df ''' compos = self.compos_df groups = compos.groupby('group').groups backgrounds = {'Compo': 'grey', 'Text': 'green'} for i in groups: c = CSS( '.' + i, width=str(int(compos.loc[groups[i], 'width'].mean())) + 'px', height=str(int(compos.loc[groups[i], 'height'].mean())) + 'px', background=backgrounds[compos.loc[groups[i][0], 'class']]) self.compos_css['.' + i] = c for i in self.compos_css: self.list_css += self.compos_css[i].css
def gather_lists_by_pair_and_group(compos): ''' :param compos: type of dataframe :return: lists: [list_obj] non_list_compos: [compoHTML] ''' lists = [] non_list_compos = [] groups = compos.groupby('group_pair').groups list_id = 0 for i in groups: if i == -1 or len(groups[i]) == 1: continue lists.append( List(list_id, compos.loc[groups[i]], 'multiple', compos.loc[groups[i][0]]['alignment_in_group'])) list_id += 1 compos = compos.drop(list(groups[i])) groups = compos.groupby('group').groups for i in groups: if i == -1 or len(groups[i]) == 1: continue lists.append( List(list_id, compos.loc[groups[i]], 'single', compos.loc[groups[i][0]]['alignment_in_group'])) list_id += 1 compos = compos.drop(list(groups[i])) for i in range(len(compos)): compo = compos.iloc[i] html_id = tag_map[compo['class']] + '-' + str(compo['id']) css = CSS(name='#' + html_id, background=backgrounds[compo['class']], width=str(compo['width']) + 'px', height=str(compo['height']) + 'px') compo_html = CompoHTML(compo_id=compo['id'], compo_df=compo, html_tag=tag_map[compo['class']], html_id=html_id, css={css.name: css}) non_list_compos.append(compo_html) return lists, non_list_compos
def update_css(self, css_name, **attrs): if css_name in self.css: self.css[css_name].add_attrs(**attrs) else: self.css[css_name] = CSS(css_name, **attrs) self.assembly_css()
def slice_blocks(compos_html, direction='v', border='none'): ''' Vertically or horizontally scan compos :param direction: slice vertically or horizontally :param compos_html: CompoHTML objects, including elements and lists :param border: block CSS border # solid 2px black :return blocks: list of [Block objs] :return compos_html: list of compos not blocked: list of [CompoHTML objects] ''' blocks = [] block_compos = [] non_blocked_compos = compos_html global block_id is_divided = False divider = -1 prev_divider = 0 # slice from top to bottom if direction == 'v': # reverse the direction of next slicing next_direction = 'h' compos_html.sort(key=lambda x: x.top) for compo in compos_html: # new block # if divider is above than this compo's top, then gather the previous block_compos as a block if divider < compo.top: prev_divider = divider divider = compo.bottom gap = int(compo.top - prev_divider) # gather previous compos in a block # a single compo is not be counted as a block if len(block_compos) == 1: is_divided = True if len(block_compos) > 1: is_divided = True tops = [c.top for c in block_compos] bottoms = [c.bottom for c in block_compos] height = int(max(bottoms) - min(tops)) block_id += 1 css_name = '#block-' + str(block_id) # css = CSS(css_name, margin_bottom=str(gap) + 'px', clear='left', border="none") css = CSS(css_name, clear='left', border=border, height=str(height) + 'px') blocks.append(Block(id=block_id, compos=block_compos, slice_sub_block_direction=next_direction, html_id='block-'+str(block_id), css={css_name: css})) # remove blocked compos non_blocked_compos = list(set(non_blocked_compos) - set(block_compos)) block_compos = [] # extend block elif compo.top < divider < compo.bottom: divider = compo.bottom block_compos.append(compo) # if there are some sub-blocks, gather the left compos as a block if is_divided and len(block_compos) > 1: tops = [c.top for c in block_compos] bottoms = [c.bottom for c in block_compos] height = int(max(bottoms) - min(tops)) block_id += 1 css_name = '#block-' + str(block_id) # css = CSS(css_name, margin_bottom=str(int(block_compos[0].top - prev_divider)) + 'px', clear='left', border="none") css = CSS(css_name, clear='left', border=border, height=str(height) + 'px') blocks.append(Block(id=block_id, compos=block_compos, slice_sub_block_direction=next_direction, html_id='block-' + str(block_id), css={css_name: css})) # remove blocked compos non_blocked_compos = list(set(non_blocked_compos) - set(block_compos)) # slice from left to right elif direction == 'h': # reverse the direction of next slicing next_direction = 'v' compos_html.sort(key=lambda x: x.left) for compo in compos_html: # new block # if divider is lefter than this compo's right, then gather the previous block_compos as a block if divider < compo.left: prev_divider = divider divider = compo.right gap = int(compo.left - prev_divider) # gather previous compos in a block # a single compo is not to be counted as a block if len(block_compos) == 1: is_divided = True if len(block_compos) > 1: is_divided = True tops = [c.top for c in block_compos] bottoms = [c.bottom for c in block_compos] height = int(max(bottoms) - min(tops)) block_id += 1 css_name = '#block-' + str(block_id) # css = CSS(css_name, margin_right=str(gap) + 'px', float='left', border="none") css = CSS(css_name, float='left', border=border, height=str(height) + 'px') blocks.append(Block(id=block_id, compos=block_compos, slice_sub_block_direction=next_direction, html_id='block-' + str(block_id), css={css_name: css})) # remove blocked compos non_blocked_compos = list(set(non_blocked_compos) - set(block_compos)) block_compos = [] # extend block elif compo.left < divider < compo.right: divider = compo.right block_compos.append(compo) # if there are some sub-blocks, gather the left compos as a block if is_divided and len(block_compos) > 1: tops = [c.top for c in block_compos] bottoms = [c.bottom for c in block_compos] height = int(max(bottoms) - min(tops)) block_id += 1 css_name = '#block-' + str(block_id) # css = CSS(css_name, margin_right=str(int(block_compos[0].left - prev_divider)) + 'px', float='left', border="none") css = CSS(css_name, float='left', border=border, height=str(height) + 'px') blocks.append(Block(id=block_id, compos=block_compos, slice_sub_block_direction=next_direction, html_id='block-' + str(block_id), css={css_name: css})) # remove blocked compos non_blocked_compos = list(set(non_blocked_compos) - set(block_compos)) return blocks, non_blocked_compos
def slice_blocks(compos_html, direction='v'): ''' Vertically or horizontally scan compos :param direction: slice vertically or horizontally :param compos_html: CompoHTML objects, including elements and lists :return blocks: list of [Block objs] :return compos_html: list of compos not blocked: list of [CompoHTML objects] ''' blocks = [] block_compos = [] non_blocked_compos = compos_html global block_id divider = -1 prev_divider = 0 if direction == 'v': # reverse the direction of next slicing next_direction = 'h' compos_html.sort(key=lambda x: x.top) for compo in compos_html: # new block # if divider is above than this compo's top, then gather the previous block_compos as a block if divider < compo.top: prev_divider = divider divider = compo.bottom margin = int(compo.top - prev_divider) # gather previous compos in a block # a single compo is not be counted as a block if len(block_compos) > 1: block_id += 1 css_name = '#block-' + str(block_id) css = CSS(css_name, margin_bottom=str(margin) + 'px', clear='left', border="solid 2px black") blocks.append( Block(id=block_id, compos=block_compos, slice_sub_block_direction=next_direction, html_id='block-' + str(block_id), css={css_name: css})) # remove blocked compos non_blocked_compos = list( set(non_blocked_compos) - set(block_compos)) block_compos = [] # extend block elif compo.top < divider < compo.bottom: divider = compo.bottom block_compos.append(compo) # if there are some sub-blocks, gather the left compos as a block if len(blocks) > 0 and len(block_compos) > 1: block_id += 1 css_name = '#block-' + str(block_id) css = CSS( css_name, margin_bottom=str(int(block_compos[0].top - prev_divider)) + 'px', clear='left', border="solid 2px black") blocks.append( Block(id=block_id, compos=block_compos, slice_sub_block_direction=next_direction, html_id='block-' + str(block_id), css={css_name: css})) # remove blocked compos non_blocked_compos = list( set(non_blocked_compos) - set(block_compos)) elif direction == 'h': # reverse the direction of next slicing next_direction = 'v' compos_html.sort(key=lambda x: x.left) for compo in compos_html: # new block # if divider is lefter than this compo's right, then gather the previous block_compos as a block if divider < compo.left: prev_divider = divider divider = compo.right margin = int(compo.left - prev_divider) # gather previous compos in a block # a single compo is not to be counted as a block if len(block_compos) > 1: block_id += 1 css_name = '#block-' + str(block_id) css = CSS(css_name, margin_right=str(margin) + 'px', float='left', border="solid 2px black") blocks.append( Block(id=block_id, compos=block_compos, slice_sub_block_direction=next_direction, html_id='block-' + str(block_id), css={css_name: css})) # remove blocked compos non_blocked_compos = list( set(non_blocked_compos) - set(block_compos)) block_compos = [] # extend block elif compo.left < divider < compo.right: divider = compo.right block_compos.append(compo) # if there are some sub-blocks, gather the left compos as a block if len(blocks) > 0 and len(block_compos) > 1: block_id += 1 css_name = '#block-' + str(block_id) css = CSS( css_name, margin_right=str(int(block_compos[0].left - prev_divider)) + 'px', float='left', border="solid 2px black") blocks.append( Block(id=block_id, compos=block_compos, slice_sub_block_direction=next_direction, html_id='block-' + str(block_id), css={css_name: css})) # remove blocked compos non_blocked_compos = list( set(non_blocked_compos) - set(block_compos)) return blocks, non_blocked_compos
def slice_blocks(compos_html, direction='v', is_slice_sub_block=True): ''' Vertically or horizontally scan compos :param compos_html: CompoHTML objects, including elements and lists :return blocks: list of [block], block: list of [CompoHTML objects] ''' blocks = [] block_compos = [] global block_id dividers = [] divider = -1 prev_divider = 0 if direction == 'v': compos_html.sort(key=lambda x: x.top) for compo in compos_html: # new block if divider < compo.top: prev_divider = divider dividers.append(compo.top) divider = compo.bottom dividers.append(divider) if len(block_compos) > 0: block_id += 1 css_name = '#block-' + str(block_id) css = CSS(css_name, margin_top=str(int(compo.top - prev_divider)) + 'px', clear='left', border="solid 2px black") blocks.append( Block(id=block_id, compos=block_compos, is_slice_sub_block=is_slice_sub_block, html_id='block-' + str(block_id), css={css_name: css})) block_compos = [] # extend block elif compo.top < divider < compo.bottom: divider = compo.bottom dividers[-1] = divider block_compos.append(compo) # collect left compos if len(block_compos) > 0: block_id += 1 css_name = '#block-' + str(block_id) css = CSS(css_name, margin_top=str(int(block_compos[0].top - prev_divider)) + 'px', clear='left', border="solid 2px black") blocks.append( Block(id=block_id, compos=block_compos, is_slice_sub_block=is_slice_sub_block, html_id='block-' + str(block_id), css={css_name: css})) elif direction == 'h': compos_html.sort(key=lambda x: x.left) for compo in compos_html: # new block if divider < compo.left: prev_divider = divider dividers.append(compo.left) divider = compo.right dividers.append(divider) if len(block_compos) > 0: block_id += 1 css_name = '#block-' + str(block_id) css = CSS(css_name, margin_left=str(int(compo.left - prev_divider)) + 'px', float='left', border="solid 2px black") blocks.append( Block(id=block_id, compos=block_compos, is_slice_sub_block=is_slice_sub_block, html_id='block-' + str(block_id), css={css_name: css})) block_compos = [] # extend block elif compo.left < divider < compo.right: divider = compo.right dividers[-1] = divider block_compos.append(compo) # collect left compos if len(block_compos) > 0: block_id += 1 css_name = '#block-' + str(block_id) css = CSS( css_name, margin_left=str(int(block_compos[0].left - prev_divider)) + 'px', float='left', border="solid 2px black") blocks.append( Block(id=block_id, compos=block_compos, is_slice_sub_block=is_slice_sub_block, html_id='block-' + str(block_id), css={css_name: css})) return blocks