Groovy script - organize map - I need help?

animal
2010-11-30
2012-10-07
  • animal
    animal
    2010-11-30

    Hello all!
    (all apologies for my english, i'm a french guy ;)

    First : Thanks for this great application which save many of my time.
    I discovered it first with Freemind since many years, and now with Freeplane
    which bring us many useful fetaures & a most user-friendly interface!
    And now, it is how groovy scripts can help me, I discover with great interest!

    I have a project, but I am not an expert in groovy nor in java. (only some
    good experience with VBA... :-/)
    I try to make some test with samples on Freeplane website and other websites,
    but the results are even weaker, as my needs grow :'(
    So, I will detail all my project, and points where I need your help, guessing
    I can finish the rest alone...

    The project:

    Build a script which organize a mindmap with all required informations for a
    project management, and ready to be exported to a gantt software.

    For the whole map:
    1 - identify all nodes which have one child at least
    a => by adding to them an attribute 'type' with the value 'jobgroup'

    2 - identify all nodes which have no children,
    a => by adding to them an attribute 'type' with the value 'job'
    b => by adding to them an attribute 'duration' with the value '1'

    3 - for each node having an attribute 'type' with the value 'job'
    a => add a 1st sub node with the text 'skill?'
    a => to this subnode, add an attribute 'type' with the value 'skill'
    b => to this subnode, add an attribute 'rate' with the value '100'
    b => add a 2nd sub node with the text 'material?'
    a => to this subnode, add an attribute 'type' with the value 'material'
    b => to this subnode, add an attribute 'rate' with the value '100'

    4 - for each node having an attribute 'type' with the value 'jobgroup'
    a => changing font to big bold forcolor = dark blue
    a => adding an attribute 'duration' with the value '<sum values="" of="" all="" attributes="" 'duration'="" of="" all="" children="" nodes="">'

    5 - for each node having an attribute 'type' with the value 'job'
    a => changing font to big bold forcolor = black

    6 - for each node having an attribute 'type' with the value 'skill'
    a => changing font to big bold forcolor = dark green

    7 - for each node having an attribute 'type' with the value 'material'
    a => changing font to big bold forcolor = dark orange

    __The help I need:__
    If someone can only begin the script, and implement : 1a, 2a, 2b, 3a, 3aa, 4a,
    4aa, it will be really great!
    After, I will try to complete the script, and post it for the communitie and
    your feedback.

    I know it is a huge work, but this could also be interesting for the
    communitie. isn't it ?

    Sincerly, thanks for help.

    animal

     
  • Hi animal,

    Freeplane already supports some kind of project management: http://www.google
    .fr/search?q=freeplane+taskjuggler
    . Nevertheless that was a nice task and here is the result:

    import java.awt.Color;
    import org.freeplane.plugin.script.proxy.Proxy.Node
    
    def void formatNode(Node node, Color color) {
        node.style.font.bold = 1
        node.style.font.size += 2
        node.style.nodeTextColor = color
    }
    
    def double duration(node) {
        // only read duration of jobs. All other values will be computed
        def dString = ('job' == node.attributes.getFirst('type')) ?
           node.attributes.getFirst('duration') : null
        def d = (dString == null) ? 0 : Double.parseDouble(dString)
        return node.children.sum(d) { duration(it) }
    }
    
    // Controller.find() searches whole map
    c.find{
        // ignore all nodes that already have a type
        it.attributes.getFirst('type') == null
    }.each {
        if (it.isLeaf()) {
            // Job
            it.attributes.set('type', 'job')
            it.attributes.set('duration', '1')
            formatNode(it, Color.black)
            // Skill
            def skill = it.createChild()
            skill.attributes.set('type', 'skill')
            skill.attributes.set('rate', '100')
            formatNode(skill, Color.green)
            // Material
            def material = it.createChild()
            material.attributes.set('type', 'material')
            material.attributes.set('rate', '100')
            formatNode(material, Color.orange)
        }
        else {
            // Jobgroup
            it.attributes.set('type', 'jobgroup')
            formatNode(it, Color.blue)
        }
    }
    
    // calculate recursively beginning from root
    c.find{
        // ignore all nodes that already have a type
        it.attributes.getFirst('type') == 'jobgroup'
    }.each {
        it.attributes.set('duration', duration(it)+"")
    }
    

    And here a test map (copy 'n paste it into an open map):

    Jobs
        Jobgroup
            Jobgroup
                Job
                Job
                Job
            Job
            Job
        Jobgroup
            Job
        Jobgroup
            Jobgroup
                Job
                Job
                Job
            Jobgroup
                Job
                Job
                Job
        Jobgroup
            Job
            Job
        Jobgroup
            Job
            Job
        Jobgroup
            Job
            Job
            Job
        Jobgroup
            Job
    

    Regards,
    Volker

     
  • Hi,

    Nodes can be formatted depending on its content without using scripts.
    The rules for automatic node formatting consist of a condition like the
    conditions used for filters and a style name.
    Thus node formatting can depend on its content, attributes, icons or level.
    They can be set using "Styles->Manage conditional styles".

    Because each style may set only some of the formatting attributes the
    resulting node formatting is a sum of formats defined by explicitly set
    format, explicitly assigned style and all matching conditional styles.

    http://freeplane.sourceforge.net/wiki/index.php/Conditional_node_styles
    Regards, Dimitry

     
  • animal
    animal
    2010-12-02

    Wow! Thank you guys!
    I will test that asap...

     
  • animal
    animal
    2010-12-02

    Ooops...
    I try it many times, but it failed.

    I always got the following dialog box error :
    "Error executing the script.
    Check the log file for details."

    I try to reduce the code content until having only :

    import org.freeplane.plugin.script.proxy.Proxy.Node;
    
    // Controller.find() searches whole map
    c.find{
        // ignore all nodes that already have a type
        it.attributes.getFirst("type") == null
    }.each {
        if (it.isLeaf()) {
            // Job
            it.attributes.set("type", "job")
        }
        else {
            // Jobgroup
            it.attributes.set("type", "jobgroup")
        }
    }
    

    But it again failed.
    And here is what the log file said:

    STDOUT: message: No signature of method: org.freeplane.plugin.script.proxy.AttributesProxy.getFirst() is applicable for argument types: (java.lang.String) values: {"type"}
    STDOUT: Line number: -1Dec 2, 2010 5:31:21 PM org.freeplane.core.util.LogTool warn
    WARNING: error executing script C:\Documents and Settings\username\.freeplane\scripts\tstA_01.groovy - giving up
    Dec 2, 2010 5:31:21 PM org.freeplane.core.util.LogTool warn
    WARNING: Error executing the script.
    Check the log file for details.
    

    Please, could tell me what I am doing wrong? :-/

     
  • Do you use the preview or the stable Freeplane version? Try with last preview
    version from http://freeplane.sourceforge.net/preview/
    (however I am not scripting man, may be the version is not the reason of the
    failure) -- Dimitry

     
  • Hi animal,

    sorry, my fault. I've used one method that's only available in the preview
    versions that Dimitry has mentioned. Using the new version will be a good idea
    anyway since it has support for formula computation as in spreadsheet engines
    like Excel.

    Here a version that will work with 1.1 series:

    import java.awt.Color;
    import org.freeplane.plugin.script.proxy.Proxy.Node
    
    def void formatNode(Node node, Color color) {
        node.style.font.bold = 1
        node.style.font.size += 2
        node.style.nodeTextColor = color
    }
    
    def double duration(node) {
        // only read duration of jobs. All other values will be computed
        def dString = ('job' == node.attributes.get('type')) ?
           node.attributes.get('duration') : null
        def d = (dString == null) ? 0 : Double.parseDouble(dString)
        return node.children.sum(d) { duration(it) }
    }
    
    // Controller.find() searches whole map
    c.find{
        // ignore all nodes that already have a type
        it.attributes.get('type') == null
    }.each {
        if (it.isLeaf()) {
            // Job
            it.attributes.set('type', 'job')
            it.attributes.set('duration', '1')
            formatNode(it, Color.black)
            // Skill
            def skill = it.createChild()
            skill.attributes.set('type', 'skill')
            skill.attributes.set('rate', '100')
            formatNode(skill, Color.green)
            // Material
            def material = it.createChild()
            material.attributes.set('type', 'material')
            material.attributes.set('rate', '100')
            formatNode(material, Color.orange)
        }
        else {
            // Jobgroup
            it.attributes.set('type', 'jobgroup')
            formatNode(it, Color.blue)
        }
    }
    
    // calculate recursively beginning from root
    c.find{
        // ignore all nodes that already have a type
        it.attributes.get('type') == 'jobgroup'
    }.each {
        it.attributes.set('duration', duration(it)+"")
    }
    

    Before running the script you have to allow file access for scripts. This
    option is available at Tools->Preferences->Scripting. When working on
    extensions of the script you should look up the error in the logfile. (See the
    FAQ in the Wiki for the location of the file.)

    Volker

     
  • Sorry, I have overlooked that you already have cited from the logfile...

     
  • animal
    animal
    2010-12-02

    Woooow! It work fine fine fine !
    Really : thanks for you help boercher !
    Thank you also dpolivaev!

    Where can I find those informations by myself, boercher ?
    Please, could you give me 2 or 3 useful links, where can find all
    methods/objects/functions/... about groovy for Freeplane?
    Where can I find differences between groovy for last dev version and stable
    version ?

    For example, I want to define a color by RGB setting. Where can I find that?

    Now, I will achieve this projet (which is 90% done... ;) + the xslt
    stylesheet, and post it asap..

    Thanks a lot again.
    animal

     
  • animal
    animal
    2010-12-16

    Ok. Thanks for your help boercher.

    But unfortunately, (and probably because i am not well aware of java coding),
    i do not find all infos i expected.
    I have not find way of set a RGB color, neither how to autofit the
    node/atrribute size according to its content... :-/
    Where should you start to search first/from to find such info? What
    keyword/site should you use ?

    animal

     
  • But unfortunately, (and probably because i am not well aware of java
    coding),
    i do not find all infos i expected.

    Without any Java/Groovy knowledge it's surely difficult to understand the
    available documentation. I would suggest that you first search for some Groovy
    introductory books/web pages/tutorials. Note that the scripting APIs are
    defined in Java so many classes and types that are used in the API, like
    Color, are defined in standard Java libraries.

    I have not find way of set a RGB color, neither how to autofit the
    node/atrribute
    size according to its content... :-/

    Color are defined as java.awt.Color instances - google for it's API. Here are
    some definitions from the class body:

    Color lightGray = new Color(192, 192, 192);
    Color red       = new Color(255, 0, 0);
    Color green     = new Color(0, 255, 0);
    Color blue  = new Color(0, 0, 255);
    

    There is no specific way to adjust the size of attributes and nodes, auto
    adjustment should just work. Please provide a minimal example script that
    shows the error.

    Regards,
    Volker

     
  • animal
    animal
    2010-12-20

    Ok. Fine for Colors. It work very well.
    But I didn't find where activate 'auto adjustment' ? :-/

    Thanks
    animal

     
  • There is no specific way to adjust the size of attributes and nodes, auto
    adjustment should just work. Please provide a minimal example script that
    shows the error.

    A minimal script is one that does just enough to show the error. I don't think
    that you have to provide a map since it shouldn't matter. If the script is
    larger than, say, 10-20 lines then please open a new bug in Mantis and attach
    the script. Otherwise you could post it here.

    Volker

     
  • Hi animal,

    For example, I want to define a color by RGB setting. Where can I find that?

    I have added the possibility to set colors as HTML rgb strings like #ff0000
    (red) or #222222 (darkgrey). New methods:

    • String Edge.getColorCode()
    • Edge.setColorCode(rgbString)
    • String Connector.getColorCode()
    • Connector.setColorCode(rgbString)
    • String Edge.getTextColorCode()
    • Edge.setTextColorCode(rgbString)
    • String Edge.getBackgroundColorCode()
    • Edge.setBackgroundColorCode(rgbString)

    Example script:

    node.style.textColorCode = "#9f00fF"
    node.text = node.style.textColorCode

    This will be available with the next preview version, 1.2.1_33.

    Regards,
    Volker

     
  • animal
    animal
    2010-12-24

    Great! Really great!!!
    This is the more easy way of setting a color, we might expected!
    Thanks.

     
  • animal
    animal
    2011-01-03

    Here is the last version of my script:

    import java.awt.Color;
    import org.freeplane.plugin.script.proxy.Proxy.Node
    
    // Colors codes used:
    clr = [   "BluePetrol":"#006699"
            , "GrayVeryLight":"#f5f5f5"
            , "Gray":"#999999"
            , "GreenDark":"#336600"
            , "Orange":"#ff9933"
            , "Pink":"#ff9999"]
    
    // Functions
    def void formatNode(Node node, String nodeTextColor, String nodeBgColor, nodeFontSize, String edgeColor, edgeWidth) {
        node.style.font.bold = 1
        node.style.setTextColorCode(nodeTextColor)
        node.style.setBackgroundColorCode(nodeBgColor)
        node.style.font.size = nodeFontSize
        node.style.edge.setColorCode(edgeColor)
        node.style.edge.setWidth(edgeWidth)
        ////node.style.edge.setType("bezier") /*==> DOES NOT WORK*/
    }
    
    // Main script
    // Controller.find() searches whole map
    c.find{
        // ignore all nodes that already have a type
        it.attributes.getFirst('NodeType') == null
    }.each {
        if (it.isLeaf()) {
            // Job
              //it.attributes.set('NodeType', 'Job')
              it.attributes.set('NodeType', '=(node.isRoot()) ? "Root" : (node.isLeaf()) ? "Job" : "JobGroup"')
              it.attributes.set('Duration', '1')
              formatNode(it, clr.BluePetrol, clr.GrayVeryLight, 13, clr.BluePetrol, 2)
              it.icons.addIcon('clock2')
    
            // Skill
              def skill = it.createChild()
              skill.text = 'skill?'
              skill.attributes.set('NodeType', 'Skill')
              skill.attributes.set('rate', '100%')
              formatNode(skill, clr.Pink, clr.GrayVeryLight, 11, clr.Pink, 1)
              skill.icons.addIcon('fema')
            // Material
              def material = it.createChild()
              material.text = 'material?'
              material.attributes.set('NodeType', 'Material')
              material.attributes.set('rate', '100%')
              formatNode(material, clr.Orange, clr.GrayVeryLight, 11, clr.Orange, 1)
              material.icons.addIcon('desktop_new')
    
            //
              it.setFolded(true)
        }
        else if (it.isRoot()) {
            // Root
              //it.attributes.set('NodeType', 'Root')
              it.attributes.set('NodeType', '=(node.isRoot()) ? "Root" : (node.isLeaf()) ? "Job" : "JobGroup"')
              it.attributes.set('prj-Author', 'Project leader name?')
              it.attributes.set('prj-StartDate', 'Project implementation start date?')
              it.attributes.set('prj-Title', 'Project title?')
              it.attributes.set('prj-Subject', 'Project statement?')
              it.attributes.set('prj-MinutesPerDay', '420')
              it.attributes.set('prj-MinutesPerWeek', '2100')
              it.attributes.set('prj-DaysPerMonth', '20')
              it.attributes.set('Duration', '= children.sum(0){ it["Duration"].num0 }')
              formatNode(it, clr.GrayVeryLight, clr.BluePetrol, 22, clr.Gray, 10)
        }
        else {
            // Jobgroup
              //it.attributes.set('NodeType', 'Job group')
              it.attributes.set('NodeType', '=(node.isRoot()) ? "Root" : (node.isLeaf()) ? "Job" : "JobGroup"')
              it.attributes.set('Duration', '= children.sum(0){ it["Duration"].num0 }')
              formatNode(it, clr.GreenDark, clr.GrayVeryLight, 18, clr.GreenDark, 4)
              it.icons.addIcon('folder')
        }
    }
    

    But I will reduce it further. Now the conditional formatting work also with f
    ormulas
    , I can handle the format in live without re-running the script.