class MeetingVenueProxy(Meeting): objects = TranslatingManager(dict(meeting_num="number")) def from_object(self, base): for f in base._meta.fields: setattr(self, f.name, getattr(base, f.name)) return self #meeting_num = models.ForeignKey(Meeting, db_column='meeting_num', unique=True) @property def meeting_num(self): return self.number #break_area_name = models.CharField(max_length=255) @property def break_area_name(self): return self.break_area #reg_area_name = models.CharField(max_length=255) @property def reg_area_name(self): return self.reg_area def __str__(self): return "IETF %s" % (self.meeting_num) class Meta: proxy = True
class IDState(State): PUBLICATION_REQUESTED = 10 LAST_CALL_REQUESTED = 15 IN_LAST_CALL = 16 WAITING_FOR_WRITEUP = 18 WAITING_FOR_AD_GO_AHEAD = 19 IESG_EVALUATION = 20 IESG_EVALUATION_DEFER = 21 APPROVED_ANNOUNCEMENT_SENT = 30 AD_WATCHING = 42 DEAD = 99 DO_NOT_PUBLISH_STATES = (33, 34) objects = TranslatingManager(dict(pk=lambda v: ("order", v, "type", "draft-iesg"), document_state_id=lambda v: ("order", v, "type", "draft-iesg"), document_state_id__in=lambda v: ("order__in", v, "type", "draft-iesg")), always_filter=dict(type="draft-iesg")) def from_object(self, base): for f in base._meta.fields: setattr(self, f.name, getattr(base, f.name)) return self #document_state_id = models.AutoField(primary_key=True) @property def document_state_id(self): return self.order #state = models.CharField(max_length=50, db_column='document_state_val') @property def state(self): return self.name #equiv_group_flag = models.IntegerField(null=True, blank=True) # unused #description = models.TextField(blank=True, db_column='document_desc') @property def description(self): return self.desc @property def nextstate(self): # simulate related queryset return IDState.objects.filter(pk__in=[x.pk for x in self.next_states.all()]) @property def next_state(self): # simulate IDNextState return self def __str__(self): return self.state @staticmethod def choices(): return [(state.pk, state.name) for state in IDState.objects.all()] class Meta: proxy = True
class AnnotationTagObjectRelationProxy(DocTagName): objects = TranslatingManager(dict(annotation_tag__name="name")) @property def annotation_tag(self): return self class Meta: proxy = True
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)) return self SECRETARIAT_LEVEL = 0 AD_LEVEL = 1 INACTIVE_AD_LEVEL = 2 #login_name = models.CharField(blank=True, max_length=255) @property def login_name(self): raise NotImplemented #password = models.CharField(max_length=25) @property def password(self): raise NotImplemented #user_level = models.IntegerField(choices=USER_LEVEL_CHOICES) @property def user_level(self): raise NotImplemented #first_name = models.CharField(blank=True, max_length=25) @property def first_name(self): return self.name_parts()[1] #last_name = models.CharField(blank=True, max_length=25) @property def last_name(self): 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) # apparently unused #pgp_id = models.CharField(blank=True, null=True, max_length=20) #default_search = models.NullBooleanField() def __str__(self): return self.plain_name() def is_current_ad(self): return bool(self.role_set.filter(name="ad", group__state="active")) @staticmethod def active_iesg(): return IESGLogin.objects.filter( role__name="ad", role__group__state="active").distinct().order_by('name') class Meta: proxy = True
class DocumentComment(DocEvent): objects = TranslatingManager(dict(comment_text="desc", date="time" )) BALLOT_DISCUSS = 1 BALLOT_COMMENT = 2 BALLOT_CHOICES = ( (BALLOT_DISCUSS, 'discuss'), (BALLOT_COMMENT, 'comment'), ) #document = models.ForeignKey(IDInternal) @property def document(self): return self.doc #rfc_flag = models.IntegerField(null=True, blank=True) #public_flag = models.BooleanField() #unused #date = models.DateField(db_column='comment_date', default=datetime.date.today) @property def date(self): return self.time.date() #time = models.CharField(db_column='comment_time', max_length=20, default=lambda: datetime.datetime.now().strftime("%H:%M:%S")) #version = models.CharField(blank=True, max_length=3) @property def version(self): e = self.doc.latest_event(NewRevisionDocEvent, type="new_revision", time__lte=self.time) return e.rev if e else "0" #comment_text = models.TextField(blank=True) @property def comment_text(self): return self.desc #created_by = BrokenForeignKey(IESGLogin, db_column='created_by', null=True, null_values=(0, 999)) #result_state = BrokenForeignKey(IDState, db_column='result_state', null=True, related_name="comments_leading_to_state", null_values=(0, 99)) #origin_state = models.ForeignKey(IDState, db_column='origin_state', null=True, related_name="comments_coming_from_state") #ballot = models.IntegerField(null=True, choices=BALLOT_CHOICES) def get_absolute_url(self): return "/doc/%s/" % self.doc.name def get_author(self): return self.by.plain_name() def get_username(self): return unicode(self.by) def get_fullname(self): return self.by.plain_name() def datetime(self): return self.time def doc_id(self): return self.doc_id def doc_name(self): return self.doc.name def __str__(self): return "\"%s...\" by %s" % (self.comment_text[:20], self.get_author()) class Meta: proxy = True
class SlideProxy(Document): objects = TranslatingManager(dict(), always_filter=dict(type="slides")) SLIDE_TYPE_CHOICES = ( ('1', '(converted) HTML'), ('2', 'PDF'), ('3', 'Text'), ('4', 'PowerPoint -2003 (PPT)'), ('5', 'Microsoft Word'), ('6', 'PowerPoint 2007- (PPTX)'), ) #meeting = models.ForeignKey(Meeting, db_column='meeting_num') @property def meeting_id(self): return self.name.split("-")[1] #group_acronym_id = models.IntegerField(null=True, blank=True) #slide_num = models.IntegerField(null=True, blank=True) @property def slide_name(self): return int(self.name.split("-")[3]) #slide_type_id = models.IntegerField(choices=SLIDE_TYPE_CHOICES) #slide_name = models.CharField(blank=True, max_length=255) @property def slide_name(self): return self.title #irtf = models.IntegerField() #interim = models.BooleanField() #order_num = models.IntegerField(null=True, blank=True) @property def order_num(self): return self.order #in_q = models.IntegerField(null=True, blank=True) def acronym(): return self.name.split("-")[2] def __str__(self): return "IETF%d: %s slides (%s)" % (self.meeting_id, self.acronym(), self.slide_name) def file_loc(self): return "%s/slides/%s" % (self.meeting_id, self.external_url) class Meta: proxy = True
class AreaGroup(Group): objects = TranslatingManager(dict(group=lambda v: ("pk", v.pk)), always_filter=dict(type="wg")) def from_object(self, base): for f in base._meta.fields: setattr(self, f.name, getattr(base, f.name)) return self @property def area(self): return Area().from_object(self.parent) @property def group(self): return self class Meta: proxy = True
class ProceedingProxy(Meeting): objects = TranslatingManager(dict(meeting_num="number")) #meeting_num = models.ForeignKey(Meeting, db_column='meeting_num', unique=True, primary_key=True) @property def meeting_num(self): return MeetingProxy().from_object(self) @property def meeting_num_id(self): return self.number #dir_name = models.CharField(blank=True, max_length=25) @property def dir_name(self): return self.number #sub_begin_date = models.DateField(null=True, blank=True) @property def sub_begin_date(self): return self.get_submission_start_date() #sub_cut_off_date = models.DateField(null=True, blank=True) @property def sub_cut_off_date(self): return self.get_submission_cut_off_date() #frozen = models.IntegerField(null=True, blank=True) #c_sub_cut_off_date = models.DateField(null=True, blank=True) @property def c_sub_cut_off_date(self): return self.get_submission_correction_date() #pr_from_date = models.DateField(null=True, blank=True) #pr_to_date = models.DateField(null=True, blank=True) def __str__(self): return "IETF %s" % (self.meeting_num_id) class Meta: proxy = True
class IRTF(Group): objects = TranslatingManager(dict(), always_filter=dict(type="rg")) #irtf_id = models.AutoField(primary_key=True) @property def irtf_id(self): return self.pk #acronym = models.CharField(blank=True, max_length=25, db_column='irtf_acronym') # same name #name = models.CharField(blank=True, max_length=255, db_column='irtf_name') # same name #charter_text = models.TextField(blank=True,null=True) #meeting_scheduled = models.BooleanField(blank=True) def __str__(self): return self.acronym def __unicode__(self): return self.acronym #def chairs(self): # return a set of IRTFChair objects for this work group # return IRTFChair.objects.filter(irtf=self) class Meta: proxy = True
class InternetDraft(Document): objects = TranslatingManager(dict(filename="name", filename__contains="name__contains", id_document_tag="pk", status=lambda v: ("states__slug", { 1: 'active', 2: 'expired', 3: 'rfc', 4: 'auth-rm', 5: 'repl', 6: 'ietf-rm'}[v], "states__type", "draft"), job_owner="ad", rfc_number=lambda v: ("docalias__name", "rfc%s" % v), cur_state=lambda v: ("states__order", v, 'states__type', 'draft-iesg'), idinternal__primary_flag=None, idinternal__cur_state__state=lambda v: ("states__name", v, 'states__type', 'draft-iesg'), ), always_filter=dict(type="draft")) DAYS_TO_EXPIRE=185 # things from InternetDraft #id_document_tag = models.AutoField(primary_key=True) @property def id_document_tag(self): return self.name # Will only work for some use cases #title = models.CharField(max_length=255, db_column='id_document_name') # same name #id_document_key = models.CharField(max_length=255, editable=False) @property def id_document_key(self): return self.title.upper() #group = models.ForeignKey(Acronym, db_column='group_acronym_id') @property def group(self): from ietf.group.proxy import Acronym as AcronymProxy g = super(InternetDraft, self).group return AcronymProxy().from_object(g) if g else None #filename = models.CharField(max_length=255, unique=True) @property def filename(self): return self.name #revision = models.CharField(max_length=2) @property def revision(self): return self.rev #revision_date = models.DateField() @property def revision_date(self): if hasattr(self, "new_revision"): e = self.new_revision else: e = self.latest_event(type="new_revision") return e.time.date() if e else None # helper function def get_file_type_matches_from(self, base_path): possible_types = [".txt", ".pdf", ".xml", ".ps"] res = [] for m in glob.glob(base_path + '.*'): for t in possible_types: if base_path + t == m: res.append(t) return ",".join(res) #file_type = models.CharField(max_length=20) @property def file_type(self): return self.get_file_type_matches_from(os.path.join(settings.INTERNET_DRAFT_PATH, self.name + "-" + self.rev)) or ".txt" #txt_page_count = models.IntegerField() @property def txt_page_count(self): return self.pages #local_path = models.CharField(max_length=255, blank=True) # unused #start_date = models.DateField() @property def start_date(self): e = NewRevisionDocEvent.objects.filter(doc=self).order_by("time")[:1] return e[0].time.date() if e else None #expiration_date = models.DateField() @property def expiration_date(self): e = self.latest_event(type__in=('expired_document', 'new_revision', "completed_resurrect")) return e.time.date() if e and e.type == "expired_document" else None #abstract = models.TextField() # same name #dunn_sent_date = models.DateField(null=True, blank=True) # unused #extension_date = models.DateField(null=True, blank=True) # unused #status = models.ForeignKey(IDStatus) @property def status(self): s = self.get_state() return IDStatus().from_object(s) if s else None @property def status_id(self): return { 'active': 1, 'repl': 5, 'expired': 2, 'rfc': 3, 'auth-rm': 4, 'ietf-rm': 6 }[self.get_state_slug()] #intended_status = models.ForeignKey(IDIntendedStatus) @property def intended_status(self): return self.intended_std_level #lc_sent_date = models.DateField(null=True, blank=True) @property def lc_sent_date(self): e = self.latest_event(type="sent_last_call") return e.time.date() if e else None #lc_changes = models.CharField(max_length=3) # used in DB, unused in Django code? #lc_expiration_date = models.DateField(null=True, blank=True) @property def lc_expiration_date(self): e = self.latest_event(LastCallDocEvent, type="sent_last_call") return e.expires.date() if e else None #b_sent_date = models.DateField(null=True, blank=True) @property def b_sent_date(self): e = self.latest_event(type="sent_ballot_announcement") return e.time.date() if e else None #b_discussion_date = models.DateField(null=True, blank=True) # unused #b_approve_date = models.DateField(null=True, blank=True) @property def b_approve_date(self): e = self.latest_event(type="iesg_approved") return e.time.date() if e else None #wgreturn_date = models.DateField(null=True, blank=True) # unused #rfc_number = models.IntegerField(null=True, blank=True, db_index=True) @property def rfc_number(self): n = self.canonical_name() return int(n[3:]) if n.startswith("rfc") else None #comments = models.TextField(blank=True) # unused #last_modified_date = models.DateField() @property def last_modified_date(self): return self.time.date() #replaced_by = models.ForeignKey('self', db_column='replaced_by', blank=True, null=True, related_name='replaces_set') @property def replaced_by(self): r = InternetDraft.objects.filter(relateddocument__target__document=self, relateddocument__relationship="replaces") return r[0] if r else None @property def replaced_by_id(self): r = self.replaced_by return r.id_document_tag if r else None #replaces = FKAsOneToOne('replaces', reverse=True) @property def replaces(self): r = self.replaces_set return r[0] if r else None @property def replaces_set(self): return InternetDraft.objects.filter(docalias__relateddocument__source=self, docalias__relateddocument__relationship="replaces") #review_by_rfc_editor = models.BooleanField() @property def review_by_rfc_editor(self): return bool(self.tags.filter(slug='rfc-rev')) #expired_tombstone = models.BooleanField() @property def expired_tombstone(self): return False #shepherd = BrokenForeignKey('PersonOrOrgInfo', null=True, blank=True, null_values=(0, )) # same name #idinternal = FKAsOneToOne('idinternal', reverse=True, query=models.Q(rfc_flag = 0)) @property def idinternal(self): # since IDInternal is now merged into the document, we try to # guess here if hasattr(self, "changed_ballot_position"): e = self.changed_ballot_position else: e = self.latest_event(type="changed_ballot_position") return self if e or self.get_state("draft-iesg") else None # reverse relationship @property def authors(self): return IDAuthor.objects.filter(document=self) @property def protowriteup_set(self): from ietf.wgchairs.models import ProtoWriteUpProxy return ProtoWriteUpProxy.objects.filter(doc=self, type="changed_protocol_writeup") # methods from InternetDraft def displayname(self): return self.name def file_tag(self): return "<%s>" % self.filename_with_rev() def filename_with_rev(self): return "%s-%s.txt" % (self.filename, self.revision_display()) def group_acronym(self): g = super(Document, self).group if g.type_id == "area": return "none" else: return g.acronym def group_ml_archive(self): return self.group.list_archive def idstate(self): return self.docstate() def revision_display(self): return self.rev def expiration(self): return self.expires.date() def can_expire(self): # Copying the logic from expire-ids-1 without thinking # much about it. if self.review_by_rfc_editor: return False idinternal = self.idinternal if idinternal: cur_state_id = idinternal.cur_state_id # 42 is "AD is Watching"; this matches what's in the # expire-ids-1 perl script. # A better way might be to add a column to the table # saying whether or not a document is prevented from # expiring. if cur_state_id < 42: return False return True def clean_abstract(self): # Cleaning based on what "id-abstracts-text" script does import re a = self.abstract a = re.sub(" *\r\n *", "\n", a) # get rid of DOS line endings a = re.sub(" *\r *", "\n", a) # get rid of MAC line endings a = re.sub("(\n *){3,}", "\n\n", a) # get rid of excessive vertical whitespace a = re.sub("\f[\n ]*[^\n]*\n", "", a) # get rid of page headers # Get rid of 'key words' boilerplate and anything which follows it: # (No way that is part of the abstract...) a = re.sub("(?s)(Conventions [Uu]sed in this [Dd]ocument|Requirements [Ll]anguage)?[\n ]*The key words \"MUST\", \"MUST NOT\",.*$", "", a) # Get rid of status/copyright boilerplate a = re.sub("(?s)\nStatus of [tT]his Memo\n.*$", "", a) # wrap long lines without messing up formatting of Ok paragraphs: while re.match("([^\n]{72,}?) +", a): a = re.sub("([^\n]{72,}?) +([^\n ]*)(\n|$)", "\\1\n\\2 ", a) # Remove leading and trailing whitespace a = a.strip() return a # things from IDInternal #draft = models.ForeignKey(InternetDraft, primary_key=True, unique=True, db_column='id_document_tag') @property def draft(self): return self @property def draft_id(self): return self.name #rfc_flag = models.IntegerField(null=True) @property def rfc_flag(self): return self.get_state_slug() == "rfc" #ballot = models.ForeignKey(BallotInfo, related_name='drafts', db_column="ballot_id") @property def ballot(self): if not self.idinternal: raise BallotInfo.DoesNotExist() return self @property def ballot_id(self): return self.ballot.name #primary_flag = models.IntegerField(blank=True, null=True) @property def primary_flag(self): # left-over from multi-ballot documents which we don't really # support anymore, just pretend we're always primary return True #group_flag = models.IntegerField(blank=True, default=0) # not used anymore, contained the group acronym_id once upon a time (so it wasn't a flag) #token_name = models.CharField(blank=True, max_length=25) @property def token_name(self): return self.ad.plain_name() #token_email = models.CharField(blank=True, max_length=255) @property def token_email(self): return self.ad.role_email("ad") #note = models.TextField(blank=True) # same name #status_date = models.DateField(blank=True,null=True) @property def status_date(self): return self.time.date() #email_display = models.CharField(blank=True, max_length=50) # unused #agenda = models.IntegerField(null=True, blank=True) @property def agenda(self): e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat") return bool(e and e.telechat_date and e.telechat_date >= datetime.date.today()) #cur_state = models.ForeignKey(IDState, db_column='cur_state', related_name='docs') @property def cur_state(self): s = self.get_state("draft-iesg") return IDState().from_object(s) if s else None @property def cur_state_id(self): s = self.get_state("draft-iesg") return s.order if s else None #prev_state = models.ForeignKey(IDState, db_column='prev_state', related_name='docs_prev') @property def prev_state(self): ds = self.history_set.exclude(states=self.get_state("draft-iesg")).order_by('-time')[:1] if ds: s = ds[0].get_state("draft-iesg") if s: return IDState().from_object(s) if ds else None return None #assigned_to = models.CharField(blank=True, max_length=25) # unused #mark_by = models.ForeignKey(IESGLogin, db_column='mark_by', related_name='marked') @property def mark_by(self): e = self.latest_event() from ietf.person.proxy import IESGLogin as IESGLoginProxy return IESGLoginProxy().from_object(e.by) if e else None # job_owner = models.ForeignKey(IESGLogin, db_column='job_owner', related_name='documents') @property def job_owner(self): from ietf.person.proxy import IESGLogin as IESGLoginProxy return IESGLoginProxy().from_object(self.ad) if self.ad else None #event_date = models.DateField(null=True) @property def event_date(self): e = self.latest_event() return e.time if e else None #area_acronym = models.ForeignKey(Area) @property def area_acronym(self): from ietf.group.proxy import Area g = super(InternetDraft, self).group # be careful with group which is proxied if g: if g.type_id == "area": return Area().from_object(g) elif g.type_id != "individ": return Area().from_object(g.parent) else: return None #cur_sub_state = BrokenForeignKey(IDSubState, related_name='docs', null=True, blank=True, null_values=(0, -1)) @property def cur_sub_state(self): s = self.tags.filter(slug__in=['extpty', 'need-rev', 'ad-f-up', 'point']) return IDSubState().from_object(s[0]) if s else None @property def cur_sub_state_id(self): s = self.cur_sub_state return s.order if s else None #prev_sub_state = BrokenForeignKey(IDSubState, related_name='docs_prev', null=True, blank=True, null_values=(0, -1)) @property def prev_sub_state(self): ds = self.history_set.all().order_by('-time')[:1] substates = ds[0].tags.filter(slug__in=['extpty', 'need-rev', 'ad-f-up', 'point']) if ds else None return IDSubState().from_object(substates[0]) if substates else None @property def prev_sub_state_id(self): s = self.prev_sub_state return s.order if s else None #returning_item = models.IntegerField(null=True, blank=True) @property def returning_item(self): e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat") return e.returning_item if e else None #telechat_date = models.DateField(null=True, blank=True) @property def telechat_date(self): e = self.latest_event(TelechatDocEvent, type="scheduled_for_telechat") return e.telechat_date if e else None #via_rfc_editor = models.IntegerField(null=True, blank=True) @property def via_rfc_editor(self): return self.stream_id in ('ise','irtf') #state_change_notice_to = models.CharField(blank=True, max_length=255) @property def state_change_notice_to(self): return self.notify #dnp = models.IntegerField(null=True, blank=True) @property def dnp(self): e = self.latest_event(type__in=("iesg_disapproved", "iesg_approved")) return e != None and e.type == "iesg_disapproved" #dnp_date = models.DateField(null=True, blank=True) @property def dnp_date(self): e = self.latest_event(type__in=("iesg_disapproved", "iesg_approved")) return e.time.date() if e != None and e.type == "iesg_disapproved" else None #noproblem = models.IntegerField(null=True, blank=True) @property def noproblem(self): e = self.latest_event(type__in=("iesg_disapproved", "iesg_approved")) return e != None and e.type == "iesg_approved" #resurrect_requested_by = BrokenForeignKey(IESGLogin, db_column='resurrect_requested_by', related_name='docsresurrected', null=True, blank=True) @property def resurrect_requested_by(self): e = self.latest_event(type__in=("requested_resurrect", "completed_resurrect")) from ietf.person.proxy import IESGLogin as IESGLoginProxy return IESGLoginProxy().from_object(e.by) if e and e.type == "requested_resurrect" else None #approved_in_minute = models.IntegerField(null=True, blank=True) @property def approved_in_minute(self): return self.latest_event(type="approved_in_minute") def get_absolute_url(self): if self.rfc_flag and self.rfc_number: return "/doc/rfc%d/" % self.rfc_number else: return "/doc/%s/" % self.name def document(self): return self def comments(self): return DocumentComment.objects.filter(doc=self).order_by('-time') def public_comments(self): return self.comments() def ballot_set(self): return [self] def ballot_primary(self): return [self] def ballot_others(self): return [] def docstate(self): s = self.get_state("draft-iesg") if s: subs = self.cur_sub_state if subs: return "%s::%s" % (s.name, subs) return s.name else: return "I-D Exists" # things from BallotInfo #active = models.BooleanField() @property def active(self): # taken from BallotWrapper s = self.get_state("draft-iesg") return self.latest_event(type="sent_ballot_announcement") and s and s.name in ['In Last Call', 'Waiting for Writeup', 'Waiting for AD Go-Ahead', 'IESG Evaluation', 'IESG Evaluation - Defer'] and (self.get_state_slug() in ("rfc", "active")) #an_sent = models.BooleanField() @property def an_sent(self): return bool(self.latest_event(type="iesg_approved")) #an_sent_date = models.DateField(null=True, blank=True) @property def an_sent_date(self): e = self.latest_event(type="iesg_approved") return e.time if e else None #an_sent_by = models.ForeignKey(IESGLogin, db_column='an_sent_by', related_name='ansent', null=True) @property def an_sent_by(self): e = self.latest_event(type="iesg_approved") from ietf.person.proxy import IESGLogin as IESGLoginProxy return IESGLoginProxy().from_object(e.by) if e else None #defer = models.BooleanField() @property def defer(self): # we're deferred if we're in the deferred state return self.get_state_slug("draft-iesg") == "defer" #defer_by = models.ForeignKey(IESGLogin, db_column='defer_by', related_name='deferred', null=True) @property def defer_by(self): e = self.latest_event(type="changed_document", desc__startswith="State changed to <b>IESG Evaluation - Defer</b>") from ietf.person.proxy import IESGLogin as IESGLoginProxy return IESGLoginProxy().from_object(e.by) if e else None #defer_date = models.DateField(null=True, blank=True) @property def defer_date(self): e = self.latest_event(type="changed_document", desc__startswith="State changed to <b>IESG Evaluation - Defer</b>") return e.time.date() if e else None #approval_text = models.TextField(blank=True) @property def approval_text(self): e = self.latest_event(WriteupDocEvent, type="changed_ballot_approval_text") return e.text if e else "" #last_call_text = models.TextField(blank=True) @property def last_call_text(self): e = self.latest_event(WriteupDocEvent, type="changed_last_call_text") return e.text if e else "" #ballot_writeup = models.TextField(blank=True) @property def ballot_writeup(self): e = self.latest_event(WriteupDocEvent, type="changed_ballot_writeup_text") return e.text if e else "" #ballot_issued = models.IntegerField(null=True, blank=True) @property def ballot_issued(self): return bool(self.latest_event(type="sent_ballot_announcement")) # def remarks(self): # apparently not used # remarks = list(self.discusses.all()) + list(self.comments.all()) # return remarks def active_positions(self): """Returns a list of dicts, with AD and Position tuples""" from ietf.person.proxy import IESGLogin as IESGLoginProxy res = [] for ad, pos in self.active_ballot().active_ad_positions().iteritems(): res.append(dict(ad=IESGLoginProxy().from_object(ad), pos=Position().from_object(pos) if pos else None)) res.sort(key=lambda x: x["ad"].last_name) return res def needed(self, standardsTrack=True): """Returns text answering the question what does this document need to pass?. The return value is only useful if the document is currently in IESG evaluation.""" tmp = self.active_positions() positions = [x["pos"] for x in tmp if x["pos"]] ads = [x["ad"] for x in tmp] yes = noobj = discuss = recuse = 0 for position in positions: p = position.pos_id if p == "yes": yes += 1 if p == "noobj": noobj += 1 if p == "discuss": discuss += 1 if p == "recuse": recuse += 1 answer = '' if yes < 1: answer += "Needs a YES. " if discuss > 0: if discuss == 1: answer += "Has a DISCUSS. " else: answer += "Has %d DISCUSSes. " % discuss if standardsTrack: # For standards-track, need positions from 2/3 of the # non-recused current IESG. needed = int(math.ceil((len(ads) - recuse) * 2.0/3.0)) else: # Info and experimental only need one position. # Info and experimental without Yes have their full spec now. if yes < 1: return answer.rstrip() else: needed = 1 have = yes + noobj if have < needed: more = needed - have if more == 1: answer += "Needs one more YES or NO OBJECTION position " else: answer += "Needs %d more YES or NO OBJECTION positions " % more if discuss: if discuss == 1: answer += "once the DISCUSS is resolved." else: answer += "once %d DISCUSSes are resolved." % discuss else: answer += ". " else: answer += "Has enough positions to pass" if discuss: if discuss == 1: answer += "once the DISCUSS is resolved" else: answer += "once %d DISCUSSes are resolved" % discuss answer += ". " return answer.rstrip() # things from RfcIndex #rfc_number = models.IntegerField(primary_key=True) # already taken care of #title = models.CharField(max_length=250) # same name #authors = models.CharField(max_length=250) # exists already #rfc_published_date = models.DateField() @property def rfc_published_date(self): if hasattr(self, 'published_rfc'): e = self.published_rfc else: e = self.latest_event(type="published_rfc") return e.time.date() if e else datetime.date(1990,1,1) #current_status = models.CharField(max_length=50,null=True) @property def current_status(self): if self.std_level: return self.std_level.name else: return u"(None)" #updates = models.CharField(max_length=200,blank=True,null=True) @property def updates(self): return ",".join("RFC%s" % n for n in sorted(d.rfc_number for d in InternetDraft.objects.filter(docalias__relateddocument__source=self, docalias__relateddocument__relationship="updates"))) #updated_by = models.CharField(max_length=200,blank=True,null=True) @property def updated_by(self): if not hasattr(self, "updated_by_list"): self.updated_by_list = [d.rfc_number for d in InternetDraft.objects.filter(relateddocument__target__document=self, relateddocument__relationship="updates")] return ",".join("RFC%s" % n for n in sorted(self.updated_by_list)) #obsoletes = models.CharField(max_length=200,blank=True,null=True) @property def obsoletes(self): return ",".join("RFC%s" % n for n in sorted(d.rfc_number for d in InternetDraft.objects.filter(docalias__relateddocument__source=self, docalias__relateddocument__relationship="obs"))) #obsoleted_by = models.CharField(max_length=200,blank=True,null=True) @property def obsoleted_by(self): if not hasattr(self, "obsoleted_by_list"): self.obsoleted_by_list = [d.rfc_number for d in InternetDraft.objects.filter(relateddocument__target__document=self, relateddocument__relationship="obs")] return ",".join("RFC%s" % n for n in sorted(self.obsoleted_by_list)) #also = models.CharField(max_length=50,blank=True,null=True) @property def also(self): aliases = self.docalias_set.filter(models.Q(name__startswith="bcp") | models.Q(name__startswith="std") | models.Q(name__startswith="bcp")) return aliases[0].name.upper() if aliases else None #draft = models.CharField(max_length=200,null=True) # have to ignore this, it's already implemented #has_errata = models.BooleanField() @property def has_errata(self): return bool(self.tags.filter(slug="errata")) #stream = models.CharField(max_length=15,blank=True,null=True) @property def stream(self): s = super(InternetDraft, self).stream return s.name if s else None #wg = models.CharField(max_length=15,blank=True,null=True) @property def wg(self): return self.group.acronym #file_formats = models.CharField(max_length=20,blank=True,null=True) @property def file_formats(self): return self.get_file_type_matches_from(os.path.join(settings.RFC_PATH, "rfc" + str(self.rfc_number))).replace(".", "").replace("txt", "ascii") @property def positions(self): res = [] found = set() for pos in Position.objects.filter(doc=self, type="changed_ballot_position").select_related('ad').order_by("-time", "-id"): if pos.ad not in found: found.add(pos.ad) res.append(pos) class Dummy: def all(self): return self.res d = Dummy() d.res = res return d @property def ipr(self): from ietf.ipr.models import IprDraftProxy return IprDraftProxy.objects.filter(doc_alias__document=self.pk) class Meta: proxy = True
class Area(Group): objects = TranslatingManager(dict(area_acronym__acronym="acronym", area_acronym__name="name", status=lambda v: ("state", { 1: "active", 2: "dormant", 3: "conclude" }[v])), always_filter=dict(type="area")) def from_object(self, base): for f in base._meta.fields: setattr(self, f.name, getattr(base, f.name)) return self ACTIVE = 1 #area_acronym = models.OneToOneField(Acronym, primary_key=True) @property def area_acronym(self): return Acronym().from_object(self) #start_date = models.DateField(auto_now_add=True) #concluded_date = models.DateField(null=True, blank=True) #status = models.ForeignKey(AreaStatus) @property def status_id(self): return { "active": 1, "dormant": 2, "conclude": 3, "proposed": 4 }[self.state_id] #comments = models.TextField(blank=True) #last_modified_date = models.DateField(auto_now=True) @property def last_modified_date(self): return self.time.date() #extra_email_addresses = models.TextField(blank=True,null=True) #def additional_urls(self): # return AreaWGURL.objects.filter(name=self.area_acronym.name) def active_wgs(self): return IETFWG.objects.filter(type="wg", state="active", parent=self).select_related( 'type', 'state', 'parent').order_by("acronym") @property def areadirector_set(self): return proxied_role_emails( Email.objects.filter(role__group=self, role__name="ad")) @staticmethod def active_areas(): return Area.objects.filter(type="area", state="active").select_related( 'type', 'state', 'parent').order_by('acronym') def __str__(self): return self.acronym def __unicode__(self): return self.acronym class Meta: proxy = True
class IETFWG(Group): objects = TranslatingManager(dict( group_acronym="id", group_acronym__acronym="acronym", group_acronym__acronym__in="acronym__in", group_acronym__acronym__contains="acronym__contains", email_archive__startswith="list_archive__startswith", group_type=lambda v: ("type__in", { 1: ("wg", "rg") }[int(v)]), status=lambda v: ("state__in", { 1: ("active", "bof") }[int(v)]), areagroup__area__status=lambda v: ("parent__state", { 1: "active" }[v]), start_date__isnull=lambda v: None if v else ("groupevent__changestategroupevent__state__slug", "active"), ), always_filter=dict(type__in=("wg", "rg", "individ", "area"))) def from_object(self, base): for f in base._meta.fields: setattr(self, f.name, getattr(base, f.name)) return self ACTIVE = 1 #group_acronym = models.OneToOneField(Acronym, primary_key=True, editable=False) @property def group_acronym(self): return Acronym().from_object(self) #group_type = models.ForeignKey(WGType) #proposed_date = models.DateField(null=True, blank=True) #start_date = models.DateField(null=True, blank=True) @property def start_date(self): e = GroupEvent.objects.filter( group=self, type="changed_state", changestategroupevent__state="active").order_by('time')[:1] return e[0].time.date() if e else None #dormant_date = models.DateField(null=True, blank=True) #concluded_date = models.DateField(null=True, blank=True) #status = models.ForeignKey(WGStatus) @property def status_id(self): return { "active": 1, "dormant": 2, "conclude": 3, "bof-conc": 3, "proposed": 4, "bof": 4, "abandon": 4 }[self.state_id] #area_director = models.ForeignKey(AreaDirector, null=True) #meeting_scheduled = models.CharField(blank=True, max_length=3) @property def meeting_scheduled(self): from ietf.meeting.models import Meeting latest_meeting = Meeting.objects.order_by('-date')[0] return "YES" if self.session_set.filter( meeting=latest_meeting) else "NO" #email_address = models.CharField(blank=True, max_length=60) @property def email_address(self): return self.list_email #email_subscribe = models.CharField(blank=True, max_length=120) @property def email_subscribe(self): return self.list_subscribe #email_keyword = models.CharField(blank=True, max_length=50) #email_archive = models.CharField(blank=True, max_length=95) @property def email_archive(self): return self.list_archive #comments = models.TextField(blank=True) #last_modified_date = models.DateField() @property def last_modified_date(self): return self.time.date() #meeting_scheduled_old = models.CharField(blank=True, max_length=3) #area = FKAsOneToOne('areagroup', reverse=True) @property def area(self): if self.parent: areagroup = AreaGroup().from_object(self) return areagroup else: return None def __str__(self): return self.group_acronym.acronym def __unicode__(self): return self.group_acronym.acronym def active_drafts(self): from ietf.doc.proxy import InternetDraft return InternetDraft.objects.filter(group=self, states__type="draft", states__slug="active") # def choices(): # return [(wg.group_acronym_id, wg.group_acronym.acronym) for wg in IETFWG.objects.all().filter(group_type__type='WG').select_related().order_by('acronym.acronym')] # choices = staticmethod(choices) def area_acronym(self): return Area().from_object(self.parent) if self.parent else None def area_directors(self): if not self.parent: return None return proxied_role_emails( sorted(Email.objects.filter(role__group=self.parent, role__name="ad"), key=lambda e: e.person.name_parts()[3])) def chairs(self): # return a set of WGChair objects for this work group return proxied_role_emails( sorted(Email.objects.filter(role__group=self, role__name="chair"), key=lambda e: e.person.name_parts()[3])) # def secretaries(self): # return a set of WGSecretary objects for this group # return WGSecretary.objects.filter(group_acronym__exact=self.group_acronym) # def milestones(self): # return a set of GoalMilestone objects for this group # return GoalMilestone.objects.filter(group_acronym__exact=self.group_acronym) # def rfcs(self): # return a set of Rfc objects for this group # return Rfc.objects.filter(group_acronym__exact=self.group_acronym) # def drafts(self): # return a set of Rfc objects for this group # return InternetDraft.objects.filter(group__exact=self.group_acronym) def charter_text( self): # return string containing WG description read from file from ietf.group.utils import get_charter_text return get_charter_text(self) def additional_urls(self): return self.groupurl_set.all().order_by("name") def clean_email_archive(self): return self.list_archive @property def wgchair_set(self): # gross hack ... class Dummy: pass d = Dummy() d.all = self.chairs return d @property def wgdelegate_set(self): from ietf.wgchairs.models import WGDelegate return WGDelegate.objects.filter(group=self, name="delegate") class Meta: proxy = True
class LiaisonDetailProxy(LiaisonStatement): objects = TranslatingManager(dict(submitted_date="submitted", deadline_date="deadline", to_body="to_name", from_raw_body="from_name")) def from_object(self, base): for f in base._meta.fields: setattr(self, f.name, getattr(base, f.name)) return self #detail_id = models.AutoField(primary_key=True) @property def detail_id(self): return self.id #person = models.ForeignKey(PersonOrOrgInfo, null=True, db_column='person_or_org_tag') @property def person(self): return self.from_contact.person if self.from_contact else "" #submitted_date = models.DateField(null=True, blank=True) @property def submitted_date(self): return self.submitted.date() if self.submitted else None #last_modified_date = models.DateField(null=True, blank=True) @property def last_modified_date(self): return self.modified.date() if self.modified else None #from_id = models.IntegerField(null=True, blank=True) @property def from_id(self): return self.from_group_id #to_body = models.CharField(blank=True, null=True, max_length=255) @property def to_body(self): return self.to_name #title = models.CharField(blank=True, null=True, max_length=255) # same name #response_contact = models.CharField(blank=True, null=True, max_length=255) # same name #technical_contact = models.CharField(blank=True, null=True, max_length=255) # same name #purpose_text = models.TextField(blank=True, null=True, db_column='purpose') @property def purpose_text(self): return "" #body = models.TextField(blank=True,null=True) # same name #deadline_date = models.DateField(null=True, blank=True) @property def deadline_date(self): return self.deadline #cc1 = models.TextField(blank=True, null=True) @property def cc1(self): return self.cc #cc2 = models.CharField(blank=True, null=True, max_length=50) # unused @property def cc2(self): return "" #submitter_name = models.CharField(blank=True, null=True, max_length=255) @property def submitter_name(self): i = self.to_name.find('<') if i > 0: return self.to_name[:i - 1] else: return self.to_name #submitter_email = models.CharField(blank=True, null=True, max_length=255) @property def submitter_email(self): import re re_email = re.compile("<(.*)>") match = re_email.search(self.to_name) if match: return match.group(1) else: return "" #by_secretariat = models.IntegerField(null=True, blank=True) @property def by_secretariat(self): return not self.from_contact #to_poc = models.CharField(blank=True, null=True, max_length=255) @property def to_poc(self): return self.to_contact #to_email = models.CharField(blank=True, null=True, max_length=255) @property def to_email(self): return "" #purpose = models.ForeignKey(LiaisonPurpose,null=True) #replyto = models.CharField(blank=True, null=True, max_length=255) @property def replyto(self): return self.reply_to #from_raw_body = models.CharField(blank=True, null=True, max_length=255) @property def from_raw_body(self): return self.from_name def raw_codify(self, group): if not group: return "" if group.type_id in ("sdo", "wg", "area"): return "%s_%s" % (group.type_id, group.id) return group.acronym #from_raw_code = models.CharField(blank=True, null=True, max_length=255) @property def from_raw_code(self): return self.raw_codify(self.from_group) #to_raw_code = models.CharField(blank=True, null=True, max_length=255) @property def to_raw_code(self): return self.raw_codify(self.to_group) #approval = models.ForeignKey(OutgoingLiaisonApproval, blank=True, null=True) @property def approval(self): return bool(self.approved) #action_taken = models.BooleanField(default=False, db_column='taken_care') # same name #related_to = models.ForeignKey('LiaisonDetail', blank=True, null=True) # same name @property def uploads_set(self): return UploadsProxy.objects.filter(liaisonstatement=self).order_by("name", "external_url") @property def liaisondetail_set(self): return self.liaisonstatement_set def __str__(self): return unicode(self) def __unicode__(self): return self.title or "<no title>" def from_body(self): return self.from_name def from_sdo(self): return self.from_group if self.from_group and self.from_group.type_id == "sdo" else None def from_email(self): return self.from_contact.address def get_absolute_url(self): return '/liaison/%d/' % self.detail_id class Meta: proxy = True def send_by_email(self, fake=False): # grab this from module instead of stuffing in on the model from ietf.liaisons.mails import send_liaison_by_email # we don't have a request so just pass None for the time being return send_liaison_by_email(None, self, fake) def notify_pending_by_email(self, fake=False): # grab this from module instead of stuffing in on the model from ietf.liaisons.mails import notify_pending_by_email # we don't have a request so just pass None for the time being return notify_pending_by_email(None, self, fake) def is_pending(self): return not self.approved
class MeetingProxy(Meeting): objects = TranslatingManager(dict(meeting_num="number"), always_filter=dict(type="ietf")) def from_object(self, base): for f in base._meta.fields: setattr(self, f.name, getattr(base, f.name)) return self #meeting_num = models.IntegerField(primary_key=True) @property def meeting_num(self): return self.number #start_date = models.DateField() @property def start_date(self): return self.date #end_date = models.DateField() @property def end_date(self): return self.date + datetime.timedelta(days=5) #city = models.CharField(blank=True, max_length=255) #state = models.CharField(blank=True, max_length=255) #country = models.CharField(blank=True, max_length=255) #time_zone = models.IntegerField(null=True, blank=True, choices=TIME_ZONE_CHOICES) #ack = models.TextField(blank=True) #agenda_html = models.TextField(blank=True) #agenda_text = models.TextField(blank=True) #future_meeting = models.TextField(blank=True) #overview1 = models.TextField(blank=True) #overview2 = models.TextField(blank=True) def __str__(self): return "IETF-%s" % (self.meeting_num) def get_meeting_date(self, offset): return self.start_date + datetime.timedelta(days=offset) def num(self): return self.number @property def meeting_venue(self): return MeetingVenueProxy().from_object(self) @classmethod def get_first_cut_off(cls): start_date = cls.objects.all().order_by('-date')[0].start_date offset = datetime.timedelta(days=settings.FIRST_CUTOFF_DAYS) return start_date - offset @classmethod def get_second_cut_off(cls): start_date = cls.objects.all().order_by('-date')[0].start_date offset = datetime.timedelta(days=settings.SECOND_CUTOFF_DAYS) return start_date - offset @classmethod def get_ietf_monday(cls): start_date = cls.objects.all().order_by('-date')[0].start_date return start_date + datetime.timedelta(days=-start_date.weekday(), weeks=1) class Meta: proxy = True
class WgMeetingSessionProxy(TimeSlot): # we model WgMeetingSession as a TimeSlot, to make the illusion # complete we thus have to forward all the session stuff to the # real session objects = TranslatingManager( dict(group_acronym_id="session__group", status__id=lambda v: ("state", { 4: "sched" }[v]))) def from_object(self, base): for f in base._meta.fields: setattr(self, f.name, getattr(base, f.name)) return self #session_id = models.AutoField(primary_key=True) # same name #meeting = models.ForeignKey(Meeting, db_column='meeting_num') # same name #group_acronym_id = models.IntegerField() @property def group_acronym_id(self): return self.session.group_id if self.session else -1 #irtf = models.NullBooleanField() @property def irtf(self): return 1 if self.session and self.session.group and self.session.group.type == "rg" else 0 #num_session = models.IntegerField() @property def num_session(self): return 1 if self.session else 0 #length_session1 = models.CharField(blank=True, max_length=100) @property def length_session1(self): if not self.session: return "0" secs = self.session.requested_duration.seconds if secs == 0: return "0" return str((secs / 60 - 30) / 30) #length_session2 = models.CharField(blank=True, max_length=100) @property def length_session2(self): return "0" #length_session3 = models.CharField(blank=True, max_length=100) @property def length_session3(self): return "0" def conflicting_group_acronyms(self, level): if not self.session: return "" conflicts = Constraint.objects.filter(meeting=self.meeting_id, target=self.session.group, name=level) return " ".join(c.source.acronym for c in conflicts) #conflict1 = models.CharField(blank=True, max_length=255) @property def conflict1(self): return self.conflicting_group_acronyms("conflict") #conflict2 = models.CharField(blank=True, max_length=255) @property def conflict2(self): return self.conflicting_group_acronyms("conflic2") #conflict3 = models.CharField(blank=True, max_length=255) @property def conflict3(self): return self.conflicting_group_acronyms("conflic3") #conflict_other = models.TextField(blank=True) @property def conflict_other(self): return "" #special_req = models.TextField(blank=True) @property def special_req(self): return self.session.comments if self.session else "" #number_attendee = models.IntegerField(null=True, blank=True) @property def number_attendee(self): return self.session.attendees if self.session else 0 #approval_ad = models.IntegerField(null=True, blank=True) #status = models.ForeignKey(SessionStatus, null=True, blank=True) # same name #ts_status_id = models.IntegerField(null=True, blank=True) #requested_date = models.DateField(null=True, blank=True) @property def requested_date(self): return self.session.requested.date() if self.session else None #approved_date = models.DateField(null=True, blank=True) #requested_by = BrokenForeignKey(PersonOrOrgInfo, db_column='requested_by', null=True, null_values=(0, 888888)) @property def requested_by(self): return self.session.requested_by if self.session else None #scheduled_date = models.DateField(null=True, blank=True) @property def scheduled_date(self): return self.session.scheduled.date() if self.session else "" #last_modified_date = models.DateField(null=True, blank=True) @property def last_modified_date(self): return self.session.modified.date() if self.session else "" #ad_comments = models.TextField(blank=True,null=True) #sched_room_id1 = models.ForeignKey(MeetingRoom, db_column='sched_room_id1', null=True, blank=True, related_name='here1') #sched_time_id1 = BrokenForeignKey(MeetingTime, db_column='sched_time_id1', null=True, blank=True, related_name='now1') #sched_date1 = models.DateField(null=True, blank=True) #sched_room_id2 = models.ForeignKey(MeetingRoom, db_column='sched_room_id2', null=True, blank=True, related_name='here2') #sched_time_id2 = BrokenForeignKey(MeetingTime, db_column='sched_time_id2', null=True, blank=True, related_name='now2') #sched_date2 = models.DateField(null=True, blank=True) #sched_room_id3 = models.ForeignKey(MeetingRoom, db_column='sched_room_id3', null=True, blank=True, related_name='here3') #sched_time_id3 = BrokenForeignKey(MeetingTime, db_column='sched_time_id3', null=True, blank=True, related_name='now3') #sched_date3 = models.DateField(null=True, blank=True) #special_agenda_note = models.CharField(blank=True, max_length=255) @property def special_agenda_note(self): return self.session.agenda_note if self.session else "" #combined_room_id1 = models.ForeignKey(MeetingRoom, db_column='combined_room_id1', null=True, blank=True, related_name='here4') #combined_time_id1 = models.ForeignKey(MeetingTime, db_column='combined_time_id1', null=True, blank=True, related_name='now4') #combined_room_id2 = models.ForeignKey(MeetingRoom, db_column='combined_room_id2', null=True, blank=True, related_name='here5') #combined_time_id2 = models.ForeignKey(MeetingTime, db_column='combined_time_id2', null=True, blank=True, related_name='now5') def __str__(self): return "%s at %s" % (self.acronym(), self.meeting) def agenda_file(self, interimvar=0): if not hasattr(self, '_agenda_file'): self._agenda_file = "" if not self.session: return "" docs = self.session.materials.filter(type="agenda", states__type="agenda", states__slug="active") if not docs: return "" # we use external_url at the moment, should probably regularize # the filenames to match the document name instead filename = docs[0].external_url self._agenda_file = "%s/agenda/%s" % (self.meeting.number, filename) return self._agenda_file def minute_file(self, interimvar=0): if not self.session: return "" docs = self.session.materials.filter(type="minutes", states__type="minutes", states__slug="active") if not docs: return "" # we use external_url at the moment, should probably regularize # the filenames to match the document name instead filename = docs[0].external_url return "%s/minutes/%s" % (self.meeting.number, filename) def slides(self, interimvar=0): return SlideProxy.objects.filter( session__timeslot=self).order_by("order") def interim_meeting(self): return False def length_session1_desc(self): l = self.length_session1 return { "0": "", "1": "1 hour", "2": "1.5 hours", "3": "2 hours", "4": "2.5 hours" }[l] def length_session2_desc(self): return "" def length_session3_desc(self): return "" @property def ordinality(self): return 1 @property def room_id(self): class Dummy: pass d = Dummy() d.room_name = self.location.name return d # from ResolveAcronym: def acronym(self): if self.type_id == "plenary": if "Operations and Administration" in self.name: return "plenaryw" if "Technical" in self.name: return "plenaryt" for m in self.materials.filter(type="agenda", states__type="agenda", states__slug="active"): if "plenaryw" in m.name: return "plenaryw" if "plenaryt" in m.name: return "plenaryt" if not self.session: return "%s" % self.pk if hasattr(self, "interim"): return "i" + self.session.group.acronym else: return self.session.group.acronym def acronym_lower(self): return self.acronym().lower() def acronym_name(self): if not self.session: return self.name if hasattr(self, "interim"): return self.session.group.name + " (interim)" elif self.session.name: return self.session.name else: return self.session.group.name def area(self): if not self.session or not self.session.group: return "" if self.session.group.type_id == "irtf": return "irtf" if self.type_id == "plenary": return "1plenary" if not self.session.group.parent or not self.session.group.parent.type_id in [ "area", "irtf", "ietf" ]: return "" return self.session.group.parent.acronym def area_name(self): if self.type_id == "plenary": return "Plenary Sessions" elif self.session and self.session.group and self.session.group.acronym == "edu": return "Training" elif not self.session or not self.session.group or not self.session.group.parent or not self.session.group.parent.type_id == "area": return "" return self.session.group.parent.name def isWG(self): if not self.session or not self.session.group: return False if self.session.group.type_id == "wg" and self.session.group.state_id != "bof": return True def group_type_str(self): if not self.session or not self.session.group: return "" if self.session.group and self.session.group.type_id == "wg": if self.session.group.state_id == "bof": return "BOF" else: return "WG" return "" class Meta: proxy = True