Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add note as past in attendee detail page #12

Merged
merged 11 commits into from
Jun 14, 2021
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,16 @@ https://dbdiagram.io/d/5d5ff66eced98361d6dddc48

## Todo:
- [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)
- [ ] 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] Generic models such as Note, Place, Past need to have organization column instead of infos
- [x] Add Past as Note
- [ ] Create new instance of Attendee & attending update page with params with meet
- [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
Expand Down
2 changes: 2 additions & 0 deletions attendees/context_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def common_variables(request): # TODO move organization info to view
auth_groups__in=request.user.groups.all(),
category='main',
menuauthgroup__read=True,
organization=request.user.organization, # maybe None
is_removed=False,
).distinct()
if request.user.is_authenticated and request.user.organization:
user_organization = request.user.organization
Expand Down
34 changes: 26 additions & 8 deletions attendees/persons/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,33 @@ class PastAdmin(admin.ModelAdmin):
# Todo 20210528 combine with NoteAdmin's show_secret
search_fields = ('id', 'display_name', 'infos')
readonly_fields = ['id', 'created', 'modified']
list_display = ('subject', 'category', 'display_order', 'display_name', 'start')
list_display = ('subject', 'category', 'display_order', 'display_name', 'when')

def get_queryset(self, request):
qs = super().get_queryset(request)
counseling_category = Category.objects.get(type='note', display_name=Past.COUNSELING)

if request.resolver_match.func.__name__ == 'changelist_view':
messages.warning(request, 'Not all, but only those records accessible to you will be listed here.')
requester_permission = {'infos__show_secret__' + request.user.attendee_uuid_str(): True}
return qs.filter(
Q(organization=request.user.organization),
( Q(**requester_permission) | Q(infos__show_secret={}) | Q(infos__show_secret__isnull=True) ),
)

if request.user.is_counselor():
counselors_permission = {'infos__show_secret__' + Past.ALL_COUNSELORS: True}
return qs.filter(
Q(organization=request.user.organization),
(~Q(category=counseling_category)
|
(Q(category=counseling_category) and (Q(**requester_permission)
|
Q(**counselors_permission))
)),
)

else:
return qs.filter(
Q(organization=request.user.organization),
( Q(**requester_permission) | Q(infos__show_secret={}) | Q(infos__show_secret__isnull=True) ),
).exclude(category=counseling_category)


class FamilyAdmin(admin.ModelAdmin):
Expand Down Expand Up @@ -153,21 +169,23 @@ class NoteAdmin(SummernoteModelAdmin):

def get_queryset(self, request): # even super user cannot see all in DjangoAdmin
qs = super().get_queryset(request)
counseling_category = Category.objects.get(type='note', display_name=Note.COUNSELING)

