From 04d5da19391b01d33ecd714c0c24f1bf0e9123f0 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Sat, 26 Dec 2009 05:40:32 +0000 Subject: [PATCH] Basic framework for edit/view forms svn: r13919 --- src/data/templates/view_family_detail.html | 2 +- src/data/templates/view_name_detail.html | 16 +++++ src/data/templates/view_page.html | 48 +++++++------ src/data/templates/view_person_detail.html | 2 +- src/web/grampsdb/fixtures/initial_data.json | 38 +++++++++- src/web/grampsdb/forms.py | 38 +++++++--- src/web/grampsdb/models.py | 74 +++++++++++++++++++- src/web/grampsdb/templatetags/my_tags.py | 14 +--- src/web/grampsdb/views.py | 10 ++- src/web/init.py | 5 +- src/web/libdjango.py | 6 +- src/web/settings.py | 2 +- src/web/sqlite.db | Bin 193536 -> 202752 bytes src/web/utils.py | 23 ++++-- 14 files changed, 217 insertions(+), 61 deletions(-) diff --git a/src/data/templates/view_family_detail.html b/src/data/templates/view_family_detail.html index 2ffb5ac69..6777012dc 100644 --- a/src/data/templates/view_family_detail.html +++ b/src/data/templates/view_family_detail.html @@ -89,7 +89,7 @@ {{ family|family_event_table:user|safe }}
- {{ family|family_source_table:user|safe }} + {{ family|source_table:user|safe }}
{{ family|family_attribute_table:user|safe }} diff --git a/src/data/templates/view_name_detail.html b/src/data/templates/view_name_detail.html index fabf8b957..4d6707c7f 100644 --- a/src/data/templates/view_name_detail.html +++ b/src/data/templates/view_name_detail.html @@ -68,9 +68,25 @@
  • Sources
  • Notes
  • +
    + + + + + + + + + + + + + +
    {{form.group_as.label}}: {{form.group_as|render:action}}
    {{form.sort_as.label}}: {{form.sort_as|render:action}}
    {{form.display_as.label}}: {{form.display_as|render:action}}
    {{form.text.label}}: {{form.text|render:action}}
    + {% source_table form.model user action "/person/%s/name/%s/source/add" person.handle form.model.order %}
    diff --git a/src/data/templates/view_page.html b/src/data/templates/view_page.html index 8eef2a8e2..3d38145b8 100644 --- a/src/data/templates/view_page.html +++ b/src/data/templates/view_page.html @@ -6,41 +6,45 @@ {% block content %} -

    +

    -

    +
    @@ -54,32 +58,36 @@ diff --git a/src/data/templates/view_person_detail.html b/src/data/templates/view_person_detail.html index 2d8d5be6b..95fcdec7a 100644 --- a/src/data/templates/view_person_detail.html +++ b/src/data/templates/view_person_detail.html @@ -86,7 +86,7 @@ {{ person|person_name_table:user|safe }}
    - {{ person|person_source_table:user|safe }} + {% source_table person user action "/person/%s/source/add" person.handle %}
    {{ person|person_attribute_table:user|safe }} diff --git a/src/web/grampsdb/fixtures/initial_data.json b/src/web/grampsdb/fixtures/initial_data.json index 1ad2b0531..15bad817f 100644 --- a/src/web/grampsdb/fixtures/initial_data.json +++ b/src/web/grampsdb/fixtures/initial_data.json @@ -18,7 +18,7 @@ "setting" : "db_created" , "description" : "database creation date/time" , "value_type" : "str" , - "value" : "2009-12-12 20:42" + "value" : "2009-12-26 00:38" } }, { @@ -1568,5 +1568,41 @@ "val" : 13, "name": "Uncleared" } + } , + { + "model": "grampsdb.nameformattype", + "pk": 1, + "fields": + { + "val" : 0, + "name": "Default format" + } + } , + { + "model": "grampsdb.nameformattype", + "pk": 2, + "fields": + { + "val" : 1, + "name": "Surname, Given Patronymic" + } + } , + { + "model": "grampsdb.nameformattype", + "pk": 3, + "fields": + { + "val" : 2, + "name": "Given Surname" + } + } , + { + "model": "grampsdb.nameformattype", + "pk": 4, + "fields": + { + "val" : 3, + "name": "Patronymic, Given" + } } ] diff --git a/src/web/grampsdb/forms.py b/src/web/grampsdb/forms.py index a06271bc5..218fbfeb4 100644 --- a/src/web/grampsdb/forms.py +++ b/src/web/grampsdb/forms.py @@ -20,18 +20,36 @@ class PersonForm(forms.ModelForm): class NameForm(forms.ModelForm): class Meta: model = Name - fields = ("suffix", "first_name", "title", "prefix", - "call", "surname", "patronymic", "name_type", - "preferred") + # Exclude these, so they don't get checked: + exclude = ["order", "calendar", "modifier", + "quality", + #"quality_estimated", "quality_calculated", + #"quality_interpreted", + "year1", "day1", "month1", + "sortval", "newyear", "person"] # Add these because they are TextFields, which render as # Textareas: - surname = forms.CharField(required=False, widget=TextInput(attrs={'size':'30'})) - first_name = forms.CharField(required=False, widget=TextInput(attrs={'size':'30'})) - title = forms.CharField(required=False, widget=TextInput(attrs={'size':'30'})) - prefix = forms.CharField(required=False, widget=TextInput(attrs={'size':'30'})) - suffix = forms.CharField(required=False, widget=TextInput(attrs={'size':'30'})) - call = forms.CharField(required=False, widget=TextInput(attrs={'size':'30'})) - patronymic = forms.CharField(required=False, widget=TextInput(attrs={'size':'30'})) + surname = forms.CharField(required=False, + widget=TextInput(attrs={'size':'30'})) + first_name = forms.CharField(label="Given", + required=False, + widget=TextInput(attrs={'size':'30'})) + title = forms.CharField(required=False, + widget=TextInput(attrs={'size':'30'})) + prefix = forms.CharField(required=False, + widget=TextInput(attrs={'size':'30'})) + suffix = forms.CharField(required=False, + widget=TextInput(attrs={'size':'30'})) + call = forms.CharField(label="Callname", + required=False, + widget=TextInput(attrs={'size':'30'})) + patronymic = forms.CharField(required=False, + widget=TextInput(attrs={'size':'30'})) + group_as = forms.CharField(required=False, + widget=TextInput(attrs={'size':'30'})) + text = forms.CharField(label="Date", + required=False, + widget=TextInput(attrs={'size':'30'})) '''class NameFormset(BaseModelFormSet): def __init__(self, *args, **kwargs): diff --git a/src/web/grampsdb/models.py b/src/web/grampsdb/models.py index b1f6a3588..6bb5a2576 100644 --- a/src/web/grampsdb/models.py +++ b/src/web/grampsdb/models.py @@ -229,6 +229,68 @@ class LdsStatus(mGrampsType): _DEFAULT = _DATAMAP[0] val = models.IntegerField('lds status', choices=_DATAMAP, blank=False) +class NameFormatType(mGrampsType): + _DATAMAP = [(0, "Default format"), + (1, "Surname, Given Patronymic"), + (2, "Given Surname"), + (3, "Patronymic, Given"),] + _DEFAULT = _DATAMAP[0] + val = models.IntegerField('Name formats', choices=_DATAMAP, blank=False) + +class CalendarType(mGrampsType): + CAL_GREGORIAN = 0 # CODE + CAL_JULIAN = 1 + CAL_HEBREW = 2 + CAL_FRENCH = 3 + CAL_PERSIAN = 4 + CAL_ISLAMIC = 5 + CAL_SWEDISH = 6 + + _DATAMAP = [(CAL_GREGORIAN, "Gregorian"), + (CAL_JULIAN, "Julian"), + (CAL_HEBREW, "Hebrew"), + (CAL_FRENCH, "French Republican"), + (CAL_PERSIAN, "Persian"), + (CAL_ISLAMIC, "Islamic"), + (CAL_SWEDISH, "Swedish")] + + _DEFAULT = _DATAMAP[CAL_GREGORIAN] + val = models.IntegerField('Calendar', choices=_DATAMAP, blank=False) + +class DateModifierType(mGrampsType): + MOD_NONE = 0 # CODE + MOD_BEFORE = 1 + MOD_AFTER = 2 + MOD_ABOUT = 3 + MOD_RANGE = 4 + MOD_SPAN = 5 + MOD_TEXTONLY = 6 + + _DATAMAP = [(MOD_NONE, ""), + (MOD_BEFORE, "Before"), + (MOD_AFTER, "After"), + (MOD_ABOUT, "About"), + (MOD_RANGE, "Range"), + (MOD_SPAN, "Span"), + (MOD_TEXTONLY, "Text only")] + + _DEFAULT = _DATAMAP[MOD_NONE] + val = models.IntegerField('Date modifier', choices=_DATAMAP, blank=False) + +class DateNewYearType(mGrampsType): + NEWYEAR_JAN1 = 0 # CODE + NEWYEAR_MAR1 = 1 + NEWYEAR_MAR25 = 2 + NEWYEAR_SEP1 = 3 + + _DATAMAP = [(NEWYEAR_JAN1, ""), + (NEWYEAR_MAR1, "March 1"), + (NEWYEAR_MAR25, "March 25"), + (NEWYEAR_SEP1, "September 1")] + + _DEFAULT = _DATAMAP[NEWYEAR_JAN1] + val = models.IntegerField('New Year start date', choices=_DATAMAP, blank=False) + #--------------------------------------------------------------------------- # # Support definitions @@ -241,6 +303,9 @@ class DateObject(models.Model): calendar = models.IntegerField() modifier = models.IntegerField() quality = models.IntegerField() + #quality_estimated = models.BooleanField() + #quality_calculated = models.BooleanField() + #quality_interpreted = models.BooleanField() day1 = models.IntegerField() month1 = models.IntegerField() year1 = models.IntegerField() @@ -447,7 +512,7 @@ class SecondaryObject(models.Model): class Name(DateObject, SecondaryObject): name_type = models.ForeignKey('NameType', related_name="name_code") - preferred = models.BooleanField('preferred name?') + preferred = models.BooleanField('Preferred name?') first_name = models.TextField(blank=True) surname = models.TextField(blank=True) suffix = models.TextField(blank=True) @@ -456,8 +521,10 @@ class Name(DateObject, SecondaryObject): patronymic = models.TextField(blank=True) call = models.TextField(blank=True) group_as = models.TextField(blank=True) - sort_as = models.IntegerField(blank=True) - display_as = models.IntegerField(blank=True) + sort_as = models.ForeignKey('NameFormatType', + related_name="sort_as") + display_as = models.ForeignKey('NameFormatType', + related_name="display_as") ## Key: person = models.ForeignKey("Person") @@ -656,6 +723,7 @@ TABLES = [ ("type", MarkerType), ("type", MarkupType), ("type", NameType), + ("type", NameFormatType), ("type", AttributeType), ("type", UrlType), ("type", ChildRefType), diff --git a/src/web/grampsdb/templatetags/my_tags.py b/src/web/grampsdb/templatetags/my_tags.py index 20fdaa6bd..59be2150d 100644 --- a/src/web/grampsdb/templatetags/my_tags.py +++ b/src/web/grampsdb/templatetags/my_tags.py @@ -8,17 +8,6 @@ import web.utils register = Library() -util_filters = ['person_event_table', 'person_name_table', - 'person_source_table', 'person_attribute_table', - 'person_address_table', 'person_note_table', - 'person_gallery_table', 'person_internet_table', - 'person_association_table', 'person_lds_table', - 'person_reference_table', - 'family_children_table', 'family_event_table', - 'family_source_table', 'family_attribute_table', - 'family_note_table', 'family_gallery_table', - 'family_lds_table', - 'nbsp', 'render'] for filter_name in util_filters: func = getattr(web.utils, filter_name) func.is_safe = True @@ -66,6 +55,9 @@ def make_tag(func): register.tag("get_person_from_handle", make_tag(get_person_from_handle)) +register.tag("source_table", + make_tag(source_table)) + probably_alive.is_safe = True register.filter('probably_alive', probably_alive) diff --git a/src/web/grampsdb/views.py b/src/web/grampsdb/views.py index 37ddffd44..ace8936a8 100644 --- a/src/web/grampsdb/views.py +++ b/src/web/grampsdb/views.py @@ -141,7 +141,10 @@ def view_name_detail(request, handle, order, action="view"): elif action == "add": person = Person.objects.get(handle=handle) name = Name() - form = NameForm() + name.sort_as = NameFormatType.objects.get(val=0) + name.display_as = NameFormatType.objects.get(val=0) + name.name_type = NameType.objects.get(val=2) # Birth Name + form = NameForm(instance=name) form.model = name action = "edit" elif action == "save": @@ -153,7 +156,9 @@ def view_name_detail(request, handle, order, action="view"): name = Name(calendar=0, modifier=0, quality=0, year1=0, day1=0, month1=0, sortval = 0, newyear=0, order=order, - sort_as=0, display_as=0, person_id=person.id) + sort_as=NameFormatType(val=0), + display_as=NameFormatType(val=0), + person_id=person.id) form = NameForm(request.POST, instance=name) form.model = name if form.is_valid(): @@ -276,6 +281,7 @@ def view_detail(request, view, handle): else: raise Http404(_("Requested page type not known")) context[view] = obj + context["action"] = "view" return render_to_response(view_template, context) def view(request, view): diff --git a/src/web/init.py b/src/web/init.py index 99eae0849..0250b5fbd 100644 --- a/src/web/init.py +++ b/src/web/init.py @@ -43,7 +43,7 @@ from gen.lib.srcmediatype import SourceMediaType from gen.lib.eventroletype import EventRoleType from gen.lib.notetype import NoteType -from grampsdb.models import GenderType, LdsType, LdsStatus +from grampsdb.models import GenderType, LdsType, LdsStatus, NameFormatType def get_datamap(x): """ @@ -86,7 +86,8 @@ for table, entries in [("grampsdb.config", type_models = [MarkerType, NameType, AttributeType, UrlType, ChildRefType, RepositoryType, EventType, FamilyRelType, SourceMediaType, - EventRoleType, NoteType, GenderType, LdsType, LdsStatus] + EventRoleType, NoteType, GenderType, LdsType, LdsStatus, + NameFormatType] for type in type_models: count = 1 # Add each code: diff --git a/src/web/libdjango.py b/src/web/libdjango.py index 5b0c60578..dd013158a 100644 --- a/src/web/libdjango.py +++ b/src/web/libdjango.py @@ -583,7 +583,7 @@ class DjangoInterface(object): return (name.private, source_list, note_list, date, name.first_name, name.surname, name.suffix, name.title, tuple(name.name_type), name.prefix, name.patronymic, - name.group_as, name.sort_as, name.display_as, name.call) + name.group_as, name.sort_as.val, name.display_as.val, name.call) def pack_location(self, loc, with_parish): if with_parish: @@ -1005,8 +1005,8 @@ class DjangoInterface(object): name.prefix = prefix name.patronymic = patronymic name.group_as = group_as - name.sort_as = sort_as - name.display_as = display_as + name.sort_as = models.get_type(models.NameFormatType, sort_as) + name.display_as = models.get_type(models.NameFormatType, display_as) name.call = call # we know person exists # needs to have an ID for key diff --git a/src/web/settings.py b/src/web/settings.py index 12f5f8982..d2c97aad6 100644 --- a/src/web/settings.py +++ b/src/web/settings.py @@ -26,7 +26,7 @@ import const import os -DEBUG = False +DEBUG = True TEMPLATE_DEBUG = DEBUG INTERNAL_IPS = ('127.0.0.1',) diff --git a/src/web/sqlite.db b/src/web/sqlite.db index 7baadc36b88b2871159b0d0c5b9600e5800e5128..b7d627fe677c228370cf7be99d031bcd6c397cf7 100644 GIT binary patch delta 12803 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