Skip to content

Commit

Permalink
Merge pull request #386 from Talengi/import_export_members
Browse files Browse the repository at this point in the history
Import export members
  • Loading branch information
thibault committed May 17, 2016
2 parents eaf08d5 + 34d60d7 commit b378313
Show file tree
Hide file tree
Showing 12 changed files with 369 additions and 34 deletions.
15 changes: 13 additions & 2 deletions src/distriblists/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@

from distriblists.models import DistributionList
from distriblists.forms import DistributionListForm
from distriblists.views import DistributionListImport, DistributionListExport
from distriblists.views import (DistributionListImport, DistributionListExport,
ReviewMembersImport, ReviewMembersExport)


class DistributionListAdmin(admin.ModelAdmin):
Expand All @@ -26,7 +27,17 @@ def get_urls(self):
self.admin_site.admin_view(
DistributionListExport.as_view(model_admin=self)
),
name='distriblists_distriblist_export')
name='distriblists_distriblist_export'),
url(r'^review_members_import/$',
self.admin_site.admin_view(
ReviewMembersImport.as_view(model_admin=self)
),
name='distriblists_reviewmembers_import'),
url(r'^review_members_export/$',
self.admin_site.admin_view(
ReviewMembersExport.as_view(model_admin=self)
),
name='distriblists_reviewmembers_export'),
] + urls


Expand Down
Binary file not shown.
81 changes: 80 additions & 1 deletion src/distriblists/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
import openpyxl

from categories.factories import CategoryFactory
from default_documents.tests.test import ContractorDeliverableTestCase
from accounts.factories import UserFactory
from distriblists.utils import import_lists, export_lists
from distriblists.utils import (import_lists, export_lists,
export_review_members, import_review_members)
from distriblists.factories import DistributionListFactory
from distriblists.models import DistributionList

Expand Down Expand Up @@ -200,3 +202,80 @@ def test_invalid_data_invalid_category(self):
import_lists(xls_file, self.category)

self.assertEqual(qs.count(), 1)


class ReviewMembersExportTests(ContractorDeliverableTestCase):
def setUp(self):
super(ReviewMembersExportTests, self).setUp()
self.users = [
UserFactory(email='user000@test.com', category=self.category),
UserFactory(email='user001@test.com', category=self.category),
UserFactory(email='user002@test.com', category=self.category),
UserFactory(email='user003@test.com', category=self.category),
UserFactory(email='user004@test.com', category=self.category),
]
self.docs = [
self.create_doc(revision={
'leader': self.users[0]
}),
self.create_doc(revision={
'leader': self.users[1],
'approver': self.users[0]
}),
self.create_doc(revision={
'leader': self.users[0],
'approver': self.users[1],
'reviewers': [self.users[2], self.users[3]]
}),
self.create_doc(),
]

def test_successful_export(self):
exported_file = export_review_members(self.category)
buf = BytesIO(exported_file)
wb = openpyxl.load_workbook(buf)
ws = wb.active

self.assertEqual(ws.max_column, 7)
self.assertEqual(ws.max_row, 5)

self.assertEqual(ws.cell(column=1, row=2).value, self.docs[0].document_number)
self.assertEqual(ws.cell(column=2, row=2).value, None)
self.assertEqual(ws.cell(column=3, row=2).value, 'L')
self.assertEqual(ws.cell(column=4, row=2).value, None)

self.assertEqual(ws.cell(column=1, row=3).value, self.docs[1].document_number)
self.assertEqual(ws.cell(column=2, row=3).value, None)
self.assertEqual(ws.cell(column=3, row=3).value, 'A')
self.assertEqual(ws.cell(column=4, row=3).value, 'L')


class ReviewMembersImportTests(ContractorDeliverableTestCase):
def setUp(self):
super(ReviewMembersImportTests, self).setUp()
self.users = [
UserFactory(email='user000@test.com', category=self.category),
UserFactory(email='user001@test.com', category=self.category),
UserFactory(email='user002@test.com', category=self.category),
UserFactory(email='user003@test.com', category=self.category),
UserFactory(email='user004@test.com', category=self.category),
]
self.docs = [
self.create_doc(document_key='document0001'),
self.create_doc(document_key='document0002'),
self.create_doc(document_key='document0003'),
self.create_doc(document_key='document0004'),
]

