diff --git a/ietf/meeting/models.py b/ietf/meeting/models.py
index ca2f5d54a..88bc0e229 100644
--- a/ietf/meeting/models.py
+++ b/ietf/meeting/models.py
@@ -1,3 +1,139 @@
-# Copyright The IETF Trust 2007, All Rights Reserved
+# old meeting models can be found in ../proceedings/models.py
+
+import pytz
+
+from django.db import models
+from timedeltafield import TimedeltaField
+
+from redesign.group.models import Group
+from redesign.person.models import Person
+from redesign.name.models import TimeSlotTypeName, SessionStatusName, ConstraintName
+
+countries = pytz.country_names.items()
+countries.sort(lambda x,y: cmp(x[1], y[1]))
+
+timezones = [(name, name) for name in pytz.common_timezones]
+timezones.sort()
+
+class Meeting(models.Model):
+ # Number is not an integer any more, in order to be able to accomodate
+ # interim meetings (and other variations?)
+ number = models.CharField(max_length=64)
+ # Date is useful when generating a set of timeslot for this meeting, but
+ # is not used to determine date for timeslot instances thereafter, as
+ # they have their own datetime field.
+ date = models.DateField()
+ city = models.CharField(blank=True, max_length=255)
+ country = models.CharField(blank=True, max_length=2, choices=countries)
+ # We can't derive time-zone from country, as there are some that have
+ # more than one timezone, and the pytz module doesn't provide timezone
+ # lookup information for all relevant city/country combinations.
+ time_zone = models.CharField(blank=True, max_length=255, choices=timezones)
+ venue_name = models.CharField(blank=True, max_length=255)
+ venue_addr = models.TextField(blank=True)
+ break_area = models.CharField(blank=True, max_length=255)
+ reg_area = models.CharField(blank=True, max_length=255)
+
+ def __str__(self):
+ return "IETF-%s" % (self.number)
+ def get_meeting_date (self,offset):
+ return self.date + datetime.timedelta(days=offset)
+ # cut-off dates (draft submission cut-of, wg agenda cut-off, minutes
+ # submission cut-off), and more, are probably methods of this class,
+ # rather than fields on a Proceedings class.
+
+ @classmethod
+ def get_first_cut_off(cls):
+ date = cls.objects.all().order_by('-date')[0].date
+ offset = datetime.timedelta(days=settings.FIRST_CUTOFF_DAYS)
+ return date - offset
+
+ @classmethod
+ def get_second_cut_off(cls):
+ date = cls.objects.all().order_by('-date')[0].date
+ offset = datetime.timedelta(days=settings.SECOND_CUTOFF_DAYS)
+ return date - offset
+
+ @classmethod
+ def get_ietf_monday(cls):
+ date = cls.objects.all().order_by('-date')[0].date
+ return date + datetime.timedelta(days=-date.weekday(), weeks=1)
+
+
+class Room(models.Model):
+ meeting = models.ForeignKey(Meeting)
+ name = models.CharField(max_length=255)
+
+ def __unicode__(self):
+ return self.name
+
+class TimeSlot(models.Model):
+ """
+ Everything that would appear on the meeting agenda of a meeting is mapped
+ to a time slot, including breaks (i.e., also NonSession+NonSessionRef.
+ Sessions are connected to TimeSlots during scheduling.
+ A template function to populate a meeting with an appropriate set of TimeSlots
+ is probably also needed.
+ """
+ meeting = models.ForeignKey(Meeting)
+ type = models.ForeignKey(TimeSlotTypeName)
+ name = models.CharField(max_length=255)
+ time = models.DateTimeField()
+ duration = TimedeltaField()
+ location = models.ForeignKey(Room, blank=True, null=True)
+ show_location = models.BooleanField(default=True)
+
+ def __unicode__(self):
+ location = self.get_location()
+ if not location:
+ location = "(no location)"
+
+ return u"%s: %s-%s %s, %s" % (self.meeting.number, self.time.strftime("%m-%d %H:%M"), (self.time + self.duration).strftime("%H:%M"), self.name, location)
+
+ def get_location(self):
+ location = self.location
+ if location:
+ location = location.name
+ elif self.type_id == "reg":
+ location = self.meeting.reg_area
+ elif self.type_id == "break":
+ location = self.meeting.break_area
+
+ if not self.show_location:
+ location = ""
+
+ return location
+
+
+class Constraint(models.Model):
+ """Specifies a constraint on the scheduling between source and
+ target, e.g. some kind of conflict."""
+ meeting = models.ForeignKey(Meeting)
+ source = models.ForeignKey(Group, related_name="constraint_source_set")
+ target = models.ForeignKey(Group, related_name="constraint_target_set")
+ name = models.ForeignKey(ConstraintName)
+
+ def __unicode__(self):
+ return u"%s %s %s" % (self.source, self.name.lower(), self.target)
+
+class Session(models.Model):
+ meeting = models.ForeignKey(Meeting)
+ timeslot = models.ForeignKey(TimeSlot, null=True, blank=True) # Null until session has been scheduled
+ group = models.ForeignKey(Group) # The group type determines the session type. BOFs also need to be added as a group.
+ attendees = models.IntegerField(null=True, blank=True)
+ agenda_note = models.CharField(blank=True, max_length=255)
+ #
+ requested = models.DateTimeField()
+ requested_by = models.ForeignKey(Person)
+ requested_duration = TimedeltaField()
+ comments = models.TextField()
+ #
+ status = models.ForeignKey(SessionStatusName)
+ scheduled = models.DateTimeField(null=True, blank=True)
+ modified = models.DateTimeField(null=True, blank=True)
+
+# Agendas, Minutes and Slides are all mapped to Document.
+
+# IESG history is extracted from GroupHistory, rather than hand coded in a
+# separate table.
-# Meeting models can be found under ../proceedings/
diff --git a/ietf/meeting/timedeltafield.py b/ietf/meeting/timedeltafield.py
new file mode 100644
index 000000000..cc2e538c5
--- /dev/null
+++ b/ietf/meeting/timedeltafield.py
@@ -0,0 +1,191 @@
+# -*- coding: iso-8859-1 -*-
+# $Id: TimedeltaField.py 1787 2011-04-20 07:09:57Z tguettler $
+# $HeadURL: svn+ssh://svnserver/svn/djangotools/trunk/dbfields/TimedeltaField.py $
+
+# from http://djangosnippets.org/snippets/1060/ with some fixes
+
+# Python
+import datetime
+
+# Django
+from django import forms
+from django.db import models
+from django.utils.safestring import mark_safe
+from django.utils.translation import ugettext_lazy as _
+
+#Djangotools
+#from djangotools.utils.southutils import add_introspection_rules_from_baseclass
+
+SECS_PER_DAY=3600*24
+
+class TimedeltaField(models.Field):
+ u'''
+ Store Python's datetime.timedelta in an integer column.
+ Most database systems only support 32 bit integers by default.
+ '''
+ __metaclass__ = models.SubfieldBase
+ empty_strings_allowed = False
+
+ def __init__(self, *args, **kwargs):
+ super(TimedeltaField, self).__init__(*args, **kwargs)
+
+ def to_python(self, value):
+ if (value is None) or isinstance(value, datetime.timedelta):
+ return value
+
+ try:
+ # else try to convert to int (e.g. from string)
+ value = int(value)
+ except (TypeError, ValueError):
+ raise exceptions.ValidationError(
+ _("This value must be an integer or a datetime.timedelta."))
+
+ return datetime.timedelta(seconds=value)
+
+ def get_internal_type(self):
+ return 'IntegerField'
+
+ def get_db_prep_lookup(self, lookup_type, value, connection=None, prepared=False):
+ raise NotImplementedError() # SQL WHERE
+
+ def get_db_prep_save(self, value, connection=None, prepared=False):
+ if (value is None) or isinstance(value, int):
+ return value
+ return SECS_PER_DAY*value.days+value.seconds
+
+ def formfield(self, *args, **kwargs):
+ defaults={'form_class': TimedeltaFormField}
+ defaults.update(kwargs)
+ return super(TimedeltaField, self).formfield(*args, **defaults)
+
+ def value_to_string(self, obj):
+ value = self._get_val_from_obj(obj)
+ return self.get_db_prep_value(value)
+
+#South Plugin registrieren
+#add_introspection_rules_from_baseclass(TimedeltaField, ["^djangotools\.dbfields\.TimedeltaField"])
+
+class TimedeltaFormField(forms.Field):
+ default_error_messages = {
+ 'invalid': _(u'Enter a whole number.'),
+ }
+
+ def __init__(self, *args, **kwargs):
+ defaults={'widget': TimedeltaWidget}
+ defaults.update(kwargs)
+ super(TimedeltaFormField, self).__init__(*args, **defaults)
+
+ def clean(self, value):
+ # value comes from Timedelta.Widget.value_from_datadict(): tuple of strings
+ super(TimedeltaFormField, self).clean(value)
+ assert len(value)==len(self.widget.inputs), (value, self.widget.inputs)
+ i=0
+ for value, multiply in zip(value, self.widget.multiply):
+ try:
+ i+=int(value)*multiply
+ except ValueError, TypeError:
+ raise forms.ValidationError(self.error_messages['invalid'])
+ return i
+
+class TimedeltaWidget(forms.Widget):
+ INPUTS=['days', 'hours', 'minutes', 'seconds']
+ MULTIPLY=[60*60*24, 60*60, 60, 1]
+ def __init__(self, attrs=None):
+ self.widgets=[]
+ if not attrs:
+ attrs={}
+ inputs=attrs.get('inputs', self.INPUTS)
+ multiply=[]
+ for input in inputs:
+ assert input in self.INPUTS, (input, self.INPUT)
+ self.widgets.append(forms.TextInput(attrs=attrs))
+ multiply.append(self.MULTIPLY[self.INPUTS.index(input)])
+ self.inputs=inputs
+ self.multiply=multiply
+ super(TimedeltaWidget, self).__init__(attrs)
+
+ def render(self, name, value, attrs):
+ if value is None:
+ values=[0 for i in self.inputs]
+ elif isinstance(value, datetime.timedelta):
+ values=split_seconds(value.days*SECS_PER_DAY+value.seconds, self.inputs, self.multiply)
+ elif isinstance(value, int):
+ # initial data from model
+ values=split_seconds(value, self.inputs, self.multiply)
+ else:
+ assert isinstance(value, tuple), (value, type(value))
+ assert len(value)==len(self.inputs), (value, self.inputs)
+ values=value
+ id=attrs.pop('id')
+ assert not attrs, attrs
+ rendered=[]
+ for input, widget, val in zip(self.inputs, self.widgets, values):
+ rendered.append(u'%s %s' % (_(input), widget.render('%s_%s' % (name, input), val)))
+ return mark_safe('
%s
' % (id, ' '.join(rendered)))
+
+ def value_from_datadict(self, data, files, name):
+ # Don't throw ValidationError here, just return a tuple of strings.
+ ret=[]
+ for input, multi in zip(self.inputs, self.multiply):
+ ret.append(data.get('%s_%s' % (name, input), 0))
+ return tuple(ret)
+
+ def _has_changed(self, initial_value, data_value):
+ # data_value comes from value_from_datadict(): A tuple of strings.
+ if initial_value is None:
+ return bool(set(data_value)!=set([u'0']))
+ assert isinstance(initial_value, datetime.timedelta), initial_value
+ initial=tuple([unicode(i) for i in split_seconds(initial_value.days*SECS_PER_DAY+initial_value.seconds, self.inputs, self.multiply)])
+ assert len(initial)==len(data_value), (initial, data_value)
+ return bool(initial!=data_value)
+
+def main():
+ assert split_seconds(1000000)==[11, 13, 46, 40]
+
+ field=TimedeltaField()
+
+ td=datetime.timedelta(days=10, seconds=11)
+ s=field.get_db_prep_save(td)
+ assert isinstance(s, int), (s, type(s))
+ td_again=field.to_python(s)
+ assert td==td_again, (td, td_again)
+
+ td=datetime.timedelta(seconds=11)
+ s=field.get_db_prep_save(td)
+ td_again=field.to_python(s)
+ assert td==td_again, (td, td_again)
+
+ field=TimedeltaFormField()
+ assert field.widget._has_changed(datetime.timedelta(seconds=0), (u'0', u'0', u'0', u'0',)) is False
+ assert field.widget._has_changed(None, (u'0', u'0', u'0', u'0',)) is False
+ assert field.widget._has_changed(None, (u'0', u'0')) is False
+ assert field.widget._has_changed(datetime.timedelta(days=1, hours=2, minutes=3, seconds=4), (u'1', u'2', u'3', u'4',)) is False
+
+ for secs, soll, kwargs in [
+ (100, [0, 0, 1, 40], dict()),
+ (100, ['0days', '0hours', '1minutes', '40seconds'], dict(with_unit=True)),
+ (100, ['1minutes', '40seconds'], dict(with_unit=True, remove_leading_zeros=True)),
+ (100000, ['1days', '3hours'], dict(inputs=['days', 'hours'], with_unit=True, remove_leading_zeros=True)),
+ ]:
+ ist=split_seconds(secs, **kwargs)
+ if ist!=soll:
+ raise Exception('geg=%s soll=%s ist=%s kwargs=%s' % (secs, soll, ist, kwargs))
+
+ print "unittest OK"
+
+def split_seconds(secs, inputs=TimedeltaWidget.INPUTS, multiply=TimedeltaWidget.MULTIPLY,
+ with_unit=False, remove_leading_zeros=False):
+ ret=[]
+ assert len(inputs)<=len(multiply), (inputs, multiply)
+ for input, multi in zip(inputs, multiply):
+ count, secs = divmod(secs, multi)
+ if remove_leading_zeros and not ret and not count:
+ continue
+ if with_unit:
+ ret.append('%s%s' % (count, input))
+ else:
+ ret.append(count)
+ return ret
+
+if __name__=='__main__':
+ main()
diff --git a/redesign/importing/import-meetings.py b/redesign/importing/import-meetings.py
new file mode 100755
index 000000000..980a5ec4f
--- /dev/null
+++ b/redesign/importing/import-meetings.py
@@ -0,0 +1,273 @@
+#!/usr/bin/python
+
+import sys, os, re, datetime, pytz
+
+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 ietf.idtracker.models import AreaDirector, IETFWG, Acronym, IRTF
+from ietf.meeting.models import *
+from ietf.proceedings.models import Meeting as MeetingOld, MeetingVenue, MeetingRoom, NonSession, WgMeetingSession
+from redesign.person.models import *
+from redesign.importing.utils import get_or_create_email, old_person_to_person
+from redesign.name.utils import name
+
+
+# imports Meeting, MeetingVenue, MeetingRoom, NonSession, WgMeetingSession
+
+# assumptions:
+# - persons have been imported
+# - groups have been imported
+
+
+session_status_mapping = {
+ 1: name(SessionStatusName, "schedw", "Waiting for Scheduling"),
+ 2: name(SessionStatusName, "apprw", "Waiting for Approval"),
+ 3: name(SessionStatusName, "appr", "Approved"),
+ 4: name(SessionStatusName, "sched", "Scheduled"),
+ 5: name(SessionStatusName, "canceled", "Canceled"),
+ 6: name(SessionStatusName, "disappr", "Disapproved"),
+ }
+
+session_status_mapping[0] = session_status_mapping[1] # assume broken statuses of 0 are actually cancelled
+
+session_slot = name(TimeSlotTypeName, "session", "Session")
+break_slot = name(TimeSlotTypeName, "break", "Break")
+registration_slot = name(TimeSlotTypeName, "reg", "Registration")
+other_slot = name(TimeSlotTypeName, "other", "Other")
+conflict_constraint = name(ConstraintName, "conflict", "Conflicts with")
+
+system_person = Person.objects.get(name="(System)")
+obviously_bogus_date = datetime.date(1970, 1, 1)
+
+for o in MeetingOld.objects.all():
+ print "importing Meeting", o.pk
+
+ try:
+ m = Meeting.objects.get(number=o.meeting_num)
+ except:
+ m = Meeting(number="%s" % o.meeting_num)
+ m.pk = o.pk
+
+ m.date = o.start_date
+ m.city = o.city
+
+ # convert country to code
+ country_code = None
+ for k, v in pytz.country_names.iteritems():
+ if v == o.country:
+ country_code = k
+ break
+
+ if not country_code:
+ country_fallbacks = {
+ 'USA': 'US'
+ }
+
+ country_code = country_fallbacks.get(o.country)
+
+ if country_code:
+ m.country = country_code
+ else:
+ print "unknown country", o.country
+
+
+ time_zone_lookup = {
+ ("IE", "Dublin"): "Europe/Dublin",
+ ("FR", "Paris"): "Europe/Paris",
+ ("CA", "Vancouver"): "America/Vancouver",
+ ("CZ", "Prague"): "Europe/Prague",
+ ("US", "Chicago"): "America/Chicago",
+ ("US", "Anaheim"): "America/Los_Angeles",
+ ("NL", "Maastricht"): "Europe/Amsterdam",
+ ("CN", "Beijing"): "Asia/Shanghai",
+ ("JP", "Hiroshima"): "Asia/Tokyo",
+ ("SE", "Stockholm"): "Europe/Stockholm",
+ ("US", "San Francisco"): "America/Los_Angeles",
+ ("US", "Minneapolis"): "America/Menominee",
+ }
+
+ m.time_zone = time_zone_lookup.get((m.country, m.city), "")
+ if not m.time_zone:
+ print "unknown time zone for", m.get_country_display(), m.city
+
+ m.venue_name = "" # no source for that in the old DB?
+ m.venue_addr = "" # no source for that in the old DB?
+ try:
+ venue = o.meetingvenue_set.get()
+ m.break_area = venue.break_area_name
+ m.reg_area = venue.reg_area_name
+ except MeetingVenue.DoesNotExist:
+ pass
+
+ # missing following semi-used fields from old Meeting: end_date,
+ # ack, agenda_html/agenda_text, future_meeting
+
+ m.save()
+
+for o in MeetingRoom.objects.all():
+ print "importing MeetingRoom", o.pk
+
+ try:
+ r = Room.objects.get(pk=o.pk)
+ except Room.DoesNotExist:
+ r = Room(pk=o.pk)
+
+ r.meeting = Meeting.objects.get(number="%s" % o.meeting_id)
+ r.name = o.room_name
+ r.save()
+
+def parse_time_desc(o):
+ t = o.time_desc.replace(' ', '')
+
+ start_time = datetime.time(int(t[0:2]), int(t[2:4]))
+ end_time = datetime.time(int(t[5:7]), int(t[7:9]))
+
+ d = o.meeting.start_date + datetime.timedelta(days=o.day_id)
+
+ return (datetime.datetime.combine(d, start_time), datetime.datetime.combine(d, end_time))
+
+def get_or_create_session_timeslot(meeting_time, room):
+ meeting = Meeting.objects.get(number=s.meeting_id)
+ starts, ends = parse_time_desc(meeting_time)
+
+ try:
+ slot = TimeSlot.objects.get(meeting=meeting, time=starts, location=room)
+ except TimeSlot.DoesNotExist:
+ slot = TimeSlot(meeting=meeting, time=starts, location=room)
+
+ slot.type = session_slot
+ slot.name = meeting_time.session_name.session_name if meeting_time.session_name_id else "Unknown"
+ slot.duration = ends - starts
+ slot.save()
+
+ return slot
+
+requested_length_mapping = {
+ None: 0, # assume NULL to mean nothing particular requested
+ "1": 60 * 60,
+ "2": 90 * 60,
+ "3": 120 * 60,
+ "4": 150 * 60,
+ }
+
+for o in WgMeetingSession.objects.all().order_by("pk"):
+ # num_session is unfortunately not quite reliable, seems to be
+ # right for 1 or 2 but not 3 and it's sometimes null
+ sessions = o.num_session or 1
+ if o.sched_time_id3:
+ sessions = 3
+
+ print "importing WgMeetingSession", o.pk, "subsessions", sessions
+
+ for i in range(1, 1 + sessions):
+ try:
+ s = Session.objects.get(pk=o.pk + (i - 1) * 10000)
+ except:
+ s = Session(pk=o.pk)
+
+ s.meeting = Meeting.objects.get(number=o.meeting_id)
+ sched_time_id = getattr(o, "sched_time_id%s" % i)
+ if sched_time_id:
+ room = Room.objects.get(pk=getattr(o, "sched_room_id%s_id" % i))
+ s.timeslot = get_or_create_session_timeslot(sched_time_id, room)
+ else:
+ s.timeslot = None
+ if o.irtf:
+ s.group = Group.objects.get(acronym=IRTF.objects.get(pk=o.group_acronym_id).acronym.lower())
+ else:
+ acronym = Acronym.objects.get(pk=o.group_acronym_id)
+ if o.group_acronym_id < 0:
+ # this wasn't actually a WG session, but rather a tutorial
+ # or similar, don't create a session but instead modify
+ # the time slot appropriately
+ if not s.timeslot:
+ print "IGNORING unscheduled non-WG-session", acronym.name
+ continue
+ s.timeslot.name = acronym.name
+ s.timeslot.type = other_slot
+ s.timeslot.save()
+ continue
+
+ s.group = Group.objects.get(acronym=acronym.acronym)
+ s.attendees = o.number_attendee
+ s.agenda_note = (o.special_agenda_note or "").strip()
+ s.requested = o.requested_date or obviously_bogus_date
+ s.requested_by = old_person_to_person(o.requested_by) if o.requested_by else system_person
+ s.requested_duration = requested_length_mapping[getattr(o, "length_session%s" % i)]
+ comments = []
+ special_req = (o.special_req or "").strip()
+ if special_req:
+ comments.append(u"Special requests:\n" + special_req)
+ conflict_other = (o.conflict_other or "").strip()
+ if conflict_other:
+ comments.append(u"Other conflicts:\n" + conflict_other)
+ s.comments = u"\n\n".join(comments)
+ s.status = session_status_mapping[o.status_id or 5]
+
+ s.scheduled = o.scheduled_date
+ s.modified = o.last_modified_date
+
+ s.save()
+
+ conflict = (getattr(o, "conflict%s" % i) or "").replace(",", " ").lower()
+ conflicting_groups = [g for g in conflict.split() if g]
+ for target in Group.objects.filter(acronym__in=conflicting_groups):
+ Constraint.objects.get_or_create(
+ meeting=s.meeting,
+ source=target,
+ target=s.group,
+ name=conflict_constraint)
+
+
+ # missing following fields from old: ts_status_id (= third session
+ # status id, third session required AD approval),
+ # combined_room_id1/2, combined_time_id1/2
+
+for o in NonSession.objects.all().order_by('pk').select_related("meeting"):
+ print "importing NonSession", o.pk
+
+ if o.time_desc in ("", "0"):
+ print "IGNORING non-scheduled NonSession", o.non_session_ref.name
+ continue
+
+ meeting = Meeting.objects.get(number=o.meeting_id)
+
+ # some non-sessions are scheduled every day, but only if there's a
+ # session nearby, figure out which days this corresponds to
+ days = set()
+ if o.day_id == None:
+ t = datetime.time(int(o.time_desc[-4:][0:2]), int(o.time_desc[-4:][2:4]))
+
+ for s in TimeSlot.objects.filter(meeting=meeting):
+ if s.time.time() == t:
+ days.add((s.time.date() - meeting.date).days)
+ else:
+ days.add(o.day_id)
+
+ for day in days:
+ o.day_id = day
+ starts, ends = parse_time_desc(o)
+ name = o.non_session_ref.name
+
+ try:
+ slot = TimeSlot.objects.get(meeting=meeting, time=starts, name=name)
+ except TimeSlot.DoesNotExist:
+ slot = TimeSlot(meeting=meeting, time=starts, name=name)
+
+ slot.location = None
+ if o.non_session_ref_id == 1:
+ slot.type = registration_slot
+ else:
+ slot.type = break_slot
+
+ slot.duration = ends - starts
+ slot.show_location = o.show_break_location
+ slot.save()
diff --git a/redesign/importing/import-persons.py b/redesign/importing/import-persons.py
index 82e490596..4cb6a935c 100755
--- a/redesign/importing/import-persons.py
+++ b/redesign/importing/import-persons.py
@@ -11,16 +11,20 @@ settings.USE_DB_REDESIGN_PROXY_CLASSES = False
from django.core import management
management.setup_environ(settings)
-from ietf.idtracker.models import AreaDirector, IETFWG
+from ietf.idtracker.models import AreaDirector, IETFWG, PersonOrOrgInfo, IDAuthor
from redesign.person.models import *
-from redesign.importing.utils import get_or_create_email
+from redesign.importing.utils import clean_email_address, get_or_create_email
# creates system person and email
-# imports AreaDirector persons that are connected to an IETFWG
+# imports AreaDirector persons that are connected to an IETFWG,
+# persons from IDAuthor, announcement originators from Announcements,
+# requesters from WgMeetingSession
-# should probably also import the old person/email tables
+# should probably import
+# PersonOrOrgInfo/PostalAddress/EmailAddress/PhoneNumber fully
+# make sure special system user/email is created
print "creating (System) person and email"
try:
system_person = Person.objects.get(name="(System)")
@@ -48,7 +52,39 @@ system_email = Email.objects.get_or_create(
defaults=dict(active=True, person=system_person)
)
+# AreaDirector from IETFWG persons
for o in AreaDirector.objects.filter(ietfwg__in=IETFWG.objects.all()).exclude(area=None).distinct().order_by("pk").iterator():
print "importing AreaDirector (from IETFWG) persons", o.pk
get_or_create_email(o, create_fake=False)
+
+# WgMeetingSession persons
+for o in PersonOrOrgInfo.objects.filter(wgmeetingsession__pk__gte=1).distinct().order_by("pk").iterator():
+ print "importing WgMeetingSession persons", o.pk, o.first_name.encode('utf-8'), o.last_name.encode('utf-8')
+
+ get_or_create_email(o, create_fake=False)
+
+# Announcement persons
+for o in PersonOrOrgInfo.objects.filter(announcement__announcement_id__gte=1).order_by("pk").distinct():
+ print "importing Announcement originator", o.pk, o.first_name.encode('utf-8'), o.last_name.encode('utf-8')
+
+ email = get_or_create_email(o, create_fake=False)
+
+# IDAuthor persons
+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/import-roles.py b/redesign/importing/import-roles.py
index c2ce32f3c..a19850086 100755
--- a/redesign/importing/import-roles.py
+++ b/redesign/importing/import-roles.py
@@ -15,24 +15,19 @@ 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 old_person_to_email, clean_email_address, get_or_create_email
+from redesign.importing.utils import get_or_create_email
-from ietf.idtracker.models import IESGLogin, AreaDirector, IDAuthor, PersonOrOrgInfo, WGChair, WGEditor, WGSecretary, WGTechAdvisor, ChairsHistory, Role as OldRole, Acronym, IRTFChair
+from ietf.idtracker.models import IESGLogin, AreaDirector, PersonOrOrgInfo, WGChair, WGEditor, WGSecretary, WGTechAdvisor, ChairsHistory, Role as OldRole, Acronym, IRTFChair
# assumptions:
+# - persons have been imported
# - groups have been imported
-# PersonOrOrgInfo/PostalAddress/EmailAddress/PhoneNumber are not
-# imported, although some information is retrieved from those
-
# imports IESGLogin, AreaDirector, WGEditor, WGChair, IRTFChair,
# WGSecretary, WGTechAdvisor, NomCom chairs from ChairsHistory,
-#
-# also imports persons from IDAuthor, announcement originators from
-# Announcements
-# FIXME: should probably import Role
+# FIXME: should probably import Role, LegacyWgPassword, LegacyLiaisonUser
area_director_role = name(RoleName, "ad", "Area Director")
inactive_area_director_role = name(RoleName, "ex-ad", "Ex-Area Director", desc="Inactive Area Director")
@@ -176,29 +171,3 @@ for o in AreaDirector.objects.all():
Role.objects.get_or_create(name=role_type, group=area, email=email)
-# Announcement persons
-for o in PersonOrOrgInfo.objects.filter(announcement__announcement_id__gte=1).distinct():
- print "importing Announcement originator", o.person_or_org_tag, o.first_name.encode('utf-8'), o.last_name.encode('utf-8')
-
- o.person = o # satisfy the get_or_create_email interface
-
- email = get_or_create_email(o, create_fake=False)
-
-# IDAuthor persons
-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 ceec5a4a5..84a07c0dc 100644
--- a/redesign/importing/utils.py
+++ b/redesign/importing/utils.py
@@ -11,7 +11,10 @@ def clean_email_address(addr):
return addr
def old_person_to_person(person):
- return Person.objects.get(id=person.pk)
+ try:
+ return Person.objects.get(id=person.pk)
+ except Person.DoesNotExist:
+ return Person.objects.get(alias__name=u"%s %s" % (person.first_name, person.last_name))
def old_person_to_email(person):
hardcoded_emails = { 'Dinara Suleymanova': "dinaras@ietf.org" }
@@ -19,25 +22,27 @@ def old_person_to_email(person):
return clean_email_address(person.email()[1] or hardcoded_emails.get("%s %s" % (person.first_name, person.last_name)) or "")
def get_or_create_email(o, create_fake):
- # take person on o and get or create new Email and Person objects
- email = old_person_to_email(o.person)
+ # take o.person (or o) and get or create new Email and Person objects
+ person = o.person if hasattr(o, "person") else o
+
+ email = old_person_to_email(person)
if not email:
if create_fake:
- email = u"unknown-email-%s-%s" % (o.person.first_name, o.person.last_name)
- print ("USING FAKE EMAIL %s for %s %s %s" % (email, o.person.pk, o.person.first_name, o.person.last_name)).encode('utf-8')
+ email = u"unknown-email-%s-%s" % (person.first_name, person.last_name)
+ print ("USING FAKE EMAIL %s for %s %s %s" % (email, person.pk, person.first_name, person.last_name)).encode('utf-8')
else:
- print ("NO EMAIL FOR %s %s %s %s %s" % (o.__class__, o.pk, o.person.pk, o.person.first_name, o.person.last_name)).encode('utf-8')
+ print ("NO EMAIL FOR %s %s %s %s %s" % (o.__class__, o.pk, person.pk, person.first_name, person.last_name)).encode('utf-8')
return None
e, _ = Email.objects.select_related("person").get_or_create(address=email)
if not e.person:
- n = u"%s %s" % (o.person.first_name, o.person.last_name)
+ n = u"%s %s" % (person.first_name, person.last_name)
asciified = unaccent.asciify(n)
aliases = Alias.objects.filter(name__in=(n, asciified))
if aliases:
p = aliases[0].person
else:
- p = Person.objects.create(id=o.person.pk, name=n, ascii=asciified)
+ p = Person.objects.create(id=person.pk, name=n, ascii=asciified)
# FIXME: fill in address?
Alias.objects.create(name=n, person=p)
diff --git a/redesign/name/admin.py b/redesign/name/admin.py
index 5f73fb542..347be27d7 100644
--- a/redesign/name/admin.py
+++ b/redesign/name/admin.py
@@ -21,3 +21,6 @@ admin.site.register(DocInfoTagName, NameAdmin)
admin.site.register(StdLevelName, NameAdmin)
admin.site.register(IntendedStdLevelName, NameAdmin)
admin.site.register(BallotPositionName, NameAdmin)
+admin.site.register(SessionStatusName, NameAdmin)
+admin.site.register(TimeSlotTypeName, NameAdmin)
+admin.site.register(ConstraintName, NameAdmin)
diff --git a/redesign/name/models.py b/redesign/name/models.py
index 0eb9af699..62997a9ff 100644
--- a/redesign/name/models.py
+++ b/redesign/name/models.py
@@ -62,6 +62,12 @@ class IntendedStdLevelName(NameModel):
Practice, Historic, ..."""
class BallotPositionName(NameModel):
""" Yes, NoObjection, Abstain, Discuss, Recuse """
+class SessionStatusName(NameModel):
+ """ Waiting for Approval, Approved, Waiting for Scheduling, Scheduled, Cancelled, Disapproved"""
+class TimeSlotTypeName(NameModel):
+ """Session, Break, Registration"""
+class ConstraintName(NameModel):
+ """Conflict"""
def get_next_iesg_states(iesg_state):