diff bitten/trac_ext/web_ui.py @ 73:6d7753ea1798

Implemented basic management of target platforms. Closes #14.
author cmlenz
date Sun, 03 Jul 2005 21:11:59 +0000
parents b92d7c7d70fd
children 1d4fa4c32afa
line wrap: on
line diff
--- a/bitten/trac_ext/web_ui.py
+++ b/bitten/trac_ext/web_ui.py
@@ -24,10 +24,10 @@
 from trac.core import *
 from trac.Timeline import ITimelineEventProvider
 from trac.util import escape, pretty_timedelta
-from trac.web.chrome import INavigationContributor
+from trac.web.chrome import INavigationContributor, add_link
 from trac.web.main import IRequestHandler
 from trac.wiki import wiki_to_html
-from bitten.model import Build, BuildConfig, SlaveInfo
+from bitten.model import Build, BuildConfig, TargetPlatform
 
 
 class BuildModule(Component):
@@ -50,34 +50,38 @@
    if:build.can_create ?><div class="buttons">
     <form method="get" action=""><div>
      <input type="hidden" name="action" value="new" />
-     <input type="submit" value="Add new configuration" />
-    </div></form><?cs
+     <input type="submit" value="Add configuration" />
+    </div></form></div><?cs
    /if ?></div><?cs
 
   elif:build.mode == 'edit_config' ?>
-   <form method="post" action="">
-    <div class="field"><label>Name:<br />
-     <input type="text" name="name" value="<?cs var:build.config.name ?>" />
-    </label></div>
-    <div class="field"><label>Label (for display):<br />
-     <input type="text" name="label" size="32" value="<?cs
-       var:build.config.label ?>" />
-    </label></div>
-    <div class="field"><label><input type="checkbox" name="active"<?cs
-      if:build.config.active ?> checked="checked" <?cs /if ?>/> Active
-    </label></div>
-    <div class="field"><label>Repository path:<br />
-     <input type="text" name="path" size="48" value="<?cs
-       var:build.config.path ?>" />
-    </label></div>
-    <div class="field"><fieldset class="iefix">
-     <label for="description">Description (you may use <a tabindex="42" href="<?cs
-       var:trac.href.wiki ?>/WikiFormatting">WikiFormatting</a> here):</label>
-     <p><textarea id="description" name="description" class="wikitext" rows="10" cols="78"><?cs
-       var:build.config.description ?></textarea></p>
-     <script type="text/javascript" src="<?cs
-       var:htdocs_location ?>js/wikitoolbar.js"></script>
-    </fieldset></div>
+   <form class="config" method="post" action="">
+    <table><tr>
+     <td class="name"><label>Name:<br />
+      <input type="text" name="name" value="<?cs var:build.config.name ?>" />
+     </label></td>
+     <td class="label"><label>Label (for display):<br />
+      <input type="text" name="label" size="32" value="<?cs
+        var:build.config.label ?>" />
+     </label></td>
+    </tr><tr>
+     <td class="active"><label><input type="checkbox" name="active"<?cs
+       if:build.config.active ?> checked="checked" <?cs /if ?>/> Active
+     </label></td>
+     <td class="path"><label>Repository path:<br />
+      <input type="text" name="path" size="48" value="<?cs
+        var:build.config.path ?>" />
+     </label></td>
+    </tr><tr>
+     <td colspan="2"><fieldset class="iefix">
+      <label for="description">Description (you may use <a tabindex="42" href="<?cs
+        var:trac.href.wiki ?>/WikiFormatting">WikiFormatting</a> here):</label>
+      <p><textarea id="description" name="description" class="wikitext" rows="5" cols="78"><?cs
+        var:build.config.description ?></textarea></p>
+      <script type="text/javascript" src="<?cs
+        var:htdocs_location ?>js/wikitoolbar.js"></script>
+     </fieldset></td>
+    </tr></table>
     <div class="buttons">
      <input type="hidden" name="action" value="<?cs
        if:build.config.exists ?>edit<?cs else ?>new<?cs /if ?>" />
@@ -86,6 +90,21 @@
        if:build.config.exists ?>Save changes<?cs else ?>Create<?cs /if ?>" />
     </div>
    </form><?cs
+   if:build.config.exists ?><div class="platforms">
+    <h2>Target Platforms</h2><?cs
+     if:len(build.platforms) ?><ul><?cs
+      each:platform = build.platforms ?><li><a href="<?cs
+       var:platform.href ?>"><?cs var:platform.name ?></a></li><?cs
+      /each ?></ul><?cs
+     /if ?>
+    <div class="buttons">
+     <form method="get" action=""><div>
+      <input type="hidden" name="action" value="new" />
+      <input type="submit" value="Add target platform" />
+     </div></form>
+    </div>
+   </div><?cs
+   /if ?><?cs
 
   elif:build.mode == 'view_config' ?><ul>
    <li>Active: <?cs if:build.config.active ?>yes<?cs else ?>no<?cs /if ?></li>
@@ -94,20 +113,56 @@
      var:build.config.path ?></a></li><?cs /if ?></ul><?cs
    if:build.config.description ?><div class="description"><?cs
      var:build.config.description ?></div><?cs /if ?><?cs
-   if:build.can_modify ?><div class="buttons">
+   if:build.config.can_modify ?><div class="buttons">
     <form method="get" action=""><div>
      <input type="hidden" name="action" value="edit" />
      <input type="submit" value="Edit configuration" />
     </div></form><?cs
    /if ?></div><?cs
 
+  elif:build.mode == 'edit_platform' ?>
+   <form class="platform" method="post" action="">
+    <div class="field"><label>Name:<br />
+     <input type="text" name="name" value="<?cs var:build.platform.name ?>" />
+    </label></div>
+    <h2>Rules</h2>
+    <table><thead><tr>
+     <th>Property name</th><th>Match pattern</th>
+    </tr></thead><tbody><?cs
+     each:rule = build.platform.rules ?><tr>
+      <td><input type="text" name="property_<?cs var:name(rule) ?>" value="<?cs
+       var:rule.property ?>" /></td>
+      <td><input type="text" name="pattern_<?cs var:name(rule) ?>" value="<?cs
+       var:rule.pattern ?>" /></td>
+      <td><input type="submit" name="rm_rule_<?cs
+        var:name(rule) ?>" value="-" /><input type="submit" name="add_rule_<?cs
+        var:name(rule) ?>" value="+" />
+      </td>
+     </tr><?cs /each ?>
+    </tbody></table>
+    <div class="buttons">
+     <form method="get" action=""><div>
+     <input type="hidden" name="action" value="<?cs
+       if:build.platform.exists ?>edit<?cs else ?>new<?cs /if ?>" />
+      <input type="hidden" name="platform" value="<?cs
+       var:build.platform.id ?>" />
+      <input type="submit" name="cancel" value="Cancel" />
+      <input type="submit" value="<?cs
+       if:build.platform.exists ?>Save changes<?cs else ?>Add platform<?cs
+       /if ?>" />
+     </div></form>
+    </div>
+   </form><?cs
+
   elif:build.mode == 'view_build' ?>
    <p class="trigger">Triggered by: Changeset <a href="<?cs
      var:build.chgset_href ?>">[<?cs var:build.rev ?>]</a> of <a href="<?cs
      var:build.config.href ?>"><?cs var:build.config.name ?></a></p>
-   <p class="slave">Built by: <strong><?cs
-     var:build.slave.name ?></strong> (<?cs var:build.slave.os ?> <?cs
-     var:build.slave.os.version ?> on <?cs var:build.slave.machine ?>)</p>
+   <p class="slave">Built by: <strong title="<?cs
+     var:build.slave.ip_address ?>"><?cs var:build.slave.name ?></strong> (<?cs
+     var:build.slave.os ?> <?cs var:build.slave.os.version ?><?cs
+     if:build.slave.machien ?> on <?cs var:build.slave.machine ?><?cs
+     /if ?>)</p>
    <p class="time">Completed: <?cs var:build.started ?> (<?cs
      var:build.started_delta ?> ago)<br />Took: <?cs var:build.duration ?></p><?cs
   /if ?>
@@ -135,7 +190,7 @@
     # IRequestHandler methods
 
     def match_request(self, req):
-        match = re.match(r'/build(?:/([\w.-]+))?(?:/([\w.-]+))?', req.path_info)
+        match = re.match(r'/build(?:/([\w.-]+))?(?:/([\d]+))?', req.path_info)
         if match:
             if match.group(1):
                 req.args['config'] = match.group(1)
@@ -151,23 +206,41 @@
         id = req.args.get('id')
 
         if req.method == 'POST':
-            if not config and action == 'new':
-                self._do_create(req)
-            elif config and action == 'edit':
-                self._do_save(req, config)
+            if config:
+                if action == 'new':
+                    self._do_create_platform(req, config)
+                else:
+                    platform_id = req.args.get('platform')
+                    if platform_id:
+                        if action == 'edit':
+                            self._do_save_platform(req, config, platform_id)
+                    else:
+                        self._do_save_config(req, config)
+            else:
+                if action == 'new':
+                    self._do_create_config(req)
         else:
-            if not config:
+            if id:
+                self._render_build(req, id)
+            elif config:
+                if action == 'edit':
+                    platform_id = req.args.get('platform')
+                    if platform_id:
+                        platform = TargetPlatform(self.env, int(platform_id))
+                        self._render_platform_form(req, platform)
+                    else:
+                        self._render_config_form(req, config)
+                elif action == 'new':
+                    platform = TargetPlatform(self.env)
+                    platform.config = config
+                    self._render_platform_form(req, platform)
+                else:
+                    self._render_config(req, config)
+            else:
                 if action == 'new':
                     self._render_config_form(req)
                 else:
                     self._render_overview(req)
-            elif not id:
-                if action == 'edit':
-                    self._render_config_form(req, config)
-                else:
-                    self._render_config(req, config)
-            else:
-                self._render_build(req, config, id)
 
         return req.hdf.parse(self.build_cs), None
 
@@ -198,7 +271,7 @@
 
     # Internal methods
 
-    def _do_create(self, req):
+    def _do_create_config(self, req):
         """Create a new build configuration."""
         req.perm.assert_permission('BUILD_CREATE')
 
@@ -215,7 +288,7 @@
 
         req.redirect(self.env.href.build(config.name))
 
-    def _do_save(self, req, config_name):
+    def _do_save_config(self, req, config_name):
         """Save changes to a build configuration."""
         req.perm.assert_permission('BUILD_MODIFY')
 
@@ -232,6 +305,81 @@
 
         req.redirect(self.env.href.build(config.name))
 
+    def _do_create_platform(self, req, config_name):
+        """Create a new target platform."""
+        req.perm.assert_permission('BUILD_MODIFY')
+
+        if 'cancel' in req.args.keys():
+            req.redirect(self.env.href.build(config_name, action='edit'))
+
+        platform = TargetPlatform(self.env)
+        platform.config = config_name
+        platform.name = req.args.get('name')
+
+        properties = [int(key[9:]) for key in req.args.keys()
+                      if key.startswith('property_')]
+        properties.sort()
+        patterns = [int(key[8:]) for key in req.args.keys()
+                    if key.startswith('pattern_')]
+        patterns.sort()
+        platform.rules = [(req.args.get('property_%d' % property),
+                           req.args.get('pattern_%d' % pattern))
+                          for property, pattern in zip(properties, patterns)]
+
+        add_rules = [int(key[9:]) for key in req.args.keys()
+                     if key.startswith('add_rule_')]
+        if add_rules:
+            platform.rules.insert(add_rules[0] + 1, ('', ''))
+            self._render_platform_form(req, platform)
+            return
+        rm_rules = [int(key[8:]) for key in req.args.keys()
+                     if key.startswith('rm_rule_')]
+        if rm_rules:
+            del platform.rules[rm_rules[0]]
+            self._render_platform_form(req, platform)
+            return
+
+        platform.insert()
+
+        req.redirect(self.env.href.build(config_name, action='edit'))
+
+    def _do_save_platform(self, req, config_name, platform_id):
+        """Save changes to a target platform."""
+        req.perm.assert_permission('BUILD_MODIFY')
+
+        if 'cancel' in req.args.keys():
+            req.redirect(self.env.href.build(config_name, action='edit'))
+
+        platform = TargetPlatform(self.env, platform_id)
+        platform.name = req.args.get('name')
+
+        properties = [int(key[9:]) for key in req.args.keys()
+                      if key.startswith('property_')]
+        properties.sort()
+        patterns = [int(key[8:]) for key in req.args.keys()
+                    if key.startswith('pattern_')]
+        patterns.sort()
+        platform.rules = [(req.args.get('property_%d' % property),
+                           req.args.get('pattern_%d' % pattern))
+                          for property, pattern in zip(properties, patterns)]
+
+        add_rules = [int(key[9:]) for key in req.args.keys()
+                     if key.startswith('add_rule_')]
+        if add_rules:
+            platform.rules.insert(add_rules[0] + 1, ('', ''))
+            self._render_platform_form(req, platform)
+            return
+        rm_rules = [int(key[8:]) for key in req.args.keys()
+                     if key.startswith('rm_rule_')]
+        if rm_rules:
+            del platform.rules[rm_rules[0]]
+            self._render_platform_form(req, platform)
+            return
+
+        platform.update()
+
+        req.redirect(self.env.href.build(config_name, action='edit'))
+
     def _render_overview(self, req):
         req.hdf['title'] = 'Build Status'
         configurations = BuildConfig.select(self.env, include_inactive=True)
@@ -251,37 +399,73 @@
         config = BuildConfig(self.env, config_name)
         req.hdf['title'] = 'Build Configuration "%s"' \
                            % escape(config.label or config.name)
+        add_link(req, 'up', self.env.href.build(), 'Build Status')
         description = config.description
         if description:
             description = wiki_to_html(description, self.env, req)
         req.hdf['build.config'] = {
             'name': config.name, 'label': config.label, 'path': config.path,
             'active': config.active, 'description': description,
-            'browser_href': self.env.href.browser(config.path)
+            'browser_href': self.env.href.browser(config.path),
+            'can_modify': req.perm.has_permission('BUILD_MODIFY')
         }
+        req.hdf['build.mode'] = 'view_config'
 
-        req.hdf['build.mode'] = 'view_config'
-        req.hdf['build.can_modify'] = req.perm.has_permission('BUILD_MODIFY')
+        repos = self.env.get_repository(req.authname)
+        root = repos.get_node(config.path)
+        num = 0
+        for idx, (path, rev, chg) in enumerate(root.get_history()):
+            prefix = 'build.config.builds.%d' % rev
+            for build in Build.select(self.env, config=config.name, rev=rev):
+                req.hdf[prefix + '.' + build.slave] = self._build_to_hdf(build)
+            if idx > 5:
+                break
 
     def _render_config_form(self, req, config_name=None):
         config = BuildConfig(self.env, config_name)
         if config.exists:
             req.perm.assert_permission('BUILD_MODIFY')
-            req.hdf['title'] = 'Edit Build Configuration "%s"' \
-                               % escape(config.label or config.name)
             req.hdf['build.config'] = {
                 'name': config.name, 'label': config.label, 'path': config.path,
                 'active': config.active, 'description': config.description,
                 'exists': config.exists
             }
+
+            if 'new' in req.args.keys() or 'platform' in req.args.keys():
+                self._render_platform_form(req, config_name,
+                                           req.args.get('platform'))
+                return
+
+            req.hdf['title'] = 'Edit Build Configuration "%s"' \
+                               % escape(config.label or config.name)
+            for idx, platform in enumerate(TargetPlatform.select(self.env,
+                                                                 config_name)):
+                req.hdf['build.platforms.%d' % idx] = {
+                    'id': platform.id, 'name': platform.name,
+                    'href': self.env.href.build(config_name, action='edit',
+                                                platform=platform.id)
+                }
         else:
             req.perm.assert_permission('BUILD_CREATE')
             req.hdf['title'] = 'Create Build Configuration'
         req.hdf['build.mode'] = 'edit_config'
 
-    def _render_build(self, req, config_name, build_id):
+    def _render_platform_form(self, req, platform):
+        req.perm.assert_permission('BUILD_MODIFY')
+        req.hdf['title'] = 'Edit Target Platform "%s"' \
+                           % escape(platform.name)
+        req.hdf['build.platform'] = {
+            'name': platform.name, 'id': platform.id, 'exists': platform.exists,
+            'rules': [{'property': propname, 'pattern': pattern}
+                      for propname, pattern in platform.rules]
+        }
+        req.hdf['build.mode'] = 'edit_platform'
+
+    def _render_build(self, req, build_id):
         build = Build(self.env, build_id)
         assert build.exists
+        add_link(req, 'up', self.env.href.build(build.config),
+                 'Build Configuration')
         status2title = {Build.SUCCESS: 'Success', Build.FAILURE: 'Failure'}
         req.hdf['title'] = 'Build %s - %s' % (build_id,
                                               status2title[build.status])
@@ -294,17 +478,6 @@
             'href': self.env.href.build(config.name)
         }
 
-        slave_info = SlaveInfo(self.env, build.id)
-        req.hdf['build.slave'] = {
-            'name': build.slave,
-            'ip_address': slave_info.properties.get(SlaveInfo.IP_ADDRESS),
-            'os': slave_info.properties.get(SlaveInfo.OS_NAME),
-            'os.family': slave_info.properties.get(SlaveInfo.OS_FAMILY),
-            'os.version': slave_info.properties.get(SlaveInfo.OS_VERSION),
-            'machine': slave_info.properties.get(SlaveInfo.MACHINE),
-            'processor': slave_info.properties.get(SlaveInfo.PROCESSOR)
-        }
-
     def _build_to_hdf(self, build):
         hdf = {'name': build.slave, 'status': self._status_label[build.status],
                'rev': build.rev,
@@ -316,4 +489,13 @@
             hdf['stopped'] = strftime('%x %X', localtime(build.stopped))
             hdf['stopped_delta'] = pretty_timedelta(build.stopped)
             hdf['duration'] = pretty_timedelta(build.stopped, build.started)
+        hdf['slave'] = {
+            'name': build.slave,
+            'ip_address': build.slave_info.get(Build.IP_ADDRESS),
+            'os': build.slave_info.get(Build.OS_NAME),
+            'os.family': build.slave_info.get(Build.OS_FAMILY),
+            'os.version': build.slave_info.get(Build.OS_VERSION),
+            'machine': build.slave_info.get(Build.MACHINE),
+            'processor': build.slave_info.get(Build.PROCESSOR)
+        }
         return hdf
Copyright (C) 2012-2017 Edgewall Software