[TIC-Core] UI element HF and QT implemented. GBS support of category
[archive/20170607/tools/tic-core.git] / tic / parser / view_parser.py
1 #!/usr/bin/python
2 # Copyright (c) 2016 Samsung Electronics Co., Ltd
3 #
4 # Licensed under the Flora License, Version 1.1 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #     http://floralicense.org/license/
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16 # Contributors:
17 # - S-Core Co., Ltd
18
19 import logging
20 import requests
21 import re
22 from operator import itemgetter
23 from tic.utils.rpmmisc import meetRequireVersion
24
25
26 def make_view_data(pkg_group):
27     logger = logging.getLogger(__name__)
28     
29     def _select_provide_rpm(capability, require):
30         provide_list = []
31         if require.get('ver') is not None:
32             for provide in capability:
33                 ver_data = provide['data']
34                 if not ver_data.get('ver'):
35                     ver_data = pkg_dict.get(provide['name']).get('version')
36                 if meetRequireVersion(require, ver_data):
37                     provide_list.append(provide)
38         else:
39             provide_list = capability
40             
41         result = []
42         for provide in provide_list:
43             result.append(pkg_dict[provide['name']])
44             
45         return result
46     
47     def set_meta_require(meta_info):
48         meta_nodes = meta_info.get('nodes')
49         children = set()
50         for child_meta in meta_nodes:
51             set_meta_require(child_meta)
52             children.add(child_meta['metaname'])
53         
54         duplicate = set()
55         pkg_info = pkg_dict[meta_info['metaname']]
56         for dep_tag in ['requires', 'recommends', 'suggests']:
57             if pkg_info.get(dep_tag):
58                 for req in pkg_info.get(dep_tag):
59                     targets = []
60                     if req['name'] in provides:
61                         targets = _select_provide_rpm(provides[req['name']], req)
62                     elif req['name'] in files:
63                         for fname in files[req['name']]:
64                             targets.append(pkg_dict.get(fname))
65                     elif req['name'] in pkg_dict:
66                         targets.append(pkg_dict.get(req['name']))
67                     else:
68                         # the dependent package does not exist.
69                         pass
70                         
71                     for pkg in targets:
72                         # The meta-pkg of the other group are excluded.
73                         if pkg['name'] not in duplicate:
74                             refer_count[pkg['id']] += 1
75                             duplicate.add(pkg['name'])
76
77                             if pkg.get('meta'):
78                                 if not pkg['name'] in children:
79                                     meta_nodes.append(make_linked_meta_node(pkg['name'], pkg['summary'], meta_info['category']))
80                             else:
81                                 meta_nodes.append(make_node(pkg, meta_info.get('category')))
82         # Added 'zz' to non meta-package because they are to be listed last
83         meta_nodes = sorted(meta_nodes, key = lambda k: 'aa'+k['metaname'] if 'metaname' in k else 'zz'+k['text'])
84         meta_info['nodes'] = meta_nodes
85
86         if meta_info['metaname'] =='building-blocks-sub2-Preset_iot-examples-3_RPI3_headless_devboard':
87             print(pkg_info)
88     def make_node(pkg_info, category=None):
89         n = dict(text=pkg_info['name'], nodes=[])
90         if category: n['category'] = category
91         return n
92     def make_meta_node(pkgname, viewtext):
93         return dict(text=viewtext, metaname=pkgname, nodes=[])
94     def make_linked_meta_node(pkgname, viewtext, cat):
95         return dict(text='&nbsp;&nbsp;<i>'+viewtext+'</i>', metaname=pkgname, nodes=[], category=cat, tooltip="This is a link of a building block.")
96     def is_blank_ui_meta_node(pkgname):
97         return (pkgname[-8:-2] == '__UI__')
98     def handle_ui_meta_node(tag, node):
99         node['selectable'] = False
100         node['hideCheckbox'] = True
101         if tag == 'BR' or tag == 'br':
102             node['text'] = ''
103         elif tag == 'HR' or tag == 'hr':
104             node['text'] = '<hr style="margin-bottom: 0px; margin-top: 0px; border-style: inset; border-width: 3px" />'
105         elif tag == 'SD' or tag == 'sd':
106             node['text'] = ''
107             node['backColor'] = '#101010'
108         elif tag == 'SM' or tag == 'sm':
109             # Keep the summary text
110             node['text'] = node['text']
111         elif tag == 'HT' or tag == 'ht':
112             # Keep the summary (TODO: verify the usage of HTML tags.)
113             node['text'] = node['text'] # Do we need conversion?
114         elif tag == 'HF' or tag == 'hf':
115             node['text'] = '<p style="height:0px; margin-bottom:-10px"></p>'
116         elif tag == 'QT' or tag == 'qt':
117             node['text'] = '<p style="height:0px; margin-bottom:-16px"></p>'
118         else:
119             node['text'] = ''
120         return node
121     def handle_description(node):
122         if 'metaname' in node:
123             name = node['metaname']
124             pkg_info = pkg_dict[name]
125             desc = pkg_info.get('description')
126             if desc[0:10] == '__KS_URL__':
127                 logger.info("Processing "+name+" for its description: "+pkg_info.get('description'))
128                 # Extract URL from __KS_URL__
129                 ksURL = desc[11:].splitlines()[0].strip()
130                 # Omit the first line with __KS_URL__ from showing.
131                 pkg_info['description'] = desc[len(desc.splitlines(True)[0]):]
132
133                 # Search for filename if directory is given
134                 # e.g., Convert http://a.com/a/ to https://a.com/a/blahblah.ks
135                 # Works for file-indexing html
136                 if ksURL[-3:].lower() != ".ks":
137                     m = None
138                     error = 0
139                     if ksURL[-1:] != "/":
140                         ksURL += "/"
141                     r = requests.get(ksURL)
142                     if r.status_code == requests.codes.ok:
143                         m = re.search('>([^<]*\\.ks)\\s*<', r.text)
144                     else:
145                         error = 1
146                     if error == 0 and not m:
147                         m = re.search('"([^"]*\\.ks)\\s*"', r.text)
148                         if not m:
149                             m = re.search("'([^']*\\.ks)\\s*'", r.text)
150                             if not m:
151                                 error = 1
152                     if error == 1:
153                         node['icon'] = 'glyphicon glyphicon-remove-sign'
154                         node['tooltip'] = 'Cannot find image base from' + ksURL
155                         return node
156                     ksURL += m.group(0)
157                 node['tooltip'] = 'Image base from '+ksURL
158                 node['icon'] = 'glyphicon glyphicon-list-alt'
159                 node['ks'] = ksURL
160             elif desc[0:10] == '__EXPAND__':
161                 # Omit the first line with __EXPAND__ from showing.
162                 pkg_info['description'] = desc[len(desc.splitlines(True)[0]):]
163                 option = desc.splitlines()[0][10:].strip(': \t')
164                 if len(option) == 0:
165                     node['expandcondition'] = 'all'
166                 else:
167                     node['expandcondition'] = option
168         return node
169
170     # view_data for tree view on web-ui
171     view_data = []
172     # temporary dictionary for referencing meta
173     view_ref = {}
174     
175     if not pkg_group:
176         return view_data
177       
178     pkg_dict = pkg_group['pkg_dict']
179     provides = pkg_group['provides']
180     files = pkg_group['files']
181     refer_count = [0] * len(pkg_dict)
182     
183     # sort meta-pkg by pkt_name in ascending order
184     meta_info = pkg_group.get('meta_info')
185     meta_info['root'] = sorted(meta_info['root'], key=itemgetter(0))
186     meta_info['sub1'] = sorted(meta_info['sub1'], key=itemgetter(0))
187     meta_info['sub2'] = sorted(meta_info['sub2'], key=itemgetter(0))
188     
189     # make category rpms
190     category_dict = {}
191     if meta_info.get('category'):
192         for category in meta_info['category']:
193             c_rpm = pkg_dict[category[0]]
194             if hasattr(c_rpm.get('suggests'), '__iter__'):
195                 for suggest in c_rpm.get('suggests'):
196                     category_dict[suggest['name']] = category[1]
197             else:
198                 logger.info(c_rpm.get('suggests'))
199                 logger.info(c_rpm.get('name'))
200
201     
202     # make tree of meta
203     for root in meta_info['root']:
204         root_node = make_meta_node(root[0], root[1])
205         view_ref[root[0]] = root_node
206         if root[0] in category_dict:
207             root_node['category'] = category_dict[root[0]]
208         else:
209             # Backup routine for GBS, which does not seem to publish suggest info correctly.
210             # TODO: This routine has hardcoded category names, which should be improved.
211             m = re.search('(?<=root-)[a-zA-Z]*', root[0])
212             if m:
213                 cat = m.group(0)
214                 if cat == 'domain':
215                     cat = 'domains'
216                 if cat == 'feature':
217                     cat = 'epicfeatures'
218                 root_node['category'] = cat
219                 logger.info("Fallback for "+cat+" for "+root[0])
220         if is_blank_ui_meta_node(root[0]):
221             name = root[0]
222             sub1_node = handle_ui_meta_node(name[-2:], root_node)
223         root_node = handle_description(root_node)
224         view_data.append(root_node)
225         
226     for sub1 in meta_info['sub1']:
227         sub1_node = make_meta_node(sub1[0], sub1[2])
228         view_ref[sub1[0]] = sub1_node
229         if is_blank_ui_meta_node(sub1[0]):
230             name = sub1[0]
231             sub1_node = handle_ui_meta_node(name[-2:], sub1_node)
232         sub1_node = handle_description(sub1_node)
233         # search root
234         if sub1[1] in view_ref:
235             # add to root node
236             if 'category' in view_ref[sub1[1]] and view_ref[sub1[1]]['category']:
237                 sub1_node['category'] = view_ref[sub1[1]]['category']
238             view_ref[sub1[1]]['nodes'].append(sub1_node)
239         else:
240             # If root-meta does not exist, sub1 is added to top-level
241             view_data.append(sub1_node)
242             
243     for sub2 in meta_info['sub2']:
244         sub2_node = make_meta_node(sub2[0], sub2[3])
245         view_ref[sub2[0]] = sub2_node
246         if is_blank_ui_meta_node(sub2[0]):
247             name = sub2[0]
248             sub1_node = handle_ui_meta_node(name[-2:], sub2_node)
249         sub2_node = handle_description(sub2_node)
250         # search sub1
251         if sub2[2] in view_ref:
252             if 'category' in view_ref[sub2[2]] and view_ref[sub2[2]]['category']:
253                 sub2_node['category'] = view_ref[sub2[2]]['category']
254             view_ref[sub2[2]]['nodes'].append(sub2_node)
255         # search root    
256         elif sub2[1] in view_ref: 
257             if view_ref[sub2[1]]['category']:
258                 sub2_node['category'] = view_ref[sub2[1]]['category']
259             # If sub1-meta does not exist, sub2 is added to child of root-meta
260             view_ref[sub2[1]]['nodes'].append(sub2_node)
261         else:
262             # If root-meta/sub1-meta does not exist, sub1 is added to top-level
263             view_data.append(sub2_node)
264     
265     # configure meta dependency tree (requires)
266     for meta_pkg in view_data:
267         set_meta_require(meta_pkg)
268         
269     #The remaining rpms are grouped into a MISC tree
270     misc_info = {}
271     misc_info['text'] = 'Advanced (individual packages)'
272     misc_info['nodes'] = []
273     for k, v in pkg_dict.iteritems():
274         # Add ALL non-block packages
275         if v['name'][0:15] != 'building-blocks':
276             misc_info['nodes'].append(make_node(v))
277     misc_info['nodes'] = sorted(misc_info['nodes'], key = lambda k: k['metaname'] if 'metaname' in k else k['text'])
278
279     view_data.append(misc_info)
280     logger.info('meta: %d, misc: %d', len(view_ref), len(misc_info['nodes']))
281     return view_data