From 1bea94eb12ffaac57d55087906b8ad7c3489e9ec Mon Sep 17 00:00:00 2001 From: Ole Laursen Date: Wed, 25 May 2011 12:33:24 +0000 Subject: [PATCH] Change auth model for new schema to use Person rather than Email where email is not necessary, use Person for user profiles rather than the old unused profile class, use REMOTE_USER backend directly instead of custom backend, use group roles for authorization rather than Django groups, port/proxy code from using IESGLogin to the new model - Legacy-Id: 3151 --- ietf/announcements/models.py | 9 +- ietf/announcements/views.py | 2 - ietf/idrfc/expire.py | 8 +- ietf/idrfc/idrfc_wrapper.py | 8 +- ietf/idrfc/lastcall.py | 10 +- ietf/idrfc/mails.py | 28 ++--- ietf/idrfc/mirror_rfc_index.py | 9 +- ietf/idrfc/templatetags/ballot_icon.py | 12 ++- ietf/idrfc/testsREDESIGN.py | 34 +++--- ietf/idrfc/utils.py | 12 +-- ietf/idrfc/views_ballot.py | 53 ++++----- ietf/idrfc/views_doc.py | 4 +- ietf/idrfc/views_edit.py | 36 +++---- ietf/idrfc/views_search.py | 37 ++++--- ietf/idtracker/admin.py | 3 +- ietf/idtracker/models.py | 2 + ietf/idtracker/templatetags/ietf_filters.py | 11 ++ ietf/iesg/feeds.py | 2 +- ietf/iesg/tests.py | 62 +++++++++-- ietf/iesg/views.py | 3 +- ietf/ietfauth/decorators.py | 47 ++++++++ ietf/ietfauth/tests.py | 10 +- ietf/ietfauth/views.py | 17 +++ ietf/settings.py | 4 + .../idrfc/edit_positionREDESIGN.html | 4 +- .../idrfc/send_ballot_commentREDESIGN.html | 4 +- ietf/templates/iesg/scribe_doc.html | 4 +- .../registration/profileREDESIGN.html | 31 ++++++ ietf/utils/test_data.py | 101 +++++++----------- redesign/doc/models.py | 16 +-- redesign/doc/proxy.py | 12 +-- redesign/group/models.py | 10 +- redesign/importing/import-announcements.py | 11 +- redesign/importing/import-document-state.py | 93 ++++++++-------- redesign/importing/import-groups.py | 28 +++-- redesign/importing/import-persons.py | 44 ++++++++ redesign/importing/import-roles.py | 36 +++++-- redesign/importing/utils.py | 16 ++- redesign/person/admin.py | 2 +- redesign/person/models.py | 25 ++++- redesign/person/proxy.py | 24 +++-- 41 files changed, 576 insertions(+), 308 deletions(-) create mode 100644 ietf/templates/registration/profileREDESIGN.html create mode 100755 redesign/importing/import-persons.py diff --git a/ietf/announcements/models.py b/ietf/announcements/models.py index 75ca19aad..ab8fa5645 100644 --- a/ietf/announcements/models.py +++ b/ietf/announcements/models.py @@ -88,15 +88,15 @@ class ScheduledAnnouncement(models.Model): db_table = 'scheduled_announcements' -if settings.USE_DB_REDESIGN_PROXY_CLASSES: +if settings.USE_DB_REDESIGN_PROXY_CLASSES or hasattr(settings, "IMPORTING_ANNOUNCEMENTS"): import datetime - from person.models import Email + from person.models import Email, Person from group.models import Group class Message(models.Model): time = models.DateTimeField(default=datetime.datetime.now) - by = models.ForeignKey(Email) + by = models.ForeignKey(Person) subject = models.CharField(max_length=255) frm = models.CharField(max_length=255) @@ -116,7 +116,8 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES: class SendQueue(models.Model): time = models.DateTimeField(default=datetime.datetime.now) - by = models.ForeignKey(Email) + by = models.ForeignKey(Person) + comment = models.TextField() message = models.ForeignKey(Message) send_at = models.DateTimeField(blank=True, null=True) diff --git a/ietf/announcements/views.py b/ietf/announcements/views.py index 1e2fad1d8..d13b9a2a1 100644 --- a/ietf/announcements/views.py +++ b/ietf/announcements/views.py @@ -35,7 +35,6 @@ def nomcom(request): 'regimes' : regimes }) def nomcomREDESIGN(request): - from person.models import Email from group.models import Group from ietf.announcements.models import Message @@ -75,7 +74,6 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES: def message_detail(request, object_id, queryset): - from person.models import Email from group.models import Group from ietf.announcements.models import Message diff --git a/ietf/idrfc/expire.py b/ietf/idrfc/expire.py index 73f3ea1b3..cb8c49ff5 100644 --- a/ietf/idrfc/expire.py +++ b/ietf/idrfc/expire.py @@ -11,7 +11,7 @@ from ietf.utils.mail import send_mail, send_mail_subj from ietf.idrfc.utils import log_state_changed, add_document_comment from doc.models import Document, Event, save_document_in_history from name.models import IesgDocStateName, DocStateName, DocInfoTagName -from person.models import Email +from person.models import Person, Email INTERNET_DRAFT_DAYS_TO_EXPIRE = 185 @@ -186,7 +186,7 @@ def expire_id(doc): add_document_comment(None, doc, "Document is expired by system") def expire_idREDESIGN(doc): - system_email = Email.objects.get(address="(System)") + system = Person.objects.get(name="(System)") # clean up files def move_file(f): @@ -219,9 +219,9 @@ def expire_idREDESIGN(doc): if doc.iesg_state != dead_state: prev = doc.iesg_state doc.iesg_state = dead_state - log_state_changed(None, doc, system_email, prev) + log_state_changed(None, doc, system, prev) - e = Event(doc=doc, by=system_email) + e = Event(doc=doc, by=system) e.type = "expired_document" e.desc = "Document has expired" e.save() diff --git a/ietf/idrfc/idrfc_wrapper.py b/ietf/idrfc/idrfc_wrapper.py index 2ff4f3ab4..9ca33df4d 100644 --- a/ietf/idrfc/idrfc_wrapper.py +++ b/ietf/idrfc/idrfc_wrapper.py @@ -641,8 +641,8 @@ class BallotWrapper: self.old_init() return - from redesign.person.models import Email - active_ads = Email.objects.filter(role__name="ad", role__group__state="active") + from redesign.person.models import Person + active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active").distinct() positions = [] seen = {} @@ -650,7 +650,7 @@ class BallotWrapper: from doc.models import BallotPositionEvent for pos in BallotPositionEvent.objects.filter(doc=self.ballot, type="changed_ballot_position", time__gte=self.ballot.process_start, time__lte=self.ballot.process_end).select_related('ad').order_by("-time", '-id'): if pos.ad not in seen: - p = dict(ad_name=pos.ad.get_name(), + p = dict(ad_name=pos.ad.name, ad_username=pos.ad.pk, # ought to rename this in doc_ballot_list position=pos.pos.name, is_old_ad=pos.ad not in active_ads, @@ -684,7 +684,7 @@ class BallotWrapper: if self.ballot_active: for ad in active_ads: if ad not in seen: - d = dict(ad_name=ad.get_name(), + d = dict(ad_name=ad.name, ad_username=ad.pk, position="No Record", ) diff --git a/ietf/idrfc/lastcall.py b/ietf/idrfc/lastcall.py index 880b001af..96b30e1b4 100644 --- a/ietf/idrfc/lastcall.py +++ b/ietf/idrfc/lastcall.py @@ -4,13 +4,13 @@ import datetime from django.conf import settings -from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo, IESGLogin +from ietf.idtracker.models import InternetDraft, DocumentComment, BallotInfo from ietf.idrfc.mails import * from ietf.idrfc.utils import * from doc.models import Document, Event, LastCallEvent, WriteupEvent, save_document_in_history from name.models import IesgDocStateName -from person.models import Email +from person.models import Person def request_last_call(request, doc): try: @@ -33,9 +33,9 @@ def request_last_callREDESIGN(request, doc): e = Event() e.type = "requested_last_call" - e.by = request.user.get_profile().email() + e.by = request.user.get_profile() e.doc = doc - e.desc = "Last call was requested by %s" % e.by.get_name() + e.desc = "Last call was requested by %s" % e.by.name e.save() if settings.USE_DB_REDESIGN_PROXY_CLASSES: @@ -83,7 +83,7 @@ def expire_last_callREDESIGN(doc): prev = doc.iesg_state doc.iesg_state = state - e = log_state_changed(None, doc, Email.objects.get(address="(System)"), prev) + e = log_state_changed(None, doc, Person.objects.get(name="(System)"), prev) doc.time = e.time doc.save() diff --git a/ietf/idrfc/mails.py b/ietf/idrfc/mails.py index 799d7dea3..2041d6a5e 100644 --- a/ietf/idrfc/mails.py +++ b/ietf/idrfc/mails.py @@ -10,7 +10,7 @@ from django.conf import settings from ietf.utils.mail import send_mail, send_mail_text from ietf.idtracker.models import * from doc.models import WriteupEvent, BallotPositionEvent, LastCallEvent -from person.models import Email +from person.models import Person def email_state_changed(request, doc, text): to = [x.strip() for x in doc.idinternal.state_change_notice_to.replace(';', ',').split(',')] @@ -55,7 +55,7 @@ def email_ownerREDESIGN(request, doc, owner, changed_by, text, subject=None): to = owner.formatted_email() send_mail(request, to, "DraftTracker Mail System ", - "%s updated by %s" % (doc.file_tag(), changed_by.get_name()), + "%s updated by %s" % (doc.file_tag(), changed_by.name), "idrfc/change_notice.txt", dict(text=html_to_text(text), doc=doc, @@ -88,9 +88,9 @@ def full_intended_status(intended_status): def generate_ballot_writeup(request, doc): e = WriteupEvent() e.type = "changed_ballot_writeup_text" - e.by = request.user.get_profile().email() + e.by = request.user.get_profile() e.doc = doc - e.desc = u"Ballot writeup was generated by %s" % e.by.get_name() + e.desc = u"Ballot writeup was generated by %s" % e.by.name e.text = unicode(render_to_string("idrfc/ballot_writeup.txt")) e.save() @@ -160,9 +160,9 @@ def generate_last_call_announcementREDESIGN(request, doc): e = WriteupEvent() e.type = "changed_last_call_text" - e.by = request.user.get_profile().email() + e.by = request.user.get_profile() e.doc = doc - e.desc = u"Last call announcement was generated by %s" % e.by.get_name() + e.desc = u"Last call announcement was generated by %s" % e.by.name e.text = unicode(mail) e.save() @@ -255,9 +255,9 @@ def generate_approval_mailREDESIGN(request, doc): e = WriteupEvent() e.type = "changed_ballot_approval_text" - e.by = request.user.get_profile().email() + e.by = request.user.get_profile() e.doc = doc - e.desc = u"Ballot approval text was generated by %s" % e.by.get_name() + e.desc = u"Ballot approval text was generated by %s" % e.by.name e.text = unicode(mail) e.save() @@ -292,12 +292,12 @@ def generate_approval_mail_approved(request, doc): made_by = "This document is the product of the %s." % doc.group.name_with_wg director = doc.ad - other_director = Email.objects.filter(role__group__role__email=director, role__group__role__name="ad").exclude(pk=director.pk) + other_director = Person.objects.filter(email__role__group__role__email__person=director, email__role__group__role__name="ad").exclude(pk=director.pk) if doc.group.type_id != "individ" and other_director: - contacts = "The IESG contact persons are %s and %s." % (director.get_name(), other_director[0].get_name()) + contacts = "The IESG contact persons are %s and %s." % (director.name, other_director[0].name) else: - contacts = "The IESG contact person is %s." % director.get_name() + contacts = "The IESG contact person is %s." % director.name doc_type = "RFC" if doc.state_id == "rfc" else "Internet Draft" @@ -476,7 +476,7 @@ def generate_issue_ballot_mailREDESIGN(request, doc): full_status = full_intended_status(doc.intended_std_level.name) status = full_status.replace("a ", "").replace("an ", "") - active_ads = Email.objects.filter(role__name="ad", role__group__state="active") + active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active").distinct() e = doc.latest_event(type="started_iesg_process") positions = BallotPositionEvent.objects.filter(doc=doc, type="changed_ballot_position", time__gte=e.time).order_by("-time", '-id').select_related('ad') @@ -499,7 +499,7 @@ def generate_issue_ballot_mailREDESIGN(request, doc): return "[ ]" fmt = u"%-21s%-10s%-11s%-9s%-10s" % ( - p.ad.get_name()[:21], + p.ad.name[:21], formatted(p.pos_id == "yes"), formatted(p.pos_id == "noobj"), formatted(p.pos_id == "discuss"), @@ -517,7 +517,7 @@ def generate_issue_ballot_mailREDESIGN(request, doc): active_ad_positions.sort() inactive_ad_positions.sort() - ad_feedback.sort(key=lambda p: p.ad.get_name()) + ad_feedback.sort(key=lambda p: p.ad.name) e = doc.latest_event(LastCallEvent, type="sent_last_call") last_call_expires = e.expires if e else None diff --git a/ietf/idrfc/mirror_rfc_index.py b/ietf/idrfc/mirror_rfc_index.py index a76ed05d5..9def4094d 100644 --- a/ietf/idrfc/mirror_rfc_index.py +++ b/ietf/idrfc/mirror_rfc_index.py @@ -178,13 +178,13 @@ import django.db.transaction @django.db.transaction.commit_on_success def insert_to_databaseREDESIGN(data): - from person.models import Email + from person.models import Person from doc.models import Document, DocAlias, Event, RelatedDocument from group.models import Group from name.models import DocInfoTagName, DocRelationshipName from name.utils import name - system_email = Email.objects.get(address="(System)") + system = Person.objects.get(name="(System)") std_level_mapping = get_std_level_mapping() stream_mapping = get_stream_mapping() tag_has_errata = name(DocInfoTagName, 'errata', "Has errata") @@ -203,7 +203,8 @@ def insert_to_databaseREDESIGN(data): # we assume two things can happen: we get a new RFC, or an # attribute has been updated at the RFC Editor (RFC Editor - # attributes currently take precedence) + # attributes currently take precedence over our local + # attributes) # make sure we got the document and alias created = False @@ -257,7 +258,7 @@ def insert_to_databaseREDESIGN(data): if not doc.latest_event(type="published_rfc", time=pubdate): e = Event(doc=doc, type="published_rfc") e.time = pubdate - e.by = system_email + e.by = system e.desc = "RFC published" e.save() changed = True diff --git a/ietf/idrfc/templatetags/ballot_icon.py b/ietf/idrfc/templatetags/ballot_icon.py index ffd642384..8a804d435 100644 --- a/ietf/idrfc/templatetags/ballot_icon.py +++ b/ietf/idrfc/templatetags/ballot_icon.py @@ -42,16 +42,20 @@ register = template.Library() def get_user_adid(context): if 'user' in context and in_group(context['user'], "Area_Director"): if settings.USE_DB_REDESIGN_PROXY_CLASSES: - # with the new design, we need the email address (FIXME: - # we shouldn't go through the old classes though, - # IESGLogin needs revamping) - return context['user'].get_profile().person().email()[1] + return context['user'].get_profile().id return context['user'].get_profile().iesg_login_id() else: return None def get_user_name(context): if 'user' in context and context['user'].is_authenticated(): + if settings.USE_DB_REDESIGN_PROXY_CLASSES: + from person.models import Person + try: + return context['user'].get_profile().name + except Person.DoesNotExist: + return None + person = context['user'].get_profile().person() if person: return str(person) diff --git a/ietf/idrfc/testsREDESIGN.py b/ietf/idrfc/testsREDESIGN.py index c7d4cedee..ab2d47f11 100644 --- a/ietf/idrfc/testsREDESIGN.py +++ b/ietf/idrfc/testsREDESIGN.py @@ -41,7 +41,7 @@ from django.conf import settings from pyquery import PyQuery -from ietf.idtracker.models import IESGLogin, PersonOrOrgInfo, EmailAddress, IDDates +from ietf.idtracker.models import IDDates from doc.models import * from name.models import * from group.models import * @@ -182,7 +182,7 @@ class EditInfoTestCase(django.test.TestCase): events_before = draft.event_set.count() mailbox_before = len(mail_outbox) - new_ad = Email.objects.get(address="ad1@ietf.org") + new_ad = Person.objects.get(name="Ad No1") r = self.client.post(url, dict(intended_std_level=str(draft.intended_std_level.pk), @@ -266,13 +266,13 @@ class EditInfoTestCase(django.test.TestCase): events_before = draft.event_set.count() mailbox_before = len(mail_outbox) - ad = Email.objects.get(address="aread@ietf.org") + ad = Person.objects.get(name="Aread Irector") r = self.client.post(url, dict(intended_std_level=str(draft.intended_std_level_id), status_date=str(date.today() + timedelta(2)), via_rfc_editor="1", - ad=ad, + ad=ad.pk, notify="test@example.com", note="This is a note", telechat_date="", @@ -320,7 +320,7 @@ class ResurrectTestCase(django.test.TestCase): self.assertEquals(draft.event_set.count(), events_before + 1) e = draft.latest_event(type="requested_resurrect") self.assertTrue(e) - self.assertEquals(e.by, Email.objects.get(address="aread@ietf.org")) + self.assertEquals(e.by, Person.objects.get(name="Aread Irector")) self.assertTrue("Resurrection" in e.desc) self.assertEquals(len(mail_outbox), mailbox_before + 1) self.assertTrue("Resurrection" in mail_outbox[-1]['Subject']) @@ -331,7 +331,7 @@ class ResurrectTestCase(django.test.TestCase): draft.save() Event.objects.create(doc=draft, type="requested_resurrect", - by=Email.objects.get(address="aread@ietf.org")) + by=Person.objects.get(name="Aread Irector")) url = urlreverse('doc_resurrect', kwargs=dict(name=draft.name)) @@ -392,7 +392,7 @@ class EditPositionTestCase(django.test.TestCase): url = urlreverse('doc_edit_position', kwargs=dict(name=draft.name)) login_testing_unauthorized(self, "ad", url) - ad = Email.objects.get(address="aread@ietf.org") + ad = Person.objects.get(name="Aread Irector") # normal get r = self.client.get(url) @@ -451,7 +451,7 @@ class EditPositionTestCase(django.test.TestCase): def test_edit_position_as_secretary(self): draft = make_test_data() url = urlreverse('doc_edit_position', kwargs=dict(name=draft.name)) - ad = Email.objects.get(address="aread@ietf.org") + ad = Person.objects.get(name="Aread Irector") url += "?ad=%s" % ad.pk login_testing_unauthorized(self, "secretary", url) @@ -476,7 +476,7 @@ class EditPositionTestCase(django.test.TestCase): draft.notify = "somebody@example.com" draft.save() - ad = Email.objects.get(address="aread@ietf.org") + ad = Person.objects.get(name="Aread Irector") BallotPositionEvent.objects.create(doc=draft, type="changed_ballot_position", by=ad, ad=ad, pos=BallotPositionName.objects.get(slug="yes"), @@ -643,7 +643,7 @@ class BallotWriteupsTestCase(django.test.TestCase): login_testing_unauthorized(self, "ad", url) def create_pos(num, vote, comment="", discuss=""): - ad = Email.objects.get(address="ad%s@ietf.org" % num) + ad = Person.objects.get(name="Ad No%s" % num) e = BallotPositionEvent() e.doc = draft e.by = ad @@ -671,7 +671,7 @@ class BallotWriteupsTestCase(django.test.TestCase): # we need approval text to be able to submit e = WriteupEvent() e.doc = draft - e.by = Email.objects.get(address="aread@ietf.org") + e.by = Person.objects.get(name="Aread Irector") e.type = "changed_ballot_approval_text" e.text = "The document has been approved." e.save() @@ -866,7 +866,7 @@ class ExpireIDsTestCase(django.test.TestCase): NewRevisionEvent.objects.create( type="new_revision", - by=Email.objects.get(address="aread@ietf.org"), + by=Person.objects.get(name="Aread Irector"), doc=draft, desc="New revision", time=datetime.datetime.now() - datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE - 7), @@ -897,7 +897,7 @@ class ExpireIDsTestCase(django.test.TestCase): NewRevisionEvent.objects.create( type="new_revision", - by=Email.objects.get(address="aread@ietf.org"), + by=Person.objects.get(name="Aread Irector"), doc=draft, desc="New revision", time=datetime.datetime.now() - datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE + 1), @@ -986,7 +986,7 @@ class ExpireIDsTestCase(django.test.TestCase): e = Event() e.doc = draft - e.by = Email.objects.get(address="(System)") + e.by = Person.objects.get(name="(System)") e.type = "expired_document" e.text = "Document has expired" e.time = datetime.date.today() - datetime.timedelta(days=INTERNET_DRAFT_DAYS_TO_EXPIRE + 1) @@ -1028,12 +1028,14 @@ class ExpireLastCallTestCase(django.test.TestCase): draft = make_test_data() draft.iesg_state_id = "lc" draft.save() + + secretary = Person.objects.get(name="Sec Retary") self.assertEquals(len(list(get_expired_last_calls())), 0) e = LastCallEvent() e.doc = draft - e.by = Email.objects.get(address="sec.retary@ietf.org") + e.by = secretary e.type = "sent_last_call" e.text = "Last call sent" e.expires = datetime.datetime.now() + datetime.timedelta(days=14) @@ -1044,7 +1046,7 @@ class ExpireLastCallTestCase(django.test.TestCase): # test expired e = LastCallEvent() e.doc = draft - e.by = Email.objects.get(address="sec.retary@ietf.org") + e.by = secretary e.type = "sent_last_call" e.text = "Last call sent" e.expires = datetime.datetime.now() diff --git a/ietf/idrfc/utils.py b/ietf/idrfc/utils.py index d885f3575..d33b6f422 100644 --- a/ietf/idrfc/utils.py +++ b/ietf/idrfc/utils.py @@ -67,7 +67,7 @@ def log_state_changedREDESIGN(request, doc, by, prev_iesg_state): e.desc = u"State changed to %s from %s by %s" % ( doc.iesg_state.name, prev_iesg_state.name if prev_iesg_state else "None", - by.get_name()) + by.name) e.save() return e @@ -145,19 +145,19 @@ def update_telechatREDESIGN(request, doc, by, new_telechat_date, new_returning_i if on_agenda != prev_agenda: if on_agenda: e.desc = "Placed on agenda for telechat - %s by %s" % ( - new_telechat_date, by.get_name()) + new_telechat_date, by.name) else: - e.desc = "Removed from agenda for telechat by %s" % by.get_name() + e.desc = "Removed from agenda for telechat by %s" % by.name elif on_agenda and new_telechat_date != prev_telechat: e.desc = "Telechat date has been changed to %s from %s by %s" % ( - new_telechat_date, prev_telechat, by.get_name()) + new_telechat_date, prev_telechat, by.name) else: # we didn't reschedule but flipped returning item bit - let's # just explain that if returning: - e.desc = "Added as returning item on telechat by %s" % by.get_name() + e.desc = "Added as returning item on telechat by %s" % by.name else: - e.desc = "Removed as returning item on telechat by %s" % by.get_name() + e.desc = "Removed as returning item on telechat by %s" % by.name e.save() diff --git a/ietf/idrfc/views_ballot.py b/ietf/idrfc/views_ballot.py index 023778ca2..b8827b176 100644 --- a/ietf/idrfc/views_ballot.py +++ b/ietf/idrfc/views_ballot.py @@ -15,6 +15,7 @@ from django.conf import settings from ietf.utils.mail import send_mail_text, send_mail_preformatted from ietf.ietfauth.decorators import group_required from ietf.idtracker.templatetags.ietf_filters import in_group +from ietf.ietfauth.decorators import has_role from ietf.idtracker.models import * from ietf.iesg.models import * from ietf.ipr.models import IprDetail @@ -205,7 +206,7 @@ def edit_positionREDESIGN(request, name): if not doc.iesg_state or not started_process: raise Http404() - ad = login = request.user.get_profile().email() + ad = login = request.user.get_profile() if 'HTTP_REFERER' in request.META: return_to_url = request.META['HTTP_REFERER'] @@ -213,11 +214,12 @@ def edit_positionREDESIGN(request, name): return_to_url = doc.get_absolute_url() # if we're in the Secretariat, we can select an AD to act as stand-in for - if not in_group(request.user, "Area_Director"): + if not has_role(request.user, "Area Director"): ad_id = request.GET.get('ad') if not ad_id: raise Http404() - ad = get_object_or_404(Email, pk=ad_id) + from person.models import Person + ad = get_object_or_404(Person, pk=ad_id) old_pos = doc.latest_event(BallotPositionEvent, type="changed_ballot_position", ad=ad, time__gte=started_process.time) @@ -270,17 +272,17 @@ def edit_positionREDESIGN(request, name): # figure out a description if not old_pos and pos.pos.slug != "norecord": - pos.desc = u"[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.get_name()) + pos.desc = u"[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.name) elif old_pos and pos.pos != old_pos.pos: - pos.desc = "[Ballot Position Update] Position for %s has been changed to %s from %s" % (pos.ad.get_name(), pos.pos.name, old_pos.pos.name) + pos.desc = "[Ballot Position Update] Position for %s has been changed to %s from %s" % (pos.ad.name, pos.pos.name, old_pos.pos.name) if not pos.desc and changes: - pos.desc = u"Ballot %s text updated for %s" % (u" and ".join(changes), ad.get_name()) + pos.desc = u"Ballot %s text updated for %s" % (u" and ".join(changes), ad.name) # only add new event if we actually got a change if pos.desc: if login != ad: - pos.desc += u" by %s" % login.get_name() + pos.desc += u" by %s" % login.name pos.save() @@ -399,7 +401,7 @@ def send_ballot_commentREDESIGN(request, name): if not started_process: raise Http404() - ad = login = request.user.get_profile().email() + ad = login = request.user.get_profile() return_to_url = request.GET.get('return_to_url') if not return_to_url: @@ -411,11 +413,12 @@ def send_ballot_commentREDESIGN(request, name): back_url = doc.get_absolute_url() # if we're in the Secretariat, we can select an AD to act as stand-in for - if not in_group(request.user, "Area_Director"): + if not has_role(request.user, "Area Director"): ad_id = request.GET.get('ad') if not ad_id: raise Http404() - ad = get_object_or_404(Email, pk=ad_id) + from person.models import Person + ad = get_object_or_404(Person, pk=ad_id) pos = doc.latest_event(BallotPositionEvent, type="changed_ballot_position", ad=ad, time__gte=started_process.time) if not pos: @@ -503,7 +506,7 @@ def defer_ballotREDESIGN(request, name): if not doc.iesg_state: raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() telechat_date = TelechatDates.objects.all()[0].date2 if request.method == 'POST': @@ -519,7 +522,7 @@ def defer_ballotREDESIGN(request, name): email_state_changed(request, doc, e.desc) update_telechat(request, doc, login, telechat_date) - email_ballot_deferred(request, doc, login.get_name(), telechat_date) + email_ballot_deferred(request, doc, login.name, telechat_date) return HttpResponseRedirect(doc.get_absolute_url()) @@ -565,7 +568,7 @@ def undefer_ballotREDESIGN(request, name): if not doc.iesg_state: raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() if request.method == 'POST': save_document_in_history(doc) @@ -702,7 +705,7 @@ def lastcalltextREDESIGN(request, name): if not doc.iesg_state: raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() existing = doc.latest_event(WriteupEvent, type="changed_last_call_text") if not existing: @@ -719,7 +722,7 @@ def lastcalltextREDESIGN(request, name): e = WriteupEvent(doc=doc, by=login) e.by = login e.type = "changed_last_call_text" - e.desc = "Last call announcement was changed by %s" % login.get_name() + e.desc = "Last call announcement was changed by %s" % login.name e.text = t e.save() @@ -867,7 +870,7 @@ def ballot_writeupnotesREDESIGN(request, name): if not started_process: raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() approval = doc.latest_event(WriteupEvent, type="changed_ballot_approval_text") @@ -885,7 +888,7 @@ def ballot_writeupnotesREDESIGN(request, name): e = WriteupEvent(doc=doc, by=login) e.by = login e.type = "changed_ballot_writeup_text" - e.desc = "Ballot writeup was changed by %s" % login.get_name() + e.desc = "Ballot writeup was changed by %s" % login.name e.text = t e.save() @@ -893,13 +896,13 @@ def ballot_writeupnotesREDESIGN(request, name): doc.save() if "issue_ballot" in request.POST and approval: - if in_group(request.user, "Area_Director") and not doc.latest_event(BallotPositionEvent, ad=login, time__gte=started_process.time): + if has_role(request.user, "Area Director") and not doc.latest_event(BallotPositionEvent, ad=login, time__gte=started_process.time): # sending the ballot counts as a yes pos = BallotPositionEvent(doc=doc, by=login) pos.type = "changed_ballot_position" pos.ad = login pos.pos_id = "yes" - pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.get_name()) + pos.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.pos.name, pos.ad.name) pos.save() msg = generate_issue_ballot_mail(request, doc) @@ -910,7 +913,7 @@ def ballot_writeupnotesREDESIGN(request, name): e = Event(doc=doc, by=login) e.by = login e.type = "sent_ballot_announcement" - e.desc = "Ballot has been issued by %s" % login.get_name() + e.desc = "Ballot has been issued by %s" % login.name e.save() doc.time = e.time @@ -1002,7 +1005,7 @@ def ballot_approvaltextREDESIGN(request, name): if not doc.iesg_state: raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() existing = doc.latest_event(WriteupEvent, type="changed_ballot_approval_text") if not existing: @@ -1019,7 +1022,7 @@ def ballot_approvaltextREDESIGN(request, name): e = WriteupEvent(doc=doc, by=login) e.by = login e.type = "changed_ballot_approval_text" - e.desc = "Ballot approval text was changed by %s" % login.get_name() + e.desc = "Ballot approval text was changed by %s" % login.name e.text = t e.save() @@ -1131,7 +1134,7 @@ def approve_ballotREDESIGN(request, name): if not doc.iesg_state: raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() e = doc.latest_event(WriteupEvent, type="changed_ballot_approval_text") if not e: @@ -1281,7 +1284,7 @@ def make_last_callREDESIGN(request, name): if not doc.iesg_state: raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() e = doc.latest_event(WriteupEvent, type="changed_last_call_text") if not e: @@ -1325,7 +1328,7 @@ def make_last_callREDESIGN(request, name): e = LastCallEvent(doc=doc, by=login) e.type = "sent_last_call" - e.desc = "Last call sent by %s" % login.get_name() + e.desc = "Last call sent by %s" % login.name if form.cleaned_data['last_call_sent_date'] != e.time.date(): e.time = datetime.datetime.combine(form.cleaned_data['last_call_sent_date'], e.time.time()) e.expires = form.cleaned_data['last_call_expiration_date'] diff --git a/ietf/idrfc/views_doc.py b/ietf/idrfc/views_doc.py index 4aa019825..da4450685 100644 --- a/ietf/idrfc/views_doc.py +++ b/ietf/idrfc/views_doc.py @@ -109,7 +109,7 @@ def document_main(request, name): return document_main_rfc(request, int(m.group(1))) id = get_object_or_404(InternetDraft, filename=name) doc = IdWrapper(id) - + info = {} stream_id = doc.stream_id() if stream_id == 2: @@ -168,7 +168,7 @@ def _get_history(doc, versions): info["dontmolest"] = True info['text'] = e.desc - info['by'] = e.by.get_name() + info['by'] = e.by.name info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25) info['snipped'] = info['textSnippet'][-3:] == "..." and e.type != "new_revision" results.append({'comment':e, 'info':info, 'date':e.time, 'is_com':True}) diff --git a/ietf/idrfc/views_edit.py b/ietf/idrfc/views_edit.py index d4f75aece..6fe8cfbd7 100644 --- a/ietf/idrfc/views_edit.py +++ b/ietf/idrfc/views_edit.py @@ -16,6 +16,7 @@ from django.conf import settings from ietf.utils.mail import send_mail_text from ietf.ietfauth.decorators import group_required from ietf.idtracker.templatetags.ietf_filters import in_group +from ietf.ietfauth.decorators import has_role from ietf.idtracker.models import * from ietf.iesg.models import * from ietf.idrfc.mails import * @@ -24,6 +25,7 @@ from ietf.idrfc.lastcall import request_last_call from doc.models import Document, Event, StatusDateEvent, TelechatEvent, save_document_in_history, DocHistory from name.models import IesgDocStateName, IntendedStdLevelName, DocInfoTagName, get_next_iesg_states, DocStateName +from person.models import Person, Email class ChangeStateForm(forms.Form): state = forms.ModelChoiceField(IDState.objects.all(), empty_label=None, required=True) @@ -93,7 +95,7 @@ def change_stateREDESIGN(request, name): if (not doc.latest_event(type="started_iesg_process")) or doc.state_id == "expired": raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() if request.method == 'POST': form = ChangeStateForm(request.POST) @@ -371,15 +373,11 @@ def edit_info(request, name): login=login), context_instance=RequestContext(request)) -class NameFromEmailModelChoiceField(forms.ModelChoiceField): - def label_from_instance(self, obj): - return obj.get_name() - class EditInfoFormREDESIGN(forms.Form): intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.all(), empty_label=None, required=True) status_date = forms.DateField(required=False, help_text="Format is YYYY-MM-DD") via_rfc_editor = forms.BooleanField(required=False, label="Via IRTF or RFC Editor") - ad = NameFromEmailModelChoiceField(Email.objects.filter(role__name__in=("ad", "ex-ad")).order_by('role__name', 'person__name'), label="Responsible AD", empty_label=None, required=True) + ad = forms.ModelChoiceField(Person.objects.filter(email__role__name__in=("ad", "ex-ad")).order_by('email__role__name', 'name'), label="Responsible AD", empty_label=None, required=True) notify = forms.CharField(max_length=255, label="Notice emails", help_text="Separate email addresses with commas", required=False) note = forms.CharField(widget=forms.Textarea, label="IESG note", required=False) telechat_date = forms.TypedChoiceField(coerce=lambda x: datetime.datetime.strptime(x, '%Y-%m-%d').date(), empty_value=None, required=False) @@ -392,7 +390,7 @@ class EditInfoFormREDESIGN(forms.Form): # fix up ad field choices = self.fields['ad'].choices - ex_ads = dict((e.pk, e) for e in Email.objects.filter(role__name="ex-ad")) + ex_ads = dict((e.pk, e) for e in Person.objects.filter(email__role__name="ex-ad").distinct()) if old_ads: # separate active ADs from inactive for i, t in enumerate(choices): @@ -455,7 +453,7 @@ def edit_infoREDESIGN(request, name): if doc.state_id == "expired": raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() new_document = False if not doc.iesg_state: # FIXME: should probably get this as argument to view @@ -486,7 +484,7 @@ def edit_infoREDESIGN(request, name): # place where the replace relationship is established e = Event() e.type = "added_comment" - e.by = Email.objects.get(address="(System)") + e.by = Person.objects.get(name="(System)") e.doc = doc e.desc = "Earlier history may be found in the Comment Log for %s" % (replaces[0], replaces[0].get_absolute_url()) e.save() @@ -535,7 +533,7 @@ def edit_infoREDESIGN(request, name): for c in changes: e = Event(doc=doc, by=login) e.type = "changed_document" - e.desc = c + " by %s" % login.get_name() + e.desc = c + " by %s" % login.name e.save() update_telechat(request, doc, login, @@ -548,11 +546,11 @@ def edit_infoREDESIGN(request, name): e.type ="changed_status_date" d = desc("Status date", r["status_date"], status_date) changes.append(d) - e.desc = d + " by %s" % login.get_name() + e.desc = d + " by %s" % login.name e.date = r["status_date"] e.save() - if in_group(request.user, 'Secretariat'): + if has_role(request.user, 'Secretariat'): via_rfc = DocInfoTagName.objects.get(slug="via-rfc") if r['via_rfc_editor']: doc.tags.add(via_rfc) @@ -580,7 +578,7 @@ def edit_infoREDESIGN(request, name): form = EditInfoForm(old_ads=False, initial=init) - if not in_group(request.user, 'Secretariat'): + if not has_role(request.user, 'Secretariat'): # filter out Via RFC Editor form.standard_fields = [x for x in form.standard_fields if x.name != "via_rfc_editor"] @@ -627,14 +625,14 @@ def request_resurrectREDESIGN(request, name): if doc.state_id != "expired": raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() if request.method == 'POST': email_resurrect_requested(request, doc, login) e = Event(doc=doc, by=login) e.type = "requested_resurrect" - e.desc = "Resurrection was requested by %s" % login.get_name() + e.desc = "Resurrection was requested by %s" % login.name e.save() return HttpResponseRedirect(doc.get_absolute_url()) @@ -682,7 +680,7 @@ def resurrectREDESIGN(request, name): if doc.state_id != "expired": raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() if request.method == 'POST': save_document_in_history(doc) @@ -693,7 +691,7 @@ def resurrectREDESIGN(request, name): e = Event(doc=doc, by=login) e.type = "completed_resurrect" - e.desc = "Resurrection was completed by %s" % login.get_name() + e.desc = "Resurrection was completed by %s" % login.name e.save() doc.state = DocStateName.objects.get(slug="active") @@ -746,7 +744,7 @@ def add_commentREDESIGN(request, name): if not doc.iesg_state: raise Http404() - login = request.user.get_profile().email() + login = request.user.get_profile() if request.method == 'POST': form = AddCommentForm(request.POST) @@ -759,7 +757,7 @@ def add_commentREDESIGN(request, name): e.save() email_owner(request, doc, doc.ad, login, - "A new comment added by %s" % login.get_name()) + "A new comment added by %s" % login.name) return HttpResponseRedirect(doc.get_absolute_url()) else: form = AddCommentForm() diff --git a/ietf/idrfc/views_search.py b/ietf/idrfc/views_search.py index b91e4d4fd..4207acbd4 100644 --- a/ietf/idrfc/views_search.py +++ b/ietf/idrfc/views_search.py @@ -276,18 +276,16 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES: def __init__(self, *args, **kwargs): super(SearchForm, self).__init__(*args, **kwargs) responsible = Document.objects.values_list('ad', flat=True).distinct() - active_ads = list(Email.objects.filter(role__name="ad", - role__group__type="area", - role__group__state="active") - .select_related('person')) - inactive_ads = list(Email.objects.filter(pk__in=responsible) - .exclude(pk__in=[x.pk for x in active_ads]) - .select_related('person')) - extract_last_name = lambda x: x.get_name().split(' ')[-1] + active_ads = list(Person.objects.filter(email__role__name="ad", + email__role__group__type="area", + email__role__group__state="active").distinct()) + inactive_ads = list(Person.objects.filter(pk__in=responsible) + .exclude(pk__in=[x.pk for x in active_ads])) + extract_last_name = lambda x: x.name_parts()[3] active_ads.sort(key=extract_last_name) inactive_ads.sort(key=extract_last_name) - self.fields['ad'].choices = c = [('', 'any AD')] + [(ad.pk, ad.get_name()) for ad in active_ads] + [('', '------------------')] + [(ad.pk, ad.get_name()) for ad in inactive_ads] + self.fields['ad'].choices = c = [('', 'any AD')] + [(ad.pk, ad.name) for ad in active_ads] + [('', '------------------')] + [(ad.pk, ad.name) for ad in inactive_ads] self.fields['subState'].choices = [('', 'any substate'), ('0', 'no substate')] + [(n.slug, n.name) for n in DocInfoTagName.objects.filter(slug__in=('point', 'ad-f-up', 'need-rev', 'extpty'))] def clean_name(self): value = self.cleaned_data.get('name','') @@ -479,16 +477,21 @@ def search_main(request): def by_ad(request, name): ad_id = None ad_name = None - for i in IESGLogin.objects.filter(user_level__in=[1,2]): - iname = str(i).lower().replace(' ','.') - if name == iname: - ad_id = i.id - ad_name = str(i) - break + if settings.USE_DB_REDESIGN_PROXY_CLASSES: + for p in Person.objects.filter(email__role__name__in=("ad", "ex-ad")): + if name == p.name.lower().replace(" ", "."): + ad_id = p.id + ad_name = p.name + break + else: + for i in IESGLogin.objects.filter(user_level__in=[1,2]): + iname = str(i).lower().replace(' ','.') + if name == iname: + ad_id = i.id + ad_name = str(i) + break if not ad_id: raise Http404 - if settings.USE_DB_REDESIGN_PROXY_CLASSES: - ad_id = i.person.email()[1] form = SearchForm({'by':'ad','ad':ad_id, 'rfcs':'on', 'activeDrafts':'on', 'oldDrafts':'on'}) if not form.is_valid(): diff --git a/ietf/idtracker/admin.py b/ietf/idtracker/admin.py index 5c90f0261..26bca3ae6 100644 --- a/ietf/idtracker/admin.py +++ b/ietf/idtracker/admin.py @@ -88,7 +88,8 @@ class IESGLoginAdmin(admin.ModelAdmin): ordering=['user_level', 'last_name'] list_display=('login_name', 'first_name', 'last_name', 'user_level') raw_id_fields=['person'] -admin.site.register(IESGLogin, IESGLoginAdmin) +if not settings.USE_DB_REDESIGN_PROXY_CLASSES: + admin.site.register(IESGLogin, IESGLoginAdmin) class IETFWGAdmin(admin.ModelAdmin): list_display=('group_acronym', 'group_type', 'status', 'area_acronym', 'start_date', 'concluded_date') diff --git a/ietf/idtracker/models.py b/ietf/idtracker/models.py index 9f134a11b..125123cb0 100644 --- a/ietf/idtracker/models.py +++ b/ietf/idtracker/models.py @@ -1094,8 +1094,10 @@ if settings.USE_DB_REDESIGN_PROXY_CLASSES: IDSubStateOld = IDSubState AreaOld = Area AcronymOld = Acronym + IESGLoginOld = IESGLogin from redesign.doc.proxy import InternetDraft, IDInternal, BallotInfo, IDState, IDSubState from redesign.group.proxy import Area, Acronym + from redesign.person.proxy import IESGLogin # changes done by convert-096.py:changed maxlength to max_length diff --git a/ietf/idtracker/templatetags/ietf_filters.py b/ietf/idtracker/templatetags/ietf_filters.py index 03c262c78..3f9e1a4d3 100644 --- a/ietf/idtracker/templatetags/ietf_filters.py +++ b/ietf/idtracker/templatetags/ietf_filters.py @@ -2,6 +2,7 @@ import textwrap from django import template +from django.conf import settings from django.utils.html import escape, fix_ampersands from django.template.defaultfilters import linebreaksbr, wordwrap, stringfilter from django.template import resolve_variable @@ -358,8 +359,18 @@ def startswith(x, y): # based on http://www.djangosnippets.org/snippets/847/ by 'whiteinge' @register.filter def in_group(user, groups): + if settings.USE_DB_REDESIGN_PROXY_CLASSES: + return has_role(user, groups.replace("Area_Director", "Area Director")) + return user and user.is_authenticated() and bool(user.groups.filter(name__in=groups.split(',')).values('name')) +@register.filter +def has_role(user, role_names): + from ietf.ietfauth.decorators import has_role + if not user: + return False + return has_role(user, role_names.split(',')) + @register.filter def stable_dictsort(value, arg): """ diff --git a/ietf/iesg/feeds.py b/ietf/iesg/feeds.py index 356f2d697..66d49719b 100644 --- a/ietf/iesg/feeds.py +++ b/ietf/iesg/feeds.py @@ -42,6 +42,6 @@ class IESGAgenda(Feed): return str( item.job_owner ) def item_author_email(self, item): if settings.USE_DB_REDESIGN_PROXY_CLASSES: - return item.ad.address + return item.ad.email_address() return item.job_owner.person.email()[1] diff --git a/ietf/iesg/tests.py b/ietf/iesg/tests.py index f40c92858..8486eab48 100644 --- a/ietf/iesg/tests.py +++ b/ietf/iesg/tests.py @@ -66,7 +66,7 @@ class RescheduleOnAgendaTestCaseREDESIGN(django.test.TestCase): def test_reschedule(self): from ietf.utils.test_data import make_test_data - from redesign.person.models import Email + from redesign.person.models import Person from doc.models import TelechatEvent draft = make_test_data() @@ -74,7 +74,7 @@ class RescheduleOnAgendaTestCaseREDESIGN(django.test.TestCase): # add to schedule e = TelechatEvent(type="scheduled_for_telechat") e.doc = draft - e.by = Email.objects.get(address="aread@ietf.org") + e.by = Person.objects.get(name="Aread Irector") e.telechat_date = TelechatDates.objects.all()[0].date1 e.returning_item = True e.save() @@ -159,6 +159,56 @@ class ManageTelechatDatesTestCase(django.test.TestCase): self.assertTrue(dates.date4 == new_date) self.assertTrue(dates.date1 == old_date2) +class ManageTelechatDatesTestCaseREDESIGN(django.test.TestCase): + fixtures = ['names'] + + def test_set_dates(self): + from ietf.utils.test_data import make_test_data + make_test_data() + + dates = TelechatDates.objects.all()[0] + url = urlreverse('ietf.iesg.views.telechat_dates') + login_testing_unauthorized(self, "secretary", url) + + # normal get + r = self.client.get(url) + self.assertEquals(r.status_code, 200) + q = PyQuery(r.content) + self.assertEquals(len(q('form input[name=date1]')), 1) + + # post + new_date = dates.date1 + timedelta(days=7) + + r = self.client.post(url, dict(date1=new_date.isoformat(), + date2=new_date.isoformat(), + date3=new_date.isoformat(), + date4=new_date.isoformat(), + )) + self.assertEquals(r.status_code, 200) + + dates = TelechatDates.objects.all()[0] + self.assertTrue(dates.date1 == new_date) + + def test_rollup_dates(self): + from ietf.utils.test_data import make_test_data + make_test_data() + + dates = TelechatDates.objects.all()[0] + url = urlreverse('ietf.iesg.views.telechat_dates') + login_testing_unauthorized(self, "secretary", url) + + old_date2 = dates.date2 + new_date = dates.date4 + timedelta(days=14) + r = self.client.post(url, dict(rollup_dates="1")) + self.assertEquals(r.status_code, 200) + + dates = TelechatDates.objects.all()[0] + self.assertTrue(dates.date4 == new_date) + self.assertTrue(dates.date1 == old_date2) + +if settings.USE_DB_REDESIGN_PROXY_CLASSES: + ManageTelechatDatesTestCase = ManageTelechatDatesTestCaseREDESIGN + class WorkingGroupActionsTestCase(django.test.TestCase): fixtures = ['base', 'wgactions'] @@ -320,7 +370,7 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): def test_edit_wgaction(self): from ietf.utils.test_data import make_test_data - from redesign.person.models import Email + from redesign.person.models import Person make_test_data() @@ -337,7 +387,7 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): # change dates = TelechatDates.objects.all()[0] - token_name = Email.objects.get(address="ad1@ietf.org").get_name().split(" ")[0] + token_name = Person.objects.get(name="Ad No1").name_parts()[1] old = wga.pk r = self.client.post(url, dict(status_date=dates.date1.isoformat(), token_name=token_name, @@ -355,7 +405,7 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): def test_add_possible_wg(self): from ietf.utils.test_data import make_test_data - from redesign.person.models import Email + from redesign.person.models import Person from redesign.group.models import Group make_test_data() @@ -388,7 +438,7 @@ class WorkingGroupActionsTestCaseREDESIGN(django.test.TestCase): wgas_before = WGAction.objects.all().count() dates = TelechatDates.objects.all()[0] - token_name = Email.objects.get(address="ad1@ietf.org").get_name().split(" ")[0] + token_name = Person.objects.get(name="Ad No1").name_parts()[1] r = self.client.post(add_url, dict(status_date=dates.date1.isoformat(), token_name=token_name, diff --git a/ietf/iesg/views.py b/ietf/iesg/views.py index d8dcf241e..51602e77e 100644 --- a/ietf/iesg/views.py +++ b/ietf/iesg/views.py @@ -309,6 +309,7 @@ def agenda_scribe_template(request): def _agenda_moderator_package(request): data = _agenda_data(request) data['ad_names'] = [str(x) for x in IESGLogin.active_iesg()] + data['ad_names'].sort(key=lambda x: x.split(' ')[-1]) return render_to_response("iesg/moderator_package.html", data, context_instance=RequestContext(request)) @group_required('Area_Director','Secretariat') @@ -381,7 +382,7 @@ def handle_reschedule_form(request, idinternal, dates): form = RescheduleForm(request.POST, **formargs) if form.is_valid(): if settings.USE_DB_REDESIGN_PROXY_CLASSES: - login = request.user.get_profile().email() + login = request.user.get_profile() update_telechat(request, idinternal, login, form.cleaned_data['telechat_date'], False if form.cleaned_data['clear_returning_item'] else None) diff --git a/ietf/ietfauth/decorators.py b/ietf/ietfauth/decorators.py index a1c3db2fc..c1b1d66a6 100644 --- a/ietf/ietfauth/decorators.py +++ b/ietf/ietfauth/decorators.py @@ -31,6 +31,8 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from django.utils.http import urlquote +from django.conf import settings +from django.db.models import Q from django.contrib.auth.decorators import _CheckLogin from django.http import HttpResponseRedirect, HttpResponseForbidden @@ -62,3 +64,48 @@ def group_required(*group_names): def decorate(view_func): return _CheckLogin403(view_func, lambda u: bool(u.groups.filter(name__in=group_names)), "Restricted to group%s %s" % ("s" if len(group_names) != 1 else "", ",".join(group_names))) return decorate + + +def has_role(user, role_names): + """Determines whether user has any of the given standard roles + given. Role names must be a list or, in case of a single value, a + string.""" + if isinstance(role_names, str) or isinstance(role_names, unicode): + role_names = [ role_names ] + + if not user or not user.is_authenticated(): + return False + + from redesign.person.models import Person + + try: + person = user.get_profile() + except Person.DoesNotExist: + return False + + role_qs = { + "Area Director": Q(email__person=person, name="ad"), + "Secretariat": Q(email__person=person, name="secr", group__acronym="secretariat") + } + + filter_expr = Q() + for r in role_names: + filter_expr |= role_qs[r] + + from redesign.group.models import Role + return bool(Role.objects.filter(filter_expr)[:1]) + +def role_required(*role_names): + """View decorator for checking that the user is logged in and + belongs to (at least) one of the listed roles. Users who are not + logged in are redirected to the login page; users who don't have + one of the roles get a "403" page. + """ + def decorate(view_func): + return _CheckLogin403(view_func, + lambda u: has_role(u, role_names), + "Restricted to role%s %s" % ("s" if len(role_names) != 1 else "", ",".join(role_names))) + return decorate + +if settings.USE_DB_REDESIGN_PROXY_CLASSES: + group_required = lambda *group_names: role_required(*[n.replace("Area_Director", "Area Director") for n in group_names]) diff --git a/ietf/ietfauth/tests.py b/ietf/ietfauth/tests.py index e4c6d6675..c9e2d8371 100644 --- a/ietf/ietfauth/tests.py +++ b/ietf/ietfauth/tests.py @@ -31,6 +31,7 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import unittest +from django.conf import settings from django.contrib.auth.models import User from django.test.client import Client from ietf.utils.test_utils import SimpleUrlTestCase, RealDatabaseTest @@ -41,6 +42,8 @@ class IetfAuthUrlTestCase(SimpleUrlTestCase): def testUrls(self): self.doTestUrls(__file__) +# this test case should really work on a test database instead of the +# real one class IetfAuthTestCase(unittest.TestCase,RealDatabaseTest): def setUp(self): self.setUpRealDatabase() @@ -61,7 +64,7 @@ class IetfAuthTestCase(unittest.TestCase,RealDatabaseTest): response = c.get(nexturl[2], {}, False, REMOTE_USER=username) self.assertEquals(response.status_code, 200) - self.assert_("Roles/Groups:" in response.content) + self.assert_("User name" in response.content) return response def testLogin(self): @@ -95,4 +98,7 @@ class IetfAuthTestCase(unittest.TestCase,RealDatabaseTest): self.assert_("IETF_Chair" in groups) print "OK" - + +if settings.USE_DB_REDESIGN_PROXY_CLASSES: + # this test doesn't make any sense anymore + IetfAuthTestCase.testGroups = lambda x: None diff --git a/ietf/ietfauth/views.py b/ietf/ietfauth/views.py index e350791a8..5750d23cc 100644 --- a/ietf/ietfauth/views.py +++ b/ietf/ietfauth/views.py @@ -69,4 +69,21 @@ def ietf_loggedin(request): @login_required def profile(request): + if settings.USE_DB_REDESIGN_PROXY_CLASSES: + from person.models import Person + from group.models import Role + + roles = [] + person = None + try: + person = request.user.get_profile() + roles = Role.objects.filter(email__person=person).distinct() + except Person.DoesNotExist: + pass + + return render_to_response('registration/profileREDESIGN.html', + dict(roles=roles, + person=person), + context_instance=RequestContext(request)) + return render_to_response('registration/profile.html', context_instance=RequestContext(request)) diff --git a/ietf/settings.py b/ietf/settings.py index 110773850..bd3f06720 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -198,6 +198,10 @@ LIAISON_ATTACH_URL = '/documents/LIAISON/' # DB redesign USE_DB_REDESIGN_PROXY_CLASSES = True +if USE_DB_REDESIGN_PROXY_CLASSES: + AUTH_PROFILE_MODULE = 'person.Person' + AUTHENTICATION_BACKENDS = ( 'django.contrib.auth.backends.RemoteUserBackend', ) + # Put SECRET_KEY in here, or any other sensitive or site-specific # changes. DO NOT commit settings_local.py to svn. from settings_local import * diff --git a/ietf/templates/idrfc/edit_positionREDESIGN.html b/ietf/templates/idrfc/edit_positionREDESIGN.html index 8b2c6cf2e..a81b8e738 100644 --- a/ietf/templates/idrfc/edit_positionREDESIGN.html +++ b/ietf/templates/idrfc/edit_positionREDESIGN.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Change position for {{ ad.get_name }}{% endblock %} +{% block title %}Change position for {{ ad.name }}{% endblock %} {% block morecss %} form.position-form .position ul { @@ -29,7 +29,7 @@ form.position-form .comment-text { {% endblock %} {% block content %} -