def test_successful_import(self):
"""Importing the file creates the distribution lists."""
self.assertIsNone(self.docs[0].latest_revision.leader)

xls_file = os.path.join(
os.path.dirname(__file__),
'fixtures',
'valid_review_members.xlsx')
import_review_members(xls_file, self.category)
self.assertEqual(self.docs[0].latest_revision.leader, self.users[0])
self.assertEqual(self.docs[0].latest_revision.approver, self.users[1])
self.assertEqual(self.docs[0].latest_revision.reviewers.all().count(), 3)
119 changes: 119 additions & 0 deletions src/distriblists/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,125 @@
)


def import_review_members(filepath, category):
"""Import review members from an excel file."""
wb = openpyxl.load_workbook(filepath)
ws = wb.active

# Extracts the user list from the header row
emails, user_ids = _extract_users(ws)

max_col = len(user_ids) + 1 # Don't use ws.max_column, it's not reliable
rows = ws.iter_rows(min_row=2, max_row=ws.max_row, max_col=max_col)
results = []
for idx, row in enumerate(rows):
result = _import_review_members(row, emails, user_ids, category)
result['line'] = idx + 2
results.append(result)

return results


def _import_review_members(row, emails, user_ids, category):
"""Saves a review member list for a single row."""
errors = []

key = row[0].value
try:
instance = category.document_class().objects \
.filter(document__category=category) \
.select_related('latest_revision') \
.get(document__document_key=key)
except:
instance = None
errors.append(_('Document {} does not exist').format(key))

if instance.latest_revision.is_under_review():
errors.append(_('This document is under review'))

# Extract user roles from xls
reviewers = []
leader = None
approver = None
for idx, cell in enumerate(row[1:]):
role = cell.value
if role:
user_id = user_ids[idx]
if user_id is None:
errors.append('Unknown user "{}"'.format(emails[idx]))

if role == 'R':
reviewers.append(user_id)
elif role == 'L':
leader = user_id
elif role == 'A':
approver = user_id
else:
errors.append('Unknown role "{}"'.format(role))

if instance and not errors:
rev = instance.latest_revision
if leader:
rev.leader_id = leader

if approver:
rev.approver_id = approver
rev.reviewers.clear()
rev.reviewers.add(*reviewers)
rev.save()

return {
'document_key': key,
'success': not errors,
'errors': errors,
}


def export_review_members(category):
"""Export members of the review for all documents in the category."""
documents = category.document_class().objects \
.filter(document__category=category) \
.select_related(
'document',
'latest_revision',
'latest_revision__leader',
'latest_revision__approver') \
.prefetch_related('latest_revision__reviewers') \
.order_by('document__document_number')
users_qs = User.objects.filter(categories=category).order_by('email')
all_users = list(users_qs)

wb = openpyxl.Workbook()
ws = wb.active

_export_header(ws, all_users)
for idx, doc in enumerate(documents):
_export_members(ws, idx, doc, all_users)

return save_virtual_workbook(wb)


def _export_members(ws, idx, doc, all_users):
"""Add a single line to the exported file."""
line = idx + 2
ws.cell(row=line, column=1).value = doc.document.document_number

rev = doc.latest_revision

if rev.leader:
_export_role(ws, line, all_users, rev.leader, 'L')

if rev.approver:
_export_role(ws, line, all_users, rev.approver, 'A')

for reviewer in rev.reviewers.all():
_export_role(ws, line, all_users, reviewer, 'R')

# Set borders for all cells
for column in range(1, len(all_users) + 2):
ws.cell(row=line, column=column).border = cell_border


def export_lists(category):
"""Export distribution lists in a single category as xlsx file."""
lists = DistributionList.objects \
Expand Down

0 comments on commit b378313

Please sign in to comment.