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

Rename attendee pages #19

Merged
merged 8 commits into from
Jul 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,10 +115,10 @@ All libraries are included to facilitate offline development
- [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
- [x] 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
- [x] [PR#17](https://github.com/xjlin0/attendees30/pull/17) remove all previous attendee edit testing pages
- [x] 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
- [x] [PR#18](https://github.com/xjlin0/attendees30/pull/18) remove attendee list page dependency of path params and take search params from user for assembly slug
- [x] [PR#19](https://github.com/xjlin0/attendees30/pull/19) 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)
Expand All @@ -137,3 +137,12 @@ All libraries are included to facilitate offline development
- [ ] i18n Translation on model data, django-parler maybe?

</details>

## Issues:

<details>
<summary>Click to expand all</summary>

- [ ] for ordinary users:
-[ ] even as scheduler seeing other's attendee detail view, the joined meet doesn't show group name (i.e. Hagar cannot see Ishmael in "the Rock")
</details>
18 changes: 17 additions & 1 deletion attendees/persons/models/attendee.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,17 @@ def under_same_org_with(self, other_attendee_id):
return Attendee.objects.filter(pk=other_attendee_id, division__organization=self.division.organization).exists()
return False

def can_schedule_attendee(self, other_attendee_id):
if str(self.id) == other_attendee_id:
return True
return self.__class__.objects.filter(
(Q(from_attendee__finish__isnull=True) | Q(from_attendee__finish__gt=Utility.now_with_timezone())),
from_attendee__to_attendee__id=self.id,
from_attendee__from_attendee__id=other_attendee_id,
from_attendee__scheduler=True,
from_attendee__is_removed=False,
).exists()

def scheduling_attendees(self):
"""
:return: all attendees that can be scheduled by the self(included) based on relationships. For example, if a kid
Expand All @@ -97,7 +108,12 @@ def scheduling_attendees(self):
return self.__class__.objects.filter(
Q(id=self.id)
|
Q(from_attendee__to_attendee__id=self.id, from_attendee__scheduler=True)
Q(
(Q(from_attendee__finish__isnull=True) | Q(from_attendee__finish__gt=Utility.now_with_timezone())),
from_attendee__to_attendee__id=self.id,
from_attendee__scheduler=True,
from_attendee__is_removed=False,
)
).distinct()

@cached_property
Expand Down
8 changes: 7 additions & 1 deletion attendees/persons/services/attendee_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,15 @@ def filter_parser(filters_list, meets, current_user):
or_query.add(AttendeeService.filter_parser(or_element, meets, current_user), Q.OR)
return or_query
elif filters_list[1] == '=':
return Q(**{filters_list[0].replace('.', '__'): filters_list[2]})
return Q(**{AttendeeService.field_convert(filters_list[0], meets, current_user): filters_list[2]})
elif filters_list[1] == 'startswith':
return Q(**{AttendeeService.field_convert(filters_list[0], meets, current_user) + '__istartswith': filters_list[2]})
elif filters_list[1] == 'endswith':
return Q(**{AttendeeService.field_convert(filters_list[0], meets, current_user) + '__iendswith': filters_list[2]})
elif filters_list[1] == 'contains':
return Q(**{AttendeeService.field_convert(filters_list[0], meets, current_user) + '__icontains': filters_list[2]})
elif filters_list[1] == '<>':
return ~Q(**{AttendeeService.field_convert(filters_list[0], meets, current_user): filters_list[2]})
return Q()

@staticmethod
Expand Down
30 changes: 15 additions & 15 deletions attendees/persons/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
api_datagrid_data_familyattendees_viewset,
api_attendee_relationships_viewset,
datagrid_assembly_all_attendings_list_view,
datagrid_assembly_data_attendees_list_view,
attendees_list_view,
datagrid_assembly_data_attendings_list_view,
datagrid_attendee_update_view,
attendee_update_view,
api_attendee_attendings_viewset,
api_user_meet_attendings_viewset,
api_family_organization_attendings_viewset,
Expand Down Expand Up @@ -126,9 +126,9 @@
),

path(
"<slug:division_slug>/<slug:assembly_slug>/datagrid_assembly_data_attendees/",
view=datagrid_assembly_data_attendees_list_view,
name="datagrid_assembly_data_attendees",
'attendees/',
view=attendees_list_view,
name='attendees_list_view',
),

path(
Expand All @@ -138,19 +138,19 @@
),

path(
'datagrid_attendee_update_view/self',
view=datagrid_attendee_update_view,
name='datagrid_attendee_update_self', # null attendee_id will be replaced by request.user's attendee_id
'attendee/self',
view=attendee_update_view,
name='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', 'can_create_nonfamily_attendee': False},
view=datagrid_attendee_update_view,
name='datagrid_attendee_create_view', # for create non-family-attendee permission
'attendee/new',
kwargs={'attendee_id': 'new', 'show_create_nonfamily_attendee': False},
view=attendee_update_view,
name='attendee_create_view', # for create non-family-attendee permission
),
path(
'datagrid_attendee_update_view/<str:attendee_id>',
view=datagrid_attendee_update_view,
name='datagrid_attendee_update_view',
'attendee/<str:attendee_id>',
view=attendee_update_view,
name='attendee_update_view',
),
]
4 changes: 2 additions & 2 deletions attendees/persons/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
from .api.datagrid_data_familyattendees import api_datagrid_data_familyattendees_viewset
from .api.assembly_meet_attendees import api_assembly_meet_attendees_viewset
from .page.datagrid_assembly_all_attendings import datagrid_assembly_all_attendings_list_view
from .page.datagrid_assembly_data_attendees import datagrid_assembly_data_attendees_list_view
from .page.attendees_list_view import 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.attendee_update_view import attendee_update_view
from .api.user_meet_attendings import api_user_meet_attendings_viewset
from .api.family_organization_attendings import api_family_organization_attendings_viewset
2 changes: 1 addition & 1 deletion attendees/persons/views/api/attendee_attendings.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def get_queryset(self):
current_user_organization = self.request.user.organization
is_self = current_user_organization and self.request.user.attendee and self.request.user.attendee == target_attendee
is_privileged = current_user_organization and self.request.user.privileged()
if target_attendee and (is_self or is_privileged): # Todo: scheduler should be able to do it too
if target_attendee and (is_self or is_privileged or self.request.user.attendee.can_schedule_attendee(target_attendee.id)):
attending_id = self.kwargs.get('pk')
qs = Attending.objects.filter(
attendee=target_attendee,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
from attendees.utils.view_helpers import get_object_or_delayed_403


class DatagridAttendeeUpdateView(LoginRequiredMixin, RouteAndSpyGuard, UpdateView):
class AttendeeUpdateView(LoginRequiredMixin, RouteAndSpyGuard, UpdateView):
model = Attendee
fields = '__all__'
template_name = 'persons/datagrid_attendee_update_view.html'
template_name = 'persons/attendee_update_view.html'

def get_object(self, queryset=None):
# queryset = self.get_queryset() if queryset is None else queryset
Expand All @@ -32,12 +32,12 @@ def get_context_data(self, **kwargs):
# current_assembly_slug = self.kwargs.get('assembly_slug', 'cfcch_unspecified')
# current_assembly_id = Assembly.objects.get(slug=current_assembly_slug).id
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
can_create_nonfamily_attendee = self.kwargs.get('can_create_nonfamily_attendee', Menu.user_can_create_attendee(self.request.user))
show_create_nonfamily_attendee = self.kwargs.get('show_create_nonfamily_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,
'empty_image_link': f"{settings.STATIC_URL}images/empty.png",
'can_create_nonfamily_attendee': can_create_nonfamily_attendee,
'show_create_nonfamily_attendee': show_create_nonfamily_attendee,
'characters_endpoint': '/occasions/api/user_assembly_characters/',
'meets_endpoint': '/occasions/api/user_assembly_meets/',
'attendingmeets_endpoint': '/persons/api/datagrid_data_attendingmeet/',
Expand All @@ -58,12 +58,12 @@ def get_context_data(self, **kwargs):
# 'current_organization_slug': current_organization_slug,
# 'current_division_slug': current_division_slug,
# 'current_assembly_id': current_assembly_id,
'attendee_urn': f"/persons/datagrid_attendee_update_view/",
'attendee_urn': f"/persons/attendee/",
})
return context

def render_to_response(self, context, **kwargs):
if self.request.user.attendee.under_same_org_with(context['targeting_attendee_id']):
def render_to_response(self, context, **kwargs): # attendee_id "new" only happened in attendee_create_view checked by RouteAndSpyGuard
if context['targeting_attendee_id'] == 'new' or self.request.user.attendee.under_same_org_with(context['targeting_attendee_id']):
if self.request.is_ajax():
pass

Expand All @@ -75,5 +75,5 @@ def render_to_response(self, context, **kwargs):
raise Http404('Have you registered any events of the organization?')


datagrid_attendee_update_view = DatagridAttendeeUpdateView.as_view()
attendee_update_view = AttendeeUpdateView.as_view()

Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@


@method_decorator([login_required], name='dispatch')
class DatagridAssemblyDataAttendeesListView(RouteGuard, ListView):
class AttendeesListView(RouteGuard, ListView):
queryset = []
template_name = 'persons/datagrid_assembly_data_attendees.html'
template_name = 'persons/attendees_list_view.html'

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
Expand All @@ -32,7 +32,7 @@ def get_context_data(self, **kwargs):
'family_attendances_urn': family_attendances_menu.urn if family_attendances_menu else None,
'available_meets_json': dumps(list(available_meets)),
'allowed_to_create_attendee': allowed_to_create_attendee,
'create_attendee_urn': f'/persons/datagrid_attendee_update_view/new',
'create_attendee_urn': f'/persons/attendee/new',
})
return context

Expand All @@ -47,7 +47,7 @@ def render_to_response(self, context, **kwargs): # view only provides empty tab
context.update({'divisions_endpoint': f"/whereabouts/api/user_divisions/"})
# context.update({'teams_endpoint': f"/occasions/api/{context['current_division_slug']}/{context['current_assembly_slug']}/assembly_meet_teams/"})
# context.update({'attendees_endpoint': f"/persons/api/{context['current_division_slug']}/{context['current_assembly_slug']}/assembly_meet_attendees/"})
context.update({'attendee_urn': f"/persons/datagrid_attendee_update_view/"})
context.update({'attendee_urn': f"/persons/attendee/"})
# context.update({'gatherings_endpoint': f"/occasions/api/{context['current_division_slug']}/{context['current_assembly_slug']}/assembly_meet_gatherings/"})
# context.update({'characters_endpoint': f"/occasions/api/{context['current_division_slug']}/{context['current_assembly_slug']}/assembly_meet_characters/"})
# context.update({'attendings_endpoint': f"/persons/api/{context['current_division_slug']}/{context['current_assembly_slug']}/data_attendings/"})
Expand All @@ -64,4 +64,4 @@ def render_to_response(self, context, **kwargs): # view only provides empty tab
# return ''


datagrid_assembly_data_attendees_list_view = DatagridAssemblyDataAttendeesListView.as_view()
attendees_list_view = AttendeesListView.as_view()
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Attendees.datagridUpdate = {
},

init: () => {
console.log('/static/js/persons/datagrid_attendee_update_view.js');
console.log('/static/js/persons/attendee_update_view.js');
Attendees.datagridUpdate.displayNotifierFromSearchParam('success');
Attendees.datagridUpdate.initAttendeeForm();
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Attendees.dataAttendees = {
meetTagBox: null,
init: () => {
console.log("attendees/static/js/persons/datagrid_assembly_data_attendees.js");
console.log("attendees/static/js/persons/attendees_list_view.js");
Attendees.utilities.setAjaxLoaderOnDevExtreme();
Attendees.dataAttendees.startMeetSelector();
Attendees.dataAttendees.setDataAttrs();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

<script defer
type="text/javascript"
src="{% static 'js/persons/datagrid_attendee_update_view.js' %}">
src="{% static 'js/persons/attendee_update_view.js' %}">
</script>
{% endblock extra_head %}

Expand All @@ -44,7 +44,7 @@ <h3 class="page-title">
</span>
</div>
<div class="d-flex align-items-center form-actions">
{% if can_create_nonfamily_attendee %}
{% if show_create_nonfamily_attendee %}
<a class="add-attendee btn btn-outline-success btn-xs"
title="add a new attendee"
role="button"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

<script defer
type="text/javascript"
src="{% static 'js/persons/datagrid_assembly_data_attendees.js' %}">
src="{% static 'js/persons/attendees_list_view.js' %}">
</script>

{% endblock extra_head %}
Expand Down
10 changes: 5 additions & 5 deletions attendees/users/authorization/route_guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@ def handle_no_permission(self):
class SpyGuard(UserPassesTestMixin):

def test_func(self): # Superusers can still access such attendee in admin UI
targeting_attendee_id = self.request.META.get('HTTP_X_TARGET_ATTENDEE_ID', self.kwargs.get('attendee_id'))
targeting_attendee_id = self.request.META.get('HTTP_X_TARGET_ATTENDEE_ID', self.kwargs.get('attendee_id', self.request.user.attendee_uuid_str()))
current_attendee = self.request.user.attendee

if targeting_attendee_id == 'new':
return Menu.user_can_create_attendee(self.request.user) # assume url is datagrid_attendee_create_view
return Menu.user_can_create_attendee(self.request.user) # create nonfamily attendee is attendee_create_view
if targeting_attendee_id:
if current_attendee:
if str(current_attendee.id) == targeting_attendee_id:
return True
if current_attendee.under_same_org_with(targeting_attendee_id):
return True # Todo: check relation/scheduler permission for non-dada-admin
else:
return True
return self.request.user.privileged() or current_attendee.can_schedule_attendee(targeting_attendee_id)

time.sleep(2)
return False
Expand Down
4 changes: 2 additions & 2 deletions attendees/users/models/menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@


class Menu(MPTTModel, TimeStampedModel, SoftDeletableModel):
CREATE_VIEW_NAME = 'datagrid_attendee_create_view'
ATTENDEE_CREATE_VIEW_NAME = 'attendee_create_view'

id = models.BigAutoField(
auto_created=True,
Expand Down Expand Up @@ -109,7 +109,7 @@ def organization_slug(self):
return self.organization.slug if self.organization else ''

@staticmethod
def user_can_create_attendee(user, url_name=CREATE_VIEW_NAME):
def user_can_create_attendee(user, url_name=ATTENDEE_CREATE_VIEW_NAME):
return Menu.objects.filter(
auth_groups__in=user.groups.all(),
url_name=url_name,
Expand Down
Loading