Change position for {{ ad.get_name }}

+

Change position for {{ ad.name }}

{{ form.position }}
diff --git a/ietf/templates/idrfc/send_ballot_commentREDESIGN.html b/ietf/templates/idrfc/send_ballot_commentREDESIGN.html index d9301a678..1de2fca67 100644 --- a/ietf/templates/idrfc/send_ballot_commentREDESIGN.html +++ b/ietf/templates/idrfc/send_ballot_commentREDESIGN.html @@ -1,6 +1,6 @@ {% extends "base.html" %} -{% block title %}Email Discuss and Comment text for {{ ad.get_name }} to IESG list{% endblock %} +{% block title %}Email Discuss and Comment text for {{ ad.name }} to IESG list{% endblock %} {% block morecss %} form.send-ballot pre { @@ -12,7 +12,7 @@ form.send-ballot pre { {% endblock %} {% block content %} -

Email Discuss and Comment text for {{ ad.get_name }} to IESG list

+

Email Discuss and Comment text for {{ ad.name }} to IESG list

diff --git a/ietf/templates/iesg/scribe_doc.html b/ietf/templates/iesg/scribe_doc.html index dc072c10b..2945f6f9c 100644 --- a/ietf/templates/iesg/scribe_doc.html +++ b/ietf/templates/iesg/scribe_doc.html @@ -49,9 +49,9 @@ Some parts Copyright (c) 2009 The IETF Trust, all rights reserved. {% endfor %} {% if doc.obj.ballot.active %}
Discusses/comments (from ballot {{doc.obj.ballot.ballot }}):
    {% if USE_DB_REDESIGN_PROXY_CLASSES %} -{% for p in doc.obj.active_positions|dictsort:"ad.get_name" %}{% if p.pos %}{% ifequal p.pos.pos_id "discuss" %}
  1. {{ p.pos.ad.get_name }}: Discuss [{{ p.pos.discuss_time.date }}]: +{% for p in doc.obj.active_positions|dictsort:"ad.name" %}{% if p.pos %}{% ifequal p.pos.pos_id "discuss" %}
  2. {{ p.pos.ad.name }}: Discuss [{{ p.pos.discuss_time.date }}]:
    ... -{% endifequal %}{% if p.pos.comment %}
  3. {{ p.pos.ad.get_name }}: Comment [{{ p.pos.comment_time.date }}]: +{% endifequal %}{% if p.pos.comment %}
  4. {{ p.pos.ad.name }}: Comment [{{ p.pos.comment_time.date }}]:
    ... {% endif %}{% endif %}{% endfor %} {% else %} diff --git a/ietf/templates/registration/profileREDESIGN.html b/ietf/templates/registration/profileREDESIGN.html new file mode 100644 index 000000000..5c6083d6c --- /dev/null +++ b/ietf/templates/registration/profileREDESIGN.html @@ -0,0 +1,31 @@ +{# Copyright The IETF Trust 2007, All Rights Reserved #} +{% extends "base.html" %} + +{% block morecss %} +table.userProfile th { + text-align: left; +} +{% endblock %} + +{% block title %}Profile for {{ user }}{% endblock %} + +{% block content %} +

    User information

    + +
+ + + + + + + + + {% for role in roles %} + + + + + {% endfor %} +
User name:{{ user.username }}
Person:{{ person.name|default:"?" }}
{% if forloop.first %}Roles:{% endif %}{{ role.name }} in {{ role.group.name }} ({{ role.group.type }})
+{% endblock %} diff --git a/ietf/utils/test_data.py b/ietf/utils/test_data.py index bb74100ea..2bdcf3fdb 100644 --- a/ietf/utils/test_data.py +++ b/ietf/utils/test_data.py @@ -1,4 +1,5 @@ -from ietf.idtracker.models import IESGLogin, PersonOrOrgInfo, EmailAddress +from django.contrib.auth.models import User + from ietf.iesg.models import TelechatDates, WGAction from redesign.doc.models import * from redesign.name.models import * @@ -7,6 +8,12 @@ from redesign.person.models import * def make_test_data(): # groups + secretariat = Group.objects.create( + name="Secretariat", + acronym="secretariat", + state_id="active", + type_id="ietf", + parent=None) area = Group.objects.create( name="Far Future", acronym="farfut", @@ -22,45 +29,43 @@ def make_test_data(): ) # persons - Email.objects.get_or_create(address="(System)") + + # system + system_person = Person.objects.create( + id=0, # special value + name="(System)", + ascii="(System)", + address="", + ) + + if system_person.id != 0: # work around bug in Django + Person.objects.filter(id=system_person.id).update(id=0) + system_person = Person.objects.get(id=0) + + Alias.objects.get_or_create(person=system_person, name=system_person.name) + Email.objects.get_or_create(address="", person=system_person) # ad - p = Person.objects.create( + u = User.objects.create(username="ad") + ad = p = Person.objects.create( name="Aread Irector", ascii="Aread Irector", - ) - ad = Email.objects.create( + user=u) + email = Email.objects.create( address="aread@ietf.org", person=p) Role.objects.create( name_id="ad", group=area, - email=ad) - porg = PersonOrOrgInfo.objects.create( - first_name="Aread", - last_name="Irector", - middle_initial="", - ) - EmailAddress.objects.create( - person_or_org=porg, - priority=1, - address=ad.address, - ) - IESGLogin.objects.create( - login_name="ad", - password="foo", - user_level=1, - first_name=porg.first_name, - last_name=porg.last_name, - person=porg, - ) + email=email) # create a bunch of ads for swarm tests for i in range(1, 10): + u = User.objects.create(username="ad%s" % i) p = Person.objects.create( name="Ad No%s" % i, ascii="Ad No%s" % i, - ) + user=u) email = Email.objects.create( address="ad%s@ietf.org" % i, person=p) @@ -68,24 +73,6 @@ def make_test_data(): name_id="ad" if i <= 5 else "ex-ad", group=area, email=email) - porg = PersonOrOrgInfo.objects.create( - first_name="Ad", - last_name="No%s" % i, - middle_initial="", - ) - EmailAddress.objects.create( - person_or_org=porg, - priority=1, - address=ad.address, - ) - IESGLogin.objects.create( - login_name="ad%s" % i, - password="foo", - user_level=1, - first_name=porg.first_name, - last_name=porg.last_name, - person=porg, - ) # group chair p = Person.objects.create( @@ -96,36 +83,24 @@ def make_test_data(): address="wgchairman@ietf.org", person=p) Role.objects.create( - name=RoleName.objects.get(slug="chair"), + name_id="chair", group=group, email=wgchair, ) # secretary + u = User.objects.create(username="secretary") p = Person.objects.create( name="Sec Retary", ascii="Sec Retary", - ) - Email.objects.create( + user=u) + email = Email.objects.create( address="sec.retary@ietf.org", person=p) - porg = PersonOrOrgInfo.objects.create( - first_name="Sec", - last_name="Retary", - middle_initial="", - ) - EmailAddress.objects.create( - person_or_org=porg, - priority=1, - address="sec.retary@ietf.org", - ) - IESGLogin.objects.create( - login_name="secretary", - password="foo", - user_level=0, - first_name=porg.first_name, - last_name=porg.last_name, - person=porg, + Role.objects.create( + name_id="secr", + group=secretariat, + email=email, ) # draft diff --git a/redesign/doc/models.py b/redesign/doc/models.py index 4615b1959..ea7969584 100644 --- a/redesign/doc/models.py +++ b/redesign/doc/models.py @@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse as urlreverse from redesign.group.models import * from redesign.name.models import * -from redesign.person.models import Email +from redesign.person.models import Email, Person from redesign.util import admin_link import datetime @@ -31,8 +31,8 @@ class DocumentInfo(models.Model): pages = models.IntegerField(blank=True, null=True) intended_std_level = models.ForeignKey(IntendedStdLevelName, blank=True, null=True) std_level = models.ForeignKey(StdLevelName, blank=True, null=True) - ad = models.ForeignKey(Email, verbose_name="area director", related_name='ad_%(class)s_set', blank=True, null=True) - shepherd = models.ForeignKey(Email, related_name='shepherd_%(class)s_set', blank=True, null=True) + ad = models.ForeignKey(Person, verbose_name="area director", related_name='ad_%(class)s_set', blank=True, null=True) + shepherd = models.ForeignKey(Person, related_name='shepherd_%(class)s_set', blank=True, null=True) notify = models.CharField(max_length=255, blank=True) external_url = models.URLField(blank=True) # Should be set for documents with type 'External'. note = models.TextField(blank=True) @@ -60,8 +60,8 @@ class RelatedDocument(models.Model): class DocumentAuthor(models.Model): document = models.ForeignKey('Document') - author = models.ForeignKey(Email) - order = models.IntegerField() + author = models.ForeignKey(Email, help_text="Email address used by author for submission") + order = models.IntegerField(default=1) def __unicode__(self): return u"%s %s (%s)" % (self.document.name, self.author.get_name(), self.order) @@ -224,12 +224,12 @@ class Event(models.Model): """An occurrence for a document, used for tracking who, when and what.""" time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened") type = models.CharField(max_length=50, choices=EVENT_TYPES) - by = models.ForeignKey(Email, blank=True, null=True) # FIXME: make NOT NULL? + by = models.ForeignKey(Person) doc = models.ForeignKey('doc.Document') desc = models.TextField() def __unicode__(self): - return u"%s %s at %s" % (self.by.get_name(), self.get_type_display().lower(), self.time) + return u"%s %s at %s" % (self.by.name, self.get_type_display().lower(), self.time) class Meta: ordering = ['-time', 'id'] @@ -239,7 +239,7 @@ class NewRevisionEvent(Event): # IESG events class BallotPositionEvent(Event): - ad = models.ForeignKey(Email) + ad = models.ForeignKey(Person) pos = models.ForeignKey(BallotPositionName, verbose_name="position", default="norecord") discuss = models.TextField(help_text="Discuss text if position is discuss", blank=True) discuss_time = models.DateTimeField(help_text="Time discuss text was written", blank=True, null=True) diff --git a/redesign/doc/proxy.py b/redesign/doc/proxy.py index c7e75465d..2c2b0ae28 100644 --- a/redesign/doc/proxy.py +++ b/redesign/doc/proxy.py @@ -305,12 +305,12 @@ class InternetDraft(Document): #token_name = models.CharField(blank=True, max_length=25) @property def token_name(self): - return self.ad.get_name() + return self.ad.name #token_email = models.CharField(blank=True, max_length=255) @property def token_email(self): - return self.ad.address + return self.ad.email_address() #note = models.TextField(blank=True) # same name @@ -556,7 +556,7 @@ class InternetDraft(Document): # return remarks def active_positions(self): """Returns a list of dicts, with AD and Position tuples""" - active_ads = Email.objects.filter(role__name="ad", role__group__state="active") + active_ads = Person.objects.filter(email__role__name="ad", email__role__group__state="active") res = [] def add(ad, pos): from person.proxy import IESGLogin as IESGLoginProxy @@ -638,7 +638,7 @@ class InternetDraft(Document): e = self.published_rfc else: e = self.latest_event(type="published_rfc") - return e.time.date() if e else None + return e.time.date() if e else datetime.date(1990,1,1) #current_status = models.CharField(max_length=50,null=True) @property @@ -782,11 +782,11 @@ class DocumentComment(Event): def get_absolute_url(self): return "/doc/%s/" % self.doc.name def get_author(self): - return self.by.get_name() + return self.by.name def get_username(self): return unicode(self.by) def get_fullname(self): - return self.by.get_name() + return self.by.name def datetime(self): return self.time def __str__(self): diff --git a/redesign/group/models.py b/redesign/group/models.py index 6e7ef00ce..327970f4a 100644 --- a/redesign/group/models.py +++ b/redesign/group/models.py @@ -2,7 +2,7 @@ from django.db import models from redesign.name.models import * -from redesign.person.models import Email +from redesign.person.models import Email, Person import datetime @@ -35,11 +35,11 @@ class GroupEvent(models.Model): group = models.ForeignKey(Group) time = models.DateTimeField(default=datetime.datetime.now, help_text="When the event happened") type = models.CharField(max_length=50, choices=GROUP_EVENT_CHOICES) - by = models.ForeignKey(Email) + by = models.ForeignKey(Person) desc = models.TextField() def __unicode__(self): - return u"%s %s at %s" % (self.by.get_name(), self.get_type_display().lower(), self.time) + return u"%s %s at %s" % (self.by.name, self.get_type_display().lower(), self.time) class Meta: ordering = ['-time', 'id'] @@ -73,8 +73,8 @@ class GroupHistory(models.Model): class Role(models.Model): name = models.ForeignKey(RoleName) group = models.ForeignKey(Group) - email = models.ForeignKey(Email) - auth = models.CharField(max_length=255, blank=True) + email = models.ForeignKey(Email, help_text="Email address used by person for this role") + auth = models.CharField(max_length=255, blank=True) # unused? def __unicode__(self): return u"%s is %s in %s" % (self.email.get_name(), self.name.name, self.group.acronym) diff --git a/redesign/importing/import-announcements.py b/redesign/importing/import-announcements.py index 4a0984bdf..f423565cc 100755 --- a/redesign/importing/import-announcements.py +++ b/redesign/importing/import-announcements.py @@ -7,6 +7,7 @@ sys.path = [ basedir ] + sys.path from ietf import settings settings.USE_DB_REDESIGN_PROXY_CLASSES = False +settings.IMPORTING_ANNOUNCEMENTS = True from django.core import management management.setup_environ(settings) @@ -14,7 +15,7 @@ management.setup_environ(settings) from redesign.person.models import * from redesign.group.models import * from redesign.name.utils import name -from redesign.importing.utils import person_email +from redesign.importing.utils import old_person_to_person from ietf.announcements.models import Message from ietf.announcements.models import Announcement, PersonOrOrgInfo, AnnouncedTo, AnnouncedFrom @@ -26,7 +27,7 @@ from ietf.announcements.models import Announcement, PersonOrOrgInfo, AnnouncedTo # FIXME: should import ScheduledAnnouncements -system_email, _ = Email.objects.get_or_create(address="(System)") +system = Person.objects.get(name="(System)") # Announcement for o in Announcement.objects.all().select_related('announced_to', 'announced_from').order_by('announcement_id').iterator(): @@ -41,12 +42,12 @@ for o in Announcement.objects.all().select_related('announced_to', 'announced_fr try: x = o.announced_by except PersonOrOrgInfo.DoesNotExist: - message.by = system_email + message.by = system else: if not o.announced_by.first_name and o.announced_by.last_name == 'None': - message.by = system_email + message.by = system else: - message.by = Email.objects.get(address=person_email(o.announced_by)) + message.by = old_person_to_person(o.announced_by) message.subject = o.subject.strip() if o.announced_from_id == 99: diff --git a/redesign/importing/import-document-state.py b/redesign/importing/import-document-state.py index 961635054..b7c9fff0a 100755 --- a/redesign/importing/import-document-state.py +++ b/redesign/importing/import-document-state.py @@ -14,7 +14,7 @@ management.setup_environ(settings) from redesign.doc.models import * from redesign.group.models import * from redesign.name.models import * -from redesign.importing.utils import person_email +from redesign.importing.utils import old_person_to_person from redesign.name.utils import name from ietf.idtracker.models import InternetDraft, IDInternal, IESGLogin, DocumentComment, PersonOrOrgInfo, Rfc, IESGComment, IESGDiscuss, BallotInfo, Position from ietf.idrfc.models import RfcIndex, DraftVersions @@ -145,7 +145,7 @@ tag_has_errata = name(DocInfoTagName, 'errata', "Has errata") # helpers def save_event(doc, event, comment): event.time = comment.datetime() - event.by = iesg_login_to_email(comment.created_by) + event.by = iesg_login_to_person(comment.created_by) event.doc = doc if not event.desc: event.desc = comment.comment_text # FIXME: consider unquoting here @@ -159,17 +159,16 @@ def sync_tag(d, include, tag): buggy_iesg_logins_cache = {} -# make sure system email exists -system_email, _ = Email.objects.get_or_create(address="(System)") +system = Person.objects.get(name="(System)") -def iesg_login_to_email(l): +def iesg_login_to_person(l): if not l: - return system_email + return system else: # there's a bunch of old weird comments made by "IESG # Member", transform these into "System" instead if l.id == 2: - return system_email + return system # fix logins without the right person if not l.person: @@ -185,19 +184,22 @@ def iesg_login_to_email(l): else: buggy_iesg_logins_cache[l.id] = None l = buggy_iesg_logins_cache[l.id] + + if not l: + return system try: - return Email.objects.get(address=person_email(l.person)) + return old_person_to_person(l.person) except Email.DoesNotExist: try: - return Email.objects.get(person__name="%s %s" % (l.person.first_name, l.person.last_name)) - except Email.DoesNotExist: - print "MISSING IESG LOGIN", l.person.email() + return Person.objects.get(name="%s %s" % (l.person.first_name, l.person.last_name)) + except Person.DoesNotExist: + print "MISSING IESG LOGIN", l.person, l.person.email() return None def iesg_login_is_secretary(l): - # Amy has two users, for some reason - return l.user_level == IESGLogin.SECRETARIAT_LEVEL or l.first_name == "Amy" and l.last_name == "Vezza" + # Amy has two users, for some reason, we sometimes get the wrong one + return l.user_level == IESGLogin.SECRETARIAT_LEVEL or (l.first_name == "Amy" and l.last_name == "Vezza") # regexps for parsing document comments @@ -234,10 +236,9 @@ re_comment_discuss_by_tag = re.compile(r" by [\w-]+ [\w-]+$") def import_from_idinternal(d, idinternal): d.time = idinternal.event_date d.iesg_state = iesg_state_mapping[idinternal.cur_state.state] - d.ad = iesg_login_to_email(idinternal.job_owner) + d.ad = iesg_login_to_person(idinternal.job_owner) d.notify = idinternal.state_change_notice_to or "" - d.note = idinternal.note or "" - d.note = d.note.replace('
', '\n').strip().replace('\n', '
') + d.note = (idinternal.note or "").replace('
', '\n').strip().replace('\n', '
') d.save() # extract events @@ -267,14 +268,14 @@ def import_from_idinternal(d, idinternal): save_event(d, e, c) handled = True - ad = iesg_login_to_email(c.created_by) + ad = iesg_login_to_person(c.created_by) last_pos = d.latest_event(BallotPositionEvent, type="changed_ballot_position", ad=ad) if not last_pos and not iesg_login_is_secretary(c.created_by): # when you issue a ballot, you also vote yes; add that vote e = BallotPositionEvent() e.type = "changed_ballot_position" e.ad = ad - e.desc = "[Ballot Position Update] New position, Yes, has been recorded by %s" % e.ad.get_name() + e.desc = "[Ballot Position Update] New position, Yes, has been recorded by %s" % e.ad.name e.pos = ballot_position_mapping["Yes"] e.discuss = last_pos.discuss if last_pos else "" @@ -309,7 +310,7 @@ def import_from_idinternal(d, idinternal): found = False for p in positions: - if not d.event_set.filter(type="changed_ballot_position", ballotposition__pos=position, ballotposition__ad=iesg_login_to_email(p.ad)): + if not d.event_set.filter(type="changed_ballot_position", ballotposition__pos=position, ballotposition__ad=iesg_login_to_person(p.ad)): login = p.ad found = True break @@ -335,7 +336,7 @@ def import_from_idinternal(d, idinternal): e = BallotPositionEvent() e.type = "changed_ballot_position" - e.ad = iesg_login_to_email(login) + e.ad = iesg_login_to_person(login) last_pos = d.latest_event(BallotPositionEvent, type="changed_ballot_position", ad=e.ad) e.pos = position e.discuss = last_pos.discuss if last_pos else "" @@ -353,7 +354,7 @@ def import_from_idinternal(d, idinternal): if c.ballot in (DocumentComment.BALLOT_DISCUSS, DocumentComment.BALLOT_COMMENT): e = BallotPositionEvent() e.type = "changed_ballot_position" - e.ad = iesg_login_to_email(c.created_by) + e.ad = iesg_login_to_person(c.created_by) last_pos = d.latest_event(BallotPositionEvent, type="changed_ballot_position", ad=e.ad) e.pos = last_pos.pos if last_pos else ballot_position_mapping[None] c.comment_text = re_comment_discuss_by_tag.sub("", c.comment_text) @@ -565,7 +566,7 @@ def import_from_idinternal(d, idinternal): if idinternal.status_date != status_date: e = StatusDateEvent(type="changed_status_date", date=idinternal.status_date) e.time = made_up_date - e.by = system_email + e.by = system e.doc = d e.desc = "Status date has been changed to %s from %s" % (idinternal.status_date, status_date) e.save() @@ -584,7 +585,7 @@ def import_from_idinternal(d, idinternal): # comments, in that case the time is simply the day after # the telechat e.time = telechat_date + datetime.timedelta(days=1) if telechat_date and not idinternal.telechat_date else made_up_date - e.by = system_email + e.by = system args = ("Placed on", idinternal.telechat_date) if idinternal.telechat_date else ("Removed from", telechat_date) e.doc = d e.desc = "%s agenda for telechat - %s by system" % args @@ -611,7 +612,7 @@ def import_from_idinternal(d, idinternal): if iesg_login_is_secretary(p.ad): continue - ad = iesg_login_to_email(p.ad) + ad = iesg_login_to_person(p.ad) if p.noobj > 0: pos = ballot_position_mapping["No Objection"] elif p.yes > 0: @@ -636,7 +637,7 @@ def import_from_idinternal(d, idinternal): e.type = "changed_ballot_position" e.doc = d e.time = position_date - e.by = system_email + e.by = system e.ad = ad last_pos = d.latest_event(BallotPositionEvent, type="changed_ballot_position", ad=e.ad) e.pos = pos @@ -649,9 +650,9 @@ def import_from_idinternal(d, idinternal): e.comment = last_pos.comment if last_pos else "" e.comment_time = last_pos.comment_time if last_pos else None if last_pos: - e.desc = "[Ballot Position Update] Position for %s has been changed to %s from %s" % (ad.get_name(), pos.name, last_pos.pos.name) + e.desc = "[Ballot Position Update] Position for %s has been changed to %s from %s" % (ad.name, pos.name, last_pos.pos.name) else: - e.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.name, ad.get_name()) + e.desc = "[Ballot Position Update] New position, %s, has been recorded for %s" % (pos.name, ad.name) e.save() # make sure we got the ballot issued event @@ -666,14 +667,14 @@ def import_from_idinternal(d, idinternal): e.type = "sent_ballot_announcement" e.doc = d e.time = sent_date - e.by = system_email + e.by = system e.desc = "Ballot has been issued" e.save() # make sure the comments and discusses are updated positions = list(BallotPositionEvent.objects.filter(doc=d).order_by("-time", '-id')) for c in IESGComment.objects.filter(ballot=ballot): - ad = iesg_login_to_email(c.ad) + ad = iesg_login_to_person(c.ad) for p in positions: if p.ad == ad: if p.comment != c.text: @@ -683,7 +684,7 @@ def import_from_idinternal(d, idinternal): break for c in IESGDiscuss.objects.filter(ballot=ballot): - ad = iesg_login_to_email(c.ad) + ad = iesg_login_to_person(c.ad) for p in positions: if p.ad == ad: if p.discuss != c.text: @@ -701,26 +702,26 @@ def import_from_idinternal(d, idinternal): text_date = made_up_date if idinternal.ballot.approval_text: - e, _ = WriteupEvent.objects.get_or_create(type="changed_ballot_approval_text", doc=d) + e, _ = WriteupEvent.objects.get_or_create(type="changed_ballot_approval_text", doc=d, + defaults=dict(by=system)) e.text = idinternal.ballot.approval_text e.time = text_date - e.by = system_email e.desc = "Ballot approval text was added" e.save() if idinternal.ballot.last_call_text: - e, _ = WriteupEvent.objects.get_or_create(type="changed_last_call_text", doc=d) + e, _ = WriteupEvent.objects.get_or_create(type="changed_last_call_text", doc=d, + defaults=dict(by=system)) e.text = idinternal.ballot.last_call_text e.time = text_date - e.by = system_email e.desc = "Last call text was added" e.save() if idinternal.ballot.ballot_writeup: - e, _ = WriteupEvent.objects.get_or_create(type="changed_ballot_writeup_text", doc=d) + e, _ = WriteupEvent.objects.get_or_create(type="changed_ballot_writeup_text", doc=d, + defaults=dict(by=system)) e.text = idinternal.ballot.ballot_writeup e.time = text_date - e.by = system_email e.desc = "Ballot writeup text was added" e.save() @@ -728,10 +729,9 @@ def import_from_idinternal(d, idinternal): if len(ballot_set) > 1: others = sorted(b.draft.filename for b in ballot_set if b != idinternal) desc = u"This was part of a ballot set with: %s" % ",".join(others) - e, _ = Event.objects.get_or_create(type="added_comment", doc=d, desc=desc) - e.time = made_up_date - e.by = system_email - e.save() + Event.objects.get_or_create(type="added_comment", doc=d, desc=desc, + defaults=dict(time=made_up_date, + by=system)) # fix tags sync_tag(d, idinternal.via_rfc_editor, tag_via_rfc_editor) @@ -798,10 +798,11 @@ for index, o in enumerate(all_drafts.iterator()): if o.rfc_number: alias_doc("rfc%s" % o.rfc_number, d) + # authors d.authors.clear() for i, a in enumerate(o.authors.all().select_related("person").order_by('author_order', 'person')): try: - e = Email.objects.get(address=a.person.email()[1] or u"unknown-email-%s-%s" % (a.person.first_name, a.person.last_name)) + e = Email.objects.get(address__iexact=a.email() or a.person.email()[1] or u"unknown-email-%s-%s" % (a.person.first_name, a.person.last_name)) # renumber since old numbers may be a bit borked DocumentAuthor.objects.create(document=d, author=e, order=i) except Email.DoesNotExist: @@ -827,7 +828,7 @@ for index, o in enumerate(all_drafts.iterator()): # hack the seconds to include the revision to ensure # they're ordered correctly e.time = datetime.datetime.combine(v.revision_date, datetime.time(0, 0, 0)) + datetime.timedelta(seconds=int(v.revision)) - e.by = system_email + e.by = system e.doc = d e.desc = "New version available" e.save() @@ -844,7 +845,7 @@ for index, o in enumerate(all_drafts.iterator()): disapproved = o.idinternal and o.idinternal.dnp e = Event(type="iesg_disapproved" if disapproved else "iesg_approved") e.time = o.b_approve_date - e.by = system_email + e.by = system e.doc = d e.desc = "Do Not Publish note has been sent to RFC Editor" if disapproved else "IESG has approved" e.save() @@ -856,7 +857,7 @@ for index, o in enumerate(all_drafts.iterator()): # event time is more accurate with actual time instead of just # date, gives better sorting e.time = events[0].time if events else o.lc_sent_date - e.by = events[0].by if events else system_email + e.by = events[0].by if events else system e.doc = d e.desc = "Last call sent" e.save() @@ -956,9 +957,9 @@ for index, o in enumerate(all_rfcs.iterator()): import_from_idinternal(d, internals[0]) # publication date - e, _ = Event.objects.get_or_create(doc=d, type="published_rfc") + e, _ = Event.objects.get_or_create(doc=d, type="published_rfc", + defaults=dict(by=system)) e.time = o.rfc_published_date - e.by = system_email e.desc = "RFC published" e.save() diff --git a/redesign/importing/import-groups.py b/redesign/importing/import-groups.py index 48632e966..080064539 100755 --- a/redesign/importing/import-groups.py +++ b/redesign/importing/import-groups.py @@ -21,6 +21,8 @@ from ietf.idtracker.models import AreaGroup, IETFWG, Area, AreaGroup, Acronym, A # also creates nomcom groups +# assumptions: persons have been imported + state_names = dict( bof=name(GroupStateName, slug="bof", name="BOF"), proposed=name(GroupStateName, slug="proposed", name="Proposed"), @@ -33,6 +35,7 @@ state_names = dict( type_names = dict( ietf=name(GroupTypeName, slug="ietf", name="IETF"), area=name(GroupTypeName, slug="area", name="Area"), + ag=name(GroupTypeName, slug="ag", name="AG"), wg=name(GroupTypeName, slug="wg", name="WG"), rg=name(GroupTypeName, slug="rg", name="RG"), team=name(GroupTypeName, slug="team", name="Team"), @@ -53,7 +56,14 @@ irtf_group.state = state_names["active"] irtf_group.type = type_names["ietf"] irtf_group.save() -system_email, _ = Email.objects.get_or_create(address="(System)") +# create Secretariat for use with roles +secretariat_group, _ = Group.objects.get_or_create(acronym="secretariat") +secretariat_group.name = "IETF Secretariat" +secretariat_group.state = state_names["active"] +secretariat_group.type = type_names["ietf"] +secretariat_group.save() + +system = Person.objects.get(name="(System)") # NomCom @@ -75,13 +85,13 @@ for o in ChairsHistory.objects.filter(chair_type=Role.NOMCOM_CHAIR).order_by("st # we need start/end year so fudge events e = GroupEvent(group=group, type="started") e.time = datetime.datetime(o.start_year, 5, 1, 12, 0, 0) - e.by = system_email + e.by = system e.desc = e.get_type_display() e.save() e = GroupEvent(group=group, type="concluded") e.time = datetime.datetime(o.end_year, 5, 1, 12, 0, 0) - e.by = system_email + e.by = system e.desc = e.get_type_display() e.save() @@ -108,7 +118,7 @@ for o in Area.objects.all(): if o.concluded_date: e = GroupEvent(group=group, type="concluded") e.time = datetime.datetime.combine(o.concluded_date, datetime.time(12, 0, 0)) - e.by = system_email + e.by = system e.desc = e.get_type_display() e.save() @@ -164,15 +174,15 @@ for o in IETFWG.objects.all(): elif o.group_acronym.acronym == "iab": group.type = type_names["ietf"] group.parent = None - elif o.group_acronym.acronym in ("tsvdir", "secdir", "saag"): + elif o.group_acronym.acronym in ("tsvdir", "secdir", "saag", "usac"): group.type = type_names["team"] elif o.group_acronym.acronym == "iesg": pass # we already treated iesg - elif o.group_acronym.acronym in ('apparea', 'opsarea', 'rtgarea', 'usvarea', 'genarea', 'tsvarea', 'raiarea'): - pass # we already treated areas + elif o.group_acronym.acronym in ("apparea", "opsarea", "rtgarea", "usvarea", "genarea", "tsvarea", "raiarea", "apptsv"): + group.type = type_names["ag"] else: # the remaining groups are - # apptsv, apples, usac, null, dirdir + # apples, null, dirdir # for now, we don't transfer them if group.id: group.delete() @@ -200,7 +210,7 @@ for o in IETFWG.objects.all(): if d: e = GroupEvent(group=group, type=name) e.time = datetime.datetime.combine(d, datetime.time(12, 0, 0)) - e.by = system_email + e.by = system e.desc = e.get_type_display() e.save() diff --git a/redesign/importing/import-persons.py b/redesign/importing/import-persons.py new file mode 100755 index 000000000..6c593bf82 --- /dev/null +++ b/redesign/importing/import-persons.py @@ -0,0 +1,44 @@ +#!/usr/bin/python + +import sys, os, re, datetime + +basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) +sys.path = [ basedir ] + sys.path + +from ietf import settings +settings.USE_DB_REDESIGN_PROXY_CLASSES = False + +from django.core import management +management.setup_environ(settings) + +from redesign.person.models import * + +# creates system person and email + +# should probably also import the old person/email tables + +try: + system_person = Person.objects.get(name="(System)") +except Person.DoesNotExist: + system_person = Person.objects.create( + id=0, # special value + name="(System)", + ascii="(System)", + address="", + ) + + if system_person.id != 0: # work around bug in Django + Person.objects.filter(id=system_person.id).update(id=0) + system_person = Person.objects.get(id=0) + + +system_alias = Alias.objects.get_or_create( + person=system_person, + name=system_person.name + ) + +system_email = Email.objects.get_or_create( + address="", + person=system_person, + active=True + ) diff --git a/redesign/importing/import-roles.py b/redesign/importing/import-roles.py index e1fe5b118..a13ce61e0 100755 --- a/redesign/importing/import-roles.py +++ b/redesign/importing/import-roles.py @@ -1,7 +1,6 @@ #!/usr/bin/python import sys, os, re, datetime -import unaccent basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) sys.path = [ basedir ] + sys.path @@ -12,11 +11,12 @@ settings.USE_DB_REDESIGN_PROXY_CLASSES = False from django.core import management management.setup_environ(settings) +from redesign import unaccent from redesign.person.models import * from redesign.group.models import * from redesign.name.models import * from redesign.name.utils import name -from redesign.importing.utils import person_email +from redesign.importing.utils import old_person_to_email, clean_email_address from ietf.idtracker.models import IESGLogin, AreaDirector, IDAuthor, PersonOrOrgInfo, WGChair, WGEditor, WGSecretary, WGTechAdvisor, ChairsHistory, Role as OldRole, Acronym, IRTFChair @@ -43,7 +43,7 @@ techadvisor_role = name(RoleName, "techadv", "Tech Advisor") # helpers for creating the objects def get_or_create_email(o, create_fake): - email = person_email(o.person) + email = old_person_to_email(o.person) if not email: if create_fake: email = u"unknown-email-%s-%s" % (o.person.first_name, o.person.last_name) @@ -62,6 +62,7 @@ def get_or_create_email(o, create_fake): else: p = Person.objects.create(id=o.person.pk, name=n, ascii=asciified) # FIXME: fill in address? + Alias.objects.create(name=n, person=p) if asciified != n: Alias.objects.create(name=asciified, person=p) @@ -162,14 +163,22 @@ for o in IESGLogin.objects.all(): continue email = get_or_create_email(o, create_fake=False) + if not email: + continue - if o.user_level == IESGLogin.INACTIVE_AD_LEVEL: + user, _ = User.objects.get_or_create(username=o.login_name) + email.person.user = user + email.person.save() + + # current ADs are imported below + if o.user_level == IESGLogin.SECRETARIAT_LEVEL: + if not Role.objects.filter(name=secretary_role, email=email): + Role.objects.create(name=secretary_role, group=Group.objects.get(acronym="secretariat"), email=email) + elif o.user_level == IESGLogin.INACTIVE_AD_LEVEL: if not Role.objects.filter(name=inactive_area_director_role, email=email): # connect them directly to the IESG as we don't really know where they belong Role.objects.create(name=inactive_area_director_role, group=Group.objects.get(acronym="iesg"), email=email) - # FIXME: import o.login_name, o.user_level - # AreaDirector for o in AreaDirector.objects.all(): if not o.area: @@ -206,7 +215,20 @@ for o in PersonOrOrgInfo.objects.filter(announcement__announcement_id__gte=1).di email = get_or_create_email(o, create_fake=False) # IDAuthor persons -for o in IDAuthor.objects.all().order_by('id').select_related('person'): +for o in IDAuthor.objects.all().order_by('id').select_related('person').iterator(): print "importing IDAuthor", o.id, o.person_id, o.person.first_name.encode('utf-8'), o.person.last_name.encode('utf-8') email = get_or_create_email(o, create_fake=True) + + # we may also need to import email address used specifically for + # the document + addr = clean_email_address(o.email() or "") + if addr and addr.lower() != email.address.lower(): + try: + e = Email.objects.get(address=addr) + if e.person != email.person or e.active != False: + e.person = email.person + e.active = False + e.save() + except Email.DoesNotExist: + Email.objects.create(address=addr, person=email.person, active=False) diff --git a/redesign/importing/utils.py b/redesign/importing/utils.py index 92d056f60..95d25a179 100644 --- a/redesign/importing/utils.py +++ b/redesign/importing/utils.py @@ -1,5 +1,17 @@ -def person_email(person): +from person.models import Person + +def clean_email_address(addr): + addr = addr.replace("<", "").replace(">", "").replace("!", "@").replace("(at)", "@").strip() + if not "@" in addr: + return "" + else: + return addr + +def old_person_to_person(person): + return Person.objects.get(id=person.pk) + +def old_person_to_email(person): hardcoded_emails = { 'Dinara Suleymanova': "dinaras@ietf.org" } - return person.email()[1] or hardcoded_emails.get("%s %s" % (person.first_name, person.last_name)) + return clean_email_address(person.email()[1] or hardcoded_emails.get("%s %s" % (person.first_name, person.last_name)) or "") diff --git a/redesign/person/admin.py b/redesign/person/admin.py index 40cdb8dc9..8bc2b52bd 100644 --- a/redesign/person/admin.py +++ b/redesign/person/admin.py @@ -20,7 +20,7 @@ class AliasInline(admin.StackedInline): model = Alias class PersonAdmin(admin.ModelAdmin): - list_display = ["name", "short", "time", ] + list_display = ["name", "short", "time", "user", ] search_fields = ["name", "ascii"] inlines = [ EmailInline, AliasInline, ] # actions = None diff --git a/redesign/person/models.py b/redesign/person/models.py index b2e735867..22939dd4b 100644 --- a/redesign/person/models.py +++ b/redesign/person/models.py @@ -1,14 +1,18 @@ # Copyright The IETF Trust 2007, All Rights Reserved from django.db import models +from django.contrib.auth.models import User class Person(models.Model): time = models.DateTimeField(auto_now_add=True) # When this Person record entered the system - name = models.CharField(max_length=255) # The normal unicode form of the name. This must be + name = models.CharField(max_length=255, db_index=True) # The normal unicode form of the name. This must be # set to the same value as the ascii-form if equal. ascii = models.CharField(max_length=255) # The normal ascii-form of the name. ascii_short = models.CharField(max_length=32, null=True, blank=True) # The short ascii-form of the name. Also in alias table if non-null address = models.TextField(max_length=255, blank=True) + + user = models.OneToOneField(User, blank=True, null=True) + def __unicode__(self): return self.name def _parts(self, name): @@ -40,6 +44,23 @@ class Person(models.Model): else: prefix, first, middle, last, suffix = self.ascii_parts() return (first and first[0]+"." or "")+(middle or "")+" "+last+(suffix and " "+suffix or "") + def email_address(self): + e = self.email_set.filter(active=True) + if e: + return e[0] + else: + return "" + def formatted_email(self): + e = self.email_set.order_by("-active") + if e: + return e[0].formatted_email() + else: + return "" + def person(self): # little temporary wrapper to help porting + return self + def full_name_as_key(self): + return self.name.lower().replace(" ", ".") + class Alias(models.Model): """This is used for alternative forms of a name. This is the @@ -48,7 +69,7 @@ class Alias(models.Model): recorded in the Person record. """ person = models.ForeignKey(Person) - name = models.CharField(max_length=255) + name = models.CharField(max_length=255, db_index=True) def __unicode__(self): return self.name class Meta: diff --git a/redesign/person/proxy.py b/redesign/person/proxy.py index e48030b5d..7fbeb9cc3 100644 --- a/redesign/person/proxy.py +++ b/redesign/person/proxy.py @@ -1,6 +1,12 @@ +from redesign.proxy_utils import TranslatingManager + from models import * -class IESGLogin(Email): +class IESGLogin(Person): + objects = TranslatingManager(dict(user_level__in=None, + first_name="name" + )) + def from_object(self, base): for f in base._meta.fields: setattr(self, f.name, getattr(base, f.name)) @@ -10,9 +16,6 @@ class IESGLogin(Email): AD_LEVEL = 1 INACTIVE_AD_LEVEL = 2 - @property - def id(self): - return self.pk # this is not really backwards-compatible #login_name = models.CharField(blank=True, max_length=255) @property def login_name(self): raise NotImplemented @@ -26,12 +29,12 @@ class IESGLogin(Email): #first_name = models.CharField(blank=True, max_length=25) @property def first_name(self): - return self.get_name().split(" ")[0] + return self.name_parts()[1] #last_name = models.CharField(blank=True, max_length=25) @property def last_name(self): - return self.get_name().split(" ")[-1] + return self.name_parts()[3] # FIXME: person isn't wrapped yet #person = BrokenForeignKey(PersonOrOrgInfo, db_column='person_or_org_tag', unique=True, null_values=(0, 888888), null=True) @@ -41,15 +44,14 @@ class IESGLogin(Email): #default_search = models.NullBooleanField() def __str__(self): - return self.get_name() + return self.name def __unicode__(self): - return self.get_name() + return self.name def is_current_ad(self): - return self in Email.objects.filter(role__name="ad", role__group__state="active") + return self in Person.objects.filter(email__role__name="ad", email__role__group__state="active").distinct() @staticmethod def active_iesg(): - raise NotImplemented - #return IESGLogin.objects.filter(user_level=1,id__gt=1).order_by('last_name') + return IESGLogin.objects.filter(email__role__name="ad", email__role__group__state="active").distinct().order_by('name') class Meta: proxy = True