Mercurial > hg > savane-forge
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"))