changeset 324:9275694cda61

Trackers: more static fields definition
author Sylvain Beucler <beuc@beuc.net>
date Sat, 21 Aug 2010 12:09:18 +0200
parents 01ffc3eb965a
children c193bb0a8819
files doc/savane3_trackers.txt doc/scripts/tracker_defsgen.py savane/tracker/defs.py savane/tracker/models.py
diffstat 4 files changed, 220 insertions(+), 129 deletions(-) [+]
line wrap: on
line diff
--- a/doc/savane3_trackers.txt
+++ b/doc/savane3_trackers.txt
@@ -1,1 +1,163 @@
-Cf. scripts/tracker_gendefs.py.
+The static definition in savane.trackers.defs is generated using
+scripts/tracker_gendefs.py.
+
+Trackers are described in Savane3 using a 3-level structure:
+
+- Field : definition
+
+- Field usage : additional definition (group=100) and partial overlay
+  (override values) infrastructure (group!=100)
+
+- Field value : possible values for dropdown (<select>) widgets
+  (default values and overlays)
+
+In trackers.defs we now have a single structure for field definition,
+which was actually static in Savane3 despite being stored in the
+database.  The database now only contains per-group optional overlays.
+In addition similar structures and values (common to
+bugs/patch/support/task) are only defined once.
+
+
+Here is a description of the Savane_3_ data structure, used as reference
+to build this new implementation:
+
+- Field
+
+    Site-wide field definitions for the 4 trackers: 70 fields each, +
+    2 more for 'task' ('planned_starting_date', 'planned_close_date'),
+    1 more for 'patch' ('revision tag' - but it's actually a bogus
+    entry in patch_field as there's nowhere to store its item value).
+
+    The field item values are stored in the 'bugs'/'patch'/etc. table,
+    which has 73 columns (1 for the IP address + 1 for spamscore + 1
+    col for each 72 Field - 1 for 'comment_type_id' which is not
+    stored).
+
+    Fields are not necessarily displayed or/and editable ('bug_id',
+    'group_id') but their definition may be used in other contexts,
+    such as list sort order.
+
+    Most fields cannot be redefined (such as display_type or
+    scope). Usually fields that can be redefined are in FieldUsage,
+    where an entry with special group_id=100 contains the default
+    value.
+
+    However some of the fields in this class ('display_size',
+    'empty_ok', and 'keep_history') can be redefined through similar
+    fields in FieldUsage - in which case it's not clear whether the
+    default value is:
+
+    - in Field(tracker_id, field_name)
+
+    - or FieldUsage(tracker_id, group_id=100, field_name)
+
+    At first glance it's defined here, all related values in
+    FieldUsage are set to NULL.
+
+    Field definition is almost identical for all trackers (59 fields /
+    73 have the same configuration).  They could be regrouped.
+
+    SELECT COUNT(*) FROM (
+            SELECT field_name FROM bugs_field
+      UNION SELECT field_name FROM patch_field
+      UNION SELECT field_name FROM support_field
+      UNION SELECT field_name FROM task_field
+    ) subquery;
+    => 73
+
+    SELECT COUNT(*), field_name FROM (
+            SELECT * FROM bugs_field
+      UNION SELECT * FROM patch_field
+      UNION SELECT * FROM support_field
+      UNION SELECT * FROM task_field
+    ) subquery
+    GROUP BY field_name HAVING COUNT(*) > 1;
+    -- Only slight differences in description , except for these 2:
+    +----------+---------------------+
+    | COUNT(*) | field_name          |
+    +----------+---------------------+
+    ...
+    |        4 | priority            | -- (required=1,empty_ok=0)(bugs,task) | (required=0,empty_ok=1)(patch,support)
+    |        2 | resolution_id       | -- (required=1)(bugs,task) | (required=0)(patch,support)
+    ...
+    +----------+---------------------+
+    14 rows in set (0.00 sec)
+    
+    So we could just set required=0,empty_ok=1 for those 2.
+
+- Field usage:
+
+    Field configuration overlay for each group
+    group == 100 means default
+
+
+    Differences between trackers for default values:
+
+    SELECT COUNT(*) FROM (
+            SELECT bug_field_id FROM bugs_field_usage
+      UNION SELECT bug_field_id FROM patch_field_usage
+      UNION SELECT bug_field_id FROM support_field_usage
+      UNION SELECT bug_field_id FROM task_field_usage
+    ) subquery;
+    => 73
+
+    SELECT bug_field_id, COUNT(*) FROM (
+            SELECT * FROM bugs_field_usage    WHERE group_id=100
+      UNION SELECT * FROM patch_field_usage   WHERE group_id=100
+      UNION SELECT * FROM support_field_usage WHERE group_id=100
+      UNION SELECT * FROM task_field_usage    WHERE group_id=100
+    ) subquery GROUP BY bug_field_id HAVING COUNT(*) > 2;
+    +--------------+----------+
+    | bug_field_id | COUNT(*) |
+    +--------------+----------+
+    |           92 |        2 | submitted_by
+    |          102 |        2 | severity
+    |          107 |        2 | bug_group_id
+    |          201 |        2 | platform_version_id
+    |          206 |        2 | hours
+    |          211 |        4 | priority
+    |          216 |        2 | originator_email
+    |          220 |        2 | percent_complete
+    +--------------+----------+
+
+     92 -> (use_it=0)(patch,support) | (use_it=1)(bugs,task)
+    102 -> (use_it,show_on_add,show_on_add_members=0)(patch,task) | (use_it,show_on_add,show_on_add_members=1)(bugs,support)
+    107 -> (show_on_add,show_on_add_members=0)(patch,support) | (show_on_add,show_on_add_members=1)(bugs,task) [but use_it=0]
+    201 -> (use_it,show_on_add,show_on_add_members=0)(bugs,patch,task) | (use_it,show_on_add,show_on_add_members=1)(support)
+    201 -> (use_it,show_on_add,show_on_add_members=0)(bugs,patch,support) | (use_it,show_on_add,show_on_add_members=1)(task)
+    211 -> (show_on_add,show_on_add_members,place)
+           bugs: 0,1,200
+           patch: 1,1,150
+           support: 0,0,150
+           task: 1,1,200
+    216 -> (use_it,show_on_add=0)(task) | (use_it=1,show_on_add=2)(bugs,patch,support)
+    220 -> (use_it,show_on_add_members=0)(bugs,patch,support) | (use_it,show_on_add_members=1)(task)
+
+- Field values:
+
+    Choices overlay for a select-box (SB) field of a specific group
+
+    Values that change between trackers:
+          SELECT bug_field_id,group_id,value_id,value FROM bugs_field_value WHERE group_id=100
+    UNION SELECT bug_field_id,group_id,value_id,value FROM patch_field_value WHERE group_id=100
+    UNION SELECT bug_field_id,group_id,value_id,value FROM support_field_value WHERE group_id=100
+    UNION SELECT bug_field_id,group_id,value_id,value FROM task_field_value WHERE group_id=100
+    ORDER BY bug_field_id,group_id,value_id;
+
+    |          108 |      100 |        1 | Fixed             | bugs
+    |          108 |      100 |        1 | Done              | patch,support,task
+    
+    |          108 |      100 |        3 | Wont Fix          | bugs
+    |          108 |      100 |        3 | Wont Do           | patch,support
+    |          108 |      100 |        3 | Cancelled         | task
+    
+    Savannah and Gna!-specific:
+    |          201 |      100 |      120 | Microsoft Windows | bugs,task
+    |          201 |      100 |      120 | *BSD              | support
+                                                               patch: (None)
+    |          201 |      100 |      130 | *BSD              | bugs,task
+    |          201 |      100 |      130 | Microsoft Windows | support
+                                                               patch: (None)
+
+    => Regroup: put "Wont Do" everywhere, "Done" everywhere, and
+       manually revert woe/bsd in support.items
--- a/doc/scripts/tracker_defsgen.py
+++ b/doc/scripts/tracker_defsgen.py
@@ -15,7 +15,7 @@
 defs = {}
 field_names = []
 c.execute("""SELECT * FROM bugs_field""")
-for row in c.fetchall():
+def process_row(row):
     name = row[1]
     field_names.append(name)
     defs[name] = ''
@@ -29,12 +29,29 @@
                 + ": "
             if tfields[i] == 'label' or tfields[i] == 'description':
                 defs[name] += '_("' + val + '"),'
+            elif (name=='priority' or name=='resolution_id' or name=='planned_starting_date' or name=='planned_close_date') \
+                    and tfields[i] == 'required':
+                # override priority.required so we have a common
+                # definition for all trackers
+                defs[name] += str(0)+","
+            elif (name=='priority' or name=='resolution_id') \
+                    and tfields[i] == 'empty_ok':
+                # override priority.empty_ok so we have a common
+                # definition for all trackers
+                defs[name] += str(1)+","
             elif type(val) == long:
                 defs[name] += str(val)+","
             else:
                 defs[name] += "'"+val+"',"
             defs[name] += "\n"
 
+for row in c.fetchall():
+    process_row(row)
+
+c.execute("""SELECT * FROM task_field WHERE field_name IN ('planned_starting_date', 'planned_close_date')""")
+for row in c.fetchall():
+    process_row(row)
+
 tfields = ['name','bug_field_id','group_id','use_it','show_on_add',
            'show_on_add_members','place','custom_label',
            'custom_description','custom_display_size',
--- a/savane/tracker/defs.py
+++ b/savane/tracker/defs.py
@@ -18,6 +18,8 @@
 
 from django.utils.translation import ugettext, ugettext_lazy as _
 
+fields = {}
+
 common = {
     'bug_id' : {
         'field_name': 'bug_id',
@@ -242,7 +244,7 @@
         'label': _("Status"),
         'description': _("Current resolution of the item"),
         'scope': 'P',
-        'required': 1,
+        'required': 0,
         'empty_ok': 1,
         'keep_history': 1,
         'special': 0,
@@ -494,8 +496,8 @@
         'label': _("Priority"),
         'description': _("How quickly the item should be handled"),
         'scope': 'S',
-        'required': 1,
-        'empty_ok': 0,
+        'required': 0,
+        'empty_ok': 1,
         'keep_history': 1,
         'special': 0,
         'custom': 0,
@@ -1279,4 +1281,33 @@
         'place': 800,
         'transition_default_auth': 'A',
     },
+    'planned_close_date' : {
+        'field_name': 'planned_close_date',
+        'display_type': 'DF',
+        'display_size': '10/15',
+        'label': _("Should be Finished on"),
+        'description': _("Date and time when the item should be completed"),
+        'scope': 'S',
+        'required': 0,
+        'empty_ok': 1,
+        'keep_history': 1,
+        'special': 0,
+        'custom': 0,
+    'planned_starting_date' : {
+        'field_name': 'planned_starting_date',
+        'display_type': 'DF',
+        'display_size': '10/15',
+        'label': _("Should Start On"),
+        'description': _("Date and time when someone should start working on the item"),
+        'scope': 'S',
+        'required': 0,
+        'empty_ok': 1,
+        'keep_history': 1,
+        'special': 0,
+        'custom': 0,
 }
+
+fields['bugs']    = common.copy()
+fields['patch']   = common.copy()
+fields['support'] = common.copy()
+fields['task']    = common.copy()
--- a/savane/tracker/models.py
+++ b/savane/tracker/models.py
@@ -140,57 +140,6 @@
 
 class Field(models.Model):
     """
-    Site-wide field definitions for the 4 trackers: 70 fields each, +
-    2 more for 'task' ('planned_starting_date', 'planned_close_date'),
-    1 more for 'patch' ('revision tag').
-
-    Most fields cannot be redefined (such as display_type or
-    scope). Usually fields that can be redefined are in FieldUsage,
-    where an entry with special group_id=100 contains the default
-    value.
-
-    However some of the fields in this class ('display_size',
-    'empty_ok', and 'keep_history') can be redefined through similar
-    fields in FieldUsage - in which case it's not clear whether the
-    default value is:
-
-    - in Field(tracker_id, field_name)
-
-    - or FieldUsage(tracker_id, group_id=100, field_name)
-
-    At first glance it's defined here, all related values in
-    FieldUsage are set to NULL.
-
-    Field definition is almost identical for all trackers (59 fields /
-    73 have the same configuration).  They could be regrouped.
-
-    SELECT COUNT(*) FROM (
-            SELECT field_name FROM bugs_field
-      UNION SELECT field_name FROM patch_field
-      UNION SELECT field_name FROM support_field
-      UNION SELECT field_name FROM task_field
-    ) subquery;
-    => 73
-
-    SELECT COUNT(*), field_name FROM (
-            SELECT * FROM bugs_field
-      UNION SELECT * FROM patch_field
-      UNION SELECT * FROM support_field
-      UNION SELECT * FROM task_field
-    ) subquery
-    GROUP BY field_name HAVING COUNT(*) > 1;
-    -- Only slight differences in description , except for these 2:
-    +----------+---------------------+
-    | COUNT(*) | field_name          |
-    +----------+---------------------+
-    ...
-    |        4 | priority            | -- (required=1,empty_ok=0)(bugs,task) | (required=0,empty_ok=1)(patch,support)
-    |        2 | resolution_id       | -- (required=1)(bugs,task) | (required=0)(patch,support)
-    ...
-    +----------+---------------------+
-    14 rows in set (0.00 sec)
-    
-    So we could just set required=0,empty_ok=1 for those 2.
     """
     class Meta:
         unique_together = (('tracker', 'name'),)
@@ -225,7 +174,7 @@
     # Default value (fields can always override this except for 'summary' and 'details', cf. 'special')
     empty_ok = models.CharField(max_length=1, choices=EMPTY_OK_CHOICES,
                                 default='0')
-    # Default value + Field may store history changes
+    # Default value
     keep_history = models.BooleanField()
     # Field cannot be made optional (displayed unless 'bug_id' and 'group_id')
     # Also, field are not displayed (filled by the system) - except for 'summary', 'comment_type' and 'details'
@@ -237,53 +186,9 @@
     def __unicode__(self):
         return "%s.%s" % (self.tracker_id, self.name)
 
-class FieldUsage(models.Model):
+class FieldOverlay(models.Model):
     """
-    Field configuration overlay for each group
-    group == NULL means default
-
-
-    Differences between trackers for default values:
-
-    SELECT COUNT(*) FROM (
-            SELECT bug_field_id FROM bugs_field_usage
-      UNION SELECT bug_field_id FROM patch_field_usage
-      UNION SELECT bug_field_id FROM support_field_usage
-      UNION SELECT bug_field_id FROM task_field_usage
-    ) subquery;
-    => 73
-
-    SELECT bug_field_id, COUNT(*) FROM (
-            SELECT * FROM bugs_field_usage    WHERE group_id=100
-      UNION SELECT * FROM patch_field_usage   WHERE group_id=100
-      UNION SELECT * FROM support_field_usage WHERE group_id=100
-      UNION SELECT * FROM task_field_usage    WHERE group_id=100
-    ) subquery GROUP BY bug_field_id HAVING COUNT(*) > 2;
-    +--------------+----------+
-    | bug_field_id | COUNT(*) |
-    +--------------+----------+
-    |           92 |        2 | submitted_by
-    |          102 |        2 | severity
-    |          107 |        2 | bug_group_id
-    |          201 |        2 | platform_version_id
-    |          206 |        2 | hours
-    |          211 |        4 | priority
-    |          216 |        2 | originator_email
-    |          220 |        2 | percent_complete
-    +--------------+----------+
-
-     92 -> (use_it=0)(patch,support) | (use_it=1)(bugs,task)
-    102 -> (use_it,show_on_add,show_on_add_members=0)(patch,task) | (use_it,show_on_add,show_on_add_members=1)(bugs,support)
-    107 -> (show_on_add,show_on_add_members=0)(patch,support) | (show_on_add,show_on_add_members=1)(bugs,task) [but use_it=0]
-    201 -> (use_it,show_on_add,show_on_add_members=0)(bugs,patch,task) | (use_it,show_on_add,show_on_add_members=1)(support)
-    201 -> (use_it,show_on_add,show_on_add_members=0)(bugs,patch,support) | (use_it,show_on_add,show_on_add_members=1)(task)
-    211 -> (show_on_add,show_on_add_members,place)
-           bugs: 0,1,200
-           patch: 1,1,150
-           support: 0,0,150
-           task: 1,1,200
-    216 -> (use_it,show_on_add=0)(task) | (use_it=1,show_on_add=2)(bugs,patch,support)
-    220 -> (use_it,show_on_add_members=0)(bugs,patch,support) | (use_it,show_on_add_members=1)(task)
+    Per-group tracker item definition override
     """
     class Meta:
         unique_together = (('field', 'group'),)
@@ -325,6 +230,7 @@
     custom_display_size = models.CharField(max_length=255, blank=True, null=True)
       # The default value is in Field.display_size
       #   rather than FieldUsage(group_id=100).custom_display_size
+    # If !Field.special
     custom_keep_history = models.BooleanField(_("keep field value changes in history"))
 
     # If Field.custom
@@ -334,32 +240,7 @@
 
 class FieldValue(models.Model):
     """
-    Choices overlay for a select-box (SB) field of a specific group
-
-    Values that change between trackers:
-          SELECT bug_field_id,group_id,value_id,value FROM bugs_field_value WHERE group_id=100
-    UNION SELECT bug_field_id,group_id,value_id,value FROM patch_field_value WHERE group_id=100
-    UNION SELECT bug_field_id,group_id,value_id,value FROM support_field_value WHERE group_id=100
-    UNION SELECT bug_field_id,group_id,value_id,value FROM task_field_value WHERE group_id=100
-    ORDER BY bug_field_id,group_id,value_id;
-
-    |          108 |      100 |        1 | Fixed             | bugs
-    |          108 |      100 |        1 | Done              | patch,support,task
-    
-    |          108 |      100 |        3 | Wont Fix          | bugs
-    |          108 |      100 |        3 | Wont Do           | patch,support
-    |          108 |      100 |        3 | Cancelled         | task
-    
-    Savannah and Gna!-specific:
-    |          201 |      100 |      120 | Microsoft Windows | bugs,task
-    |          201 |      100 |      120 | *BSD              | support
-                                                               patch: (None)
-    |          201 |      100 |      130 | *BSD              | bugs,task
-    |          201 |      100 |      130 | Microsoft Windows | support
-                                                               patch: (None)
-
-    => Regroup: put "Wont Do" everywhere, "Done" everywhere, and
-       manually revert woe/bsd in support.items
+    Per-group tracker select box values override
     """
     class Meta:
         unique_together = (('field', 'group', 'value_id'),)