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