Menu

Building task graph: architecture of faces-pm

2007-04-23
2012-09-14
  • Gary Oberbrunner

    Hi; I'm just evaluating faces-pm. The file format looks nicer than taskjuggler and the text-file format is definitely the way to go for me. I'm tired of other systems with impossible-to-edit database formats which change things that can't be changed in the GUI so they get confusing. I use scons for our build system, which is a python-based build tool kind of similar to faces, where you build a dependency graph using Python and then the scons system executes it.

    However, as a python programmer, I'm a little surprised by how the graph of tasks is built. It seems like users define a function with local variables and nested functions to set up the task graph (for both parent/child and pred/succ relations). I'm not even sure these functions get called -- are they just introspected to see what the local variables and relations are?

    I would've expected a more declarative style like
    p = Project(name="My Project")

    first = Task(effort="1w")
    p.add_task(first)

    next = Task(effort="2w")
    p.add_task(next)
    next.starts_after(first)
    ...
    p.balance() # to do the load-balancing and scheduling

    This would make it easy to extract task data from an external source (database, trac [http://trac.edgewall.org], or wherever).

    Is there actually such an API, perhaps, and the nested-function form gets translated into it? I haven't yet found any epydoc documentation for the classes, so I'm not sure what is really there. But the nested-function way of defining things seems a bit too "magical" to me.

    One other thing: in order to define charts and reports, you derive classes in the project file. You never instantiate them. This must be done via more introspection, I guess? It looks for any classes derived from base-classes it knows about, and adds them to the menu? Wouldn't it be more flexible and safer to have an interface like:
    faces.add_chart(Gantt(data=my_project))
    faces.add_report(MyReport(data=my_project))
    ?

    My concern is not that your method works; on the contrary, it seems to work fine. I just wish it were more pythonic, since I will typically want to separate the report definitions from the task/projects (the reports are common through the company, the WBS is per-project of course), and I'll want to get the actual WBS details from somewhere else like trac typically. Is that possible, or planned?

    Thanks,

    -- Gary Oberbrunner

     
    • Michael Reithinger

      Hi Gary,

      1. The nested function are indeed called and that is also the cause for the non pythonic declaration type. When calculating a project faces calls all nested functions and transforms the local variables to the tasks' attributes. In this way the tasks' attributes can be set by any python expression.
        Consider an expression like this:
        start = to_next_friday(up.antother_task.end)
        the start attribute is evaluated just in time while calculating the project. You can not archive the same with a programmatic style (at least not without writing much more project code.)

      2. Therefore there is no conventional api for faces: projects always have to be defined in a declarative style.

      3. And therefore extracting task to a database is not possible: The nested function is an intrinsic part of a task and because you cannot make a function database persistent you cannot make a task persistent.

      4. Epydoc would not help you much. faces is developed with leo, the leo file is included in the source code.

      5. faces projects are ordinary python modules. All classes will be instanciated by the gui through
        module introspection. Seperating reports from the project is actually no big deal.

       
      • Gary Oberbrunner

        1. Yes, I see your point, it would be more difficult because the evaluation of the attributes has to be delayed until you're building the project. One possible solution while retaining a declarative style would be to just quote the python expressions:
          start = ":to_next_friday(up.another_task.end)"
          where the : means, to faces, eval as expression. That would allow delaying the evaluation until you are constructing the project. Or you could just have an Expr class: start = Expr("to_next_friday..."), same thing but you don't need the : tag. Then calling the Expr would eval it (in the current context). Of course maintaining the contexts is a bit of work, but I'm sure you're doing a fair amount of clever work now to find the nested functions and call them while the parent is running. Makes my head spin! :-)

        I'm not complaining (don't get the wrong idea); just a suggestion. I think faces will work nicely for us as it is. It looks like a very complete toolset!

        Thanks;

        -- Gary

         
        • Michael Reithinger

          I also was thinking about using string evaluations. The problem is the error handling: it is very hard to find an error in a string expression (especially it is nearly impossible to find the correct line in the code). In the declarative way you get the error handling for free.

          cheers
          Michael

           
    • Dinne

      Dinne - 2007-09-22

      Hi,

      Your approach to the architecture of faces seems interesting. Would it be possible to split up a Project and subTask definitions in seperate files? The project file becomes very big for a complex project (like your facesproj example). I tried to do the following:

      file "project_a.py":

      def ProjectA():
      def SubTaskA():
      effort = "8H"

      file "project.py":

      def Root():
      def ProjectB():
      effort = "8H"

      execfile("projecta.py",locals(),globals())

      This however does not work. One of the problems is that locals() is read only. Is this the way to do it?

      Regards,

      Dinne

       
      • Michael Reithinger

        Do it like this:

        import project_a

        def Root():
        ProjectA = project_a.ProjectA

        def ProjectB():
              effort = "8H"

        Michael

         
        • dinne

          dinne - 2007-09-27

          Dear Michael,

          Thanks for the solution.

          Regards,
          Dinne

           

Log in to post a comment.

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.