Add models for new meeting schema and an importer, clean up person
import slightly - Legacy-Id: 3282
This commit is contained in:
parent
5aa189afdd
commit
0e576f4352
|
@ -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/
|
||||
|
|
191
ietf/meeting/timedeltafield.py
Normal file
191
ietf/meeting/timedeltafield.py
Normal file
|
@ -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('<div id="%s">%s</div>' % (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()
|
273
redesign/importing/import-meetings.py
Executable file
273
redesign/importing/import-meetings.py
Executable file
|
@ -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()
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue