changeset 332:7234fcf49cc0

De-static-fy part of the track fields definition + data structure improvements
author Sylvain Beucler <beuc@beuc.net>
date Sun, 22 Aug 2010 16:53:15 +0200
parents e29de6a47cc1
children 1de0e15f60d6
files doc/savane3_trackers.txt doc/scripts/tracker_defsgen.py doc/scripts/tracker_gendefs.py migrate_old_savane.sql savane/tracker/admin.py savane/tracker/defs.py savane/tracker/models.py
diffstat 7 files changed, 426 insertions(+), 1059 deletions(-) [+]
line wrap: on
line diff
--- a/doc/savane3_trackers.txt
+++ b/doc/savane3_trackers.txt
@@ -11,14 +11,9 @@
 - 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.
-
-EDIT: it happens that the Savannah database has some inconsistency and
-differs in the definition of 4 fields for the bugs tracker:
+In tracker.models we now have a single structure for field definition,
+which is pretty much static in Savane3 except that Savannah has
+historical inconsistencies:
 
 mysql> SELECT bug_field_id, group_id, use_it, show_on_add, show_on_add_members, place, id FROM temp_bugs_field_usage WHERE id IN (SELECT id FROM (SELECT B.id FROM temp_bugs_field_usage A, temp_bugs_field_usage B WHERE A.bug_field_id = B.bug_field_id AND A.group_id = B.group_id AND (A.use_it != B.use_it OR A.show_on_add != B.show_on_add OR A.show_on_add_members != B.show_on_add_members OR A.place != B.place)) AS temp) AND group_id=100 ORDER BY bug_field_id;
 +--------------+----------+--------+-------------+---------------------+-------+-----+
@@ -46,14 +41,13 @@
 +--------------+------------------+----------+
 4 rows in set (0.00 sec)
 
-In addition, if we want to convert trackers from other forges, we may
-need the extra flexibility of defining site-wide default for a given
-tracker.
+Not: if we want to convert trackers from other forges, we may need the
+extra flexibility of defining site-wide default for a given tracker.
 
-Consequently we'll probably have to revert to in-base definition of
-tracker defaults.
+Consequently let's keep in-base definition of tracker defaults.  Field
+definition is static until the field themselves are dynamic (if ever).
 
-However, merging the fields definition and their default values was
+Merging the fields definition and their default values was still
 beneficial as it allowed reworking the data structure and removing
 duplicate columns (display/custom_display, etc.).
 
@@ -124,7 +118,7 @@
     +----------+---------------------+
     14 rows in set (0.00 sec)
     
-    So we just set required=0,empty_ok=1 for those 2.
+    So we could just set required=0,empty_ok=1 for those 2.
 
 - Field usage:
 
@@ -200,5 +194,5 @@
     |          201 |      100 |      130 | Microsoft Windows | support
                                                                patch: (None)
 
-    => Regroup: put "Wont Do" everywhere, "Done" everywhere, and
+    => To regroup: put "Wont Do" everywhere, "Done" everywhere, and
        manually revert woe/bsd in support.items
deleted file mode 100644
--- a/doc/scripts/tracker_defsgen.py
+++ /dev/null
@@ -1,144 +0,0 @@
-# Output a Python dict with tracker fields definition, using a Savane3
-# initialized database as source
-
-import MySQLdb
-#import MySQLdb.cursors 
-
-db = MySQLdb.connect(unix_socket='/tmp/savane-mini/mysql/sock',
-                     user='root',db='savane')
-c=db.cursor()
-
-tfields = ['bug_field_id','field_name','display_type','display_size',
-           'label','description','scope','required','empty_ok','keep_history',
-           'special','custom']
-
-defs = {}
-field_names = []
-complex_defs = {}
-
-def process_field_row(row):
-    name = row[1]
-    field_names.append(name)
-    defs[name] = ''
-    defs[name] += "    '"+name+"' : {\n"
-    for i,val in enumerate(row):
-        if i <= 0 \
-                or (complex_defs[name]['display_type'] not in ('TA', 'TF') and tfields[i] == 'display_size'):
-            continue
-        else:
-            defs[name] += "        " \
-                + "'"+tfields[i]+"'" \
-                + ": "
-            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"
-
-query = """SELECT * FROM bugs_field"""
-c.execute(query)
-for row in c.fetchall():
-    name = row[1]
-    complex_defs[name] = {}
-    for i,val in enumerate(row):
-        complex_defs[name][tfields[i]] = val
-complex_defs['priority']['required'] = 0
-complex_defs['resolution_id']['required'] = 0
-c.execute(query)
-for row in c.fetchall():
-    process_field_row(row)
-
-query = """SELECT * FROM task_field WHERE field_name IN ('planned_starting_date', 'planned_close_date')"""
-c.execute(query)
-for row in c.fetchall():
-    name = row[1]
-    complex_defs[name] = {}
-    for i,val in enumerate(row):
-        complex_defs[name][tfields[i]] = val
-complex_defs['planned_starting_date']['required'] = 0
-complex_defs['planned_close_date']['required'] = 0
-c.execute(query)
-for row in c.fetchall():
-    process_field_row(row)
-
-
-# Doc:
-#SHOW_ON_ADD_CHOICES = (('0', _('no')),
-#                       ('1', _('show to logged in users')),
-#                       ('2', _('show to anonymous users')),
-#                       ('3', _('show to both logged in and anonymous users')),)
-
-tfields = ['name','bug_field_id','group_id','use_it','show_on_add',
-           'show_on_add_members','place','custom_label',
-           'custom_description','custom_display_size',
-           'custom_empty_ok','custom_keep_history',
-           'transition_default_auth']
-tfields[6] = 'rank' 
-
-def process_field_usage_row(row):
-    name = row[0]
-    for i,val in enumerate(row):
-        if i <= 2:
-            continue
-        elif tfields[i] == 'custom_label' \
-                or tfields[i] == 'custom_description' \
-                or tfields[i] == 'custom_display_size' \
-                or tfields[i] == 'custom_empty_ok' \
-                or tfields[i] == 'custom_keep_history' \
-                or (complex_defs[name]['required'] == 1 and tfields[i] == 'use_it') \
-                or (complex_defs[name]['display_type'] != 'SB' and tfields[i] == 'transition_default_auth') \
-                :
-            # overlays, duplicates of bugs_field in this context
-            continue
-        elif tfields[i] == 'show_on_add':
-            if val == 0:
-                defs[name] += "        'show_on_add_anonymous': 0,\n"
-                defs[name] += "        'show_on_add_connected': 0,\n"
-            elif val == 1:
-                defs[name] += "        'show_on_add_anonymous': 0,\n"
-                defs[name] += "        'show_on_add_connected': 1,\n"
-            elif val == 2:
-                defs[name] += "        'show_on_add_anonymous': 1,\n"
-                defs[name] += "        'show_on_add_connected': 0,\n"
-            elif val == 3:
-                defs[name] += "        'show_on_add_anonymous': 1,\n"
-                defs[name] += "        'show_on_add_connected': 1,\n"
-        else:
-            defs[name] += "        " \
-                + "'"+tfields[i]+"'" \
-                + ": "
-            if type(val) == long:
-                defs[name] += str(val)+","
-            elif val is None:
-                defs[name] += "None,"
-            else:
-                defs[name] += "'"+val+"',"
-            defs[name] += "\n"
-    defs[name] += "    },\n"
-
-c.execute("""SELECT bugs_field.field_name,bugs_field_usage.*
-  FROM bugs_field_usage JOIN bugs_field USING (bug_field_id) WHERE group_id=100""")
-for row in c.fetchall():
-    process_field_usage_row(row)
-
-c.execute("""SELECT task_field.field_name,task_field_usage.*
-  FROM task_field_usage JOIN task_field USING (bug_field_id) WHERE group_id=100
-  AND field_name IN ('planned_starting_date', 'planned_close_date')""")
-for row in c.fetchall():
-    process_field_usage_row(row)
-
-
-for name in field_names:
-    print defs[name],
new file mode 100644
--- /dev/null
+++ b/doc/scripts/tracker_gendefs.py
@@ -0,0 +1,145 @@
+# Output a Python dict with tracker fields definition, using a Savane3
+# initialized database as source
+
+import MySQLdb
+#import MySQLdb.cursors 
+
+db = MySQLdb.connect(unix_socket='/tmp/savane-mini/mysql/sock',
+                     user='root',db='savane')
+c=db.cursor()
+
+tfields = ['bug_field_id','field_name','display_type','scope','required','special','custom']
+
+defs = {}
+field_names = []
+complex_defs = {}
+
+def process_field_row(row):
+    name = row[1]
+    field_names.append(name)
+    defs[name] = ''
+    defs[name] += "    '"+name+"' : {\n"
+    for i,val in enumerate(row):
+        if i <= 0 \
+                or (complex_defs[name]['display_type'] not in ('TA', 'TF') and tfields[i] == 'display_size'):
+            continue
+        else:
+            defs[name] += "        " \
+                + "'"+tfields[i]+"'" \
+                + ": "
+            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"
+    defs[name] += "    },\n"
+
+query = "SELECT " + ",".join(tfields) + " FROM bugs_field"
+c.execute(query)
+for row in c.fetchall():
+    name = row[1]
+    complex_defs[name] = {}
+    for i,val in enumerate(row):
+        complex_defs[name][tfields[i]] = val
+complex_defs['priority']['required'] = 0
+complex_defs['resolution_id']['required'] = 0
+c.execute(query)
+for row in c.fetchall():
+    process_field_row(row)
+
+query = "SELECT " + ",".join(tfields) +" FROM task_field WHERE field_name IN ('planned_starting_date', 'planned_close_date')"
+c.execute(query)
+for row in c.fetchall():
+    name = row[1]
+    complex_defs[name] = {}
+    for i,val in enumerate(row):
+        complex_defs[name][tfields[i]] = val
+complex_defs['planned_starting_date']['required'] = 0
+complex_defs['planned_close_date']['required'] = 0
+c.execute(query)
+for row in c.fetchall():
+    process_field_row(row)
+
+
+for name in field_names:
+    print defs[name],
+
+
+# The following is now stored in the DB
+
+# Doc:
+#SHOW_ON_ADD_CHOICES = (('0', _('no')),
+#                       ('1', _('show to logged in users')),
+#                       ('2', _('show to anonymous users')),
+#                       ('3', _('show to both logged in and anonymous users')),)
+
+#tfields = ['name','bug_field_id','group_id','use_it','show_on_add',
+#           'show_on_add_members','place','custom_label',
+#           'custom_description','custom_display_size',
+#           'custom_empty_ok','custom_keep_history',
+#           'transition_default_auth']
+#tfields[6] = 'rank' 
+#
+#def process_field_usage_row(row):
+#    name = row[0]
+#    for i,val in enumerate(row):
+#        if i <= 2:
+#            continue
+#        elif tfields[i] == 'custom_label' \
+#                or tfields[i] == 'custom_description' \
+#                or tfields[i] == 'custom_display_size' \
+#                or tfields[i] == 'custom_empty_ok' \
+#                or tfields[i] == 'custom_keep_history' \
+#                or (complex_defs[name]['required'] == 1 and tfields[i] == 'use_it') \
+#                or (complex_defs[name]['display_type'] != 'SB' and tfields[i] == 'transition_default_auth') \
+#                :
+#            # overlays, duplicates of bugs_field in this context
+#            continue
+#        elif tfields[i] == 'show_on_add':
+#            if val == 0:
+#                defs[name] += "        'show_on_add_anonymous': 0,\n"
+#                defs[name] += "        'show_on_add_connected': 0,\n"
+#            elif val == 1:
+#                defs[name] += "        'show_on_add_anonymous': 0,\n"
+#                defs[name] += "        'show_on_add_connected': 1,\n"
+#            elif val == 2:
+#                defs[name] += "        'show_on_add_anonymous': 1,\n"
+#                defs[name] += "        'show_on_add_connected': 0,\n"
+#            elif val == 3:
+#                defs[name] += "        'show_on_add_anonymous': 1,\n"
+#                defs[name] += "        'show_on_add_connected': 1,\n"
+#        else:
+#            defs[name] += "        " \
+#                + "'"+tfields[i]+"'" \
+#                + ": "
+#            if type(val) == long:
+#                defs[name] += str(val)+","
+#            elif val is None:
+#                defs[name] += "None,"
+#            else:
+#                defs[name] += "'"+val+"',"
+#            defs[name] += "\n"
+#    defs[name] += "    },\n"
+#
+#c.execute("""SELECT bugs_field.field_name,bugs_field_usage.*
+#  FROM bugs_field_usage JOIN bugs_field USING (bug_field_id) WHERE group_id=100""")
+#for row in c.fetchall():
+#    process_field_usage_row(row)
+#
+#c.execute("""SELECT task_field.field_name,task_field_usage.*
+#  FROM task_field_usage JOIN task_field USING (bug_field_id) WHERE group_id=100
+#  AND field_name IN ('planned_starting_date', 'planned_close_date')""")
+#for row in c.fetchall():
+#    process_field_usage_row(row)
--- a/migrate_old_savane.sql
+++ b/migrate_old_savane.sql
@@ -490,6 +490,126 @@
 INSERT INTO tracker_tracker (`name`) VALUES ('support');
 INSERT INTO tracker_tracker (`name`) VALUES ('task');
 
+-- Currently done statically (tracker.defs)
+-- -- id <- (none - using name instead)
+-- -- tracker_id <- (none - definitions are now site-wide)
+-- -- name <- field_name
+-- -- other columns moved to FieldOverlay
+-- TRUNCATE tracker_field;
+-- INSERT INTO tracker_field
+--     (name, display_type, scope, required, special, custom)
+--   SELECT
+--       field_name, display_type, scope, required, special, custom
+--     FROM savane_old.bugs_field;
+-- INSERT INTO tracker_field
+--     (name, display_type, scope, required, special, custom)
+--   SELECT
+--       field_name, display_type, scope, required, special, custom
+--     FROM savane_old.task_field WHERE field_name IN ('planned_starting_date', 'planned_close_date');
+-- -- Homogeneify field definitions
+-- UPDATE tracker_field SET required=0
+--   WHERE name IN ('priority', 'resolution_id', 'planned_starting_date', 'planned_close_date');
+
+-- Get rid of field_usage duplicates (old mysql/php/savane bug?)
+-- It only affected group_id=100, maybe the installation was done
+-- twice or something.
+-- Give priority to the last one.
+-- Need to create a real table - a temporary one has issues with being "reopened" in joins
+-- The following produces a warning that and MySQL employees can't
+-- understand that it's a problem - commenting:
+-- (http://bugs.mysql.com/bug.php?id=2839)
+-- DROP TABLE IF EXISTS temp_bugs_field_usage;
+CREATE TABLE temp_bugs_field_usage AS SELECT * FROM savane_old.bugs_field_usage;
+ALTER TABLE temp_bugs_field_usage ADD (id INT auto_increment PRIMARY KEY);
+DELETE FROM temp_bugs_field_usage
+  WHERE id IN (
+    SELECT id FROM (
+      SELECT B.id FROM temp_bugs_field_usage A, temp_bugs_field_usage B
+        WHERE A.id > B.id
+          AND A.bug_field_id = B.bug_field_id AND A.group_id = B.group_id
+      ) AS temp
+    );
+CREATE TABLE temp_task_field_usage AS SELECT * FROM savane_old.task_field_usage;
+ALTER TABLE temp_task_field_usage ADD (id INT auto_increment PRIMARY KEY);
+DELETE FROM temp_task_field_usage
+  WHERE id IN (
+    SELECT id FROM (
+      SELECT B.id FROM temp_task_field_usage A, temp_task_field_usage B
+        WHERE A.id > B.id
+          AND A.bug_field_id = B.bug_field_id AND A.group_id = B.group_id
+      ) AS temp
+    );
+-- id <- <auto>
+-- field <- field_name
+-- Don't import default values, they are now defined statically in
+-- tracker.defs.
+TRUNCATE tracker_fieldoverlay;
+-- Default values:
+INSERT INTO tracker_fieldoverlay
+    (tracker_id, group_id, field, use_it, show_on_add_anonymous, show_on_add_connected, show_on_add_members, rank,
+     label, description, display_size, empty_ok,
+     keep_history, transition_default_auth)
+  SELECT
+      'bugs', NULL, field_name, use_it, IF(show_on_add<2,0,1), IF(show_on_add%2=0,0,1), show_on_add_members, place,
+      label, description, custom_display_size, custom_empty_ok,
+      keep_history, transition_default_auth
+    FROM temp_bugs_field_usage JOIN savane_old.bugs_field
+        USING (bug_field_id)
+      WHERE group_id = 100;
+INSERT INTO tracker_fieldoverlay
+    (tracker_id, group_id, field, use_it, show_on_add_anonymous, show_on_add_connected, show_on_add_members, rank,
+     label, description, display_size, empty_ok,
+     keep_history, transition_default_auth)
+  SELECT
+      'bugs', NULL, field_name, 0, IF(show_on_add<2,0,1), IF(show_on_add%2=0,0,1), show_on_add_members, place,
+      label, description, custom_display_size, custom_empty_ok,
+      keep_history, transition_default_auth
+    FROM temp_task_field_usage JOIN savane_old.task_field
+        USING (bug_field_id)
+      WHERE group_id = 100 AND field_name IN ('planned_starting_date', 'planned_close_date');
+-- Group-specific values (JOINed to get 'field_name'):
+INSERT INTO tracker_fieldoverlay
+    (tracker_id, group_id, field, use_it, show_on_add_anonymous, show_on_add_connected, show_on_add_members, rank,
+     label, description, display_size, empty_ok,
+     keep_history, transition_default_auth)
+  SELECT
+      'bugs', group_id, field_name, use_it, IF(show_on_add<2,0,1), IF(show_on_add%2=0,0,1), show_on_add_members, place,
+      custom_label, custom_description, custom_display_size, custom_empty_ok,
+      IFNULL(custom_keep_history, 0), transition_default_auth
+    FROM temp_bugs_field_usage JOIN savane_old.bugs_field
+        USING (bug_field_id)
+      WHERE group_id != 100;
+DROP TABLE temp_bugs_field_usage;
+DROP TABLE temp_task_field_usage;
+
+
+-- Get rid of field_value duplicates (old mysql/php/savane bug?)
+-- Apparently this affects 'None' values.
+-- Give priority to the last one (arbitrarily).
+DELETE FROM savane_old.bugs_field_value
+  WHERE bug_fv_id IN (
+    SELECT bug_fv_id FROM (
+      SELECT B.bug_fv_id FROM savane_old.bugs_field_value A, savane_old.bugs_field_value B
+        WHERE A.bug_fv_id > B.bug_fv_id
+          AND A.bug_field_id = B.bug_field_id AND A.group_id = B.group_id AND A.value_id = B.value_id
+      ) AS temp
+  );
+-- fieldchoice <- field_value
+-- id <- <auto>
+-- field <- field_name
+TRUNCATE tracker_fieldchoice;
+INSERT INTO tracker_fieldchoice
+    (tracker_id, group_id, field, value_id, `value`, description,
+     rank, status, email_ad, send_all_flag)
+  SELECT
+     'bugs', group_id, field_name, value_id, `value`, savane_old.bugs_field_value.description,
+     order_id, status, email_ad, send_all_flag
+   FROM savane_old.bugs_field_value JOIN savane_old.bugs_field
+      USING (bug_field_id);
+-- Specify "default" differently
+UPDATE tracker_fieldchoice SET group_id=NULL WHERE group_id=100;
+
+
 TRUNCATE tracker_item;
 INSERT INTO tracker_item
     (tracker_id, public_bugs_id, group_id, status_id, severity, privacy,
@@ -535,67 +655,3 @@
 -- Specify "default" differently
 UPDATE tracker_item SET assigned_to_id=NULL WHERE assigned_to_id=100;
 UPDATE tracker_item SET submitted_by_id=NULL WHERE submitted_by_id=100;
-
--- Get rid of field_usage duplicates (old mysql/php/savane bug?)
--- It only affected group_id=100, maybe the installation was done
--- twice or something.
--- Give priority to the last one.
--- Need to create a real table - a temporary one has issues with being "reopened" in joins
--- The following produces a warning that and MySQL employees can't
--- understand that it's a problem - commenting:
--- (http://bugs.mysql.com/bug.php?id=2839)
--- DROP TABLE IF EXISTS temp_bugs_field_usage;
-CREATE TABLE temp_bugs_field_usage AS SELECT * FROM savane_old.bugs_field_usage;
-ALTER TABLE temp_bugs_field_usage ADD (id INT auto_increment PRIMARY KEY);
-DELETE FROM temp_bugs_field_usage
-  WHERE id IN (
-    SELECT id FROM (
-      SELECT B.id FROM temp_bugs_field_usage A, temp_bugs_field_usage B
-        WHERE A.id > B.id
-          AND A.bug_field_id = B.bug_field_id AND A.group_id = B.group_id
-      ) AS temp
-    );
--- id <- <auto>
--- field_id <- bug_field_id
--- Don't import default values, they are now defined statically in
--- tracker.defs.
-TRUNCATE tracker_fieldoverlay;
-INSERT INTO tracker_fieldoverlay
-    (tracker_id, group_id, field_name, use_it, show_on_add_anonymous, show_on_add_connected, show_on_add_members, rank,
-     label, description, display_size, empty_ok,
-     keep_history, transition_default_auth)
-  SELECT
-      'bugs', group_id, field_name, use_it, IF(show_on_add<2,0,1), IF(show_on_add%2=0,0,1), show_on_add_members, place,
-      custom_label, custom_description, custom_display_size, custom_empty_ok,
-      IFNULL(custom_keep_history, 0), transition_default_auth
-    FROM temp_bugs_field_usage JOIN savane_old.bugs_field
-        USING (bug_field_id)
-      WHERE group_id != 100;
-DROP TABLE temp_bugs_field_usage;
--- Specify "default" differently
--- UPDATE tracker_fieldoverlay SET group_id=NULL WHERE group_id=100;
-
--- Get rid of field_value duplicates (old mysql/php/savane bug?)
--- Apparently this affects 'None' values.
--- Give priority to the last one (arbitrarily).
-DELETE FROM savane_old.bugs_field_value
-  WHERE bug_fv_id IN (
-    SELECT bug_fv_id FROM (
-      SELECT B.bug_fv_id FROM savane_old.bugs_field_value A, savane_old.bugs_field_value B
-        WHERE A.bug_fv_id > B.bug_fv_id
-          AND A.bug_field_id = B.bug_field_id AND A.group_id = B.group_id AND A.value_id = B.value_id
-      ) AS temp
-  );
--- id <- <auto>
--- field_id <- bug_field_id
-TRUNCATE tracker_fieldvalue;
-INSERT INTO tracker_fieldvalue
-    (tracker_id, group_id, field_name, value_id, `value`, description,
-     rank, status, email_ad, send_all_flag)
-  SELECT
-     'bugs', group_id, field_name, value_id, `value`, savane_old.bugs_field_value.description,
-     order_id, status, email_ad, send_all_flag
-   FROM savane_old.bugs_field_value JOIN savane_old.bugs_field
-      USING (bug_field_id);
--- Specify "default" differently
-UPDATE tracker_fieldvalue SET group_id=NULL WHERE group_id=100;
--- a/savane/tracker/admin.py
+++ b/savane/tracker/admin.py
@@ -20,17 +20,38 @@
 from django.utils.translation import ugettext, ugettext_lazy as _
 from models import *
 
-# It's not sensible to edit this, it's only a PK
-#class TrackerAdmin(admin.ModelAdmin):
-#    list_display  = ('name',)
+class TrackerAdmin(admin.ModelAdmin):
+    list_display  = ('name',)
+
+# class FieldOverlayInline(admin.TabularInline):
+#     model = FieldOverlay
+#     raw_id_fields = ('group',)
+# class FieldAdmin(admin.ModelAdmin):
+#     search_fields = ('name', 'label', 'description', )
+#     ordering = ('name', )
+#     list_display  = ('name', 'scope', 'required', 'special', 'custom', )
+#     list_display_links = ('name', )
+#     list_filter = ('display_type', 'scope', 'required', 'special', 'custom', )
+#     inlines = ( FieldOverlayInline, )
+#admin.site.register(Field, FieldAdmin)
 
 class FieldOverlayAdmin(admin.ModelAdmin):
     search_fields = ('group', 'label', 'description', )
-    ordering = ('group', 'field_name',)
-    list_display  = ('id', 'group', 'field_name', 'use_it', 'rank', )
+    ordering = ('group', 'field',)
+    list_display  = ('id', 'tracker', 'group', 'field', 'use_it', 'rank', )
     list_display_links = ('id',)
     list_filter = ('use_it', 'show_on_add_anonymous', 'show_on_add_connected', 'show_on_add_members',
                    'empty_ok', 'keep_history',)
     raw_id_fields = ('group',)
 
+class FieldChoiceAdmin(admin.ModelAdmin):
+    search_fields = ('group', 'value_id', 'value', 'description', )
+    ordering = ('group', 'field', 'tracker', 'rank')
+    list_display  = ('id', 'tracker', 'group', 'field', 'value_id', 'value', 'status', 'rank')
+    list_display_links = ('id',)
+    list_filter = ('status',)
+    raw_id_fields = ('group',)
+
+ 
 admin.site.register(FieldOverlay, FieldOverlayAdmin)
+admin.site.register(FieldChoice, FieldChoiceAdmin)
--- a/savane/tracker/defs.py
+++ b/savane/tracker/defs.py
@@ -20,11 +20,11 @@
 The Field definition cannot be changed, so it's been made static
 instead of stored in the database.
 
-Here's the previous model definition.  See also FieldUsage, which is
+Here's the previous model definition.  See also FieldOverlay, which is
 still used for non-default (i.e. override) values.
 
+class Field(models.Model):
     class Meta:
-        unique_together = (('tracker', 'name'),)
         verbose_name = _("field")
         verbose_name_plural = _("fields")
 
@@ -32,1370 +32,659 @@
                             ('SB', _('select box')),
                             ('TA', _('text area')),
                             ('TF', _('text field')),)
-    SCOPE_CHOICES = (('S', _('system')), # user cannot modify related FieldValue's (TF)
-                     ('P', _('project')),)  # user can modify related FieldValue's (TF)
+    SCOPE_CHOICES = (('S', _('system')), # user cannot modify related FieldChoice's (TF)
+                     ('P', _('project')),)  # user can modify related FieldChoice's (TF)
 
-    tracker = models.ForeignKey('Tracker')
-    name = models.CharField(max_length=255, db_index=True)
+    name = models.CharField(max_length=32, db_index=True, primary_key=True)
+
+    # Data type
     display_type = models.CharField(max_length=255, choices=DISPLAY_TYPE_CHOICES)
-    display_size = models.CharField(max_length=255)
-      # DF: unused
-      # SB: unused
-      # TA: cols/rows
-      # TF: visible_length/max_length
-    label  = models.CharField(max_length=255)
-    description = models.TextField()
 
-    # Field values can be changed (if TF)
+    # Field values can be added (if SB)
     scope = models.CharField(max_length=1, choices=SCOPE_CHOICES)
-    # Field cannot be hidden (but can be made optional)
-    required = models.BooleanField(help_text=_("field cannot be disabled in configuration"))
-    # 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
-    keep_history = models.BooleanField()
+    # Field cannot be hidden (but can be made optional) (except if special: might be hidden and managed by code)
+    required = models.BooleanField(help_text=_("field cannot be disabled in group configuration"))
+
     # 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'
     # (consequently, they cannot be customized in any way, except for 'summary' and 'details' where you can only customize the display size)
     special = models.BooleanField()
     # Field may change label and description
     custom = models.BooleanField(help_text=_("let the user change the label and description"))
+
+    def __unicode__(self):
+        "For admin interface"
+        return self.name
 """
 
 from django.utils.translation import ugettext, ugettext_lazy as _
 from copy import deepcopy
 
-default_fields = {}
-
-common = {
+field_defs = {
     'bug_id' : {
         'field_name': 'bug_id',
         'display_type': 'TF',
-        'display_size': '6/10',
-        'label': _("Item ID"),
-        'description': _("Unique item identifier"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 0,
-        'keep_history': 0,
         'special': 1,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 10,
     },
     'group_id' : {
         'field_name': 'group_id',
         'display_type': 'TF',
-        'display_size': '',
-        'label': _("Group ID"),
-        'description': _("Unique project identifier"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 0,
-        'keep_history': 0,
         'special': 1,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 1,
-        'show_on_add_members': 1,
-        'rank': 30,
     },
     'submitted_by' : {
         'field_name': 'submitted_by',
         'display_type': 'SB',
-        'label': _("Submitted by"),
-        'description': _("User who originally submitted the item"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 1,
-        'keep_history': 0,
         'special': 1,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 20,
-        'transition_default_auth': 'A',
     },
     'date' : {
         'field_name': 'date',
         'display_type': 'DF',
-        'label': _("Submitted on"),
-        'description': _("Date and time of the initial submission"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 0,
-        'keep_history': 0,
         'special': 1,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40,
     },
     'close_date' : {
         'field_name': 'close_date',
         'display_type': 'DF',
-        'label': _("Closed on"),
-        'description': _("Date and time when the item  status was changed to 'Closed'"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 1,
-        'keep_history': 0,
         'special': 1,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50,
     },
     'status_id' : {
         'field_name': 'status_id',
         'display_type': 'SB',
-        'label': _("Open/Closed"),
-        'description': _("Most basic status"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 0,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 600,
-        'transition_default_auth': 'A',
     },
     'severity' : {
         'field_name': 'severity',
         'display_type': 'SB',
-        'label': _("Severity"),
-        'description': _("Impact of the item on the system (Critical, Major,...)"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 1,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 1,
-        'show_on_add_members': 1,
-        'rank': 200,
-        'transition_default_auth': 'A',
     },
     'category_id' : {
         'field_name': 'category_id',
         'display_type': 'SB',
-        'label': _("Category"),
-        'description': _("Generally high level modules or functionalities of the software (e.g. User interface, Configuration Manager, etc)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 1,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 1,
-        'show_on_add_members': 1,
-        'rank': 100,
-        'transition_default_auth': 'A',
     },
     'assigned_to' : {
         'field_name': 'assigned_to',
         'display_type': 'SB',
-        'label': _("Assigned to"),
-        'description': _("Who is in charge of handling this item"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 1,
-        'rank': 500,
-        'transition_default_auth': 'A',
     },
     'summary' : {
         'field_name': 'summary',
         'display_type': 'TF',
-        'display_size': '65/120',
-        'label': _("Summary"),
-        'description': _("One line description of the item"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 0,
-        'keep_history': 1,
         'special': 1,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 1,
-        'show_on_add_members': 1,
-        'rank': 700000,
     },
     'details' : {
         'field_name': 'details',
         'display_type': 'TA',
-        'display_size': '65/20',
-        'label': _("Original Submission"),
-        'description': _("Full description of the item"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 1,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 1,
-        'show_on_add_members': 1,
-        'rank': 700001,
     },
     'bug_group_id' : {
         'field_name': 'bug_group_id',
         'display_type': 'SB',
-        'label': _("Item Group"),
-        'description': _("Characterizes the nature of the item (e.g. Crash Error, Documentation Typo, Installation Problem, etc"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 1,
-        'show_on_add_members': 1,
-        'rank': 300,
-        'transition_default_auth': 'A',
     },
     'resolution_id' : {
         'field_name': 'resolution_id',
         'display_type': 'SB',
-        'label': _("Status"),
-        'description': _("Current resolution of the item"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 1,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 1,
-        'rank': 400,
-        'transition_default_auth': 'A',
     },
     'privacy' : {
         'field_name': 'privacy',
         'display_type': 'SB',
-        'label': _("Privacy"),
-        'description': _("Determines whether the item can be seen by members of the project only or anybody."),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 1,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 1,
-        'show_on_add_members': 1,
-        'rank': 402,
-        'transition_default_auth': 'A',
     },
     'vote' : {
         'field_name': 'vote',
         'display_type': 'TF',
-        'display_size': '6/10',
-        'label': _("Votes"),
-        'description': _("How many votes this item received."),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 0,
         'special': 1,
         'custom': 0,
-        'use_it': 1,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 405,
     },
     'category_version_id' : {
         'field_name': 'category_version_id',
         'display_type': 'SB',
-        'label': _("Component Version"),
-        'description': _("Version of the System Component (aka Item Category) impacted by the item"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1000,
-        'transition_default_auth': 'A',
     },
     'platform_version_id' : {
         'field_name': 'platform_version_id',
         'display_type': 'SB',
-        'label': _("Operating System"),
-        'description': _("Operating System impacted by the issue"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1100,
-        'transition_default_auth': 'A',
     },
     'reproducibility_id' : {
         'field_name': 'reproducibility_id',
         'display_type': 'SB',
-        'label': _("Reproducibility"),
-        'description': _("How easy it is to reproduce the item"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1200,
-        'transition_default_auth': 'A',
     },
     'size_id' : {
         'field_name': 'size_id',
         'display_type': 'SB',
-        'label': _("Size (loc)"),
-        'description': _("Estimated size of the code to be developed or reworked to fix the item"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1300,
-        'transition_default_auth': 'A',
     },
     'fix_release_id' : {
         'field_name': 'fix_release_id',
         'display_type': 'SB',
-        'label': _("Fixed Release"),
-        'description': _("Release in which the item was actually fixed"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1400,
-        'transition_default_auth': 'A',
     },
     'comment_type_id' : {
         'field_name': 'comment_type_id',
         'display_type': 'SB',
-        'label': _("Comment Type"),
-        'description': _("Specify the nature of the follow up comment attached to this item"),
         'scope': 'P',
         'required': 1,
-        'empty_ok': 1,
-        'keep_history': 0,
         'special': 1,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1500,
-        'transition_default_auth': 'A',
     },
     'hours' : {
         'field_name': 'hours',
         'display_type': 'TF',
-        'display_size': '5/5',
-        'label': _("Effort"),
-        'description': _("Number of hours of work needed to fix the item"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1700,
     },
     'plan_release_id' : {
         'field_name': 'plan_release_id',
         'display_type': 'SB',
-        'label': _("Planned Release"),
-        'description': _("Release in which it is planned to have the item fixed"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1600,
-        'transition_default_auth': 'A',
     },
     'component_version' : {
         'field_name': 'component_version',
         'display_type': 'TF',
-        'display_size': '10/40',
-        'label': _("Component Version"),
-        'description': _("Version of the system component (or work product) impacted by the item (<u>in free text</u>)"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1800,
     },
     'fix_release' : {
         'field_name': 'fix_release',
         'display_type': 'TF',
-        'display_size': '10/40',
-        'label': _("Fixed Release"),
-        'description': _("Release in which the bug was actually fixed (<u>in free text</u>)"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 1900,
     },
     'plan_release' : {
         'field_name': 'plan_release',
         'display_type': 'TF',
-        'display_size': '10/40',
-        'label': _("Planned Release"),
-        'description': _("Release in which it is planned to have the item fixed (<u>in free text</u>)"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 2000,
     },
     'priority' : {
         'field_name': 'priority',
         'display_type': 'SB',
-        'label': _("Priority"),
-        'description': _("How quickly the item should be handled"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 1,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 1,
-        'rank': 200,
-        'transition_default_auth': 'A',
     },
     'keywords' : {
         'field_name': 'keywords',
         'display_type': 'TF',
-        'display_size': '60/120',
-        'label': _("Keywords"),
-        'description': _("List of comma separated keywords associated with this item"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 3000,
     },
     'release_id' : {
         'field_name': 'release_id',
         'display_type': 'SB',
-        'label': _("Release"),
-        'description': _("Release (global version number) impacted by the item"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 800,
-        'transition_default_auth': 'A',
     },
     'release' : {
         'field_name': 'release',
         'display_type': 'TF',
-        'display_size': '10/40',
-        'label': _("Release"),
-        'description': _("Release (global version number) impacted by the item (<u>in free text</u>)"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 800,
     },
     'originator_name' : {
         'field_name': 'originator_name',
         'display_type': 'TF',
-        'display_size': '20/40',
-        'label': _("Originator Name"),
-        'description': _("Name of the person who submitted the item (if different from the submitter field)"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 550,
     },
     'originator_email' : {
         'field_name': 'originator_email',
         'display_type': 'TF',
-        'display_size': '20/40',
-        'label': _("Originator Email"),
-        'description': _("Email address of the person who submitted the item (if different from the submitter field, add address to CC list)"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 1,
-        'show_on_add_anonymous': 1,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 560,
     },
     'originator_phone' : {
         'field_name': 'originator_phone',
         'display_type': 'TF',
-        'display_size': '10/40',
-        'label': _("Originator Phone"),
-        'description': _("Phone number of the person who submitted the item"),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 570,
     },
     'percent_complete' : {
         'field_name': 'percent_complete',
         'display_type': 'SB',
-        'label': _("Percent Complete"),
-        'description': _(""),
         'scope': 'S',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 500,
-        'transition_default_auth': 'A',
     },
     'custom_tf1' : {
         'field_name': 'custom_tf1',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #1"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30000,
     },
     'custom_tf2' : {
         'field_name': 'custom_tf2',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #2"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30100,
     },
     'custom_tf3' : {
         'field_name': 'custom_tf3',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #3"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30200,
     },
     'custom_tf4' : {
         'field_name': 'custom_tf4',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #4"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30300,
     },
     'custom_tf5' : {
         'field_name': 'custom_tf5',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #5"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30400,
     },
     'custom_tf6' : {
         'field_name': 'custom_tf6',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #6"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30500,
     },
     'custom_tf7' : {
         'field_name': 'custom_tf7',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #7"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30600,
     },
     'custom_tf8' : {
         'field_name': 'custom_tf8',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #8"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30700,
     },
     'custom_tf9' : {
         'field_name': 'custom_tf9',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #9"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30800,
     },
     'custom_tf10' : {
         'field_name': 'custom_tf10',
         'display_type': 'TF',
-        'display_size': '10/15',
-        'label': _("Custom Text Field #10"),
-        'description': _("Customizable Text Field (one line, up to 255 characters"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 30900,
     },
     'custom_ta1' : {
         'field_name': 'custom_ta1',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #1"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40000,
     },
     'custom_ta2' : {
         'field_name': 'custom_ta2',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #2"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40100,
     },
     'custom_ta3' : {
         'field_name': 'custom_ta3',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #3"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40200,
     },
     'custom_ta4' : {
         'field_name': 'custom_ta4',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #4"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40300,
     },
     'custom_ta5' : {
         'field_name': 'custom_ta5',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #5"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40400,
     },
     'custom_ta6' : {
         'field_name': 'custom_ta6',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #6"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40500,
     },
     'custom_ta7' : {
         'field_name': 'custom_ta7',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #7"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40600,
     },
     'custom_ta8' : {
         'field_name': 'custom_ta8',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #8"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40700,
     },
     'custom_ta9' : {
         'field_name': 'custom_ta9',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #9"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40800,
     },
     'custom_ta10' : {
         'field_name': 'custom_ta10',
         'display_type': 'TA',
-        'display_size': '60/3',
-        'label': _("Custom Text Area #10"),
-        'description': _("Customizable Text Area (multi-line text)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 40900,
     },
     'custom_sb1' : {
         'field_name': 'custom_sb1',
         'display_type': 'SB',
-        'label': _("Custom Select Box #1"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50000,
-        'transition_default_auth': 'A',
     },
     'custom_sb2' : {
         'field_name': 'custom_sb2',
         'display_type': 'SB',
-        'label': _("Custom Select Box #2"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50100,
-        'transition_default_auth': 'A',
     },
     'custom_sb3' : {
         'field_name': 'custom_sb3',
         'display_type': 'SB',
-        'label': _("Custom Select Box #3"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50200,
-        'transition_default_auth': 'A',
     },
     'custom_sb4' : {
         'field_name': 'custom_sb4',
         'display_type': 'SB',
-        'label': _("Custom Select Box #4"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50300,
-        'transition_default_auth': 'A',
     },
     'custom_sb5' : {
         'field_name': 'custom_sb5',
         'display_type': 'SB',
-        'label': _("Custom Select Box #5"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50400,
-        'transition_default_auth': 'A',
     },
     'custom_sb6' : {
         'field_name': 'custom_sb6',
         'display_type': 'SB',
-        'label': _("Custom Select Box #6"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50500,
-        'transition_default_auth': 'A',
     },
     'custom_sb7' : {
         'field_name': 'custom_sb7',
         'display_type': 'SB',
-        'label': _("Custom Select Box #7"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50600,
-        'transition_default_auth': 'A',
     },
     'custom_sb8' : {
         'field_name': 'custom_sb8',
         'display_type': 'SB',
-        'label': _("Custom Select Box #8"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50700,
-        'transition_default_auth': 'A',
     },
     'custom_sb9' : {
         'field_name': 'custom_sb9',
         'display_type': 'SB',
-        'label': _("Custom Select Box #9"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50800,
-        'transition_default_auth': 'A',
     },
     'custom_sb10' : {
         'field_name': 'custom_sb10',
         'display_type': 'SB',
-        'label': _("Custom Select Box #10"),
-        'description': _("Customizable Select Box (pull down menu with predefined values)"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 50900,
-        'transition_default_auth': 'A',
     },
     'custom_df1' : {
         'field_name': 'custom_df1',
         'display_type': 'DF',
-        'label': _("Custom Date Field #1"),
-        'description': _("Customizable Date Field"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 60000,
     },
     'custom_df2' : {
         'field_name': 'custom_df2',
         'display_type': 'DF',
-        'label': _("Custom Date Field #2"),
-        'description': _("Customizable Date Field"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 60100,
     },
     'custom_df3' : {
         'field_name': 'custom_df3',
         'display_type': 'DF',
-        'label': _("Custom Date Field #3"),
-        'description': _("Customizable Date Field"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 60200,
     },
     'custom_df4' : {
         'field_name': 'custom_df4',
         'display_type': 'DF',
-        'label': _("Custom Date Field #4"),
-        'description': _("Customizable Date Field"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 60300,
     },
     'custom_df5' : {
         'field_name': 'custom_df5',
         'display_type': 'DF',
-        'label': _("Custom Date Field #5"),
-        'description': _("Customizable Date Field"),
         'scope': 'P',
         'required': 0,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 1,
-        'use_it': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 60400,
     },
     'discussion_lock' : {
         'field_name': 'discussion_lock',
         'display_type': 'SB',
-        'label': _("Discussion Lock"),
-        'description': _("Determines whether comments can still be added to the item"),
         'scope': 'S',
         'required': 1,
-        'empty_ok': 1,
-        'keep_history': 1,
         'special': 0,
         'custom': 0,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 0,
-        'show_on_add_members': 0,
-        'rank': 800,
-        'transition_default_auth': 'A',
     },
     'planned_close_date' : {
         'field_name': 'planned_close_date',
         'display_type': 'DF',
-        '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,
-        'use_it': 1,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 1,
-        'show_on_add_members': 1,
-        'rank': 56,
     },
     'planned_starting_date' : {
         'field_name': 'planned_starting_date',
         'display_type': 'DF',
-        '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,
-        'use_it': 1,
-        'show_on_add_anonymous': 0,
-        'show_on_add_connected': 1,
-        'show_on_add_members': 1,
-        'rank': 55,
     },
 }
 
-default_fields['bugs']    = deepcopy(common)
-default_fields['patch']   = deepcopy(common)
-default_fields['support'] = deepcopy(common)
-default_fields['task']    = deepcopy(common)
 
-# A couple changes per-tracker in the default configuration
-#submitted_by     92 -> (use_it=0)(patch,support) | (use_it=1)(bugs,task)
-default_fields['patch']['submitted_by']['use_it'] = default_fields['support']['submitted_by']['use_it'] = 0
-#severity    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)
-default_fields['patch']['severity']['use_it'] = default_fields['task']['severity']['use_it'] = 0
-default_fields['patch']['severity']['show_on_add'] = default_fields['task']['severity']['show_on_add'] = 0
-default_fields['patch']['severity']['show_on_add_members'] = default_fields['task']['severity']['show_on_add_members'] = 0
-#bug_group_id    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]
-#default_fields['patch']['bug_group_id']['show_on_add'] = default_fields['support']['bug_group_id']['show_on_add'] = 0
-#default_fields['patch']['bug_group_id']['show_on_add_members'] = default_fields['support']['bug_group_id']['show_on_add_members'] = 0
-#platform_version_id    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)
-default_fields['support']['platform_version_id']['use_it'] = 1
-default_fields['support']['platform_version_id']['show_on_add'] = 1
-default_fields['support']['platform_version_id']['show_on_add_members'] = 1
-#hours    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)
-default_fields['task']['hours']['use_it'] = 1
-default_fields['task']['hours']['show_on_add'] = 1
-default_fields['task']['hours']['show_on_add_members'] = 1
-#priority    211 -> (show_on_add,show_on_add_members,place)
-#           bugs: 0,1,200
-#           patch: 1,1,150
-default_fields['patch']['priority']['show_on_add'] = 1
-default_fields['patch']['priority']['show_on_add_members'] = 1
-default_fields['patch']['priority']['place'] = 150
-#           support: 0,0,150
-default_fields['support']['priority']['show_on_add'] = 0
-default_fields['support']['priority']['show_on_add_members'] = 0
-default_fields['support']['priority']['place'] = 150
-#           task: 1,1,200
-default_fields['task']['priority']['show_on_add'] = 1
-default_fields['task']['priority']['show_on_add_members'] = 1
-default_fields['task']['priority']['place'] = 200
-#originator_email    216 -> (use_it,show_on_add=0)(task) | (use_it=1,show_on_add=2)(bugs,patch,support)
-default_fields['task']['originator_email']['use_it'] = 0
-default_fields['task']['originator_email']['show_on_add'] = 0
-#percent_complete    220 -> (use_it,show_on_add_members=0)(bugs,patch,support) | (use_it,show_on_add_members=1)(task)
-default_fields['task']['originator_email']['use_it'] = 1
-default_fields['task']['originator_email']['show_on_add_members'] = 1
-
-# 'planned_starting_date' was initially specific to 'task'
-default_fields['bugs']   ['planned_starting_date']['use_it'] = 0
-default_fields['patch']  ['planned_starting_date']['use_it'] = 0
-default_fields['support']['planned_starting_date']['use_it'] = 0
-# 'same for planned_close_date'
-default_fields['bugs']   ['planned_close_date']['use_it'] = 0
-default_fields['patch']  ['planned_close_date']['use_it'] = 0
-default_fields['support']['planned_close_date']['use_it'] = 0
+# The following is now stored in the DB
+## A couple changes per-tracker in the default configuration
+##submitted_by     92 -> (use_it=0)(patch,support) | (use_it=1)(bugs,task)
+#default_fields['patch']['submitted_by']['use_it'] = default_fields['support']['submitted_by']['use_it'] = 0
+##severity    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)
+#default_fields['patch']['severity']['use_it'] = default_fields['task']['severity']['use_it'] = 0
+#default_fields['patch']['severity']['show_on_add'] = default_fields['task']['severity']['show_on_add'] = 0
+#default_fields['patch']['severity']['show_on_add_members'] = default_fields['task']['severity']['show_on_add_members'] = 0
+##bug_group_id    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]
+##default_fields['patch']['bug_group_id']['show_on_add'] = default_fields['support']['bug_group_id']['show_on_add'] = 0
+##default_fields['patch']['bug_group_id']['show_on_add_members'] = default_fields['support']['bug_group_id']['show_on_add_members'] = 0
+##platform_version_id    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)
+#default_fields['support']['platform_version_id']['use_it'] = 1
+#default_fields['support']['platform_version_id']['show_on_add'] = 1
+#default_fields['support']['platform_version_id']['show_on_add_members'] = 1
+##hours    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)
+#default_fields['task']['hours']['use_it'] = 1
+#default_fields['task']['hours']['show_on_add'] = 1
+#default_fields['task']['hours']['show_on_add_members'] = 1
+##priority    211 -> (show_on_add,show_on_add_members,place)
+##           bugs: 0,1,200
+##           patch: 1,1,150
+#default_fields['patch']['priority']['show_on_add'] = 1
+#default_fields['patch']['priority']['show_on_add_members'] = 1
+#default_fields['patch']['priority']['place'] = 150
+##           support: 0,0,150
+#default_fields['support']['priority']['show_on_add'] = 0
+#default_fields['support']['priority']['show_on_add_members'] = 0
+#default_fields['support']['priority']['place'] = 150
+##           task: 1,1,200
+#default_fields['task']['priority']['show_on_add'] = 1
+#default_fields['task']['priority']['show_on_add_members'] = 1
+#default_fields['task']['priority']['place'] = 200
+##originator_email    216 -> (use_it,show_on_add=0)(task) | (use_it=1,show_on_add=2)(bugs,patch,support)
+#default_fields['task']['originator_email']['use_it'] = 0
+#default_fields['task']['originator_email']['show_on_add'] = 0
+##percent_complete    220 -> (use_it,show_on_add_members=0)(bugs,patch,support) | (use_it,show_on_add_members=1)(task)
+#default_fields['task']['originator_email']['use_it'] = 1
+#default_fields['task']['originator_email']['show_on_add_members'] = 1
+#
+## 'planned_starting_date' was initially specific to 'task'
+#default_fields['bugs']   ['planned_starting_date']['use_it'] = 0
+#default_fields['patch']  ['planned_starting_date']['use_it'] = 0
+#default_fields['support']['planned_starting_date']['use_it'] = 0
+## 'same for planned_close_date'
+#default_fields['bugs']   ['planned_close_date']['use_it'] = 0
+#default_fields['patch']  ['planned_close_date']['use_it'] = 0
+#default_fields['support']['planned_close_date']['use_it'] = 0
--- a/savane/tracker/models.py
+++ b/savane/tracker/models.py
@@ -44,8 +44,8 @@
                         ('SB', _('select box')),
                         ('TA', _('text area')),
                         ('TF', _('text field')),)
-SCOPE_CHOICES = (('S', _('system')), # user cannot modify related FieldValue's (TF)
-                 ('P', _('project')),)  # user can modify related FieldValue's (TF)
+SCOPE_CHOICES = (('S', _('system')), # user cannot modify related FieldChoice's (TF)
+                 ('P', _('project')),)  # user can modify related FieldChoice's (TF)
 
 RESTRICTION_CHOICES = (('2', _('anonymous')),
                        ('3', _('logged-in user')),
@@ -147,14 +147,16 @@
 
 #class SquadPermission(models.Model): pass
 
+
 class FieldOverlay(models.Model):
     """
-    Per-group tracker item definition override
+    Per-group tracker field definition override
+    (or site-wide field default if group==NULL)
     """
     class Meta:
-        unique_together = (('group', 'field_name'),)
-        verbose_name = _("field usage")
-        verbose_name_plural = _("field usages")
+        unique_together = (('tracker', 'group', 'field'),)
+        verbose_name = _("field overlay")
+        verbose_name_plural = _("field overlays")
 
     EMPTY_OK_CHOICES = (('0', _('mandatory only if it was presented to the original submitter')),
                         ('1', _('optional (empty values are accepted)')),
@@ -163,8 +165,13 @@
                                        ('A', _('allowed')),
                                        ('F', _('forbidden')),)
     tracker = models.ForeignKey(Tracker)
-    group = models.ForeignKey(auth_models.Group, blank=True, null=True, help_text=_("NULL == default"))
-    field_name = models.CharField(max_length=32)
+    group = models.ForeignKey(auth_models.Group, blank=True, null=True,
+                              help_text=_("NULL == default for all groups"))
+    field = models.CharField(max_length=32)
+
+    # If Field.custom
+    label = models.CharField(max_length=255, blank=True, null=True)
+    description = models.CharField(max_length=255, blank=True, null=True)
 
     # If not Field.required:
     use_it = models.BooleanField(_("used"))
@@ -193,46 +200,43 @@
     # If !Field.special
     keep_history = models.BooleanField(_("keep field value changes in history"))
 
-    # If Field.custom
-    # Specific (bad!) fields for custom fields (if Field.custom is True):
-    label = models.CharField(max_length=255, blank=True, null=True)
-    description = models.CharField(max_length=255, blank=True, null=True)
-
-    def apply_on(field_definition):
+    def apply_on(self, field_definition):
         """
         Modify a default field definition with FieldOverlay's override
         values.  Only apply sensible overlays.
         """
         if not field_definition['required']:
-            field_definition['use_it'] = overlay.use_it
-            field_definition['show_on_add_anonymous'] = overlay.show_on_add_anonymous
-            field_definition['show_on_add_connected'] = overlay.show_on_add_connected
-            field_definition['show_on_add_members'] = overlay.show_on_add_members
-        field_definition['empty_ok'] = overlay.empty_ok
-        field_definition['rank'] = overlay.rank
-        if field_definition['type'] == 'SB':
-            field_definition['transition_default_auth'] = overlay.transition_default_auth
-        elif field_definition['type'] in ('TA', 'TF'):
-            field_definition['display_size'] = overlay.display_size
-        if field_definition['special'] == 1:
-            field_definition['keep_history'] = overlay.keep_history
-        if field_definition['custom'] == 1:
-            field_definition['label'] = overlay.label
-            field_definition['description'] = overlay.description
+            field_definition['use_it'] = self.use_it
+            field_definition['show_on_add_anonymous'] = self.show_on_add_anonymous
+            field_definition['show_on_add_connected'] = self.show_on_add_connected
+            field_definition['show_on_add_members'] = self.show_on_add_members
+        field_definition['empty_ok'] = self.empty_ok
+        field_definition['rank'] = self.rank
+        if field_definition['display_type'] == 'SB':
+            field_definition['transition_default_auth'] = self.transition_default_auth
+        elif field_definition['display_type'] in ('TA', 'TF'):
+            field_definition['display_size'] = self.display_size
+        if self.group_id is None or field_definition['special'] != 1:
+            field_definition['keep_history'] = self.keep_history
+        if self.group_id is None or field_definition['custom'] == 1:
+            field_definition['label'] = self.label
+            field_definition['description'] = self.description
 
-class FieldValue(models.Model):
+class FieldChoice(models.Model):
     """
     Per-group tracker select box values override
+    (or site-wide field default if group==NULL)
     """
     class Meta:
-        unique_together = (('group', 'field_name', 'value_id'),)
+        unique_together = (('tracker', 'group', 'field', 'value_id'),)
 
     STATUS_CHOICES = (('A', _('active')),
                       ('H', _('hidden')), # mask previously-active or system fields
                       ('P', _('permanent')),) # status cannot be modified, always visible
     tracker = models.ForeignKey(Tracker)
-    group = models.ForeignKey(auth_models.Group, blank=True, null=True, help_text=_("NULL == default"))
-    field_name = models.CharField(max_length=32)
+    group = models.ForeignKey(auth_models.Group, blank=True, null=True,
+                              help_text=_("NULL == default for all groups"))
+    field = models.CharField(max_length=32)
     value_id = models.IntegerField(db_index=True) # group_specific value identifier
       # It's not a duplicate of 'id', as it's the value referenced by
       # Item fields, and the configuration of that value can be
@@ -248,25 +252,25 @@
     send_all_flag = models.BooleanField(_("send on all updates"), default=True)
 
     def __unicode__(self):
-        #return "%s.%s: %s (%d)" % (self.tracker_id, self.field_name, self.value, self.value_id)
+        #return "%s.%s: %s (%d)" % (self.tracker_id, self.field, self.value, self.value_id)
         group_name = '<default>'
-        if self.group_id != 0:
+        if self.group_id is not None:
             group_name = self.group.name
-        return "%s.%s: %s (%d)" % (group_name, self.field_name, self.value, self.value_id)
+        return "%s.%s: %s (%d)" % (group_name, self.field, self.value, self.value_id)
 
-def field_get_values(tracker_id, group, field_name):
+def field_get_values(tracker_id, group, field):
     """
     Return all possible values for this select box field
     """
-    if field_name == 'assigned_to':
+    if field == 'assigned_to':
         # Hard-coded processing: it's a list of project members
         default_values = [{'value_id' : -1, 'value' : _("None")}]
         for user in group.user_set.order_by('username'):
             default_values.append({'value_id' : user.pk, 'value' : user.username})
     else:
         #tracker_id=tracker_id, 
-        default_values = list(FieldValue.objects \
-            .filter(group=None, field_name=field_name).exclude(status='H') \
+        default_values = list(FieldChoice.objects \
+            .filter(group=None, field=field).exclude(status='H') \
             .values('value_id', 'value', 'rank'))
         # value overlays
         default_values.sort(key=lambda x: x['rank'])
@@ -320,8 +324,8 @@
     ##
     # Note: For select boxes, FK should be limited to same group, and
     # to a specific field each e.g.:
-    # severity = models.ForeignKey('FieldValue', to_field='value_id', default=5)
-    #            + constraint(same group or 100) + constraint(field_name='severity')
+    # severity = models.ForeignKey('FieldChoice', to_field='value_id', default=5)
+    #            + constraint(same group or 100) + constraint(field='severity')
     # To avoid unnecessary burden, let's drop the above incomplete ForeignKey
 
     # More generally one can wonder if this should be moved to a M2M
@@ -456,11 +460,12 @@
         values + group-specific overlay).  Only apply sensible
         overlay values (cf. FieldOverlay model definition).
         """
-        fields = deepcopy(default_fields[self.tracker_id])
-        overlays = FieldOverlay.objects.filter(tracker=self.tracker_id, group=self.group)
-        for overlay in overlays:
-            name = overlay.field_name
-            overlay.apply_on[fields[name]]
+        fields = deepcopy(field_defs)
+        for overlays in (FieldOverlay.objects.filter(tracker=self.tracker_id, group=None),
+                         FieldOverlay.objects.filter(tracker=self.tracker_id, group=self.group)):
+            for overlay in overlays:
+                name = overlay.field
+                overlay.apply_on(fields[name])
         for name in fields:
             if fields[name]['display_type'] == 'SB':
                 fields[name]['values'] = field_get_values(self.tracker_id, self.group, name)
@@ -473,6 +478,7 @@
         fields = self.get_fields().copy()
         ret = []
         for name in fields.keys():
+            print fields[name]
             if (not (fields[name]['required'] or fields[name]['use_it'])
                 or fields[name]['special']):
                 continue
@@ -493,7 +499,7 @@
             html += u'<th><span class="help" title="%s">%s</span></th>\n' % (field['description'], field['label']+ugettext(": "))
 
             html += '<td>'
-            if   field['display_type'] == 'DF':
+            if field['display_type'] == 'DF':
                 html += u'<select name="%s_dayfd" value="TODO">\n' % (name)
                 for i in range(1,31+1):
                     html += '<option value="%d">%d</option>\n' % (i, i)
@@ -505,7 +511,7 @@
                      locale.MON_9, locale.MON_10, locale.MON_11, locale.MON_12)):
                     html += '<option value="%d">%s</option>\n' % (i, locale.nl_langinfo(langinfo_constant))
                 html += '</select>\n'
-                html += u'<input type="text" length="4" maxlength="4" name="%s_yearfd" value="TODO">' % (name)
+                html += u'<input type="text" size="4" maxlength="4" name="%s_yearfd" value="TODO">' % (name)
             elif field['display_type'] == 'SB':
                 html += u'<select name="%s">\n' % name
                 for option in field['values']:
@@ -543,14 +549,14 @@
 class ItemHistory(models.Model):
     """
     This stores 2 kinds of values:
-    - item comments (field_name='details')
+    - item comments (field='details')
     - value changes, for fields that have history tracking enabled
     """
     item = models.ForeignKey('Item')
-    field_name = models.CharField(max_length=255)
-       # Should be: field_name = models.ForeignKey('Field', to_field='name')
+    field = models.CharField(max_length=32)
+       # Should be: field = models.ForeignKey('Field', to_field='name')
        #            + constraint (item.tracker=field.tracker)
-       # or simply: field_name = models.ForeignKey('Field')
+       # or simply: field = models.ForeignKey('Field')
        # But as it's a history field, adding constraints might be just bad.
     old_value= models.TextField(blank=True, null=True)
     new_value= models.TextField()
@@ -562,8 +568,8 @@
     # I guess 'details' could be stored separately.
     type = models.IntegerField(_("comment type"), blank=True, null=True)
       # Should be:
-      # type = models.ForeignKey('FieldValue', to_field='value_id')
-      #        + constraint(same group or 100) + constraint(field_name='comment_type_id')
+      # type = models.ForeignKey('FieldChoice', to_field='value_id')
+      #        + constraint(same group or 100) + constraint(field='comment_type_id')
       # The purpose is to add <strong>[$comment_type]</strong> when
       # displaying an item comment.
     spamscore = models.IntegerField(_("total spamscore for this comment"))