if request.resolver_match.func.__name__ == 'changelist_view':
messages.warning(request, 'Not all, but only those notes accessible to you will be listed here.')
if request.user.is_counselor():
requester_permission = {'infos__show_secret__' + request.user.attendee_uuid_str(): True}
counselors_permission = {'infos__show_secret__' + Note.ALL_COUNSELORS: True}
return qs.filter(
Q(organization=request.user.organization),
(~Q(category=Note.COUNSELING)
(~Q(category=counseling_category)
|
(Q(category=Note.COUNSELING) and (Q(**requester_permission)
(Q(category=counseling_category) and (Q(**requester_permission)
|
Q(**counselors_permission))
)),
)
return qs.filter(organization=request.user.organization).exclude(category=Note.COUNSELING)
return qs.filter(organization=request.user.organization).exclude(category=counseling_category)


class RelationshipAdmin(admin.ModelAdmin):
Expand Down
8 changes: 4 additions & 4 deletions attendees/persons/migrations/0002_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ class Migration(migrations.Migration):
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
('is_removed', models.BooleanField(default=False)),
('content_type', models.ForeignKey(on_delete=models.SET(0), to='contenttypes.ContentType')),
('display_order', models.SmallIntegerField(blank=False, default=0, null=False)),
('organization', models.ForeignKey(on_delete=models.SET(0), to='whereabouts.Organization')),
('category', models.ForeignKey(help_text="subtype: for note it's public/counseling sub-types etc", on_delete=models.SET(0), to='persons.Category')),
('display_order', models.SmallIntegerField(blank=False, default=0, null=False)),
('content_type', models.ForeignKey(on_delete=models.SET(0), to='contenttypes.ContentType')),
('object_id', models.CharField(max_length=36)),
('category', models.CharField(default='normal', help_text='normal, for-address, etc', max_length=20, blank=False, null=False, db_index=True)),
('body', models.TextField()),
('infos', JSONField(blank=True, default=Utility.relationship_infos, help_text='Example: {"owner": "John"}. Please keep {} here even no data', null=True)),
],
options={
'db_table': 'persons_notes',
'ordering': ('organization', 'content_type', 'display_order', '-modified',),
'ordering': ('organization', 'category', 'content_type', 'object_id', 'display_order', '-modified',),
},
),
migrations.AddIndex(
Expand Down
12 changes: 6 additions & 6 deletions attendees/persons/migrations/0003_past.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class Migration(migrations.Migration):
dependencies = [
('persons', '0002_note'),
]

operations = [
migrations.CreateModel(
name='Past',
Expand All @@ -26,19 +25,19 @@ class Migration(migrations.Migration):
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
('is_removed', models.BooleanField(default=False)),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('object_id', models.CharField(max_length=36)),
('organization', models.ForeignKey(on_delete=models.SET(0), to='whereabouts.Organization')),
('category', models.ForeignKey(help_text="subtype: for education it's primary/high/college sub-types etc", on_delete=models.SET(0), to='persons.Category')),
('display_order', models.SmallIntegerField(db_index=True, default=30000)),
('organization', models.ForeignKey(on_delete=models.SET(0), to='whereabouts.Organization')),
('start', models.DateTimeField(blank=True, default=attendees.persons.models.utility.Utility.now_with_timezone, null=True)),
('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')),
('object_id', models.CharField(max_length=36)),
('when', models.DateTimeField(blank=True, default=attendees.persons.models.utility.Utility.now_with_timezone, null=True)),
('finish', models.DateTimeField(blank=True, null=True)),
('display_name', models.CharField(blank=True, max_length=50, null=True)),
('infos', django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=attendees.persons.models.utility.Utility.relationship_infos, help_text='Example: {"show_secret": {"attendee1id": true, "attendee2id": false}}. Please keep {} here even no data', null=True)),
],
options={
'db_table': 'persons_pasts',
'ordering': ('organization', 'category__type', 'display_order', 'category__display_order', 'start'),
'ordering': ('organization', 'category__type', 'display_order', 'category__display_order', 'when'),
},
bases=(models.Model, attendees.persons.models.utility.Utility),
),
Expand All @@ -47,3 +46,4 @@ class Migration(migrations.Migration):
index=django.contrib.postgres.indexes.GinIndex(fields=['infos'], name='past_infos_gin'),
),
]

4 changes: 2 additions & 2 deletions attendees/persons/migrations/0007_relationship_m2m.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class Migration(migrations.Migration):
('scheduler', models.BooleanField('to_attendee is the scheduler?', default=False, null=False, blank=False, help_text="[from_attendee decide:] to_attendee can view/change the schedules of the from_attendee?")),
('from_attendee', models.ForeignKey(on_delete=models.CASCADE, related_name='from_attendee', to='persons.Attendee')),
('to_attendee', models.ForeignKey(on_delete=models.CASCADE, related_name='to_attendee', to='persons.Attendee')),
('start', models.DateField(blank=True, null=True)),
('finish', models.DateTimeField(null=False, blank=False, default=Utility.forever, help_text='The relation will be ended at when')),
('start', models.DateTimeField(blank=True, null=True)),
('finish', models.DateTimeField(null=True, blank=True, help_text='The relation will be ended at when')),
('in_family', models.ForeignKey(blank=True, null=True, on_delete=models.SET_NULL, related_name='in_family', to='persons.Family')),
('infos', JSONField(blank=True, null=True, default=Utility.relationship_infos, help_text='Example: {"show_secret": {"attendee1id": true, "attendee2id": false}}. Please keep {} here even no data')),
],
Expand Down
4 changes: 2 additions & 2 deletions attendees/persons/models/note.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Note(UUIDModel, TimeStampedModel, SoftDeletableModel):
content_type = models.ForeignKey(ContentType, on_delete=models.SET(0))
object_id = models.CharField(max_length=36)
content_object = GenericForeignKey('content_type', 'object_id')
category = models.CharField(max_length=20, default='normal', blank=False, null=False, db_index=True, help_text="normal, for-address, etc")
category = models.ForeignKey('persons.Category', null=False, blank=False, on_delete=models.SET(0), help_text="subtype: for note it's public/counseling sub-types etc")
organization = models.ForeignKey('whereabouts.Organization', null=False, blank=False, on_delete=models.SET(0))
display_order = models.SmallIntegerField(default=0, blank=False, null=False)
body = models.TextField()
Expand All @@ -26,7 +26,7 @@ def __str__(self):

