diff --git a/README.md b/README.md index a086687c..203ac2b8 100644 --- a/README.md +++ b/README.md @@ -92,32 +92,48 @@ All libraries are included to facilitate offline development * Enter Django console by `docker-compose -f local.yml run django python manage.py shell_plus` * remote debug in PyCharm for docker, please check [django cookie doc](https://github.com/pydanny/cookiecutter-django/blob/master/{{cookiecutter.project_slug}}/docs/pycharm/configuration.rst). -## Todo: +## Todo & progress: + +
+ Click to expand all + - [x] make auth group not organization specific, and counselling note check on organization - [ ] Past can replace Note, Attendee.progressions and calls/requests, so that any name lists such as status can be easily queried. (membership remains as attendance) + - [x] make Past model generic + - [ ] any past status list (Past level) + - [ ] Attendance roaster to Past auto conversion - [x] attendee detail page - [x] server side process of Attendees list & search page - [x] AttendingMeet form of Attendee update page - - [x] FamilyAttendee datagrid of Attendee update page - - [x] Personal & family Address of Attendee update page - - [x] Dynamic contacts of Attendee update page - - [x] Permission controlled blocks in single attendee update page, i.e. different blocks/user-settings for different groups - - [x] Generic models such as Note, Place, Past need to have organization column instead of infos - - [x] Add Past as Note - - [x] Create new instance of Attendee & attending update page with params with meet - - [x] delete function for human error - - [x] Modify Attendee save method to combine/convert names by OpenCC to support searches in different text encoding, and retire db level full_name. - - [x] implement secret/private relation/past general -- [ ] Move single attendee update page out of data assembly -- [ ] Gathering list (new design with server side processing) -- [ ] Attendance list (new design with server side processing) + - [x] [PR#3](https://github.com/xjlin0/attendees30/pull/3) FamilyAttendee datagrid of Attendee update page + - [x] [PR#2](https://github.com/xjlin0/attendees30/pull/2) Personal & family Address of Attendee update page + - [x] [PR#4](https://github.com/xjlin0/attendees30/pull/4) Dynamic contacts of Attendee update page + - [x] [PR#9](https://github.com/xjlin0/attendees30/pull/9) Permission controlled blocks in single attendee update page, i.e. different blocks/user-settings for different groups + - [x] [PR#11](https://github.com/xjlin0/attendees30/pull/11) Generic models such as Note, Place, Past need to have organization column instead of infos + - [x] [PR#12](https://github.com/xjlin0/attendees30/pull/12) Add Past as Note + - [x] [PR#13](https://github.com/xjlin0/attendees30/pull/13) [PR#14](https://github.com/xjlin0/attendees30/pull/14) [PR#15](https://github.com/xjlin0/attendees30/pull/15) Create new instance of Attendee & attending update page with params with meet + - [x] [PR#16](https://github.com/xjlin0/attendees30/pull/16) delete function for human error + - [x] [PR#5](https://github.com/xjlin0/attendees30/pull/5) Modify Attendee save method to combine/convert names by OpenCC to support searches in different text encoding, and retire db level full_name. + - [x] [PR#8](https://github.com/xjlin0/attendees30/pull/8) implement secret/private relation/past general +- [ ] Move attendee/attendees page out of data assembly -- some coworkers need to see all attendees of the organization, with a way to see only family members for general users + - [ ] remove all previous attendee edit testing pages + - [ ] remove attendee list page dependency of path params and take search params from user for assembly slug + - [ ] rename and move attendees/attendee page, and show attendees based on auth groups +- [ ] Gathering list (server side processing with auto-generation) +- [ ] Attendance list (server side processing with auto-generation) + - [ ] member list (attendance level with editing category) - [ ] Attending list (new design with server side processing) - [ ] Create roaster page (no real-time update in v1) -- [ ] Coworker roaster, X: characters, Y: dates(gatherings) -- [ ] Audit log/history/vision of data: django-pghistory maybe +- [ ] Coworker roaster on phone/web, X: characters, Y: dates(gatherings) +- [ ] Audit log/history/vision of data + - [ ] find library and install: django-pghistory maybe + - [ ] each model level version + - [ ] document aggregation level version - [ ] upgrade to Django 3.2LTS or 4 -[ ] use Django JSONField instead of Postgres JSONField -[ ] decide async or not (uvicorn high CPU usage) - [ ] deploy to AWS EC2 - [ ] Export directory booklet pdf - [ ] i18n Translation on model data, django-parler maybe? + +
diff --git a/attendees/persons/forms/__init__.py b/attendees/persons/forms/__init__.py deleted file mode 100644 index 8dbb24b0..00000000 --- a/attendees/persons/forms/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .attendee_crispy_form import AttendeeCrispyForm -from .attendees_formset_helper import AttendeesFormSetHelper -from .attendees_formset import AttendeesForm, AttendeesFormSet -from .attendee_form import AttendeeForm, AttendeeFormSet diff --git a/attendees/persons/forms/attendee_crispy_form.py b/attendees/persons/forms/attendee_crispy_form.py deleted file mode 100644 index e94ba11f..00000000 --- a/attendees/persons/forms/attendee_crispy_form.py +++ /dev/null @@ -1,50 +0,0 @@ -from django import forms -from crispy_forms.helper import FormHelper -from crispy_forms.layout import Submit, Layout, Fieldset, ButtonHolder, Row, Column - -from attendees.persons.models import Attendee - - -class AttendeeCrispyForm(forms.ModelForm): - class Meta: - model = Attendee - fields = ( - 'gender', - 'division', - 'first_name', - 'last_name', - 'first_name2', - 'last_name2', - ) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.helper = FormHelper() - self.helper.form_method = 'post' - self.helper.layout = Layout( - - Fieldset( - 'some explaining text or other addition fields, such as id: {{id}}', - ), - - Row( - Column('gender'), - Column('division'), - ), - - Row( - Column('first_name'), - Column('last_name'), - title="In English please", - ), - - Row( - Column('first_name2'), - Column('last_name2'), - title="Can be in a different language", - ), - - ButtonHolder( - Submit('submit', 'Save', css_class='btn btn-success', onclick="return confirm('Are you sure to save and submit all your changes?')") - ), - ) diff --git a/attendees/persons/forms/attendee_form.py b/attendees/persons/forms/attendee_form.py deleted file mode 100644 index 5b6a00e7..00000000 --- a/attendees/persons/forms/attendee_form.py +++ /dev/null @@ -1,20 +0,0 @@ -from django import forms -from django.forms.formsets import formset_factory - -from attendees.persons.models import Attendee - - -class AttendeeForm(forms.ModelForm): - fields = '__all__' - - class Meta: - model = Attendee - fields = "__all__" - - -AttendeeFormSet = formset_factory( - AttendeeForm, - extra=2, - max_num=2, - min_num=1 -) diff --git a/attendees/persons/forms/attendees_formset.py b/attendees/persons/forms/attendees_formset.py deleted file mode 100644 index 09e514cb..00000000 --- a/attendees/persons/forms/attendees_formset.py +++ /dev/null @@ -1,83 +0,0 @@ -from crispy_forms.bootstrap import Accordion, AccordionGroup -from django import forms -from crispy_forms.helper import FormHelper -from crispy_forms.layout import Layout, Row, Column, Field - -from attendees.persons.models import Attendee -from django.forms.models import modelformset_factory - - -class AttendeesForm(forms.ModelForm): - class Meta: - model = Attendee - fields = '__all__' - # help_texts = { - # "full_name": None, - # } - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.helper = FormHelper(self) # somehow form info is unavailable in separated helper - self.render_hidden_fields = True # https://django-crispy-forms.readthedocs.io/en/latest/form_helper.html - self.render_required_fields = True - self.helper.form_id = kwargs.get('prefix', '') - self.helper.form_method = 'post' - self.helper.form_tag = False - self.helper.disable_csrf = True - instance = kwargs.get('instance', Attendee()) - self.helper.layout = Layout( - Accordion( - AccordionGroup(instance.infos['names']['original'], - Row( - Column( - Field('gender')#, css_class='form-control changeable', disabled=True), - ), - Column( - Field('division')#, css_class='form-control changeable', disabled=True), - ), - ), - - Row( - Column( - Field('first_name')#, css_class='form-control changeable', disabled=True), - ), - Column( - Field('last_name')#, css_class='form-control changeable', disabled=True), - ), - title="In English please", - ), - - Row( - Column( - Field('first_name2')#, css_class='changeable', disabled=True), - ), - Column( - Field('last_name2')#, css_class='changeable', disabled=True), - ), - title="Can be in a different language", - ), - - Row( - Column( - Field('addresses')#, css_class='changeable', disabled=True), - ), - Column( - Field('families')#, css_class='changeable', disabled=True), - ), - ), - active=False, - css_id='attendee_' + str(instance.id), - ) - ) - ) - - -AttendeesFormSet = modelformset_factory( - Attendee, - form=AttendeesForm, - extra=0, - # max_num=2, - min_num=1 -) - - diff --git a/attendees/persons/forms/attendees_formset_helper.py b/attendees/persons/forms/attendees_formset_helper.py deleted file mode 100644 index 990304be..00000000 --- a/attendees/persons/forms/attendees_formset_helper.py +++ /dev/null @@ -1,42 +0,0 @@ -from crispy_forms.bootstrap import AccordionGroup, Accordion, UneditableField -from crispy_forms.helper import FormHelper -from crispy_forms.layout import Layout, Fieldset, Row, Column, ButtonHolder, Submit, HTML, Div - - -class AttendeesFormSetHelper(FormHelper): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.render_required_fields = True - self.form_method = 'post' - # self.layout = Layout( - # Accordion( - # AccordionGroup("{{formset_form.full_name}} in helper", - # HTML(" form {{forloop.counter}}"), - # UneditableField("full_name", css_class="form-control-plaintext"), - # Row( - # Column('gender'), - # Column('division'), - # ), - # - # Row( - # Column('first_name'), - # Column('last_name'), - # title="In English please", - # ), - # - # Row( - # Column('first_name2'), - # Column('last_name2'), - # title="Can be in a different language", - # ), - # Row( - # Column('related_ones'), - # Column('families'), - # ), - # Row( - # Column('addresses'), - # ), - # ) - # ) - # ) - self.render_required_fields = True diff --git a/attendees/persons/urls.py b/attendees/persons/urls.py index 98c95624..84be4a50 100644 --- a/attendees/persons/urls.py +++ b/attendees/persons/urls.py @@ -20,11 +20,7 @@ datagrid_assembly_data_attendees_list_view, datagrid_assembly_data_attendings_list_view, datagrid_attendee_update_view, - info_of_attendee_create_view, api_attendee_attendings_viewset, - attendee_update_view, - attendees_update_view, - attendee_detail_view, api_user_meet_attendings_viewset, api_family_organization_attendings_viewset, ) @@ -35,7 +31,7 @@ router.register( 'api/datagrid_data_attendees', api_datagrid_data_attendees_viewset, - basename='attending', + basename='attendee', ) router.register( 'api/(?P.+)/(?P.+)/assembly_meet_attendings', @@ -142,50 +138,15 @@ ), path( - "attendee_detail_view/", - view=attendee_detail_view, - name="attendee_detail_view", - ), - - path( - "attendee_detail_view/", - view=attendee_detail_view, - name="attendee_detail_view", - ), - - path( - "attendee_update_view/", - view=attendee_update_view, - name="attendee_update_view", - ), - - path( - "attendee_update_view/", - view=attendee_update_view, - name="attendee_update_view", - ), - - path( - "attendees_update_view/", - view=attendees_update_view, - name="attendees_update_view", - ), - - path( - "attendees_update_view/", - view=attendees_update_view, - name="attendees_update_view", - ), - - path( - '//datagrid_attendee_update_view/', + '//datagrid_attendee_update_view/self', view=datagrid_attendee_update_view, - name='datagrid_attendee_update_view', + name='datagrid_attendee_update_self', # null attendee_id will be replaced by request.user's attendee_id ), path( '//datagrid_attendee_update_view/new', + kwargs={'attendee_id': 'new', 'allowed_to_create_attendee': False}, view=datagrid_attendee_update_view, - name='datagrid_attendee_create_view', # for permission + name='datagrid_attendee_create_view', # for create non-family-attendee permission ), path( '//datagrid_attendee_update_view/', diff --git a/attendees/persons/views/__init__.py b/attendees/persons/views/__init__.py index 6075b99c..bd2378f7 100644 --- a/attendees/persons/views/__init__.py +++ b/attendees/persons/views/__init__.py @@ -17,9 +17,5 @@ from .page.datagrid_assembly_data_attendees import datagrid_assembly_data_attendees_list_view from .page.datagrid_assembly_data_attendings import datagrid_assembly_data_attendings_list_view from .page.datagrid_attendee_update_view import datagrid_attendee_update_view -from .page.info_of_attendee_create_view import info_of_attendee_create_view -from .page.attendee_detail_view import attendee_detail_view -from .page.attendee_update_view import attendee_update_view -from .page.attendees_update_view import attendees_update_view from .api.user_meet_attendings import api_user_meet_attendings_viewset from .api.family_organization_attendings import api_family_organization_attendings_viewset diff --git a/attendees/persons/views/page/attendee_detail_view.py b/attendees/persons/views/page/attendee_detail_view.py deleted file mode 100644 index a2702c1c..00000000 --- a/attendees/persons/views/page/attendee_detail_view.py +++ /dev/null @@ -1,66 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.http import Http404 - -from django.db.models import Q -from django.utils.decorators import method_decorator -from django.views.generic.detail import DetailView -from django.core.exceptions import ObjectDoesNotExist -from django.views.generic.edit import CreateView, UpdateView -from django.forms.models import inlineformset_factory -# ChildFormset = inlineformset_factory( -# Parent, Child, fields=('name',) -# ) -from attendees.utils.view_helpers import get_object_or_delayed_403 -from attendees.persons.models import Attendee -from attendees.users.authorization import RouteGuard - -# Todo: check attendee.user is current user or managers - - -@method_decorator([login_required], name='dispatch') -class AttendeeDetailView(RouteGuard, DetailView): - model = Attendee - template_name = 'persons/attendee_detail_view.html' - context_object_name = 'attendee' - - def get_object(self, queryset=None): - queryset = self.get_queryset() if queryset is None else queryset - return get_object_or_delayed_403(queryset) - - def get_queryset(self): - """ - data admin can check all attendee. User can check other attendee if user is that attendee's scheduler - :param queryset: attendee id may not be provided in the path params - :return: user's attendee if no attendee id provided, or the requested attendee if by scheduler, or empty set. - """ - attendee_queryset = super(AttendeeDetailView, self).get_queryset() - current_user = self.request.user - user_attendee = Attendee.objects.filter(user=current_user).first() - - if user_attendee: - user_allowed_qs = attendee_queryset if current_user.privileged else attendee_queryset.filter( - Q(from_attendee__to_attendee__id=user_attendee.id, from_attendee__scheduler=True) - | - Q(id=user_attendee.id) - ).distinct() - user_checking_id = self.kwargs.get('attendee_id', user_attendee.id) - return user_allowed_qs.filter(id=user_checking_id) - - else: - raise Http404('Your profile does not have attendee') - - - # def get_context_data(self, **kwargs): - # we need to overwrite get_context_data to make sure that our formset is rendered - # data = super().get_context_data(**kwargs) - # if self.request.POST: - # pass # data["children"] = ChildFormset(self.request.POST) - # else: - # pass # data["children"] = ChildFormset() - # return data - # pass - - -attendee_detail_view = AttendeeDetailView.as_view() - - diff --git a/attendees/persons/views/page/attendee_update_view.py b/attendees/persons/views/page/attendee_update_view.py deleted file mode 100644 index dad3414e..00000000 --- a/attendees/persons/views/page/attendee_update_view.py +++ /dev/null @@ -1,62 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.db.models import Q -from django.http import Http404 -from django.urls import reverse - -from django.utils.decorators import method_decorator -from django.views.generic.edit import UpdateView -from django.forms.models import inlineformset_factory -# ChildFormset = inlineformset_factory( -# Parent, Child, fields=('name',) -# ) -from attendees.persons.forms import AttendeeCrispyForm -from attendees.persons.models import Attendee -from attendees.users.authorization import RouteGuard - -# Todo: check attendee.user is current user or managers -from attendees.utils.view_helpers import get_object_or_delayed_403 - - -@method_decorator([login_required], name='dispatch') -class AttendeeUpdateView(RouteGuard, UpdateView): - - model = Attendee - # context_object_name = 'attendee' - form_class = AttendeeCrispyForm - template_name = 'persons/attendee_update_view.html' - - # def get_context_data(self, **kwargs): - # context = super(AttendeesUpdateView, self).get_context_data(**kwargs) - # return context - - def get_object(self, queryset=None): - queryset = self.get_queryset() if queryset is None else queryset - return get_object_or_delayed_403(queryset) - - def get_queryset(self): - """ - data admin can check all attendee. User can check other attendee if user is that attendee's scheduler - :param queryset: attendee id may not be provided in the path params - :return: user's attendee if no attendee id provided, or the requested attendee if by scheduler, or empty set. - """ - attendee_queryset = super(AttendeeUpdateView, self).get_queryset() - current_user = self.request.user - user_attendee = Attendee.objects.filter(user=current_user).first() - - if user_attendee: - user_allowed_qs = attendee_queryset if current_user.privileged else attendee_queryset.filter( - Q(from_attendee__to_attendee__id=user_attendee.id, from_attendee__scheduler=True) - | - Q(id=user_attendee.id) - ).distinct() - user_checking_id = self.kwargs.get('attendee_id', user_attendee.id) - return user_allowed_qs.filter(id=user_checking_id) - - else: - raise Http404('Your profile does not have attendee') - - def get_success_url(self): - return reverse("persons:attendee_detail_view") - - -attendee_update_view = AttendeeUpdateView.as_view() diff --git a/attendees/persons/views/page/attendees_update_view.py b/attendees/persons/views/page/attendees_update_view.py deleted file mode 100644 index dc1f3df3..00000000 --- a/attendees/persons/views/page/attendees_update_view.py +++ /dev/null @@ -1,28 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.utils.decorators import method_decorator -from django.views.generic.edit import FormView - -from attendees.persons.forms import AttendeesFormSet -from attendees.persons.services import AttendeeService - - -@method_decorator([login_required], name='dispatch') -class AttendeesUpdateView(FormView): - template_name = 'persons/attendees_update_view.html' - form_class = AttendeesFormSet - success_url = '/' - - def get_context_data(self, **kwargs): - data = super().get_context_data(**kwargs) - data['formset'] = AttendeesFormSet( - queryset=AttendeeService.get_related_ones_by_permission(self.request.user, self.kwargs.get('attendee_id')) - ) - return data - -# -# # def get_success_url(self): -# # return reverse("persons:attendee_detail_view") -# - - -attendees_update_view = AttendeesUpdateView.as_view() diff --git a/attendees/persons/views/page/datagrid_attendee_update_view.py b/attendees/persons/views/page/datagrid_attendee_update_view.py index 58fbc530..0588ee43 100644 --- a/attendees/persons/views/page/datagrid_attendee_update_view.py +++ b/attendees/persons/views/page/datagrid_attendee_update_view.py @@ -31,8 +31,8 @@ def get_context_data(self, **kwargs): current_organization_slug = self.kwargs.get('organization_slug', None) current_assembly_slug = self.kwargs.get('assembly_slug', 'cfcch_unspecified') current_assembly_id = Assembly.objects.get(slug=current_assembly_slug).id - targeting_attendee_id = 'new' if self.request.resolver_match.url_name == Menu.CREATE_VIEW_NAME else self.kwargs.get('attendee_id', self.request.user.attendee_uuid_str()) # if more logic needed when create new, a new view will be better - allowed_to_create_attendee = False if self.request.resolver_match.url_name == Menu.CREATE_VIEW_NAME else Menu.user_can_create_attendee(self.request.user) + targeting_attendee_id = self.kwargs.get('attendee_id', self.request.user.attendee_uuid_str()) # if more logic needed when create new, a new view will be better + allowed_to_create_attendee = self.kwargs.get('allowed_to_create_attendee', Menu.user_can_create_attendee(self.request.user)) context.update({ 'attendee_contenttype_id': ContentType.objects.get_for_model(Attendee).id, 'family_contenttype_id': ContentType.objects.get_for_model(Family).id, diff --git a/attendees/persons/views/page/info_of_attendee_create_view.py b/attendees/persons/views/page/info_of_attendee_create_view.py deleted file mode 100644 index fec39bf8..00000000 --- a/attendees/persons/views/page/info_of_attendee_create_view.py +++ /dev/null @@ -1,45 +0,0 @@ -from django.contrib.auth.decorators import login_required -from django.urls import reverse -from django.utils.decorators import method_decorator -from django.views.generic.edit import CreateView, UpdateView -from django.forms.models import inlineformset_factory -# ChildFormset = inlineformset_factory( -# Parent, Child, fields=('name',) -# ) -from attendees.persons.models import Attendee -from attendees.users.authorization import RouteGuard - -# Todo: check attendee.user is current user or managers - - -@method_decorator([login_required], name='dispatch') -class InfoOfAttendeeCreateView(RouteGuard, CreateView): - model = Attendee - fields = '__all__' - template_name = 'persons/info_of_attendee_create_view.html' - - def get_context_data(self, **kwargs): - # we need to overwrite get_context_data to make sure that our formset is rendered - data = super().get_context_data(**kwargs) - if self.request.POST: - pass # data["children"] = ChildFormset(self.request.POST) - else: - pass # data["children"] = ChildFormset() - return data - - def form_valid(self, form): - context = self.get_context_data() - # children = context["children"] - self.object = form.save() - # if children.is_valid(): - # children.instance = self.object - # children.save() - return super().form_valid(form) - - def get_success_url(self): - return reverse("users:detail", kwargs={"username": self.request.user.username}) - - -info_of_attendee_create_view = InfoOfAttendeeCreateView.as_view() - - diff --git a/attendees/static/js/persons/attendees_update_view.js b/attendees/static/js/persons/attendees_update_view.js deleted file mode 100644 index ed8d8d62..00000000 --- a/attendees/static/js/persons/attendees_update_view.js +++ /dev/null @@ -1,36 +0,0 @@ -Attendees.formsetUpdate = { - init: () => { - console.log("/static/js/persons/attendees_update_view.js"); - document.querySelector('a[data-toggle="collapse"]').click(); // open the first Accordion. -// Attendees.formsetUpdate.enableEditButtonSwitching(); - }, - - enableEditButtonSwitching: ()=>{ - const enableEditButton = document.querySelector('button.enable-edit'); - const editableFields = document.querySelectorAll('.changeable'); - const editButtons = document.querySelectorAll('input.cancel-edit-reset,input.submit-formset,input.reset-editing'); - enableEditButton.disabled = false; - - document.querySelector('form.attendees-formset').addEventListener('click', ()=>{ - switch(true) { - case /enable-edit/.test(event.target.className): - enableEditButton.classList.add('d-none'); - editButtons.forEach(ele => ele.classList.remove('d-none')); - editableFields.forEach(ele => ele.disabled=false) - break; - - case /cancel-edit-reset/.test(event.target.className): - enableEditButton.classList.remove('d-none'); - editButtons.forEach(ele => ele.classList.add('d-none')); - editableFields.forEach(ele => ele.disabled=true) - break; - - default: - } - }, false); - }, -} - -$(document).ready(() => { - Attendees.formsetUpdate.init(); -}); diff --git a/attendees/templates/persons/attendee_detail_view.html b/attendees/templates/persons/attendee_detail_view.html deleted file mode 100644 index 33222bb7..00000000 --- a/attendees/templates/persons/attendee_detail_view.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "base.html" %} -{% load static i18n %} -{% block title %} - Attendee Detail -{% endblock title %} - -{% block extra_css %} - -{% endblock extra_css %} - -{% block extra_head %} - -{% endblock extra_head %} - -{% block content %} - -

Attendee's details

-

attendee_detail_view.html

-

id {{ object.id }}.

-

full_name {{ object.infos.names.original }}.

-

created {{ object.created }}.

-{% endblock content %} diff --git a/attendees/templates/persons/attendee_update_view.html b/attendees/templates/persons/attendee_update_view.html deleted file mode 100644 index 19049869..00000000 --- a/attendees/templates/persons/attendee_update_view.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "base.html" %} -{% load static i18n %} -{% block title %} - Attendee Info -{% endblock title %} - -{% block extra_css %} - -{% endblock extra_css %} - -{% block extra_head %} - -{% endblock extra_head %} - -{% block content %} - -{% load crispy_forms_tags %} - -

Attendee test

-

attendee_update_view.html

-
- {% crispy form %} -
-{% endblock content %} diff --git a/attendees/templates/persons/attendees_update_view.html b/attendees/templates/persons/attendees_update_view.html deleted file mode 100644 index 2bdaf200..00000000 --- a/attendees/templates/persons/attendees_update_view.html +++ /dev/null @@ -1,79 +0,0 @@ -{% extends "base.html" %} -{% load static i18n %} -{% block title %} - Attendee Info -{% endblock title %} - -{% block extra_css %} - -{% endblock extra_css %} - -{% block extra_head %} - -{% endblock extra_head %} - -{% block content %} - -{% load crispy_forms_tags %} - - - -
- {% csrf_token %} - -
-
-

- All accessible attendees -

-
-
- - - - -
-
- - {{ formset.management_form|crispy }} - - {% for form in formset %} - {% crispy form %} - {% endfor %} -
- - - - - - -{% endblock content %} diff --git a/attendees/templates/persons/info_of_attendee_create_view.html b/attendees/templates/persons/info_of_attendee_create_view.html deleted file mode 100644 index 829c4da6..00000000 --- a/attendees/templates/persons/info_of_attendee_create_view.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends "base.html" %} -{% load static i18n %} -{% block title %} - Attendee Info -{% endblock title %} - -{% block extra_css %} - -{% endblock extra_css %} - -{% block extra_head %} - -{% endblock extra_head %} - -{% block content %} - -

