diff --git a/product/ERP5Form/PlanningBox.py b/product/ERP5Form/PlanningBox.py new file mode 100755 index 0000000000000000000000000000000000000000..6820ef504301e752560d5ce51650e06daa978b5b --- /dev/null +++ b/product/ERP5Form/PlanningBox.py @@ -0,0 +1,1210 @@ +############################################################################## +# +# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. +# Jonathan Loriette <john@nexedi.com> +# +# WARNING: This program as such is intended to be used by professional +# programmers who take the whole responsability of assessing all potential +# consequences resulting from its eventual inadequacies and bugs +# End users who are looking for a ready-to-use solution with commercial +# garantees and support are strongly adviced to contract a Free Software +# Service Company +# +# This program is Free Software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +############################################################################## + +import string, types, sys +from Form import BasicForm +from Products.Formulator.Field import ZMIField +from Products.Formulator.DummyField import fields +from Products.Formulator.MethodField import BoundMethod +from DateTime import DateTime +from Products.Formulator import Widget, Validator +from Products.Formulator.Errors import FormValidationError, ValidationError +from Selection import Selection, DomainSelection +from SelectionTool import makeTreeList +import OFS +from AccessControl import ClassSecurityInfo +from zLOG import LOG +from copy import copy +from Acquisition import aq_base, aq_inner, aq_parent, aq_self + +from Products.Formulator.Form import BasicForm + +from Products.ERP5Type.Utils import getPath +from Products.ERP5Type.Document import newTempBase +from Products.CMFCore.utils import getToolByName + + +class PlanningBoxValidator(Validator.StringBaseValidator): + def validate(self, field, key, REQUEST): + try: + for lang in list_value.keys(): + list_value[lang] = int(list_value[lang]) + except ValueError: + self.raise_error('not an integer', field) + + return list_value + +PlanningBoxValidatorInstance=PlanningBoxValidator() + +def createLineObject(meta_types,selection,selection_name,field,REQUEST,list_method, + here,report_root_list,y_axis_width,width_line,space_line, + height_global_div,height_header,height_axis_x,form,current_top): + report_sections = [] + section_index = 0 + if len(report_sections) > section_index: + current_section = report_sections[section_index] + elif len(report_sections): + current_section = report_sections[0] + else: + current_section = None + filtered_meta_types = map(lambda x: x[0], meta_types) + params = selection.getParams() + sort = field.get_value('sort') + kw=params + report_depth = REQUEST.get('report_depth', None) + is_report_opened = REQUEST.get('is_report_opened', selection.isReportOpened()) + + portal_categories = getattr(form, 'portal_categories', None) + if 'select_expression' in kw: + del kw['select_expression'] + if hasattr(list_method, 'method_name'): + list_method = here.objectValues + kw = copy(params) + kw['spec'] = filtered_meta_types + elif list_method in (None, ''): # Use current selection + # Use previously used list method + list_method = None + select_expression = '' + default_selection_report_path = report_root_list[0][0].split('/')[0] + if default_selection_report_path in portal_categories.objectIds() or \ + (portal_domains is not None and default_selection_report_path in portal_domains.objectIds()): + pass + else: + default_selection_report_path = report_root_list[0][0] + selection_report_path = selection.getReportPath(default = (default_selection_report_path,)) + if report_depth is not None: + selection_report_current = () + else: + selection_report_current = selection.getReportList() + report_tree_list = makeTreeList(here, form, None,selection_report_path,None,0, + selection_report_current, form.id, selection_name, + report_depth,is_report_opened, sort_on=selection.sort_on) + # Update report list if report_depth was specified + if report_depth is not None: + report_list = map(lambda s:s[0].getRelativeUrl(), report_tree_list) + selection.edit(report_list=report_list) + report_sections = [] + + list_object = [] + nbr_line=0 + object_list=[] + indic_line=0 + index_line = 0 + for s in report_tree_list: + selection.edit(report = s.getSelectDomainDict()) + + if s.getIsPureSummary(): + original_select_expression = kw.get('select_expression') + kw['select_expression'] = select_expression + selection.edit( params = kw ) + if original_select_expression is None: + del kw['select_expression'] + else: + kw['select_expression'] = original_select_expression + + if s.getIsPureSummary(): + stat_result = {} + index = 1 + report_sections += [s] + nbr_line+=1 + else: + # Prepare query + selection.edit( params = kw ) + if list_method not in (None, ''): + object_list = selection(method = list_method, context=here, REQUEST=REQUEST, + s=s, object_list= object_list) + else: + object_list = here.portal_selections.getSelectionValueList(selection_name, + context=here, REQUEST=REQUEST) + + selection.edit(report=None) + index = 0 + # we start to build our line object structure right here. + for l in report_sections: + stat_result = {} + stat_context = l.getObject().asContext(**stat_result) + stat_context.domain_url = l.getObject().getRelativeUrl() + stat_context.absolute_url = lambda x: l.getObject().absolute_url() + url=getattr(stat_context,'domain_url','') + + if l.getDepth() == 0: + paternity = 0 + if len(l.getObject().objectValues())!=0: + paternity = 1 + + height=(height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line) + line = Line(title=l.getObject().getTitle(), + name='fra' + str(indic_line), + begin=y_axis_width, + width=width_line, + height=height, + top=current_top,color='#ffffff', + paternity=paternity,url=url) + list_object.append(line) + + + if paternity == 0: + current_top=current_top+((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)) + (space_line) + + else: + if (index+1)<=(len(report_sections)-1): + if report_sections[index+1].getDepth()==0: + #current_top=current_top+((height_global_div-height_header-height_axis_x)/float(nbr_line)) + (space_line) + current_top=current_top+((height_global_div-height_header-height_axis_x-(((nbr_line-1))*space_line))/float(nbr_line))+ (space_line) + + else: + + current_top=current_top+((height_global_div-height_header-height_axis_x-(((nbr_line-1))*space_line))/float(nbr_line)) + + else: + current_index = 0 + while l.getDepth() == report_sections[index-current_index].getDepth(): + current_index += 1 + if report_sections[index-current_index].getDepth() == 0: + current_top=list_object[len(list_object)-1].createLineChild(report_sections,field,current_top,y_axis_width,width_line,space_line,height_global_div,height_header,height_axis_x,nbr_line,index,url) + + else : # in this case wee add a soon to a soon + depth=0 + current_soon=list_object[len(list_object)-1] + while depth != (l.getDepth()-1): + current_soon=list_object[len(list_object)-1].soon[len(list_object[len(list_object)-1].soon)-1] + depth+=1 + current_top=current_soon.createLineChild(report_sections,field,current_top,y_axis_width,width_line,space_line,height_global_div,height_header,height_axis_x,nbr_line,index,url) + index += 1 + indic_line+=1 + return (list_object,nbr_line,report_sections) + + +def createGraphicCall(current_line,graphic_call): + """ create html code of children used by graphic library to know which block can be moved""" + + for i in current_line.soon: + for j in i.content: + if j.types=='activity': + graphic_call+='\"'+j.name+'\",' + elif j.types=='info': + graphic_call+='\"'+j.name+'\"+NO_DRAG,' + if i.soon!=[]: # case of a soon which has soons... + graphic_call+=createGraphicCall(i,graphic_call) + return graphic_call + + + + +#class planningboxwidget ********************************* +class PlanningBoxWidget(Widget.Widget): + property_names = Widget.Widget.property_names +\ + ['height_header', 'height_global_div','height_axis_x', 'width_line','space_line','list_method','report_tree','report_root_list','selection_name','portal_types','meta_types','sort','title_line','y_unity','y_axis_width','y_range','x_range','x_axis_script_id','x_start_bloc','x_stop_bloc','y_axis_method','info_block_method','security_index'] + + default = fields.TextAreaField('default', + title='Default', + description=( + "Default value of the text in the widget."), + default="", + width=20, height=3, + required=0) + + + height_header = fields.IntegerField('height_header', + title='height of the header (px):', + description=( + "value of the height of the header, required"), + default=50, + required=1) + + height_global_div = fields.IntegerField('height_global_div', + title='height of the graphic (px):', + description=( + "value of the height of the graphic, required"), + default=700, + required=1) + height_axis_x = fields.IntegerField('height_axis_x', + title='height of X-axis (px):', + description=( + "value of the height of X-axis"), + default=50, + required=1) + + width_line = fields.IntegerField('width_line', + title='width of the graphic (px):', + description=( + "value of width_line, required"), + default=1000, + required=1) + space_line = fields.IntegerField('space_line', + title='space between each line of the graphic (px):', + description=( + "space between each line of the graphic,not required"), + default=10, + required=0) + + + report_tree = fields.CheckBoxField('report_tree', + title='Report Tree', + description=('Report Tree'), + default='', + required=0) + + + + report_root_list = fields.ListTextAreaField('report_root_list', + title="Report Root", + description=( + "A list of domains which define the possible root."), + default=[], + required=0) + + selection_name = fields.StringField('selection_name', + title='Selection Name', + description=('The name of the selection to store' + 'params of selection'), + default='', + required=0) + + portal_types = fields.ListTextAreaField('portal_types', + title="Portal Types", + description=( + "Portal Types of objects to list. Required."), + default=[], + required=0) + + meta_types = fields.ListTextAreaField('meta_types', + title="Meta Types", + description=( + "Meta Types of objects to list. Required."), + default=[], + required=0) + + sort = fields.ListTextAreaField('sort', + title='Default Sort', + description=('The default sort keys and order'), + default=[], + required=0) + + + list_method = fields.MethodField('list_method', + title='List Method', + description=('The method to use to list' + 'objects'), + default='', + required=0) + + + title_line = fields.StringField('title_line', + title='specific method which fetches the title of each line: ', + description=('specific method for inserting title in line'), + default='', + required=0) + + + y_unity = fields.StringField('y_unity', + title='Unity in Y-axis:', + description=('The unity in Y-axis,not required'), + default='', + required=0) + + y_axis_width = fields.IntegerField('y_axis_width', + title='width of Y-axis (px):', + description=( + "width of Y-axis, required"), + default=200, + required=1) + + y_range = fields.IntegerField('y_range', + title='number of range of Y-axis :', + description=( + "Number of Range of Y-axis, not required"), + default=5, + required=0) + + x_range = fields.StringField('x_range', + title='range of X-Axis:', + description=('Nature of the subdivisions of X-Axes, not Required'), + default='Day', + required=0) + + x_axis_script_id = fields.StringField('x_axis_script_id', + title='script for building the X-Axis:', + description=('script for building the X-Axis'), + default='', + required=0) + + x_start_bloc = fields.StringField('x_start_bloc', + title='specific method which fetches the data for the beginning of a block:', + description=('Method for building X-Axis such as getstartDate' + 'objects'), + default='getStartDate', + required=0) + + x_stop_bloc = fields.StringField('x_stop_bloc', + title='specific method which fetches the data for the end of each block', + description=('Method for building X-Axis such getStopDate' + 'objects'), + default='', + required=0) + + + y_axis_method = fields.StringField('y_axis_method', + title='specific method of data type for creating Y-Axis', + description=('Method for building Y-Axis' + 'objects'), + default='', + required=0) + + info_block_method = fields.StringField('info_block_method', + title='specific method for inserting infos in Block', + description=('Method for inserting info' + 'objects'), + default='', + required=0) + + security_index = fields.IntegerField('security_index', + title='variable depending of the type of web browser :', + description=("This variable is used because the rounds of each web browser seem to work differently"), + default=2, + required=0) + + def render_css(self, field, key, value, REQUEST): + + # DATA DEFINITION ############################################# + height_header = field.get_value('height_header') + height_global_div = field.get_value('height_global_div') + height_axis_x=field.get_value('height_axis_x') + width_line = field.get_value('width_line') + space_line = field.get_value('space_line') + selection_name = field.get_value('selection_name') + security_index = field.get_value('security_index') + y_unity = field.get_value('y_unity') + y_axis_width = field.get_value('y_axis_width') + y_range = field.get_value('y_range') + portal_types= field.get_value('portal_types') + meta_types = field.get_value('meta_types') + x_range=field.get_value('x_range') + here = REQUEST['here'] + list_method = field.get_value('list_method') + report_tree = field.get_value('report_tree') + report_root_list = field.get_value('report_root_list') + y_axis_method=field.get_value('y_axis_method') + script=getattr(here,field.get_value('x_axis_script_id'),None) + info_block_method = getattr(here,field.get_value('info_block_method'),None) + object_start_method_id = field.get_value('x_start_bloc') + object_stop_method_id= field.get_value('x_stop_bloc') + form = field.aq_parent + + x_occurence=[] # contains datas of start and stop of each block like this [ [ [x1,x2],[x1,x2] ],[ [x1,x2],[x1,x2] ],.....] it is not directly coordinate but datas. + x_axe=[] # will contain what wee need to display in X-axis. contains: (data used for construction, display of x-axis) + yrange=[] # we store the value in Y-axis of each block + nbr=1 + y_max=1 + current_top=height_header + total=[] + list_object=[] #in this list we store all the objects of type Line + giant_string='' #will contain all the html code. + report_sections=[] + # END DATA DEFINITION ########################################### + # we fetch fold/unfold datas ###################### + #here.portal_selections.setSelectionFor(selection_name, None)#put selection to null + selection = here.portal_selections.getSelectionFor(selection_name, REQUEST=REQUEST) + default_params = {} + sort = () + if selection is None: + selection = Selection(params=default_params, default_sort_on = sort) + domain_list = list(selection.getDomainList()) + here.portal_selections.setSelectionFor(selection_name, selection, REQUEST=REQUEST) + ######################## + + #we build line *************************** + (list_object,nbr_line,report_sections)=createLineObject(meta_types,selection,selection_name,field,REQUEST,list_method,here,report_root_list,y_axis_width,width_line,space_line,height_global_div,height_header,height_axis_x,form,current_top) + + # end build line #################################################### + + #we build x_occurence (used for the range in x-Axis ################################## + for o in report_sections: + method_start = getattr(o.getObject(),object_start_method_id,None) + method_stop= getattr(o.getObject(),object_stop_method_id,None) + block_begin = method_start() + if method_stop!=None: + block_stop= method_stop() + else: + block_stop=None + x_occurence.append([block_begin,block_stop]) + x_axe=script(x_occurence,x_range) #we call this script for the range in X-Axis + ################################################## + + # we add mobile block to the line object ################################### + indic_line=0 + #for o report_sections: + for o in list_object: + if list_object != []: + list_object[indic_line].insertActivityBlock(report_sections[indic_line].getObject(),object_start_method_id,object_stop_method_id,x_axe,field) + indic_line+=1 + # ############################################################# + # at this point list_object contains our tree of datas. Then wee add others object for the graphic. + + #one constructs the vertical dotted line ********************** + marge_left=y_axis_width+width_line/float(len(x_axe[1])) + for i in list_object: + i.appendVerticalDottedLine(x_axe,width_line,marge_left) + #************************************************************* + + + #one constructs the maximum horizontal dotted line 10px under the top of the line*************** + maximum_y=y_max + marge_top=10 + if y_range!=0: + for i in list_object: + i.appendHorizontalDottedLine(marge_top,maximum_y,height_global_div,height_header,height_axis_x,nbr_line,y_range,y_max,current_section) + #end construct of horizontal dotted line ******************************************************** + + + # we construct y-axis ****************************** + way=[] + y=[] + level=0 + current_top=height_header + idx=0 + for i in list_object: + current_top=i.buildYtype(way,y,level,y_axis_width,height_global_div,height_header,height_axis_x,nbr_line,current_top,space_line,y_max,y_range,y_unity,selection_name,form) + + #current_top=y[len(y)-1].top+((height_global_div-height_header-height_axis_x)/float(nbr_line))+space_line + + current_top=y[len(y)-1].top+((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line))+space_line + + + idx+=1 + list_object=y+list_object #we need to add the y-axis block at the beginning of our structure otherwise the display is not correct + #************************end construct y-axis + + + #build X axis ######################################## + list_object.append(Line('','axis_x',y_axis_width,width_line,height_axis_x,current_top-space_line)) + list_object[len(list_object)-1].createXAxis(x_axe,width_line,y_axis_width) + #*************************** + + SESSION = REQUEST.SESSION + SESSION.set('total',list_object) + SESSION.set('width_line',width_line) + SESSION.set('height_global', height_global_div) + SESSION.set('y_axis_width', y_axis_width) + SESSION.set('report_tree',report_tree) + SESSION.set('report_root_list',report_root_list) + SESSION.set('selection_name',selection_name) + for i in list_object: + giant_string+=i.render_css(y_axis_width,security_index) + return giant_string + + + + + def render(self,field, key, value, REQUEST): + here = REQUEST['here'] + portal_url= here.portal_url() + SESSION = REQUEST.SESSION + total = SESSION.get('total') + width_line=SESSION.get('width_line') + height_global_div=SESSION.get('height_global') + y_axis_width=SESSION.get('y_axis_width') + report_tree=SESSION.get('report_tree') + report_root_list=SESSION.get('report_root_list') + selection_name=SESSION.get('selection_name') + giant_string='<input type=\"hidden\" name=\"list_selection_name\" value='+selection_name+' />\n' + + giant_string+='<div id=\"global_block\" style=\"position:absolute;width:'+str(width_line+y_axis_width)+'px;height:'+str(height_global_div)+'px;background:#d5e6de;margin-left:-99px;border-style:solid;border-color:#000000;border-width:1px;margin-top:-21px\">\n' + + #header of the graphic****************************************** + giant_string+='<div id=\"header\" style=\"position:absolute;width:'+str(width_line+y_axis_width)+'px;height:'+str(height_global_div)+'px;background:#d5e6de;margin-left:20px;border-style:solid;border-color:#000000;border-width:1px;margin-top:1px\">' + + ##########################report tree + # Create the header of the table with the name of the columns + # Create also Report Tree Column if needed + if report_tree: + selection = here.portal_selections.getSelectionFor(selection_name, REQUEST=REQUEST) + selection_report_path = selection.getReportPath() + + report_tree_options = '' + for c in report_root_list: + if c[0] == selection_report_path: + report_tree_options += """<option selected value="%s">%s</option>\n""" % (c[0], c[1]) + else: + report_tree_options += """<option value="%s">%s</option>\n""" % (c[0], c[1]) + report_popup = """<select name="report_root_url" +onChange="submitAction(this.form,'%s/portal_selections/setReportRoot')"> + %s</select></div>""" % (here.getUrl(),report_tree_options) + giant_string += report_popup + else: + report_popup = '' + ###################################### + + for i in range(0,len(total)): + giant_string+=total[i].render(portal_url,y_axis_width) + ###################" + + giant_string+='<div id=\"lefttop\" style=\"position:absolute;width:5px;height:5px;background:#a45d10\"></div>\n' + giant_string+='<div id=\"righttop\" style=\"position:absolute;width:5px;height:5px;background:#a45d10"></div>\n' + giant_string+='<div id=\"rightbottom\" style=\"position:absolute;width:5px;height:5px;background:#a45d10\"></div>\n' + giant_string+='<div id=\"leftbottom\" style=\"position:absolute;width:5px;height:5px;background:#a45d10\"></div>\n' + giant_string+='<script type=\"text/javascript\">\n SET_DHTML(' + + + for i in range(0,len(total)): + graphic_call='' + for j in total[i].content: + if j.types=='activity': + giant_string+='\"'+j.name+'\",' + elif j.types=='info': + giant_string+='\"'+j.name+'\"+NO_DRAG,' + + current_object=total[i] + + if current_object.soon!=[]: + giant_string+=createGraphicCall(current_object,graphic_call) + + giant_string+='\"lefttop\"+CURSOR_NW_RESIZE, \"righttop\"+CURSOR_NE_RESIZE, \"rightbottom\"+CURSOR_SE_RESIZE, \"leftbottom\"+CURSOR_SW_RESIZE);\n' + giant_string+='</script>\n </div> ' + return giant_string +#*************************************************** + + + +# class line ************************************** +class Line: + def __init__(self,title='',name='',begin=0,width=0,height=0,top=0,color='',soon=None,y_type='none',paternity=0,url=''): + if soon is None: + soon = [] + self.title=title + self.name=name + self.begin=begin + self.width=width + self.height=height + self.top=top + self.content=[] + self.color=color + self.soon=soon + self.y_type=y_type + self.paternity=paternity + self.url=url + + + def render(self,portal_url,y_axis_width): + """ creates "pure" html code of the line, its Block, its soon """ + html_render='<div id=\"'+self.name+'\"></div>\n' + for j in self.content: + if j.types=='activity': + if ((j.width*self.width)+(self.begin+j.begin*self.width)>self.width+y_axis_width): #checks if the block is too large for the end of the line if it is the case, one cuts the block + html_render+='<div id=\"'+j.name+'\" ondblclick=\"showGrips()\" onclick=\"dd.elements.'+j.name+'.resizeTo('+str(round(j.width*self.width))+','+ str(j.height*(self.height-10))+') \">' + elif ((self.begin+j.begin*self.width) < self.begin): #checks if the block starts before the beginning of the line + html_render+='<div id=\"'+j.name+'\" ondblclick=\"showGrips()\" onclick=\"if (dd.elements.'+j.name+'.moved==0){dd.elements.'+j.name+'.moveBy('+str(round(j.begin*self.width))+',0);dd.elements.'+j.name+'.resizeTo('+str(round(j.width*self.width))+','+ str(j.height*(self.height-10))+');dd.elements.'+j.name+'.moved=1} \">' # "done" is used because otherwise everytime we move the block it will execute moveby() + else: + html_render+='<div id=\"'+j.name+'\" ondblclick=\"showGrips()\">' + + html_render+=j.render(self.width,self.height,portal_url,self.begin,y_axis_width) # we add info Block inside the div + html_render+='</div>\n' + elif j.types!='info': + html_render+='<div id=\"'+j.name+'\">'+str(j.text)+'</div>\n' + + + if self.soon!=[]: + for i in self.soon: + html_render+=i.render(portal_url,y_axis_width) + + return html_render + + + + def render_css(self,y_axis_width,security_index): + css_render='#'+self.name+'{position:absolute;\nborder-style:solid;\nborder-color:#53676e;\nborder-width:1px;\n' + if self.color!='': + css_render+='background:'+str(self.color)+';\n' + css_render+='height:'+str(self.height)+'px;\n' + css_render+='margin-left:'+str(self.begin)+'px;\n' + css_render+='margin-top:'+str(self.top)+'px;\n' + + if self.y_type=='father1': + css_render+='border-bottom-width:0px;' + elif self.y_type=='soon1': + css_render+='border-top-width:0px;\nborder-bottom-width:0px;\n' + elif self.y_type=='soon2': + css_render+='border-top-width:0px;' + css_render+='width:'+str(self.width)+'px;\n}' + + + for j in self.content: #we generate block's css + if j.types=='activity': + css_render+='#'+j.name+'{position:absolute;\nbackground:#bdd2e7;\nborder-style:solid;\nborder-color:#53676e;\nborder-width:1px;\n' + css_render+='height:'+str((j.height*(self.height-10))-security_index)+'px;\n' #-10 because wee don't want a block sticked to border-top of the line + # text=str('%.2f' %maximum_y)+y_unity + + if ((self.begin+j.begin*self.width) < self.begin): #checks if the block starts before the beginning of the line + css_render+='margin-left:'+str(self.begin)+'px;\n' + css_render+='width:'+str((j.width*self.width+j.begin*self.width))+'px;\n' + elif ((j.width*self.width)+(self.begin+j.begin*self.width)>self.width+y_axis_width): #checks if the block is too large for the end of the line. if it is the case, one cuts the block + css_render+='width:'+str(round(j.width*self.width)-((self.begin+j.begin*self.width+j.width*self.width)-(self.width+y_axis_width)))+'px;\n' + + css_render+='margin-left:'+str(round(self.begin+j.begin*self.width))+'px;\n' + else: + css_render+='width:'+str(round(j.width*self.width))+'px;\n' + css_render+='margin-left:'+str(round(self.begin+j.begin*self.width))+'px;\n' + + css_render+='margin-top:'+str(self.top+10+j.marge_top*(self.height-10))+'px;}\n' + css_render+=j.render_css(self.width,self.height-10,self,y_axis_width) # we add info Block inside the div + + elif j.types=='text_x' : + css_render+='#'+j.name+'{position:absolute;\nborder-style:solid;\nborder-color:#53676e;\nborder-width:1px;\n' + css_render+='margin-left:'+str(j.begin)+'px;\n' + css_render+='margin-top:'+str(round(1+self.top+self.height/2))+'px;}\n' + + elif j.types=='text_y': + css_render+='#'+j.name+'{position:absolute;\nborder-style:solid;\nborder-color:#53676e;\nborder-width:1px;\n' + css_render+='margin-left:'+str(self.width/4)+'px;\n' + css_render+='margin-top:'+str(round(1+self.top+self.height/2))+'px;\n' + css_render+='border-width:0px;}\n' + + + elif j.types=='vertical_dotted': + css_render+='#'+j.name+'{position:absolute;\nborder-style:dotted;\nborder-color:#53676e;\nborder-left-width:1px;\nborder-right-width:0px;\nborder-top-width:0px;\nborder-bottom-width:0px;\n' + css_render+='margin-left:'+str(j.begin)+'px;\n' + css_render+='height:'+str(self.height)+'px;\n' + css_render+='margin-top:'+str(1+round(self.top))+'px;}\n' + + elif j.types=='horizontal_dotted': + css_render+='#'+j.name+'{position:absolute;\nborder-style:dotted;\nborder-color:#53676e;\nborder-left-width:0px;\nborder-right-width:0px;\nborder-top-width:1px;\nborder-bottom-width:0px;\n' + css_render+='margin-left:'+str(self.begin)+'px;\n' + css_render+='height:1px;\n' + css_render+='margin-top:'+str(self.top+j.marge_top)+'px;' + css_render+='width:'+str(self.width)+'px;}\n' + + elif j.types=='y_coord': + css_render+='#'+j.name+'{position:absolute;\nborder-style:solid;\nborder-color:#53676e;\nfont-size:9px;\nborder-width:0px;\n' + css_render+='margin-left:'+str(self.width-(len(j.text)*5))+'px;\n' #x6 because this is the appropriate width for our font (9px), maybee need to be parameter + css_render+='margin-top:'+str(self.top+j.marge_top)+'px;}\n' + + elif j.types=='vertical': + css_render+='#'+j.name+'{position:absolute;\nborder-style:solid;\nborder-color:#53676e;\nborder-left-width:1px;\nborder-right-width:0px;\nborder-top-width:0px;\nborder-bottom-width:0px;\n' + css_render+='margin-left:'+str((self.width/4)+j.begin)+'px;\n' + css_render+='height:'+str(j.height)+'px;\n' + css_render+='margin-top:'+str(round(self.top)-self.height/2+13)+'px;}\n' + + elif j.types=='horizontal': + css_render+='#'+j.name+'{position:absolute;\nborder-style:solid;\nborder-color:#53676e;\nborder-left-width:0px;\nborder-right-width:0px;\nborder-top-width:1px;\nborder-bottom-width:0px;\n' + css_render+='margin-left:'+str((self.width/4)+j.begin)+'px;\n' + css_render+='width:16px;\n' + css_render+='height:1px; \n' + # css_render+='margin-top:'+str(round(self.top)-self.height/2+13)+'px;}\n' + css_render+='margin-top:'+str(round(1+self.top+self.height/2))+'px;}\n' + + if self.soon!=[]: + for i in self.soon: + css_render+=i.render_css(y_axis_width,security_index); + + return css_render + + + + def addBlock(self,name,block): + self.content.append(Block('activity',name,block[0],block[1],block[2],'',block[3],block[4])) + + def addBlockInfo(self,name): + self.content.append(Block('info',name,0,0,0,'')) + + def addBlockTextY(self,name,text): + self.content.append(Block('text_y',name,0,0,0,text)) + + def addBlockCoordY(self,name,text,marge_top): + self.content.append(Block('y_coord',name,0,0,0,text,{},marge_top)) + + def addBlockTextX(self,name,begin,text): + self.content.append(Block('text_x',name,begin,0,0,text)) + + def addBlockDottedVert(self,name,begin): + self.content.append(Block('vertical_dotted',name,begin,0,0,'')) + + def addBlockDottedHoriz(self,name,marge_top): + self.content.append(Block('horizontal_dotted',name,0,0,0,'',{},marge_top)) + + def addBlockVertical(self,name,marge_top,height,marge_left): + self.content.append(Block('vertical',name,marge_left,0,height,'',{},marge_top)) + + def addBlockHorizontal(self,name,marge_top,height,marge_left): + self.content.append(Block('horizontal',name,marge_left,0,height,'',{},marge_top)) + + + + + + def appendVerticalDottedLine(self,x_axe,width_line,marge_left): + current_marge=marge_left + indic=0 + for j in x_axe[1]: + nameblock='Block_vert_'+self.name+str(indic) + self.addBlockDottedVert(nameblock,current_marge) + current_marge+=width_line/float(len(x_axe[1])) + indic+=1 + + if self.soon!=[]: + for i in self.soon: + i.appendVerticalDottedLine(x_axe,width_line,marge_left) + + + + + def buildYtype(self,way,y,level,y_axis_width,height_global_div,height_header,height_axis_x,nbr_line,current_top,space_line,y_max,y_range,y_unity,selection_name,form): + """ used for determining the type of each part of y axis taking into account father and children + 'way' is a list whichs allows to determinate if the current block is a type 'soon1' or 'soon2' """ + report_url=self.url + if level==0: + name='axis'+str(self.name) + if self.soon!=[]: + y.append(Line('',name,1,y_axis_width,((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)),current_top,'',[],'father1')) + else: + y.append(Line('',name,1,y_axis_width,((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)),current_top,'',[],'father2')) + + if self.paternity==1: + if self.soon!=[]: + y[len(y)-1].addBlockTextY('ytext'+name,str(3*'  '*level)+'<a href="portal_selections/foldReport?report_url='+report_url+'&form_id='+form.id+'&list_selection_name='+selection_name+'">-'+self.title+'</a>') + else: + y[len(y)-1].addBlockTextY('ytext'+name,str(3*'  '*level)+'<a href="portal_selections/unfoldReport?report_url='+report_url+'&form_id='+form.id+'&list_selection_name='+selection_name+'">+'+self.title+'</a>') + + + else: + y[len(y)-1].addBlockTextY('ytext'+name,str(3*'  '*level)+self.title) + + #one constructs the indicators + if y_range!=0: + y[len(y)-1].createIndicators(y_unity,y_range,y_max,height_global_div,height_header,height_axis_x,nbr_line) + + if self.soon!=[]: + level+=1 + for j in range(0,len(self.soon)): + if j==(len(self.soon)-1): + way.append(1) + else: + way.append(0) + current_top+=((height_global_div-height_header-height_axis_x)/float(nbr_line)) + + current_top=self.soon[j].buildYtype(way,y,level,y_axis_width,height_global_div,height_header,height_axis_x,nbr_line,current_top,space_line,y_max,y_range,y_unity,selection_name,form) + del way[len(way)-1] + + else: + if self.soon!=[]: + name=str(self.name) + for num in way: + name=name+str(num) + y.append(Line('',name,1,y_axis_width,((height_global_div-height_header-height_axis_x)/float(nbr_line)),current_top,'',[],'soon1')) + + if self.paternity==1: + if self.soon!=[]: + y[len(y)-1].addBlockTextY('ytext'+name,str(3*'  '*level)+'<a href="portal_selections/foldReport?report_url='+report_url+'&form_id='+form.id+'&list_selection_name='+selection_name+'">-'+self.title+'</a>') + else: + y[len(y)-1].addBlockTextY('ytext'+name,str(3*'  '*level)+'<a href="portal_selections/unfoldReport?report_url='+report_url+'&form_id='+form.id+'&list_selection_name='+selection_name+'">+'+self.title+'</a>') + else: + y[len(y)-1].addBlockTextY('ytext'+name,str(3*'  '*level)+self.title) + + # one constructs the stick + y[len(y)-1].addBlockVertical('stickVer'+name,current_top-((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)),(height_global_div-height_header-height_axis_x)/float(nbr_line),3*level*6-18) #6 is the width of the standart font, maybe a future parameter + y[len(y)-1].addBlockHorizontal('stickHor'+name,current_top-((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)),(height_global_div-height_header-height_axis_x)/float(nbr_line),3*level*6-18) + + #one constructs the indicators + if y_range!=0: + y[len(y)-1].createIndicators(y_unity,y_range,y_max,height_global_div,height_header,height_axis_x,nbr_line) + level+=1 + for j in range(0,len(self.soon)): + current_top+=((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)) + if j==(len(self.soon)-1): + way.append(1) + else: + way.append(0) + current_top=self.soon[j].buildYtype(way,y,level,y_axis_width,height_global_div,height_header,height_axis_x,nbr_line,current_top,space_line,y_max,y_range,y_unity,selection_name,form) + del way[len(way)-1] + + else: + name=str(self.name) + test='true' + for num in way: + name=name+str(num) + if num==0: + test='false' + if test=='true': + y.append(Line('',name,1,y_axis_width,((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)),current_top,'',[],'soon2')) + else: + y.append(Line('',name,1,y_axis_width,((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)),current_top,'',[],'soon1')) + + + if self.paternity==1: + if self.soon!=[]: + y[len(y)-1].addBlockTextY('ytext'+name,str(3*'  '*level)+'<a href="portal_selections/foldReport?report_url='+report_url+'&form_id='+form.id+'&list_selection_name='+selection_name+'">-'+self.title+'</a>') + else: + y[len(y)-1].addBlockTextY('ytext'+name,str(3*'  '*level)+'<a href="portal_selections/unfoldReport?report_url='+report_url+'&form_id='+form.id+'&list_selection_name='+selection_name+'">+'+self.title+'</a>') + + else: + y[len(y)-1].addBlockTextY('ytext'+name,str(3*'  '*level)+self.title) + + # one constructs the sticks + y[len(y)-1].addBlockVertical('stickVer'+name,current_top-((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)),(height_global_div-height_header-height_axis_x)/float(nbr_line),3*level*6-18) + y[len(y)-1].addBlockHorizontal('stickHor'+name,current_top-((height_global_div-height_header-height_axis_x-((nbr_line-1)*space_line))/float(nbr_line)),(height_global_div-height_header-height_axis_x)/float(nbr_line),3*level*6-18) + + #one constructs the indicators + if y_range!=0: + y[len(y)-1].createIndicators(y_unity,y_range,y_max,height_global_div,height_header,height_axis_x,nbr_line) + return current_top + + + def createIndicators(self,y_unity,y_range,y_max,height_global_div,height_header,height_axis_x,nbr_line): + #one constructs the indicators + maximum_y=y_max + marge_top=0 + indic=0 + #while maximum_y>=(y_max/float(y_range)): + while maximum_y>=0: + nameblock='Block_'+self.name+str(indic) + text=str('%.2f' %maximum_y)+y_unity + self.addBlockCoordY(nameblock,text,marge_top) + maximum_y=maximum_y-(y_max/float(y_range)) + marge_top+=(((height_global_div-height_header-height_axis_x)/float(nbr_line))-10)/y_range + indic+=1 + ############################### + + + def appendHorizontalDottedLine(self,marge_top,maximum_y,height_global_div,height_header,height_axis_x,nbr_line,y_range,y_max): + current_top=marge_top + max_y=maximum_y + indic=0 + while max_y>=0: + nameblock='Block_hor_'+self.name+str(indic) + self.addBlockDottedHoriz(nameblock,current_top) + max_y=max_y-(y_max/float((y_range-1))) #-1 because we don't want a dotted line on the X-axis + current_top+=(((height_global_div-height_header-height_axis_x)/float(nbr_line))-marge_top)/y_range #10px under the top of the line . float is important here! """ + indic+=1 + if self.soon!=[]: + for i in self.soon: + i.appendHorizontalDottedLine(marge_top,maximum_y,height_global_div,height_header,height_axis_x,nbr_line,y_range,y_max) + + + def appendActivityBlock(self,list_block): + indic=0 + for data_block in list_block: + self.addBlock('ActivityBlock_'+self.name+'_'+str(indic),data_block) + indic+=1 + + + def createXAxis(self,x_axe,width_line,y_axis_width): + marge_left=y_axis_width + indic1=0 + for i in x_axe[1]: + nameblock='block_'+self.name+str(indic1) + self.addBlockTextX(nameblock,marge_left,i) + indic1+=1 + marge_left+=width_line/float(len(x_axe[1])) + + + def addSoon(self,soon): + self.soon.append(soon) + + + def createLineChild(self,report_section,field,current_top,y_axis_width,width_line,space_line,height_global_div,height_header,height_axis_x,nbr_line,current_index,url): + + if len(report_section[current_index].getObject().objectValues())!=0: + paternity=1 + else: + paternity=0 + soon=Line(title=str(report_section[current_index].getObject().getTitle()),name=self.name+'s'+str(current_index) ,begin=y_axis_width,width=width_line,height=(height_global_div-height_header-height_axis_x)/float(nbr_line),top=current_top,color='#ffffff',paternity=paternity,url=url) + + if (current_index+1)<=(len(report_section)-1): + if report_section[current_index+1].getDepth() == 0: + current_top=current_top+((height_global_div-height_header-height_axis_x)/float(nbr_line))+space_line + else: + current_top=current_top+((height_global_div-height_header-height_axis_x)/float(nbr_line)) + self.addSoon(soon) + return current_top + + + + def insertActivityBlock(self,line_content,object_start_method_id,object_stop_method_id,x_axe,field): + marge=0 + method_start = getattr(line_content,object_start_method_id,None) + method_stop= getattr(line_content,object_stop_method_id,None) + block_begin = method_start() + list_block=[] + if method_stop!=None: + block_stop= method_stop() + else: + block_stop=None + + for i in x_axe[0]: + if isinstance(block_begin,DateTime): + comp_test1=block_begin.Date()==i + else: + comp_test1=block_begin==i + if comp_test1==True: + + list_block.append([marge,(block_stop-block_begin)/(float(len(x_axe[0]))),0.75,{},0.25]) # 0.75(height) need to be defined + marge+=1/float(len(x_axe[0])) + if list_block!=[]: + self.appendActivityBlock(list_block) + + if self.soon!=[]: + script_x=getattr(line_content,field.get_value('data_method'),None) + soon_line=script_x() + indic=0 + for s in soon_line: + self.soon[indic].insertActivityBlock(soon_line[indic],object_start_method_id,object_stop_method_id,x_axe,field) + indic+=1 + + + +#************************************************* + +# class block *********************************** +class Block: + def __init__(self,types,name,begin,width=0,height=0,text='',content={},marge_top=0): + self.types=types + self.name=name + self.begin=begin + self.width=width + self.height=height + self.text=text + self.content=content + self.marge_top=marge_top + # self.color=color need to be implemented in the future! + + def render(self,line_width,line_height,portal_url,line_begin,y_axis_width): + """used for inserting text in a block. one calculates how to organise the space. + one defines a width and height parameter (in pixel) which can be + changed (depends on the size and the font used) + one fetches content which is a dictionnary like + this {'center':'ezrzerezr','topright':'uihiuhiuh', + 'topleft':'jnoinoin','botleft':'ioioioioi','botright':'ononono'} + """ + string='' + font_height=10 + font_width=6 + info='' + + #checks if the block starts before the beginning of the line + if ((line_begin+self.begin*line_width) < line_begin): + block_width=self.width+self.begin + #checks if the block is too large for the end of the line. if it is the case, one cuts the block + elif ((self.width*line_width)+(line_begin+self.begin*line_width)>line_width+y_axis_width): + block_width=self.width*line_width-((line_begin+self.begin*line_width+self.width*line_width)-(line_width+y_axis_width)) + block_width=block_width/line_width + else: + block_width=self.width + + return self.buildInfoBlockBody(line_width,block_width,font_width,line_height) # NEED TO BE TESTED !!! + + + def render_css(self,line_width,line_height,line,y_axis_width): + string='' + font_height=10 + font_width=6 + line_begin=line.begin + + if ((line_begin+self.begin*line_width) < line_begin): #checks if the block starts before the beginning of the line + block_width=self.width+self.begin + elif ((self.width*line_width)+(line_begin+self.begin*line_width)>line_width+y_axis_width): #checks if the block is too large for the end of the line. if it is the case, one cuts the block + block_width=self.width*line_width-((line_begin+self.begin*line_width+self.width*line_width)-(line_width+y_axis_width)) + block_width=block_width/line_width + else: + block_width=self.width + + return self.buildInfoBlockCss(font_height,line_height,block_width,line_width) # NEED TO BE TESTED!! + + + #case with five informations + + + #************************************************************************ + def addInfoCenter(self,info): #add info in the top left corner of a block + self.content['center']=info + + def addInfoTopLeft(self,info): #add info in the top left corner of a block + self.content['topleft']=info + + def addInfoTopRight(self,info): #add info in the top right corner of a block + self.content['topright']=info + + def addInfoBottomLeft(self,info): #add info in the bottom left corner of a block + self.content['botleft']=info + + def addInfoBottomRight(self,info): #add info in the bottom right corner of a block + self.content['botright']=info + + + # **************************************** + def buildInfoBlockBody(self,line_width,block_width,font_width,line_height): + """ create the body of the html for displaying info inside a block""" + displayed1=['center','topright','topleft','botleft','botright'] + displayed1=displayed1[0:len(self.content)] + string='' + for i in displayed1: + displayed[i]=0 + + if len(self.content)==5: + test_height= font_height<=((self.height*line_height)/3) + test_width= ((len(self.content[i])*font_width)<=((block_width*line_width)/3)) + if len(self.content)==4 or len(self.content)==3: + test_height= font_height<=((self.height*line_height)/2) + test_width= ((len(self.content[i])*font_width)<=((block_width*line_width)/2)) + if len(self.content)==2: + test_height=font_height<=(self.height*line_height) + test_width= (len(self.content[i]*font_width)<=((block_width*line_width)/2)) + if len(self.content)==1: + test_height= font_height<=(self.height*line_height) + test_width= (len(self.content[i]*font_width)<=(block_width*line_width)) + + for i in self.content: + if test_height & test_width: + string+='<div id=\"'+self.name+i+'\">'+ self.content[i] + string+='</div>\n' + displayed[i]=1 + #******************checks if we need to add interrogation.png + if ((self.width*line_width>=15) & (self.height*line_height>=15)): + order=displayed1 + for i in order: + if displayed[i]==0: + info+=self.content[i]+'|' + for i in order: + if displayed[i]==0: + string+='<div id=\"'+self.name+i+'\" title=\"'+info+'\"><img src=\"'+portal_url+'/images/question.png\" height=\"15\" width=\"15\"> ' + string+='</div>\n' + break + #****************** + return string + #************************************************** + + + +#******************************************* + def buildInfoBlockCss(self,font_height,line_height,block_width,line_width): + """use for creating css code when one needs to add info inside a block""" + displayed1=['center','topright','topleft','botleft','botright'] + displayed1=displayed1[0:len(self.content)] + string='' + for i in displayed1: + displayed[i]=0 + + if len(self.content)==5: + test_height= font_height<=((self.height*line_height)/3) + test_width= ((len(self.content[i])*font_width)<=((block_width*line_width)/3)) + if len(self.content)==4 or len(self.content)==3: + test_height= font_height<=((self.height*line_height)/2) + test_width= ((len(self.content[i])*font_width)<=((block_width*line_width)/2)) + if len(self.content)==2: + test_height=font_height<=(self.height*line_height) + test_width= (len(self.content[i]*font_width)<=((block_width*line_width)/2)) + if len(self.content)==1: + test_height= font_height<=(self.height*line_height) + test_width= (len(self.content[i]*font_width)<=(block_width*line_width)) + + elif len(self.content)==0: + return '' + + matrix = {('center',5):{'left':2,'top':2},('center',4):{'left':0,'top':0}, + ('center',3):{'left':2,'top':2},('center',2):{'left':0,'top':2}, + ('center',1):{'left':2,'top':2}, + ('topright',5):{'left':1,'top':0},('topright',4):{'left':1,'top':0},('topright',3):{'left':1,'top':0},('topright',2):{'left':1,'top':2}, + ('topleft',5):{'left':0,'top':0},('topleft',4):{'left':1,'top':1},('topleft',3):{'left':0,'top':0}, + ('botleft',5):{'left':0,'top':1},('topleft',4):{'left':0,'top':1}, + ('botright',5):{'left':1,'top':1} + } + + matrix_picture = {('center',5):{'left':2,'top':2},('center',4):{'left':0,'top':0}, + ('center',3):{'left':2,'top':2},('center',2):{'left':0,'top':2},('center',1):{'left':2,'top':2}, + ('topright',5):{'left':1,'top':0},('topright',4):{'left':1,'top':0},('topright',3):{'left':1,'top':0},('topright',2):{'left':1,'top':2}, + ('topleft',5):{'left':0,'top':0},('topleft',4):{'left':1,'top':1},('topleft',3):{'left':0,'top':0}, + ('botleft',5):{'left':0,'top':1},('topleft',4):{'left':0,'top':1}, + ('botright',5):{'left':1,'top':1} + } + for i in self.content: + for counter in range(1,5): + matrix_data = matrix[(i,counter)] + left = matrix_data['left'] + top= matrix_data['top'] + if left == 0: + margin_left=0 + else: + margin_left = round(((block_width*line_width)/left)-(font_width*len(self.content[i]))/left) + if top==0: + margin_top=0 + else: + margin_top = round(((self.height*line_height)/top)-(font_height/top)) + + if test_height & test_width: + string+='#'+self.name+i+'{position:absolute;\nmargin-left:'+str(margin_left)+'px;\nmargin-top:'+str(margin_top)+'px;\n}\n' + displayed[i]=1 + line.addBlockInfo(self.name+i) + + #******************checks if we need to add interrogation.png + if ((block_width*line_width>=15) & (self.height*line_height>=15)): + order=('center','topright','topleft','botleft','botright') + + for i in order: + for counter in range(1,5): + matrix_data= matrix [(i,counter)] + left = matrix_data['left'] + top = matrix_data['top'] + if left == 0: + margin_left=0 + else: + margin_left=round(((block_width*line_width)/left)-(15/left)) + + if top==0: + margin_top=0 + else: + margin_top=round(((self.height*line_height)/top)-(15/top)) + margin_top = round(((self.height*line_height)/top)-(font_height/top)) + if i=='center' & counter==3: + margin_left=round(((block_width*line_width)/left)-(font_width*len(self.content[i]))/left) + margin_top=round(((self.height*line_height)/top)-(font_height/top)) + if i=='center' & counter==2: + margin_top=round(((self.height*line_height)/top)-(font_height/top)) + string+='#'+self.name+i+'{position:absolute;\nmargin-left:'+str(margin_left)+'px;\nmargin-top:'+str(margin_top)+'0px;\n}' + line.addBlockInfo(self.name+i) + return string + ##################################################### + + + +PlanningBoxWidgetInstance = PlanningBoxWidget() + + +class PlanningBox(ZMIField): + meta_type = "PlanningBox" + widget = PlanningBoxWidgetInstance + validator = PlanningBoxValidatorInstance + security = ClassSecurityInfo() + security.declareProtected('Access contents information', 'get_value') + def get_value(self, id, **kw): + if id == 'default' and kw.get('render_format') in ('list', ): + return self.widget.render(self, self.generate_field_key() , None , kw.get('REQUEST'), render_format=kw.get('render_format')) + else: + return ZMIField.get_value(self, id, **kw) + + def render_css(self, value=None, REQUEST=None): + return self.widget.render_css(self,'',value,REQUEST) + +