class Meta:
db_table = 'persons_notes'
ordering = ('organization', 'content_type', 'display_order', '-modified',)
ordering = ('organization', 'category', 'content_type', 'object_id', 'display_order', '-modified',)
indexes = [
GinIndex(fields=['infos'], name='note_infos_gin', ),
]
Expand Down
11 changes: 6 additions & 5 deletions attendees/persons/models/past.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@
from django.contrib.postgres.fields.jsonb import JSONField
from django.contrib.postgres.indexes import GinIndex
from model_utils.models import TimeStampedModel, SoftDeletableModel, UUIDModel
from private_storage.fields import PrivateFileField
from . import Utility, Note
from . import Utility#, Note


class Past(UUIDModel, TimeStampedModel, SoftDeletableModel, Utility):
notes = GenericRelation(Note)
COUNSELING = 'counseling' # for private data, and only assigned counselors
ALL_COUNSELORS = 'all_counselors_' # for private data, but accessible to all counselors
#notes = GenericRelation(Note)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=False, blank=False)
object_id = models.CharField(max_length=36, null=False, blank=False)
subject = GenericForeignKey('content_type', 'object_id')
start = models.DateTimeField(null=True, blank=True, default=Utility.now_with_timezone)
when = models.DateTimeField(null=True, blank=True, default=Utility.now_with_timezone)
finish = models.DateTimeField(null=True, blank=True)
category = models.ForeignKey('persons.Category', null=False, blank=False, on_delete=models.SET(0), help_text="subtype: for education it's primary/high/college sub-types etc")
display_order = models.SmallIntegerField(default=30000, blank=False, null=False, db_index=True)
Expand All @@ -23,7 +24,7 @@ class Past(UUIDModel, TimeStampedModel, SoftDeletableModel, Utility):

class Meta:
db_table = 'persons_pasts'
ordering = ('organization', 'category__type', 'display_order', 'category__display_order', 'start')
ordering = ('organization', 'category__type', 'display_order', 'category__display_order', 'when')
indexes = [
GinIndex(fields=['infos'], name='past_infos_gin', ),
]
Expand Down
4 changes: 2 additions & 2 deletions attendees/persons/models/relationship.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ class Relationship(UUIDModel, TimeStampedModel, SoftDeletableModel, Utility):
emergency_contact = models.BooleanField('to_attendee is the emergency contact?', null=False, blank=False, default=False, help_text="[from_attendee decide:] Notify to_attendee of from_attendee's emergency?")
scheduler = models.BooleanField('to_attendee is the scheduler?', null=False, blank=False, default=False, help_text="[from_attendee decide:] to_attendee can view/change the schedules of the from_attendee?")
in_family = models.ForeignKey('persons.Family', null=True, blank=True, on_delete=models.SET_NULL, related_name="in_family")
start = models.DateField(null=True, blank=True)
finish = models.DateTimeField(blank=False, null=False, default=Utility.forever, help_text="The relation will be ended at when")
start = models.DateTimeField(null=True, blank=True)
finish = models.DateTimeField(blank=True, null=True, help_text="The relation will be ended at when")
infos = JSONField(null=True, blank=True, default=Utility.relationship_infos, help_text='Example: {"show_secret": {"attendee1id": true, "attendee2id": false}}. Please keep {} here even no data') # compare to NoteAdmin

class Meta:
Expand Down
2 changes: 1 addition & 1 deletion attendees/persons/models/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def attendee_infos():

@staticmethod
def relationship_infos():
return {"show_secret": {}, "comment": None}
return {"show_secret": {}, "comment": None, "body": None}

@staticmethod
def forever(): # 1923 years from now
Expand Down
2 changes: 1 addition & 1 deletion attendees/persons/serializers/past_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ class PastSerializer(serializers.ModelSerializer):

class Meta: # It is critical not to have organization in the fields, to let perform_create set it
model = Past
fields = ('id', 'display_name', 'category', 'start', 'finish', 'infos', 'content_type', 'object_id')
fields = ('id', 'display_name', 'category', 'when', 'finish', 'infos', 'content_type', 'object_id')

Loading