Create an Attendee

-
- {% csrf_token %} - {{ form.as_p }} - -
-{% endblock content %} diff --git a/attendees/users/admin.py b/attendees/users/admin.py index 0dc294e5..bf032a2e 100644 --- a/attendees/users/admin.py +++ b/attendees/users/admin.py @@ -1,8 +1,10 @@ from django.contrib import admin, messages from django.contrib.auth import admin as auth_admin from django.contrib.auth import get_user_model +from django.contrib.postgres import fields from django.forms import TextInput from django.db import models +from django_json_widget.widgets import JSONEditorWidget from mptt.admin import MPTTModelAdmin from .models import Menu, MenuAuthGroup @@ -30,6 +32,7 @@ class MenuAuthGroupInline(admin.TabularInline): class MenuAdmin(MPTTModelAdmin): readonly_fields = ['id', 'created', 'modified'] formfield_overrides = { + fields.JSONField: {'widget': JSONEditorWidget}, models.CharField: {'widget': TextInput(attrs={'size':'100%'})}, } mptt_level_indent = 20 diff --git a/fixtures/db_seed.json b/fixtures/db_seed.json index 36e08902..3d86bd5a 100644 --- a/fixtures/db_seed.json +++ b/fixtures/db_seed.json @@ -829,7 +829,7 @@ "aria-haspopup": "true" }, "lft": 1, - "rght": 20, + "rght": 22, "tree_id": 8, "level": 0 } @@ -853,8 +853,8 @@ "class": "dropdown-item", "title": "Coworkers attendance" }, - "lft": 18, - "rght": 19, + "lft": 20, + "rght": 21, "tree_id": 8, "level": 1 } @@ -878,8 +878,8 @@ "class": "dropdown-item", "title": "My (families) attendance" }, - "lft": 16, - "rght": 17, + "lft": 18, + "rght": 19, "tree_id": 8, "level": 1 } @@ -930,8 +930,8 @@ "infos": { "class": "dropdown-item" }, - "lft": 12, - "rght": 13, + "lft": 14, + "rght": 15, "tree_id": 8, "level": 1 } @@ -1004,8 +1004,8 @@ "infos": { "class": "dropdown-divider" }, - "lft": 14, - "rght": 15, + "lft": 16, + "rght": 17, "tree_id": 8, "level": 1 } @@ -1117,7 +1117,7 @@ "pk": 15, "fields": { "created": "2020-11-20T16:12:21.803Z", - "modified": "2021-04-08T17:26:54.124Z", + "modified": "2021-07-09T16:11:51.590Z", "is_removed": false, "organization": 1, "category": "main", @@ -1125,7 +1125,7 @@ "html_type": "a", "urn": "/persons/cfcch_data_management/cfcch_congregation_data/datagrid_assembly_data_attendees/", "url_name": "datagrid_assembly_data_attendees", - "display_name": "\u6703\u54e1\u8cc7\u6599 attendee", + "display_name": "\u4ed6\u4eba\u8cc7\u6599 others info", "display_order": 3100, "infos": { "class": "dropdown-item", @@ -1137,95 +1137,20 @@ "level": 1 } }, - { - "model": "users.menu", - "pk": 16, - "fields": { - "created": "2020-12-03T06:10:03.963Z", - "modified": "2020-12-06T16:38:56.603Z", - "is_removed": false, - "organization": 1, - "category": "main", - "parent": 1, - "html_type": "a", - "urn": "/persons/attendee_detail_view/", - "url_name": "attendee_detail_view", - "display_name": "Basic Info \u57fa\u672c\u8cc7\u6599", - "display_order": 500, - "infos": { - "class": "dropdown-item", - "title": "My basic info" - }, - "lft": 2, - "rght": 3, - "tree_id": 8, - "level": 1 - } - }, - { - "model": "users.menu", - "pk": 17, - "fields": { - "created": "2020-12-06T16:32:31.311Z", - "modified": "2020-12-14T04:44:01.786Z", - "is_removed": false, - "organization": 1, - "category": "main", - "parent": 1, - "html_type": "a", - "urn": "/persons/attendee_update_view/", - "url_name": "attendee_update_view", - "display_name": "Update Info \u66f4\u65b0\u8cc7\u6599formset", - "display_order": 700, - "infos": { - "class": "dropdown-item", - "title": "Update my info" - }, - "lft": 4, - "rght": 5, - "tree_id": 8, - "level": 1 - } - }, - { - "model": "users.menu", - "pk": 18, - "fields": { - "created": "2020-12-07T15:32:43.894Z", - "modified": "2020-12-07T15:32:43.894Z", - "is_removed": false, - "organization": 1, - "category": "main", - "parent": 1, - "html_type": "a", - "urn": "/persons/attendees_update_view/", - "url_name": "attendees_update_view", - "display_name": "Update family \u5bb6\u4eba\u8cc7\u6599\u66f4\u65b0", - "display_order": 750, - "infos": { - "class": "dropdown-item", - "title": "Update my info" - }, - "lft": 10, - "rght": 11, - "tree_id": 8, - "level": 1 - } - }, { "model": "users.menu", "pk": 19, "fields": { "created": "2020-12-14T04:43:29Z", - "modified": "2021-06-11T16:31:40.749Z", + "modified": "2021-07-09T15:37:03.753Z", "is_removed": false, "organization": 1, - "category": "main", + "category": "sublink", "parent": 1, - "html_type": "a", + "html_type": "permission", "urn": "/persons/cfcch_data_management/cfcch_congregation_data/datagrid_attendee_update_view/", "url_name": "datagrid_attendee_update_view", - "display_name": "update attendee \u500b\u4eba\u8cc7\u6599", + "display_name": "update attendee \u66f4\u65b0\u4ed6\u4eba", "display_order": 725, "infos": { "class": "dropdown-item", @@ -1330,7 +1255,7 @@ "pk": 24, "fields": { "created": "2021-07-03T19:11:57.123Z", - "modified": "2021-07-03T19:14:14.267Z", + "modified": "2021-07-09T15:19:42.525Z", "is_removed": false, "organization": 1, "category": "sublink", @@ -1338,9 +1263,34 @@ "html_type": "permission", "urn": "/persons/cfcch_data_management/cfcch_congregation_data/datagrid_attendee_update_view/new", "url_name": "datagrid_attendee_create_view", - "display_name": "create attendee \u5275\u5efa\u65b0\u4eba", - "display_order": 726, + "display_name": "create attendee \u53e6\u5efa\u65b0\u4eba", + "display_order": 730, "infos": {}, + "lft": 10, + "rght": 11, + "tree_id": 8, + "level": 1 + } + }, + { + "model": "users.menu", + "pk": 25, + "fields": { + "created": "2021-07-09T15:35:48.091Z", + "modified": "2021-07-09T16:11:12.824Z", + "is_removed": false, + "organization": 1, + "category": "main", + "parent": 1, + "html_type": "a", + "urn": "/persons/cfcch_data_management/cfcch_congregation_data/datagrid_attendee_update_view/self", + "url_name": "datagrid_attendee_update_self", + "display_name": "self info \u81ea\u8eab\u8cc7\u6599", + "display_order": 726, + "infos": { + "class": "dropdown-item", + "title": "Update attendee info" + }, "lft": 8, "rght": 9, "tree_id": 8, @@ -1386,14 +1336,14 @@ }, "contacts": {}, "hostname": "192.168.99.100", - "all_access_assemblies": [ - "cfcch_congregation_data" - ], "counselor": [ "data_counselor" ], "data_admins": [ "data_organizer" + ], + "all_access_assemblies": [ + "cfcch_congregation_data" ] } } @@ -2662,8 +2612,8 @@ "display_order": 20, "emergency_contact": true, "scheduler": true, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -2679,8 +2629,8 @@ "display_order": 21, "emergency_contact": true, "scheduler": true, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -2696,8 +2646,8 @@ "display_order": 25, "emergency_contact": true, "scheduler": false, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -2713,8 +2663,8 @@ "display_order": 26, "emergency_contact": true, "scheduler": false, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -2730,8 +2680,8 @@ "display_order": 1, "emergency_contact": true, "scheduler": true, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -2747,8 +2697,8 @@ "display_order": 2, "emergency_contact": true, "scheduler": true, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -2764,8 +2714,8 @@ "display_order": 70, "emergency_contact": true, "scheduler": false, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -2781,8 +2731,8 @@ "display_order": 50, "emergency_contact": false, "scheduler": false, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -2798,8 +2748,8 @@ "display_order": 50, "emergency_contact": false, "scheduler": false, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -2815,8 +2765,8 @@ "display_order": 90, "emergency_contact": false, "scheduler": false, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -2832,8 +2782,8 @@ "display_order": 90, "emergency_contact": false, "scheduler": false, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -2849,8 +2799,8 @@ "display_order": 500, "emergency_contact": true, "scheduler": true, - "consanguinity": false, - "relative": false + "relative": false, + "consanguinity": false } }, { @@ -2866,8 +2816,8 @@ "display_order": 500, "emergency_contact": true, "scheduler": false, - "consanguinity": false, - "relative": false + "relative": false, + "consanguinity": false } }, { @@ -2883,8 +2833,8 @@ "display_order": 32766, "emergency_contact": true, "scheduler": true, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -2900,8 +2850,8 @@ "display_order": 30, "emergency_contact": true, "scheduler": true, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -2917,8 +2867,8 @@ "display_order": 30, "emergency_contact": true, "scheduler": false, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -2934,8 +2884,8 @@ "display_order": 30000, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": false + "relative": false, + "consanguinity": false } }, { @@ -2951,8 +2901,8 @@ "display_order": 10000, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": false + "relative": false, + "consanguinity": false } }, { @@ -2968,8 +2918,8 @@ "display_order": 20000, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": false + "relative": false, + "consanguinity": false } }, { @@ -2985,8 +2935,8 @@ "display_order": 20000, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": false + "relative": false, + "consanguinity": false } }, { @@ -3002,8 +2952,8 @@ "display_order": 100, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -3019,8 +2969,8 @@ "display_order": 100, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -3036,8 +2986,8 @@ "display_order": 200, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -3053,8 +3003,8 @@ "display_order": 200, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -3070,8 +3020,8 @@ "display_order": 32767, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": false + "relative": false, + "consanguinity": false } }, { @@ -3087,8 +3037,8 @@ "display_order": 31000, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": false + "relative": false, + "consanguinity": false } }, { @@ -3104,8 +3054,8 @@ "display_order": 22, "emergency_contact": true, "scheduler": false, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -3121,8 +3071,8 @@ "display_order": 32767, "emergency_contact": true, "scheduler": true, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -3138,8 +3088,8 @@ "display_order": 53, "emergency_contact": false, "scheduler": false, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -3155,8 +3105,8 @@ "display_order": 22, "emergency_contact": true, "scheduler": true, - "consanguinity": true, - "relative": true + "relative": true, + "consanguinity": true } }, { @@ -3172,8 +3122,8 @@ "display_order": 54, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": false + "relative": false, + "consanguinity": false } }, { @@ -3189,8 +3139,8 @@ "display_order": 210, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -3206,8 +3156,8 @@ "display_order": 210, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -3223,8 +3173,8 @@ "display_order": 220, "emergency_contact": false, "scheduler": false, - "consanguinity": false, - "relative": true + "relative": true, + "consanguinity": false } }, { @@ -8244,240 +8194,6 @@ "menu": 15 } }, - { - "model": "users.menuauthgroup", - "pk": 31, - "fields": { - "created": "2020-12-03T06:10:03.974Z", - "modified": "2020-12-03T06:10:03.974Z", - "is_removed": false, - "auth_group": 2, - "read": true, - "write": true, - "menu": 16 - } - }, - { - "model": "users.menuauthgroup", - "pk": 32, - "fields": { - "created": "2020-12-03T06:10:03.975Z", - "modified": "2020-12-03T06:10:03.975Z", - "is_removed": false, - "auth_group": 1, - "read": true, - "write": true, - "menu": 16 - } - }, - { - "model": "users.menuauthgroup", - "pk": 33, - "fields": { - "created": "2020-12-03T06:10:03.975Z", - "modified": "2020-12-03T06:10:03.975Z", - "is_removed": false, - "auth_group": 5, - "read": true, - "write": true, - "menu": 16 - } - }, - { - "model": "users.menuauthgroup", - "pk": 34, - "fields": { - "created": "2020-12-03T06:10:03.976Z", - "modified": "2020-12-03T06:10:03.976Z", - "is_removed": false, - "auth_group": 6, - "read": true, - "write": true, - "menu": 16 - } - }, - { - "model": "users.menuauthgroup", - "pk": 35, - "fields": { - "created": "2020-12-03T06:10:03.977Z", - "modified": "2020-12-03T06:10:03.977Z", - "is_removed": false, - "auth_group": 4, - "read": true, - "write": true, - "menu": 16 - } - }, - { - "model": "users.menuauthgroup", - "pk": 36, - "fields": { - "created": "2020-12-03T06:10:03.978Z", - "modified": "2020-12-03T06:10:03.978Z", - "is_removed": false, - "auth_group": 3, - "read": true, - "write": true, - "menu": 16 - } - }, - { - "model": "users.menuauthgroup", - "pk": 37, - "fields": { - "created": "2020-12-06T16:32:31.318Z", - "modified": "2020-12-06T16:32:31.318Z", - "is_removed": false, - "auth_group": 2, - "read": true, - "write": true, - "menu": 17 - } - }, - { - "model": "users.menuauthgroup", - "pk": 38, - "fields": { - "created": "2020-12-06T16:32:31.319Z", - "modified": "2020-12-06T16:32:31.319Z", - "is_removed": false, - "auth_group": 1, - "read": true, - "write": true, - "menu": 17 - } - }, - { - "model": "users.menuauthgroup", - "pk": 39, - "fields": { - "created": "2020-12-06T16:32:31.320Z", - "modified": "2020-12-06T16:32:31.320Z", - "is_removed": false, - "auth_group": 6, - "read": true, - "write": true, - "menu": 17 - } - }, - { - "model": "users.menuauthgroup", - "pk": 40, - "fields": { - "created": "2020-12-06T16:32:31.321Z", - "modified": "2020-12-06T16:32:31.321Z", - "is_removed": false, - "auth_group": 4, - "read": true, - "write": true, - "menu": 17 - } - }, - { - "model": "users.menuauthgroup", - "pk": 41, - "fields": { - "created": "2020-12-06T16:32:31.321Z", - "modified": "2020-12-06T16:32:31.321Z", - "is_removed": false, - "auth_group": 3, - "read": true, - "write": true, - "menu": 17 - } - }, - { - "model": "users.menuauthgroup", - "pk": 42, - "fields": { - "created": "2020-12-06T16:32:31.322Z", - "modified": "2020-12-06T16:32:31.322Z", - "is_removed": false, - "auth_group": 5, - "read": true, - "write": true, - "menu": 17 - } - }, - { - "model": "users.menuauthgroup", - "pk": 43, - "fields": { - "created": "2020-12-07T15:32:43.904Z", - "modified": "2020-12-07T15:32:43.904Z", - "is_removed": false, - "auth_group": 2, - "read": true, - "write": true, - "menu": 18 - } - }, - { - "model": "users.menuauthgroup", - "pk": 44, - "fields": { - "created": "2020-12-07T15:32:43.905Z", - "modified": "2020-12-07T15:32:43.905Z", - "is_removed": false, - "auth_group": 1, - "read": true, - "write": true, - "menu": 18 - } - }, - { - "model": "users.menuauthgroup", - "pk": 45, - "fields": { - "created": "2020-12-07T15:32:43.906Z", - "modified": "2020-12-07T15:32:43.906Z", - "is_removed": false, - "auth_group": 5, - "read": true, - "write": true, - "menu": 18 - } - }, - { - "model": "users.menuauthgroup", - "pk": 46, - "fields": { - "created": "2020-12-07T15:32:43.907Z", - "modified": "2020-12-07T15:32:43.907Z", - "is_removed": false, - "auth_group": 6, - "read": true, - "write": true, - "menu": 18 - } - }, - { - "model": "users.menuauthgroup", - "pk": 47, - "fields": { - "created": "2020-12-07T15:32:43.908Z", - "modified": "2020-12-07T15:32:43.908Z", - "is_removed": false, - "auth_group": 4, - "read": true, - "write": true, - "menu": 18 - } - }, - { - "model": "users.menuauthgroup", - "pk": 48, - "fields": { - "created": "2020-12-07T15:32:43.909Z", - "modified": "2020-12-07T15:32:43.909Z", - "is_removed": false, - "auth_group": 3, - "read": true, - "write": true, - "menu": 18 - } - }, { "model": "users.menuauthgroup", "pk": 49, @@ -8764,6 +8480,84 @@ "menu": 24 } }, + { + "model": "users.menuauthgroup", + "pk": 71, + "fields": { + "created": "2021-07-09T15:35:48.129Z", + "modified": "2021-07-09T15:35:48.129Z", + "is_removed": false, + "auth_group": 2, + "read": true, + "write": true, + "menu": 25 + } + }, + { + "model": "users.menuauthgroup", + "pk": 72, + "fields": { + "created": "2021-07-09T15:35:48.136Z", + "modified": "2021-07-09T15:35:48.136Z", + "is_removed": false, + "auth_group": 1, + "read": true, + "write": true, + "menu": 25 + } + }, + { + "model": "users.menuauthgroup", + "pk": 73, + "fields": { + "created": "2021-07-09T15:35:48.141Z", + "modified": "2021-07-09T15:35:48.141Z", + "is_removed": false, + "auth_group": 5, + "read": true, + "write": true, + "menu": 25 + } + }, + { + "model": "users.menuauthgroup", + "pk": 74, + "fields": { + "created": "2021-07-09T15:35:48.144Z", + "modified": "2021-07-09T15:35:48.144Z", + "is_removed": false, + "auth_group": 6, + "read": true, + "write": true, + "menu": 25 + } + }, + { + "model": "users.menuauthgroup", + "pk": 75, + "fields": { + "created": "2021-07-09T15:35:48.144Z", + "modified": "2021-07-09T15:35:48.144Z", + "is_removed": false, + "auth_group": 4, + "read": true, + "write": true, + "menu": 25 + } + }, + { + "model": "users.menuauthgroup", + "pk": 76, + "fields": { + "created": "2021-07-09T15:35:48.145Z", + "modified": "2021-07-09T15:35:48.145Z", + "is_removed": false, + "auth_group": 3, + "read": true, + "write": true, + "menu": 25 + } + }, { "model": "whereabouts.division", "pk": 0, @@ -9235,7 +9029,7 @@ "pk": "a48e4375-1a64-4c4f-beb3-8f088bf77340", "fields": { "created": "1999-04-11T19:31:50.659Z", - "modified": "2020-12-10T21:29:11.306Z", + "modified": "2021-07-09T15:49:13.626Z", "is_removed": false, "division": 1, "user": 1, @@ -9257,11 +9051,14 @@ }, "infos": { "fixed": { + "food_pref": "hh", "test_long_text": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }, "names": { "nick": "root", - "original": "superuser1 system admin1 Lastnam2superuser2" + "original": "superuser1 system admin1 Lastnam2superuser2", + "simplified": "Lastnam2superuser2", + "traditional": "Lastnam2superuser2" }, "contacts": { "email1": "5greata@email.com",