Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
{% with table.rows as table_rows %}
{% autopaginate table_rows page_size %}
<table class="floating striped">
{% if max_matches %}
<caption>
<strong class="num_results">Over {{ max_matches }}</strong> Results
</caption>
{% else %}
<caption>
<strong class="num_results">{{ table.rows|length }}</strong> Result{{ table.rows|length|pluralize }}
</caption>
{% endif %}
<tr class="heading odd">
{% if can_edit_any %}
<th>{{ table.columns.edit_link }}</th>
{% endif %}
<th>{% sortable_column table.columns.name_latest_link %}</th>
<th>{{ table.columns.preview }}</th>
<th>{% sortable_column table.columns.category %}</th>
<th>{% sortable_column table.columns.bloc %}</th>
<th>{% sortable_column table.columns.revision_date %}</th>
</tr>
{% for row in table_rows %}
<tr class="{% cycle 'even' 'odd' %}">
{% if can_edit_any %}
<td>{{ row.edit_link }}</td>
{% endif %}
<td>{{ row.name_latest_link }}</td>
<td class="doc_text_preview">{{ row.preview }}</td>
<td>{{ row.category }}</td>
<td>{{ row.bloc }}</td>
<td class="date">{{ row.revision_date }}</td>
</tr>
{% endfor %}
</table>
{% paginate %}
{% endwith %}
from django_tables import ModelTable, Column
from pstat.authorization.templatetags.user_display import user_display
from pstat.authorization.user_auth import UserAuth
from pstat.certification.models import ReadReceipt
from pstat.core.templatetags.na_date import na_date
from pstat.core.tables import doc_bloc, _link_html_with_class
from pstat.document_control.models import (
Document,
OLD,
RETIRED,
ACTIVE,
PENDING,
)
from pstat.document_control.templatetags.restricted_icon import restricted_icon
_link_html = '''
<a href="%(url)s" class=%(class)s>
%(text)s
</a>
'''
# Buckets to classify documents and their corresponding CSS class
_expiration_bucket_classes = (
(0, 'expiring_now'),
(30, 'expiring_30'),
(60, 'expiring_60'),
(90, 'expiring_90'),
)
def doc_edit_link(document, user, tenant, force_edit_link=False, **kwargs):
if not force_edit_link and \
not UserAuth(user, tenant).can_edit_document(document):
# User can't edit this document. Don't display the edit link
return ''
link = ''
if document.status in [ACTIVE, PENDING, RETIRED, OLD]:
link = _link_html % {
'url': reverse('document_control_document_edit',
kwargs={'document_id': document.pk}),
'text': 'Edit',
'class': 'edit',
}
return mark_safe('<div class="action_cell">%s</div>' % link)
def _doc_name(document, **kwargs):
return mark_safe(document.name + restricted_icon(document))
def doc_name_link(document, **kwargs):
return mark_safe(_link_html % {
'url': reverse('document_control_document_view',
kwargs={'document_id': document.pk}),
'text': _doc_name(document),
'class': 'title',
})
def doc_name_latest_link(document, **kwargs):
return mark_safe(_link_html % {
'url': reverse('document_control_document_view_perma',
kwargs={'document_id': document.pk}),
'text': _doc_name(document),
'class': 'latest',
})
def doc_name_showchanges_link(document, **kwargs):
url = reverse('document_control_document_view',
kwargs={'document_id': document.pk})
return mark_safe(_link_html % {
'url': (url + '?showchanges=true'),
'text': _doc_name(document),
'class': 'show_changes',
})
def doc_name_revisions_link(document, **kwargs):
return mark_safe(_link_html % {
'url': reverse('document_control_document_revisions',
kwargs={'document_id': document.pk}),
'text': _doc_name(document),
'class': 'revision',
})
def doc_name_approve_link(document, **kwargs):
return mark_safe(_link_html % {
'url': reverse('document_control_document_approve',
kwargs={'document_id': document.pk}),
'text': _doc_name(document),
'class': 'approve',
})
def doc_flow(document, **kwargs):
return document.flow_template.name
def doc_flow_link(document, **kwargs):
return mark_safe(_link_html % {
'url': reverse('approval_flow_edit',
kwargs={'approval_flow_id': document.flow_template.pk}),
'text': document.flow_template.name,
'class': 'flow',
})
def doc_author(document, **kwargs):
return user_display(document.author)
def _doc_date(date, format_name='short_no_day_num'):
date_string = na_date(date, format_name)
return mark_safe('<div class="date">%s</div>' % date_string)
def doc_status_changed_date(document, **kwargs):
return _doc_date(document.status_changed_date)
def _make_reminder_link(document):
url = reverse('document_reports_approval_email_reminder',
kwargs={'document_id':document.pk})
link_tpl = ''' <a class="approval_reminder" href="%s">
<img title="Send Reminder Email"
style="border: none" src="%s/images/icons/email.gif"></img></a>'''
link = link_tpl % (url, settings.SITE_MEDIA_URL)
return mark_safe(link)
# Tables #
def make_DocumentTable(user, tenant, can_edit_all=False):
"""
Make a DocumentTable for the given ``user`` and ``tenant``.
``can_edit_all`` If True, then assume that the user can edit all of the
documents and don't make an extra permissions check. This is useful to save
multiple queries for each displayed document if we've already determined in
advance that they can edit them.
"""
class DocumentTable(ModelTable):
_user = user
_tenant = tenant
_can_edit_all = can_edit_all
name = Column(verbose_name='Title', model_rel='name')
name_csv = Column(verbose_name='Title', model_rel='name')
name_view_link = Column(verbose_name='Title', model_rel='name')
name_latest_link = Column(verbose_name='Title', model_rel='name')
name_changes_link = Column(verbose_name='Title', model_rel='name')
name_approve_link = Column(sortable=True, verbose_name='Title', model_rel='name')
name_accept_link = Column(verbose_name='Name', model_rel='name')
restricted = Column(sortable=True, model_rel='restricted')
edit_link = Column(sortable=False, verbose_name=' ')
cert_detail_link = Column(sortable=False, verbose_name=' ')
cert_totals = Column(sortable=False, verbose_name='Users')
cert_created = Column(sortable=False, verbose_name='Assigned Date')
approval_flow = Column(verbose_name='Approval Flow', model_rel='flow_template')
flow_template = Column(verbose_name='Approval Flow')
category = Column(verbose_name=_tenant.settings.category_label.title())
status = Column(
sortable=False,
verbose_name='%s Status' % _tenant.settings.document_label.title(),
model_rel='get_status_display'
)
preview = Column(sortable=False)
short_preview = Column(sortable=False, verbose_name='Preview')
author = Column(
verbose_name=_tenant.document_settings.author_label.title(),
model_rel='author__last_name')
bloc = Column(verbose_name='Origin')
bloc_csv = Column(verbose_name='Origin', model_rel='bloc__name')
needed_approver = Column(sortable=False, verbose_name='Needed Approver')
needed_approver_csv = Column(sortable=False, verbose_name='Needed Approver')
active_date = Column(
sortable=True,
verbose_name='Approval Date',
model_rel='approval_date',
)
revision_date = Column(
verbose_name=_tenant.document_settings.revision_date_label.title())
revision_date_color = Column(
verbose_name=_tenant.document_settings.revision_date_label.title(),
model_rel='revision_date')
active_lifespan = Column(sortable=True, verbose_name='Default Expiration Period')
expiration_date = Column(
verbose_name=_tenant.document_settings.expiration_date_label.title())
expiration_date_color = Column(
verbose_name=_tenant.document_settings.expiration_date_label.title(),
model_rel='expiration_date')
approval_date = Column(
verbose_name=_tenant.document_settings.approved_date_label.title())
retire_date = Column(verbose_name='Retire Date', model_rel='status_changed_date')
deleted_date = Column(sortable=False)
wait_time = Column(sortable=True, verbose_name='Wait Time')
wait_time_csv = Column(sortable=False, verbose_name='Wait Time')
effective_date = Column(
verbose_name=_tenant.document_settings.effective_date_label.title(),
)
num_steps = Column(sortable=False, verbose_name="Approval Steps")
avg_step_length = Column(sortable=False, verbose_name="Days Per Step")
checkbox = Column(sortable=False, verbose_name=' ')
checkbox_import = Column(sortable=False, verbose_name=' ')
revised_from_parent = Column(sortable=False, verbose_name='Revised?')
fix_broken_html = Column(
sortable=False,
verbose_name=' ',
)
@staticmethod
def render_name(document):
return _doc_name(document)
@staticmethod
def render_name_accept_link(document):
name = escape(document.name)
if not hasattr(document, 'document_import'):
return name
return mark_safe(_link_html % {
'url': reverse('implementation_acceptance_accept_import',
kwargs={'import_id': document.document_import.pk}),
'text': name,
'class': 'edit',
})
@staticmethod
def render_restricted(document):
if document.restricted:
return 'Private'
return 'Public'
@staticmethod
def render_checkbox(document):
return mark_safe(
'<input type="checkbox" name="document_ids" value="%d" />'
% document.pk)
@staticmethod
def render_checkbox_import(document):
return mark_safe(
'<input type="checkbox" name="import_ids" value="%d" />'
% document.document_import.pk)
def render_edit_link(self, document):
return doc_edit_link(
document,
self._user,
self._tenant,
force_edit_link=self._can_edit_all)
@staticmethod
def render_name_view_link(document):
return doc_name_link(document)
@staticmethod
def render_name_latest_link(document):
return doc_name_latest_link(document)
@staticmethod
def render_name_changes_link(document):
return doc_name_showchanges_link(document)
@staticmethod
def render_cert_detail_link(document):
return mark_safe(_link_html % {
'url': reverse('certification_document_detail',
kwargs={'document_id': document.pk}),
'text': 'Detail',
'class': 'certification_detail',
})
@staticmethod
def render_cert_totals(document):
text = '%d of %d acknowledged' % (
document.fulfilled,
document.required,
)
return mark_safe(_link_html % {
'url': reverse('certification_document_detail',
kwargs={'document_id': document.pk}),
'text': text,
'class': 'users_read_total',
})
@staticmethod
def render_cert_created(document):
read_receipts = ReadReceipt.objects.filter(
document=document,
)
date = min(read_receipt.created for read_receipt in read_receipts)
return na_date(date, 'short')
@staticmethod
def render_name_approve_link(document):
return doc_name_approve_link(document)
@staticmethod
def render_approval_flow(document):
return doc_flow_link(document)
@staticmethod
def render_flow_template(document):
return doc_flow(document)
def render_preview(self, document):
return truncatewords(document.get_preview(self._tenant)[:200], 25)
def render_short_preview(self, document):
return truncatewords(document.get_preview(self._tenant)[:100], 25)
@staticmethod
def render_author(document):
return doc_author(document)
def render_bloc(self, document):
return doc_bloc(document, self._tenant)
def render_needed_approver(self, document):
approvers = document.get_currently_pending_approvers()\
.select_related('profile')
if not approvers:
return 'Any'
approver_names = []
is_user = False
for approver in approvers:
if self._user == approver:
approver_names.append('<strong>You</strong>')
is_user = True
else:
approver_names.append(user_display(approver, 'short_forward'))
# If there is only one approver left and that is the current user,
# do not put on the reminder link
if len(approver_names) == 1 and is_user:
return mark_safe(approver_names[0])
else:
return mark_safe(
',<br />'.join(approver_names) + _make_reminder_link(document))
@staticmethod
def render_needed_approver_csv(document):
approvers = document.get_currently_pending_approvers()
if not approvers:
return 'Any'
approver_names = []
for approver in approvers:
approver_names.append(user_display(approver, 'short_forward'))
return '\n'.join(approver_names)
def render_active_date(self, document):
return na_date(
document.approval_date,
self._tenant.document_settings.date_format,
)
@staticmethod
def render_revision_date(document):
return na_date(document.revision_date, 'short_no_day_num')
@staticmethod
def render_active_lifespan(document):
return '%d days' % document.active_lifespan.days
@staticmethod
def render_expiration_date(document):
return na_date(document.expiration_date, 'short_no_day_num')
@staticmethod
def render_approval_date(document):
return na_date(document.approval_date, 'short_no_day_num')
@staticmethod
def render_effective_date(document):
return na_date(document.effective_date, 'short_no_day_num')
@staticmethod
def render_retire_date(document):
return doc_status_changed_date(document)
@staticmethod
def render_deleted_date(document):
return doc_status_changed_date(document)
@staticmethod
def render_wait_time(document):
waiting_date = document.get_waiting_date()
if waiting_date is None:
return 'N/A'
return timesince(waiting_date)
@staticmethod
def render_wait_time_csv(document):
waiting_date = document.get_waiting_date()
if waiting_date is None:
return 'N/A'
days_since = datetime.now() - waiting_date
return '%d days' % days_since.days
@staticmethod
def render_num_steps(document):
return document.approval_flow.num_steps()
@staticmethod
def render_avg_step_length(document):
return '%.0f' % document.approval_flow.avg_step_length()
@staticmethod
def render_expiration_date_color(document):
days_until_expiration = document.get_days_til_expiration()
# Classify this document in to a bucket according to days until
# expiration
span_class = None
days_display = None
for day, _span_class in _expiration_bucket_classes:
if days_until_expiration <= day:
span_class = _span_class
days_display = day
break
if span_class is None or days_display is None:
# Must have passed in a Document with a > 90 days expiration
raise AttributeError('Improper use of expiring length column')
date = na_date(document.expiration_date, 'short_no_day_num')
return mark_safe(
'<span class="%s">%s</span>' % (span_class, date))
@staticmethod
def render_revision_date_color(document):
days_since_revision_date = document.get_days_since_revision()
# Classify this document in to a bucket according to days since last
# revised
span_class = None
days_display = None
for day, _span_class in _expiration_bucket_classes:
if days_since_revision_date < day:
span_class = _span_class
days_display = day
break
if span_class is None or days_display is None:
# Must have passed in a Document with a > 90 days since revision
raise AttributeError('Improper use of revised length column')
date = na_date(document.revision_date, 'short_no_day_num')
return mark_safe(
'<span class="%s">%s</span>' % (span_class, date))
@staticmethod
def render_revised_from_parent(document):
span = '<span class="%s">%s</span>'
if document.is_revised_from_parent():
if document.parent:
return mark_safe(span % ('revised', 'Revised'))
else:
return mark_safe(span % ('new-document', 'New'))
else:
return mark_safe(span % ('unchanged', 'Unchanged'))
@staticmethod
def render_fix_broken_html(document):
return mark_safe(_link_html_with_class % (
'post_link',
reverse('document_control_fix_broken_html',
kwargs={'document_id': document.pk}),
'Fix Html',
))
class Meta:
model = Document
return DocumentTable
@auth_login
def search(request):
search_query = request.GET.get('q', '')
sort = get_order_by(request.GET, 'sort', secondary='name')
if not search_query and not sort:
sort = 'name_latest_link'
document_list = advanced_document_search(
... snip ...
)
max_matches = settings.DOCUMENT_SEARCH_MAX_MATCHES
page_size = min(settings.PAGINATION_DEFAULT_PAGINATION, max_matches)
can_edit_any = DocumentPermission(
... snip ...
)
DocumentTable = make_DocumentTable(
request.user,
request.tenant,
)
table = DocumentTable(document_list, order_by=sort)
ordered_by = _get_ordered_by_display(table)
context = dict(
table=table,
page_size=page_size,
ordered_by=ordered_by,
can_edit_any=can_edit_any,
)
return render_to_response(
'table_display.html',
context,
context_instance=RequestContext(request))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment