Added redesign work-in-progress
- Legacy-Id: 2686
This commit is contained in:
parent
443f417ea9
commit
c71f1d9f97
|
@ -98,4 +98,4 @@ def get_list_or_404(klass, *args, **kwargs):
|
|||
obj_list = list(queryset.filter(*args, **kwargs))
|
||||
if not obj_list:
|
||||
raise Http404('No %s matches the given query.' % queryset.model._meta.object_name)
|
||||
return obj_list
|
||||
return obj_list
|
||||
|
|
|
@ -36,6 +36,7 @@ import re
|
|||
from datetime import date
|
||||
from django.utils import simplejson as json
|
||||
from django.db.models import Q
|
||||
from django.conf import settings
|
||||
import types
|
||||
|
||||
BALLOT_ACTIVE_STATES = ['In Last Call',
|
||||
|
@ -602,8 +603,64 @@ class BallotWrapper:
|
|||
return []
|
||||
else:
|
||||
return self._ballot_set.exclude(draft=self._idinternal)
|
||||
|
||||
|
||||
def _init(self):
|
||||
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
self.old_init()
|
||||
return
|
||||
|
||||
from redesign.person.models import Email
|
||||
active_ads = Email.objects.filter(role__name="ad", role__group__state="active")
|
||||
|
||||
positions = []
|
||||
seen = {}
|
||||
|
||||
for pos in self.ballot.event_set.filter(type="changed_ballot_position").select_related('pos', 'ad').order_by("-time", '-id'):
|
||||
pos = pos.ballotposition
|
||||
if pos.ad not in seen:
|
||||
p = dict(ad_name=pos.ad.get_name(),
|
||||
ad_username="", # FIXME: don't seem to have username at the moment
|
||||
position=pos.pos.name,
|
||||
is_old_ad=pos.ad in active_ads,
|
||||
old_positions=[])
|
||||
|
||||
if pos.pos.slug == "discuss":
|
||||
p["has_text"] = True
|
||||
p["discuss_text"] = pos.discuss
|
||||
p["discuss_date"] = pos.discuss_time
|
||||
p["discuss_revision"] = pos.doc.rev # FIXME: wrong
|
||||
|
||||
if pos.comment:
|
||||
p["has_text"] = True
|
||||
p["comment_text"] = pos.comment
|
||||
p["comment_date"] = pos.comment_time
|
||||
p["comment_revision"] = pos.doc.rev # FIXME: wrong
|
||||
|
||||
positions.append(p)
|
||||
seen[pos.ad] = p
|
||||
else:
|
||||
latest = seen[pos.ad]
|
||||
if latest["old_positions"]:
|
||||
prev = latest["old_positions"][-1]
|
||||
else:
|
||||
prev = latest["position"]
|
||||
|
||||
if prev != pos.pos.name:
|
||||
seen[pos.ad]["old_positions"].append(pos.pos.name)
|
||||
|
||||
# add any missing ADs as No Record
|
||||
if self.ballot_active:
|
||||
for ad in active_ads:
|
||||
if ad not in seen:
|
||||
d = dict(ad_name=ad.get_name(),
|
||||
ad_username="", # FIXME: don't seem to have username at the moment
|
||||
position="No Record",
|
||||
)
|
||||
positions.append(d)
|
||||
|
||||
self._positions = positions
|
||||
|
||||
def old_init(self):
|
||||
try:
|
||||
ads = set()
|
||||
except NameError:
|
||||
|
|
|
@ -147,21 +147,32 @@ def document_main(request, name):
|
|||
# doc is either IdWrapper or RfcWrapper
|
||||
def _get_history(doc, versions):
|
||||
results = []
|
||||
if doc.is_id_wrapper:
|
||||
comments = DocumentComment.objects.filter(document=doc.tracker_id).exclude(rfc_flag=1)
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
for e in doc._draft.event_set.all().select_related('by').order_by('-time'):
|
||||
info = {}
|
||||
info['text'] = e.desc
|
||||
info['by'] = e.by.get_name()
|
||||
info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25)
|
||||
info['snipped'] = info['textSnippet'][-3:] == "..."
|
||||
e.version = e.doc.rev
|
||||
results.append({'comment':e, 'info':info, 'date':e.time, 'is_com':True})
|
||||
else:
|
||||
comments = DocumentComment.objects.filter(document=doc.rfc_number,rfc_flag=1)
|
||||
if len(comments) > 0:
|
||||
# also include rfc_flag=NULL, but only if at least one
|
||||
# comment with rfc_flag=1 exists (usually NULL means same as 0)
|
||||
comments = DocumentComment.objects.filter(document=doc.rfc_number).exclude(rfc_flag=0)
|
||||
for comment in comments.order_by('-date','-time','-id').filter(public_flag=1).select_related('created_by'):
|
||||
info = {}
|
||||
info['text'] = comment.comment_text
|
||||
info['by'] = comment.get_fullname()
|
||||
info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25)
|
||||
info['snipped'] = info['textSnippet'][-3:] == "..."
|
||||
results.append({'comment':comment, 'info':info, 'date':comment.datetime(), 'is_com':True})
|
||||
if doc.is_id_wrapper:
|
||||
comments = DocumentComment.objects.filter(document=doc.tracker_id).exclude(rfc_flag=1)
|
||||
else:
|
||||
comments = DocumentComment.objects.filter(document=doc.rfc_number,rfc_flag=1)
|
||||
if len(comments) > 0:
|
||||
# also include rfc_flag=NULL, but only if at least one
|
||||
# comment with rfc_flag=1 exists (usually NULL means same as 0)
|
||||
comments = DocumentComment.objects.filter(document=doc.rfc_number).exclude(rfc_flag=0)
|
||||
for comment in comments.order_by('-date','-time','-id').filter(public_flag=1).select_related('created_by'):
|
||||
info = {}
|
||||
info['text'] = comment.comment_text
|
||||
info['by'] = comment.get_fullname()
|
||||
info['textSnippet'] = truncatewords_html(format_textarea(fill(info['text'], 80)), 25)
|
||||
info['snipped'] = info['textSnippet'][-3:] == "..."
|
||||
results.append({'comment':comment, 'info':info, 'date':comment.datetime(), 'is_com':True})
|
||||
|
||||
if doc.is_id_wrapper and versions:
|
||||
for v in versions:
|
||||
if v['draft_name'] == doc.draft_name:
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
#coding: utf-8
|
||||
from django.contrib import admin
|
||||
from django.conf import settings
|
||||
from ietf.idtracker.models import *
|
||||
|
||||
class AcronymAdmin(admin.ModelAdmin):
|
||||
list_display=('acronym', 'name')
|
||||
admin.site.register(Acronym, AcronymAdmin)
|
||||
|
||||
class AreaAdmin(admin.ModelAdmin):
|
||||
list_display=('area_acronym', 'status')
|
||||
admin.site.register(Area, AreaAdmin)
|
||||
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
class AcronymAdmin(admin.ModelAdmin):
|
||||
list_display=('acronym', 'name')
|
||||
admin.site.register(Acronym, AcronymAdmin)
|
||||
|
||||
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
class AreaAdmin(admin.ModelAdmin):
|
||||
list_display=('area_acronym', 'status')
|
||||
admin.site.register(Area, AreaAdmin)
|
||||
|
||||
class AreaDirectorAdmin(admin.ModelAdmin):
|
||||
raw_id_fields=['person']
|
||||
|
@ -96,12 +99,13 @@ class IRTFAdmin(admin.ModelAdmin):
|
|||
pass
|
||||
admin.site.register(IRTF, IRTFAdmin)
|
||||
|
||||
class InternetDraftAdmin(admin.ModelAdmin):
|
||||
list_display=('filename', 'revision', 'title', 'status')
|
||||
search_fields=['filename', 'title']
|
||||
list_filter=['status']
|
||||
raw_id_fields=['replaced_by']
|
||||
admin.site.register(InternetDraft, InternetDraftAdmin)
|
||||
if not settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
class InternetDraftAdmin(admin.ModelAdmin):
|
||||
list_display=('filename', 'revision', 'title', 'status')
|
||||
search_fields=['filename', 'title']
|
||||
list_filter=['status']
|
||||
raw_id_fields=['replaced_by']
|
||||
admin.site.register(InternetDraft, InternetDraftAdmin)
|
||||
|
||||
class PersonOrOrgInfoAdmin(admin.ModelAdmin):
|
||||
fieldsets=((None, {'fields': (('first_name', 'middle_initial', 'last_name'), ('name_suffix', 'modified_by'))}), ('Obsolete Info', {'fields': ('record_type', 'created_by', 'address_type'), 'classes': 'collapse'}))
|
||||
|
|
|
@ -1087,6 +1087,11 @@ class DocumentWrapper(object):
|
|||
def __init__(self, document):
|
||||
self.document = document
|
||||
|
||||
if settings.USE_DB_REDESIGN_PROXY_CLASSES:
|
||||
from redesign.doc.proxy import InternetDraft
|
||||
from redesign.group.proxy import Area
|
||||
from redesign.group.proxy import Acronym
|
||||
|
||||
|
||||
# changes done by convert-096.py:changed maxlength to max_length
|
||||
# removed core
|
||||
|
|
|
@ -8,9 +8,12 @@ import os
|
|||
import syslog
|
||||
syslog.openlog("django", syslog.LOG_PID, syslog.LOG_LOCAL0)
|
||||
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
import sys
|
||||
sys.path.append(os.path.abspath(BASE_DIR + "/.."))
|
||||
sys.path.append(os.path.abspath(BASE_DIR + "/../redesign"))
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
|
@ -118,6 +121,11 @@ INSTALLED_APPS = (
|
|||
'django.contrib.admin',
|
||||
'django.contrib.humanize',
|
||||
'south',
|
||||
'redesign.person',
|
||||
'redesign.name',
|
||||
'redesign.group',
|
||||
'redesign.doc',
|
||||
'redesign.issue',
|
||||
'ietf.announcements',
|
||||
'ietf.idindex',
|
||||
'ietf.idtracker',
|
||||
|
@ -187,6 +195,9 @@ LIAISON_UNIVERSAL_FROM = 'Liaison Statement Management Tool <lsmt@' + IETF_DOMAI
|
|||
LIAISON_ATTACH_PATH = '/a/www/ietf-datatracker/documents/LIAISON/'
|
||||
LIAISON_ATTACH_URL = '/documents/LIAISON/'
|
||||
|
||||
# DB redesign
|
||||
USE_DB_REDESIGN_PROXY_CLASSES=True
|
||||
|
||||
# Put SECRET_KEY in here, or any other sensitive or site-specific
|
||||
# changes. DO NOT commit settings_local.py to svn.
|
||||
from settings_local import *
|
||||
|
|
0
redesign/__init__.py
Normal file
0
redesign/__init__.py
Normal file
0
redesign/doc/__init__.py
Normal file
0
redesign/doc/__init__.py
Normal file
45
redesign/doc/admin.py
Normal file
45
redesign/doc/admin.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
from django.contrib import admin
|
||||
from models import *
|
||||
from person.models import *
|
||||
|
||||
class RelatedDocAdmin(admin.ModelAdmin):
|
||||
list_display = ["source", "relationship", "target"]
|
||||
search_fields = ["doc_alias__name", "related_document_set__name", ]
|
||||
list_display_links = ["relationship", ]
|
||||
admin.site.register(RelatedDoc, RelatedDocAdmin)
|
||||
|
||||
class DocumentAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'rev', 'state', 'group', 'pages', 'intended_std_level', 'author_list', 'time']
|
||||
search_fields = ['name']
|
||||
raw_id_fields = ['authors', 'related', 'group', 'shepherd', 'ad']
|
||||
admin.site.register(Document, DocumentAdmin)
|
||||
|
||||
class DocHistoryAdmin(admin.ModelAdmin):
|
||||
list_display = ['doc', 'rev', 'state', 'group', 'pages', 'intended_std_level', 'author_list', 'time']
|
||||
search_fields = ['doc__name']
|
||||
ordering = ['time', 'doc', 'rev']
|
||||
raw_id_fields = ['authors', 'related']
|
||||
admin.site.register(DocHistory, DocHistoryAdmin)
|
||||
|
||||
class DocAliasAdmin(admin.ModelAdmin):
|
||||
list_display = [ 'name', 'document_link', ]
|
||||
search_fields = [ 'name', 'document__name', ]
|
||||
admin.site.register(DocAlias, DocAliasAdmin)
|
||||
|
||||
class SendQueueAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
admin.site.register(SendQueue, SendQueueAdmin)
|
||||
|
||||
|
||||
# events
|
||||
|
||||
class EventAdmin(admin.ModelAdmin):
|
||||
raw_id_fields = ["doc", "by"]
|
||||
admin.site.register(Event, EventAdmin)
|
||||
|
||||
admin.site.register(Message, EventAdmin)
|
||||
admin.site.register(Text, EventAdmin)
|
||||
admin.site.register(BallotPosition, EventAdmin)
|
||||
admin.site.register(Expiration, EventAdmin)
|
||||
admin.site.register(Telechat, EventAdmin)
|
||||
|
211
redesign/doc/models.py
Normal file
211
redesign/doc/models.py
Normal file
|
@ -0,0 +1,211 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
from django.db import models
|
||||
from redesign.group.models import *
|
||||
from redesign.name.models import *
|
||||
from redesign.person.models import Email
|
||||
from redesign.util import admin_link
|
||||
|
||||
|
||||
class RelatedDoc(models.Model):
|
||||
relationship = models.ForeignKey(DocRelationshipName)
|
||||
doc_alias = models.ForeignKey('DocAlias')
|
||||
def __unicode__(self):
|
||||
return "%s %s" % (self.relationship.name, self.doc_alias.name)
|
||||
source = admin_link("related_document_set")
|
||||
target = admin_link("doc_alias__document")
|
||||
|
||||
class DocumentInfo(models.Model):
|
||||
"""Any kind of document. Draft, RFC, Charter, IPR Statement, Liaison Statement"""
|
||||
time = models.DateTimeField() # should probably have auto_now=True
|
||||
# Document related
|
||||
type = models.ForeignKey(DocTypeName, blank=True, null=True) # Draft, Agenda, Minutes, Charter, Discuss, Guideline, Email, Review, Issue, Wiki, External ...
|
||||
title = models.CharField(max_length=255)
|
||||
# State
|
||||
state = models.ForeignKey(DocStateName, blank=True, null=True) # Active/Expired/RFC/Replaced/Withdrawn
|
||||
tags = models.ManyToManyField(DocInfoTagName, blank=True, null=True) # Revised ID Needed, ExternalParty, AD Followup, ...
|
||||
stream = models.ForeignKey(DocStreamName, blank=True, null=True) # IETF, IAB, IRTF, Independent Submission
|
||||
group = models.ForeignKey(Group, blank=True, null=True) # WG, RG, IAB, IESG, Edu, Tools
|
||||
wg_state = models.ForeignKey(WgDocStateName, blank=True, null=True) # Not/Candidate/Active/Parked/LastCall/WriteUp/Submitted/Dead
|
||||
iesg_state = models.ForeignKey(IesgDocStateName, blank=True, null=True) #
|
||||
iana_state = models.ForeignKey(IanaDocStateName, blank=True, null=True)
|
||||
rfc_state = models.ForeignKey(RfcDocStateName, blank=True, null=True)
|
||||
# Other
|
||||
abstract = models.TextField()
|
||||
rev = models.CharField(max_length=16)
|
||||
pages = models.IntegerField(blank=True, null=True)
|
||||
intended_std_level = models.ForeignKey(IntendedStatusName, blank=True, null=True)
|
||||
std_level = models.ForeignKey(StdStatusName, blank=True, null=True)
|
||||
authors = models.ManyToManyField(Email, blank=True, null=True)
|
||||
related = models.ManyToManyField(RelatedDoc, related_name='related_%(class)s_set', blank=True)
|
||||
ad = models.ForeignKey(Email, related_name='ad_%(class)s_set', blank=True, null=True)
|
||||
shepherd = models.ForeignKey(Email, related_name='shepherd_%(class)s_set', blank=True, null=True)
|
||||
notify = models.CharField(max_length=255, blank=True)
|
||||
external_url = models.URLField(blank=True) # Should be set for documents with type 'External'.
|
||||
note = models.TextField(blank=True)
|
||||
internal_comments = models.TextField(blank=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
def author_list(self):
|
||||
return ", ".join(email.address for email in self.authors.all())
|
||||
def latest_event(self, **filter_args):
|
||||
e = self.event_set.filter(**filter_args).order_by('-time')[:1]
|
||||
if e:
|
||||
return e[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
class Document(DocumentInfo):
|
||||
name = models.CharField(max_length=255, primary_key=True) # immutable
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
def values(self):
|
||||
try:
|
||||
fields = dict([(field.name, getattr(self, field.name))
|
||||
for field in self._meta.fields
|
||||
if field is not self._meta.pk])
|
||||
except:
|
||||
for field in self._meta.fields:
|
||||
print "* %24s"%field.name,
|
||||
print getattr(self, field.name)
|
||||
raise
|
||||
many2many = dict([(field.name, getattr(self, field.name).all())
|
||||
for field in self._meta.many_to_many ])
|
||||
return fields, many2many
|
||||
|
||||
def save_with_history(self, force_insert=False, force_update=False):
|
||||
fields, many2many = self.values()
|
||||
fields["doc"] = self
|
||||
try:
|
||||
snap = DocHistory.objects.get(**dict((k,v) for k,v in fields.items() if k != 'time'))
|
||||
# FIXME: what if there are two with the same set of values
|
||||
# at different points in time?
|
||||
if snap.time > fields["time"]:
|
||||
snap.time = fields["time"]
|
||||
snap.save()
|
||||
except DocHistory.DoesNotExist:
|
||||
snap = DocHistory(**fields)
|
||||
snap.save()
|
||||
for m in many2many:
|
||||
#print "m2m:", m, many2many[m]
|
||||
rel = getattr(snap, m)
|
||||
for item in many2many[m]:
|
||||
rel.add(item)
|
||||
except DocHistory.MultipleObjectsReturned:
|
||||
list = DocHistory.objects.filter(**dict((k,v) for k,v in fields.items() if k != 'time'))
|
||||
list.delete()
|
||||
snap = DocHistory(**fields)
|
||||
snap.save()
|
||||
print "Deleted list:", snap
|
||||
super(Document, self).save(force_insert, force_update)
|
||||
|
||||
class DocHistory(DocumentInfo):
|
||||
doc = models.ForeignKey(Document) # ID of the Document this relates to
|
||||
def __unicode__(self):
|
||||
return unicode(self.doc.name)
|
||||
|
||||
class DocAlias(models.Model):
|
||||
"""This is used for documents that may appear under multiple names,
|
||||
and in particular for RFCs, which for continuity still keep the
|
||||
same immutable Document.name, in the tables, but will be referred
|
||||
to by RFC number, primarily, after achieving RFC status.
|
||||
"""
|
||||
document = models.ForeignKey(Document)
|
||||
name = models.CharField(max_length=255)
|
||||
def __unicode__(self):
|
||||
return "%s-->%s" % (self.name, self.document.name)
|
||||
document_link = admin_link("document")
|
||||
class Meta:
|
||||
verbose_name_plural="Aliases"
|
||||
|
||||
class SendQueue(models.Model):
|
||||
time = models.DateTimeField() # Scheduled at this time
|
||||
agent = models.ForeignKey(Email) # Scheduled by this person
|
||||
comment = models.TextField()
|
||||
#
|
||||
msg = models.ForeignKey('Message')
|
||||
to = models.ForeignKey(Email, related_name='to_messages')
|
||||
cc = models.ManyToManyField(Email, related_name='cc_messages')
|
||||
send = models.DateTimeField() # Send message at this time
|
||||
|
||||
# class Ballot(models.Model): # A collection of ballot positions
|
||||
# """A collection of ballot positions, and the actions taken during the
|
||||
# lifetime of the ballot.
|
||||
|
||||
# The actual ballot positions are found by searching Messages for
|
||||
# BallotPositions for this document between the dates indicated by
|
||||
# self.initiated.time and (self.closed.time or now)
|
||||
# """
|
||||
# initiated = models.ForeignKey(Message, related_name="initiated_ballots")
|
||||
# deferred = models.ForeignKey(Message, null=True, blank=True, related_name="deferred_ballots")
|
||||
# last_call = models.ForeignKey(Message, null=True, blank=True, related_name="lastcalled_ballots")
|
||||
# closed = models.ForeignKey(Message, null=True, blank=True, related_name="closed_ballots")
|
||||
# announced = models.ForeignKey(Message, null=True, blank=True, related_name="announced_ballots")
|
||||
|
||||
|
||||
EVENT_TYPES = [
|
||||
# misc document events
|
||||
("new_revision", "Added new revision"),
|
||||
("document_changed", "Changed document"),
|
||||
("tombstone_added", "Added tombstone"),
|
||||
("requested_resurrect", "Requested resurrect"),
|
||||
|
||||
# IESG events
|
||||
("sent_ballot_announcement", "Sent ballot announcement"),
|
||||
("deferred_ballot", "Deferred ballot"),
|
||||
("approved_ballot", "Approved ballot"),
|
||||
("changed_ballot_position", "Changed ballot position"),
|
||||
("changed_ballot_approval_text", "Changed ballot approval text"),
|
||||
("changed_ballot_writeup_text", "Changed ballot writeup text"),
|
||||
|
||||
("changed_last_call_text", "Changed last call text"),
|
||||
("sent_last_call", "Sent last call"),
|
||||
|
||||
("scheduled_for_telechat", "Scheduled for telechat"),
|
||||
|
||||
("resolved_to_do_not_publish", "Resolved to 'do not publish'"),
|
||||
("resolved_to_no_problem", "Resolved to 'no problem'"),
|
||||
|
||||
("approved_in_minute", "Approved in minute"),
|
||||
]
|
||||
|
||||
class Event(models.Model):
|
||||
"""An occurrence in connection with a document."""
|
||||
time = models.DateTimeField()
|
||||
type = models.CharField(max_length=50, choices=EVENT_TYPES)
|
||||
by = models.ForeignKey(Email, blank=True, null=True)
|
||||
doc = models.ForeignKey('doc.Document')
|
||||
desc = models.TextField()
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s %s at %s" % (self.by.get_name(), self.get_type_display().lower(), self.time)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-time']
|
||||
|
||||
class Message(Event):
|
||||
subj = models.CharField(max_length=255)
|
||||
body = models.TextField()
|
||||
|
||||
class Text(Event):
|
||||
content = models.TextField(blank=True)
|
||||
|
||||
# IESG events
|
||||
class BallotPosition(Event):
|
||||
ad = models.ForeignKey(Email)
|
||||
pos = models.ForeignKey(BallotPositionName, verbose_name="position", default="norec")
|
||||
discuss = models.TextField(help_text="Discuss text if position is discuss", blank=True)
|
||||
discuss_time = models.DateTimeField(help_text="Time discuss text was written", blank=True, null=True)
|
||||
comment = models.TextField(help_text="Optional comment", blank=True)
|
||||
comment_time = models.DateTimeField(help_text="Time optional comment was written", blank=True, null=True)
|
||||
|
||||
class Expiration(Event):
|
||||
expires = models.DateTimeField()
|
||||
|
||||
class Telechat(Event):
|
||||
telechat_date = models.DateField()
|
||||
returning_item = models.BooleanField(default=False)
|
||||
|
||||
|
||||
|
529
redesign/doc/proxy.py
Normal file
529
redesign/doc/proxy.py
Normal file
|
@ -0,0 +1,529 @@
|
|||
from models import *
|
||||
from redesign.person.models import Email
|
||||
from redesign.proxy_utils import TranslatingManager
|
||||
|
||||
|
||||
class InternetDraft(Document):
|
||||
objects = TranslatingManager(dict(filename="name",
|
||||
id_document_tag="id",
|
||||
status="state"))
|
||||
|
||||
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):
|
||||
return super(self.__class__, self).group
|
||||
# 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):
|
||||
e = self.latest_event(type="new_revision")
|
||||
return e.time if e else None
|
||||
# file_type = models.CharField(max_length=20)
|
||||
@property
|
||||
def file_type(self):
|
||||
return ".txt" # FIXME XXX should look in the repository to see what's available
|
||||
# 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):
|
||||
return self.dochistory_set.dates("time","day","ASC")[0]
|
||||
# expiration_date = models.DateField()
|
||||
@property
|
||||
def expiration_date(self):
|
||||
return self.expiration()
|
||||
# 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):
|
||||
from redesign.name.proxy import IDStatus
|
||||
return IDStatus(self.state)
|
||||
|
||||
@property
|
||||
def status_id(self):
|
||||
from redesign.name.proxy import IDStatus
|
||||
return { 'active': 1, 'repl': 5, 'expired': 2, 'rfc': 3, 'auth-rm': 4, 'ietf-rm': 6 }[self.state_id]
|
||||
|
||||
# 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 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(type="sent_last_call")
|
||||
return e.expiration.expires 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 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="approved_ballot")
|
||||
return e.time 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):
|
||||
try:
|
||||
self.docalias_set.filter(name__startswith="rfc")[0].name[3:]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
# comments = models.TextField(blank=True) # unused
|
||||
|
||||
# last_modified_date = models.DateField()
|
||||
@property
|
||||
def last_modified_date(self):
|
||||
return self.time
|
||||
|
||||
# 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(docalias__relateddoc__relationship="replaces", docalias__relateddoc__related_document_set=self)
|
||||
return r[0] if r else None
|
||||
|
||||
# replaces = FKAsOneToOne('replaces', reverse=True)
|
||||
@property
|
||||
def replaces(self):
|
||||
r = InternetDraft.objects.filter(related__doc_alias__document=self, related__relationship="replaces")
|
||||
return r[0] if r else Non
|
||||
|
||||
|
||||
@property
|
||||
def replaces_set(self):
|
||||
# this is replaced_by
|
||||
return InternetDraft.objects.filter(docalias__relateddoc__relationship="replaces", docalias__relateddoc__related_document_set=self)
|
||||
|
||||
# review_by_rfc_editor = models.BooleanField()
|
||||
@property
|
||||
def review_by_rfc_editor(self): raise NotImplemented # should use tag
|
||||
|
||||
# expired_tombstone = models.BooleanField()
|
||||
@property
|
||||
def expired_tombstone(self):
|
||||
# FIXME: this is probably not perfect, what happens when we delete it again
|
||||
return self.latest_event(type="added_tombstone")
|
||||
|
||||
# idinternal = FKAsOneToOne('idinternal', reverse=True, query=models.Q(rfc_flag = 0))
|
||||
@property
|
||||
def idinternal(self):
|
||||
return self if self.iesg_state else None
|
||||
|
||||
# methods from InternetDraft
|
||||
def displayname(self):
|
||||
return self.name
|
||||
def file_tag(self):
|
||||
return "<%s-%s.txt>" % (self.name, self.revision_display())
|
||||
def group_acronym(self):
|
||||
return self.group.acronym
|
||||
def idstate(self):
|
||||
return self.docstate()
|
||||
def revision_display(self):
|
||||
r = int(self.revision)
|
||||
if self.state_id != 'active' and not self.expired_tombstone:
|
||||
r = max(r - 1, 0)
|
||||
return "%02d" % r
|
||||
def expiration(self):
|
||||
return self.revision_date + datetime.timedelta(self.DAYS_TO_EXPIRE)
|
||||
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
|
||||
|
||||
#rfc_flag = models.IntegerField(null=True)
|
||||
@property
|
||||
def rfc_flag(self):
|
||||
return self.state_id == "rfc"
|
||||
|
||||
#ballot = models.ForeignKey(BallotInfo, related_name='drafts', db_column="ballot_id")
|
||||
@property
|
||||
def ballot(self):
|
||||
return self # FIXME: raise BallotInfo.DoesNotExist?
|
||||
|
||||
#primary_flag = models.IntegerField(blank=True, null=True)
|
||||
@property
|
||||
def primary(self):
|
||||
return True # left-over from multi-ballot documents?
|
||||
|
||||
#group_flag = models.IntegerField(blank=True, default=0) # unused?
|
||||
|
||||
#token_name = models.CharField(blank=True, max_length=25)
|
||||
@property
|
||||
def token_name(self):
|
||||
e = self.latest_event()
|
||||
return e.by.person.name if e else None
|
||||
|
||||
#token_email = models.CharField(blank=True, max_length=255)
|
||||
@property
|
||||
def token_email(self):
|
||||
e = self.latest_event()
|
||||
return e.by.address if e else None
|
||||
|
||||
#note = models.TextField(blank=True) # same name
|
||||
|
||||
#status_date = models.DateField(blank=True,null=True)
|
||||
@property
|
||||
def status_date(self): raise NotImplemented # FIXME
|
||||
|
||||
#email_display = models.CharField(blank=True, max_length=50) # unused
|
||||
#agenda = models.IntegerField(null=True, blank=True)
|
||||
@property
|
||||
def agenda(self):
|
||||
return bool(self.latest_event(type="scheduled_for_telechat"))
|
||||
|
||||
#cur_state = models.ForeignKey(IDState, db_column='cur_state', related_name='docs')
|
||||
@property
|
||||
def cur_state(self):
|
||||
return self.iesg_state
|
||||
|
||||
#prev_state = models.ForeignKey(IDState, db_column='prev_state', related_name='docs_prev')
|
||||
@property
|
||||
def prev_state(self):
|
||||
ds = self.dochistory_set.all().order_by('-time')[:1]
|
||||
return ds[0].iesg_state if ds else 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()
|
||||
return e.by if e else None
|
||||
|
||||
# job_owner = models.ForeignKey(IESGLogin, db_column='job_owner', related_name='documents')
|
||||
@property
|
||||
def job_owner(self):
|
||||
return self.ad
|
||||
|
||||
#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):
|
||||
if self.group:
|
||||
return self.group.parent
|
||||
elif self.ad:
|
||||
# return area for AD
|
||||
return ad.role_set.get(type="ad", group__state="active").group
|
||||
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):
|
||||
return ", ".join(self.tags.all())
|
||||
@property
|
||||
def cur_sub_state_id(self):
|
||||
return 0
|
||||
|
||||
#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.dochistory_set.all().order_by('-time')[:1]
|
||||
return "|".join(ds[0].tags.all()) if ds else None
|
||||
@property
|
||||
def prev_sub_state_id(self):
|
||||
return 0
|
||||
|
||||
#returning_item = models.IntegerField(null=True, blank=True)
|
||||
@property
|
||||
def returning_item(self):
|
||||
e = self.latest_event(type="scheduled_for_telechat")
|
||||
return e.telechat.returning_item if e else None
|
||||
|
||||
#telechat_date = models.DateField(null=True, blank=True)
|
||||
@property
|
||||
def telechat_date(self):
|
||||
e = self.latest_event(type="scheduled_for_telechat")
|
||||
return e.telechat.telechat_date if e else None
|
||||
|
||||
#via_rfc_editor = models.IntegerField(null=True, blank=True)
|
||||
@property
|
||||
def via_rfc_editor(self):
|
||||
return bool(self.tags.filter(slug='viarfceditor'))
|
||||
|
||||
#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):
|
||||
return self.latest_event(type="resolved_to_do_not_publish")
|
||||
|
||||
#dnp_date = models.DateField(null=True, blank=True)
|
||||
@property
|
||||
def dnp_date(self):
|
||||
e = self.latest_event(type="resolved_to_do_not_publish")
|
||||
return e.time if e else None
|
||||
|
||||
#noproblem = models.IntegerField(null=True, blank=True)
|
||||
@property
|
||||
def noproblem(self):
|
||||
return self.latest_event(type="resolved_to_no_problem")
|
||||
|
||||
#resurrect_requested_by = BrokenForeignKey(IESGLogin, db_column='resurrect_requested_by', related_name='docsresurrected', null=True, blank=True)
|
||||
@property
|
||||
def resurrect_requested_by(self):
|
||||
return self.latest_event(type="requested_resurrect")
|
||||
|
||||
#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:
|
||||
return "/doc/rfc%d/" % self.rfc_number
|
||||
else:
|
||||
return "/doc/%s/" % self.name
|
||||
|
||||
def document(self):
|
||||
return self
|
||||
|
||||
def comments(self):
|
||||
return self.event_set.all().order_by('-time')
|
||||
|
||||
def ballot_set(self):
|
||||
return [self]
|
||||
def ballot_primary(self):
|
||||
return [self]
|
||||
def ballot_others(self):
|
||||
return []
|
||||
def docstate(self):
|
||||
if self.iesg_state:
|
||||
return self.iesg_state.name
|
||||
else:
|
||||
return "I-D Exists"
|
||||
def change_state(self, state, sub_state):
|
||||
self.iesg_state = state
|
||||
|
||||
|
||||
# things from BallotInfo
|
||||
#active = models.BooleanField()
|
||||
@property
|
||||
def active(self):
|
||||
return self.iesg_state and self.iesg_state.name in ['In Last Call', 'Waiting for Writeup', 'Waiting for AD Go-Ahead', 'IESG Evaluation', 'IESG Evaluation - Defer']
|
||||
|
||||
#an_sent = models.BooleanField()
|
||||
@property
|
||||
def an_sent(self):
|
||||
return bool(self.latest_event(type="approved_ballot"))
|
||||
|
||||
#an_sent_date = models.DateField(null=True, blank=True)
|
||||
@property
|
||||
def an_sent_date(self):
|
||||
e = self.latest_event(type="approved_ballot")
|
||||
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="approved_ballot")
|
||||
return e.by if e else None
|
||||
|
||||
#defer = models.BooleanField()
|
||||
@property
|
||||
def defer(self):
|
||||
return bool(self.latest_event(type="deferred_ballot"))
|
||||
|
||||
#defer_by = models.ForeignKey(IESGLogin, db_column='defer_by', related_name='deferred', null=True)
|
||||
@property
|
||||
def defer_by(self):
|
||||
e = self.latest_event(type="deferred_ballot")
|
||||
return e.by if e else None
|
||||
|
||||
#defer_date = models.DateField(null=True, blank=True)
|
||||
@property
|
||||
def defer_date(self):
|
||||
e = self.latest_event(type="deferred_ballot")
|
||||
return e.time if e else None
|
||||
|
||||
#approval_text = models.TextField(blank=True)
|
||||
@property
|
||||
def approval_text(self):
|
||||
e = self.latest_event(type="changed_ballot_approval_text")
|
||||
return e.text.content if e else ""
|
||||
|
||||
#last_call_text = models.TextField(blank=True)
|
||||
@property
|
||||
def last_call_text(self):
|
||||
e = self.latest_event(type="changed_last_call_text")
|
||||
return e.text.content if e else ""
|
||||
|
||||
#ballot_writeup = models.TextField(blank=True)
|
||||
@property
|
||||
def ballot_writeup(self):
|
||||
e = self.latest_event(type="changed_ballot_writeup_text")
|
||||
return e.text.content 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"""
|
||||
active_ads = Email.objects.filter(role__name="ad", role__group__state="active")
|
||||
|
||||
res = []
|
||||
def add(ad, pos):
|
||||
# FIXME: ad and pos don't emulate old interface
|
||||
res.append(dict(ad=ad, pos=pos))
|
||||
|
||||
found = set()
|
||||
for pos in self.event_set.filter(type="changed_ballot_position", ballotposition__ad__in=active_ads).select_related('ad').order_by("-time"):
|
||||
if not pos.ballotposition.ad in found:
|
||||
found.add(pos.ballotposition.ad)
|
||||
add(pos.ballotposition.ad, pos)
|
||||
|
||||
for ad in active_ads:
|
||||
if ad not in found:
|
||||
add(ad, None)
|
||||
|
||||
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 = (len(ads) - recuse) * 2 / 3
|
||||
else:
|
||||
# Info and experimental only need one position.
|
||||
needed = 1
|
||||
have = yes + noobj + discuss
|
||||
if have < needed:
|
||||
more = needed - have
|
||||
if more == 1:
|
||||
answer += "Needs %d more position. " % more
|
||||
else:
|
||||
answer += "Needs %d more positions. " % more
|
||||
else:
|
||||
answer += "Has enough positions to pass"
|
||||
if discuss:
|
||||
answer += " once DISCUSSes are resolved"
|
||||
answer += ". "
|
||||
|
||||
return answer.rstrip()
|
||||
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
0
redesign/group/__init__.py
Normal file
0
redesign/group/__init__.py
Normal file
13
redesign/group/admin.py
Normal file
13
redesign/group/admin.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
from django.contrib import admin
|
||||
from models import *
|
||||
|
||||
class GroupAdmin(admin.ModelAdmin):
|
||||
list_display = ["acronym", "name", "type"]
|
||||
search_fields = ["name"]
|
||||
ordering = ["name"]
|
||||
raw_id_fields = ["charter"]
|
||||
|
||||
admin.site.register(Group, GroupAdmin)
|
||||
admin.site.register(GroupHistory)
|
||||
|
||||
admin.site.register(Role)
|
53
redesign/group/models.py
Normal file
53
redesign/group/models.py
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
from django.db import models
|
||||
from redesign.name.models import *
|
||||
from redesign.person.models import Email
|
||||
|
||||
class Group(models.Model):
|
||||
name = models.CharField(max_length=80)
|
||||
acronym = models.CharField(max_length=16)
|
||||
state = models.ForeignKey(GroupStateName, null=True)
|
||||
type = models.ForeignKey(GroupTypeName, null=True)
|
||||
charter = models.OneToOneField('doc.Document', related_name='chartered_group', blank=True, null=True)
|
||||
parent = models.ForeignKey('Group', blank=True, null=True)
|
||||
list_email = models.CharField(max_length=64, blank=True)
|
||||
list_pages = models.CharField(max_length=64, blank=True)
|
||||
comments = models.TextField(blank=True)
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
# This will actually be extended from Groups, but that requires Django 1.0
|
||||
# This will record the new state and the date it occurred for any changes
|
||||
# to a group. The group acronym must be unique and is the invariant used
|
||||
# to select group history from this table.
|
||||
class GroupHistory(models.Model):
|
||||
group = models.ForeignKey('Group', related_name='group_history')
|
||||
# Event related
|
||||
time = models.DateTimeField()
|
||||
comment = models.TextField()
|
||||
who = models.ForeignKey(Email, related_name='group_changes')
|
||||
# inherited from Group:
|
||||
name = models.CharField(max_length=64)
|
||||
acronym = models.CharField(max_length=16)
|
||||
state = models.ForeignKey(GroupStateName)
|
||||
type = models.ForeignKey(GroupTypeName)
|
||||
charter = models.ForeignKey('doc.Document', related_name='chartered_group_history')
|
||||
parent = models.ForeignKey('Group')
|
||||
chairs = models.ManyToManyField(Email, related_name='chaired_groups_history')
|
||||
list_email = models.CharField(max_length=64)
|
||||
list_pages = models.CharField(max_length=64)
|
||||
comments = models.TextField(blank=True)
|
||||
def __unicode__(self):
|
||||
return self.group.name
|
||||
class Meta:
|
||||
verbose_name_plural="Doc histories"
|
||||
|
||||
class Role(models.Model):
|
||||
name = models.ForeignKey(RoleName)
|
||||
group = models.ForeignKey(Group)
|
||||
email = models.ForeignKey(Email)
|
||||
auth = models.CharField(max_length=255, blank=True)
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
46
redesign/group/proxy.py
Normal file
46
redesign/group/proxy.py
Normal file
|
@ -0,0 +1,46 @@
|
|||
from models import *
|
||||
|
||||
class Acronym(Group):
|
||||
def __init__(self, base):
|
||||
for f in base._meta.fields:
|
||||
setattr(self, f.name, getattr(base, f.name))
|
||||
|
||||
#acronym_id = models.AutoField(primary_key=True)
|
||||
@property
|
||||
def acronym_id(self):
|
||||
raise NotImplemented
|
||||
#acronym = models.CharField(max_length=12) # same name
|
||||
#name = models.CharField(max_length=100) # same name
|
||||
#name_key = models.CharField(max_length=50, editable=False)
|
||||
@property
|
||||
def name_key(self):
|
||||
return self.name.upper()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
||||
|
||||
class Area(Group):
|
||||
ACTIVE=1
|
||||
#area_acronym = models.OneToOneField(Acronym, primary_key=True)
|
||||
@property
|
||||
def area_acronym(self):
|
||||
return Acronym(self)
|
||||
|
||||
#start_date = models.DateField(auto_now_add=True)
|
||||
#concluded_date = models.DateField(null=True, blank=True)
|
||||
#status = models.ForeignKey(AreaStatus)
|
||||
#comments = models.TextField(blank=True)
|
||||
#last_modified_date = models.DateField(auto_now=True)
|
||||
#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(group_type=1,status=IETFWG.ACTIVE,areagroup__area=self).order_by('group_acronym__acronym')
|
||||
|
||||
@staticmethod
|
||||
def active_areas():
|
||||
return Area.objects.filter(type="area", state="active")
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
320
redesign/import-document-state.py
Executable file
320
redesign/import-document-state.py
Executable file
|
@ -0,0 +1,320 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import sys, os, re, datetime
|
||||
|
||||
basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
sys.path = [ basedir ] + sys.path
|
||||
|
||||
from ietf import settings
|
||||
settings.USE_DB_REDESIGN_PROXY_CLASSES = False
|
||||
|
||||
from django.core import management
|
||||
management.setup_environ(settings)
|
||||
|
||||
from redesign.doc.models import *
|
||||
from redesign.group.models import *
|
||||
from redesign.name.models import *
|
||||
from ietf.idtracker.models import InternetDraft, IESGLogin, DocumentComment
|
||||
|
||||
# assumptions:
|
||||
# - groups have been imported
|
||||
# - iesglogin emails have been imported
|
||||
|
||||
# FIXME: what about RFCs
|
||||
|
||||
def name(name_class, slug, name, desc=""):
|
||||
# create if it doesn't exist, set name
|
||||
obj, _ = name_class.objects.get_or_create(slug=slug)
|
||||
obj.name = name
|
||||
obj.desc = desc
|
||||
obj.save()
|
||||
return obj
|
||||
|
||||
type_draft = name(DocTypeName, "draft", "Draft")
|
||||
stream_ietf = name(DocStreamName, "ietf", "IETF")
|
||||
|
||||
intended_status_mapping = {
|
||||
"BCP": name(IntendedStatusName, "bcp", "Best Current Practice"),
|
||||
"Draft Standard": name(IntendedStatusName, "ds", name="Draft Standard"),
|
||||
"Experimental": name(IntendedStatusName, "exp", name="Experimental"),
|
||||
"Historic": name(IntendedStatusName, "hist", name="Historic"),
|
||||
"Informational": name(IntendedStatusName, "inf", name="Informational"),
|
||||
"Proposed Standard": name(IntendedStatusName, "ps", name="Proposed Standard"),
|
||||
"Standard": name(IntendedStatusName, "std", name="Standard"),
|
||||
"None": None,
|
||||
"Request": None, # FIXME: correct? from idrfc_wrapper.py
|
||||
}
|
||||
|
||||
status_mapping = {
|
||||
'Active': name(DocStateName, "active", "Active"),
|
||||
'Expired': name(DocStateName, "expired", "Expired"),
|
||||
'RFC': name(DocStateName, "rfc", "RFC"),
|
||||
'Withdrawn by Submitter': name(DocStateName, "auth-rm", "Withdrawn by Submitter"),
|
||||
'Replaced': name(DocStateName, "repl", "Replaced"),
|
||||
'Withdrawn by IETF': name(DocStateName, "ietf-rm", "Withdrawn by IETF"),
|
||||
}
|
||||
|
||||
iesg_state_mapping = {
|
||||
'RFC Published': name(IesgDocStateName, "pub", "RFC Published", 'The ID has been published as an RFC.'),
|
||||
'Dead': name(IesgDocStateName, "dead", "Dead", 'Document is "dead" and is no longer being tracked. (E.g., it has been replaced by another document with a different name, it has been withdrawn, etc.)'),
|
||||
'Approved-announcement to be sent': name(IesgDocStateName, "approved", "Approved-announcement to be sent", 'The IESG has approved the document for publication, but the Secretariat has not yet sent out on official approval message.'),
|
||||
'Approved-announcement sent': name(IesgDocStateName, "ann", "Approved-announcement sent", 'The IESG has approved the document for publication, and the Secretariat has sent out the official approval message to the RFC editor.'),
|
||||
'AD is watching': name(IesgDocStateName, "watching", "AD is watching", 'An AD is aware of the document and has chosen to place the document in a separate state in order to keep a closer eye on it (for whatever reason). Documents in this state are still not being actively tracked in the sense that no formal request has been made to publish or advance the document. The sole difference between this state and "I-D Exists" is that an AD has chosen to put it in a separate state, to make it easier to keep track of (for the AD\'s own reasons).'),
|
||||
'IESG Evaluation': name(IesgDocStateName, "iesg-eva", "IESG Evaluation", 'The document is now (finally!) being formally reviewed by the entire IESG. Documents are discussed in email or during a bi-weekly IESG telechat. In this phase, each AD reviews the document and airs any issues they may have. Unresolvable issues are documented as "discuss" comments that can be forwarded to the authors/WG. See the description of substates for additional details about the current state of the IESG discussion.'),
|
||||
'AD Evaluation': name(IesgDocStateName, "ad-eval", "AD Evaluation", 'A specific AD (e.g., the Area Advisor for the WG) has begun reviewing the document to verify that it is ready for advancement. The shepherding AD is responsible for doing any necessary review before starting an IETF Last Call or sending the document directly to the IESG as a whole.'),
|
||||
'Last Call Requested': name(IesgDocStateName, "lc-req", "Last Call requested", 'The AD has requested that the Secretariat start an IETF Last Call, but the the actual Last Call message has not been sent yet.'),
|
||||
'In Last Call': name(IesgDocStateName, "lc", "In Last Call", 'The document is currently waiting for IETF Last Call to complete. Last Calls for WG documents typically last 2 weeks, those for individual submissions last 4 weeks.'),
|
||||
'Publication Requested': name(IesgDocStateName, "pub-req", "Publication Requested", 'A formal request has been made to advance/publish the document, following the procedures in Section 7.5 of RFC 2418. The request could be from a WG chair, from an individual through the RFC Editor, etc. (The Secretariat (iesg-secretary@ietf.org) is copied on these requests to ensure that the request makes it into the ID tracker.) A document in this state has not (yet) been reviewed by an AD nor has any official action been taken on it yet (other than to note that its publication has been requested.'),
|
||||
'RFC Ed Queue': name(IesgDocStateName, "rfcqueue", "RFC Ed Queue", 'The document is in the RFC editor Queue (as confirmed by http://www.rfc-editor.org/queue.html).'),
|
||||
'IESG Evaluation - Defer': name(IesgDocStateName, "defer", "IESG Evaluation - Defer", 'During a telechat, one or more ADs requested an additional 2 weeks to review the document. A defer is designed to be an exception mechanism, and can only be invoked once, the first time the document comes up for discussion during a telechat.'),
|
||||
'Waiting for Writeup': name(IesgDocStateName, "writeupw", "Waiting for Writeup", 'Before a standards-track or BCP document is formally considered by the entire IESG, the AD must write up a protocol action. The protocol action is included in the approval message that the Secretariat sends out when the document is approved for publication as an RFC.'),
|
||||
'Waiting for AD Go-Ahead': name(IesgDocStateName, "goaheadw", "Waiting for AD Go-Ahead", 'As a result of the IETF Last Call, comments may need to be responded to and a revision of the ID may be needed as well. The AD is responsible for verifying that all Last Call comments have been adequately addressed and that the (possibly revised) document is in the ID directory and ready for consideration by the IESG as a whole.'),
|
||||
'Expert Review': name(IesgDocStateName, "review-e", "Expert Review", 'An AD sometimes asks for an external review by an outside party as part of evaluating whether a document is ready for advancement. MIBs, for example, are reviewed by the "MIB doctors". Other types of reviews may also be requested (e.g., security, operations impact, etc.). Documents stay in this state until the review is complete and possibly until the issues raised in the review are addressed. See the "note" field for specific details on the nature of the review.'),
|
||||
'DNP-waiting for AD note': name(IesgDocStateName, "nopubadw", "DNP-waiting for AD note", 'Do Not Publish: The IESG recommends against publishing the document, but the writeup explaining its reasoning has not yet been produced. DNPs apply primarily to individual submissions received through the RFC editor. See the "note" field for more details on who has the action item.'),
|
||||
'DNP-announcement to be sent': name(IesgDocStateName, "nopubanw", "DNP-announcement to be sent", 'The IESG recommends against publishing the document, the writeup explaining its reasoning has been produced, but the Secretariat has not yet sent out the official "do not publish" recommendation message.'),
|
||||
None: None, # FIXME: consider introducing the ID-exists state
|
||||
}
|
||||
|
||||
ballot_position_mapping = {
|
||||
'No Objection': name(BallotPositionName, 'noobj', 'No Objection'),
|
||||
'Yes': name(BallotPositionName, 'yes', 'Yes'),
|
||||
'Abstain': name(BallotPositionName, 'abstain', 'Abstain'),
|
||||
'Discuss': name(BallotPositionName, 'discuss', 'Discuss'),
|
||||
'Recuse': name(BallotPositionName, 'recuse', 'Recuse'),
|
||||
'Undefined': name(BallotPositionName, 'norecord', 'No record'),
|
||||
None: name(BallotPositionName, 'norecord', 'No record'),
|
||||
}
|
||||
|
||||
# regexps for parsing document comments
|
||||
|
||||
date_re_str = "(?P<year>[0-9][0-9][0-9][0-9])-(?P<month>[0-9][0-9])-(?P<day>[0-9][0-9])"
|
||||
def date_in_match(match):
|
||||
return datetime.date(int(match.group('year')), int(match.group('month')), int(match.group('day')))
|
||||
|
||||
re_telechat_agenda = re.compile(r"(Placed on|Removed from) agenda for telechat - %s by" % date_re_str)
|
||||
re_ballot_position = re.compile(r"\[Ballot Position Update\] (New position, (?P<position>.*), has been recorded (|for (?P<for>.*) )|Position (|for (?P<for2>.*) )has been changed to (?P<position2>.*) from .*)by (?P<by>.*)")
|
||||
re_ballot_issued = re.compile(r"Ballot has been issued by")
|
||||
|
||||
# helpers for events
|
||||
|
||||
def save_event(doc, event, comment):
|
||||
event.time = comment.datetime()
|
||||
event.by = iesg_login_to_email(comment.created_by)
|
||||
event.doc = doc
|
||||
event.desc = comment.comment_text # FIXME: consider unquoting here
|
||||
event.save()
|
||||
|
||||
def iesg_login_to_email(l):
|
||||
if not l:
|
||||
return None
|
||||
else:
|
||||
try:
|
||||
return Email.objects.get(address=l.person.email()[1])
|
||||
except Email.DoesNotExist:
|
||||
print "MISSING IESG LOGIN", l.person.email()
|
||||
return None
|
||||
|
||||
|
||||
|
||||
all_drafts = InternetDraft.objects.all().select_related()
|
||||
all_drafts = all_drafts.filter(filename="draft-arkko-townsley-coexistence")
|
||||
#all_drafts = all_drafts[all_drafts.count() - 1000:]
|
||||
|
||||
for o in all_drafts:
|
||||
try:
|
||||
d = Document.objects.get(name=o.filename)
|
||||
except Document.DoesNotExist:
|
||||
d = Document(name=o.filename)
|
||||
|
||||
d.time = o.idinternal.event_date if o.idinternal else o.revision_date
|
||||
d.type = type_draft
|
||||
d.title = o.title
|
||||
d.state = status_mapping[o.status.status]
|
||||
d.group = Group.objects.get(acronym=o.group.acronym)
|
||||
# d.tags =
|
||||
d.stream = stream_ietf
|
||||
d.wg_state = None
|
||||
d.iesg_state = iesg_state_mapping[o.idinternal.cur_state.state if o.idinternal else None]
|
||||
d.iana_state = None
|
||||
# d.rfc_state =
|
||||
d.rev = o.revision
|
||||
d.abstract = o.abstract
|
||||
d.pages = o.txt_page_count
|
||||
d.intended_std_level = intended_status_mapping[o.intended_status.intended_status]
|
||||
# d.std_level =
|
||||
# d.authors =
|
||||
# d.related =
|
||||
d.ad = iesg_login_to_email(o.idinternal.job_owner)
|
||||
d.shepherd = None
|
||||
d.notify = o.idinternal.state_change_notice_to or "" if o.idinternal else ""
|
||||
d.external_url = ""
|
||||
d.note = o.idinternal.note or "" if o.idinternal else ""
|
||||
d.internal_comments = o.comments or "" # FIXME: maybe put these somewhere else
|
||||
d.save()
|
||||
|
||||
if o.idinternal:
|
||||
# clear already imported events
|
||||
d.event_set.all().delete()
|
||||
|
||||
# extract events
|
||||
for c in o.idinternal.documentcomment_set.order_by('date', 'time', 'id'):
|
||||
# telechat agenda schedulings
|
||||
match = re_telechat_agenda.search(c.comment_text)
|
||||
if match:
|
||||
e = Telechat()
|
||||
e.type = "scheduled_for_telechat"
|
||||
e.telechat_date = date_in_match(match)
|
||||
# can't extract this from history so we just take the latest value
|
||||
e.returning_item = bool(o.idinternal.returning_item)
|
||||
save_event(d, e, c)
|
||||
|
||||
|
||||
# ballot issued
|
||||
match = re_ballot_issued.search(c.comment_text)
|
||||
if match:
|
||||
e = Text()
|
||||
e.type = "sent_ballot_announcement"
|
||||
save_event(d, e, c)
|
||||
|
||||
# when you issue a ballot, you also vote yes; add that vote
|
||||
e = BallotPosition()
|
||||
e.type = "changed_ballot_position"
|
||||
e.ad = iesg_login_to_email(c.created_by)
|
||||
last_pos = d.latest_event(type="changed_ballot_position", ballotposition__ad=e.ad)
|
||||
e.pos = ballot_position_mapping["Yes"]
|
||||
e.discuss = last_pos.ballotposition.discuss if last_pos else ""
|
||||
e.discuss_time = last_pos.ballotposition.discuss_time if last_pos else None
|
||||
e.comment = last_pos.ballotposition.comment if last_pos else ""
|
||||
e.comment_time = last_pos.ballotposition.comment_time if last_pos else None
|
||||
save_event(d, e, c)
|
||||
|
||||
|
||||
# ballot positions
|
||||
match = re_ballot_position.search(c.comment_text)
|
||||
if match:
|
||||
position = match.group('position') or match.group('position2')
|
||||
ad_name = match.group('for') or match.group('for2') or match.group('by') # some of the old positions don't specify who it's for, in that case assume it's "by", the person who entered the position
|
||||
ad_first, ad_last = ad_name.split(' ')
|
||||
|
||||
e = BallotPosition()
|
||||
e.type = "changed_ballot_position"
|
||||
e.ad = iesg_login_to_email(IESGLogin.objects.get(first_name=ad_first, last_name=ad_last))
|
||||
last_pos = d.latest_event(type="changed_ballot_position", ballotposition__ad=e.ad)
|
||||
e.pos = ballot_position_mapping[position]
|
||||
e.discuss = last_pos.ballotposition.discuss if last_pos else ""
|
||||
e.discuss_time = last_pos.ballotposition.discuss_time if last_pos else None
|
||||
e.comment = last_pos.ballotposition.comment if last_pos else ""
|
||||
e.comment_time = last_pos.ballotposition.comment_time if last_pos else None
|
||||
save_event(d, e, c)
|
||||
|
||||
|
||||
# ballot discusses/comments
|
||||
if c.ballot in (DocumentComment.BALLOT_DISCUSS, DocumentComment.BALLOT_COMMENT):
|
||||
e = BallotPosition()
|
||||
e.type = "changed_ballot_position"
|
||||
e.ad = iesg_login_to_email(c.created_by)
|
||||
last_pos = d.latest_event(type="changed_ballot_position", ballotposition__ad=e.ad)
|
||||
e.pos = last_pos.ballotposition.pos if last_pos else ballot_position_mapping[None]
|
||||
if c.ballot == DocumentComment.BALLOT_DISCUSS:
|
||||
e.discuss = c.comment_text
|
||||
e.discuss_time = c.datetime()
|
||||
e.comment = last_pos.ballotposition.comment if last_pos else ""
|
||||
e.comment_time = last_pos.ballotposition.comment_time if last_pos else None
|
||||
# put header into description
|
||||
c.comment_text = "[Ballot discuss]\n" + c.comment_text
|
||||
else:
|
||||
e.discuss = last_pos.ballotposition.discuss if last_pos else ""
|
||||
e.discuss_time = last_pos.ballotposition.discuss_time if last_pos else None
|
||||
e.comment = c.comment_text
|
||||
e.comment_time = c.datetime()
|
||||
# put header into description
|
||||
c.comment_text = "[Ballot comment]\n" + c.comment_text
|
||||
save_event(d, e, c)
|
||||
|
||||
|
||||
print "imported", d.name, "state", d.iesg_state
|
||||
|
||||
|
||||
|
||||
# checklist of attributes below: handled attributes are commented out
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
class CheckListInternetDraft(models.Model):
|
||||
# id_document_tag = models.AutoField(primary_key=True)
|
||||
# title = models.CharField(max_length=255, db_column='id_document_name')
|
||||
# id_document_key = models.CharField(max_length=255, editable=False)
|
||||
# group = models.ForeignKey(Acronym, db_column='group_acronym_id')
|
||||
# filename = models.CharField(max_length=255, unique=True)
|
||||
# revision = models.CharField(max_length=2)
|
||||
revision_date = models.DateField()
|
||||
file_type = models.CharField(max_length=20)
|
||||
# txt_page_count = models.IntegerField()
|
||||
local_path = models.CharField(max_length=255, blank=True, null=True)
|
||||
start_date = models.DateField()
|
||||
expiration_date = models.DateField(null=True)
|
||||
# abstract = models.TextField()
|
||||
dunn_sent_date = models.DateField(null=True, blank=True)
|
||||
extension_date = models.DateField(null=True, blank=True)
|
||||
# status = models.ForeignKey(IDStatus)
|
||||
# intended_status = models.ForeignKey(IDIntendedStatus)
|
||||
lc_sent_date = models.DateField(null=True, blank=True)
|
||||
lc_changes = models.CharField(max_length=3,null=True)
|
||||
lc_expiration_date = models.DateField(null=True, blank=True)
|
||||
b_sent_date = models.DateField(null=True, blank=True)
|
||||
b_discussion_date = models.DateField(null=True, blank=True)
|
||||
b_approve_date = models.DateField(null=True, blank=True)
|
||||
wgreturn_date = models.DateField(null=True, blank=True)
|
||||
rfc_number = models.IntegerField(null=True, blank=True, db_index=True)
|
||||
# comments = models.TextField(blank=True,null=True)
|
||||
last_modified_date = models.DateField()
|
||||
replaced_by = BrokenForeignKey('self', db_column='replaced_by', blank=True, null=True, related_name='replaces_set')
|
||||
replaces = FKAsOneToOne('replaces', reverse=True)
|
||||
review_by_rfc_editor = models.BooleanField()
|
||||
expired_tombstone = models.BooleanField()
|
||||
# idinternal = FKAsOneToOne('idinternal', reverse=True, query=models.Q(rfc_flag = 0))
|
||||
|
||||
class CheckListIDInternal(models.Model):
|
||||
# draft = models.ForeignKey(InternetDraft, primary_key=True, unique=True, db_column='id_document_tag')
|
||||
rfc_flag = models.IntegerField(null=True)
|
||||
ballot = models.ForeignKey('BallotInfo', related_name='drafts', db_column="ballot_id")
|
||||
primary_flag = models.IntegerField(blank=True, null=True)
|
||||
group_flag = models.IntegerField(blank=True, default=0)
|
||||
token_name = models.CharField(blank=True, max_length=25)
|
||||
token_email = models.CharField(blank=True, max_length=255)
|
||||
# note = models.TextField(blank=True)
|
||||
status_date = models.DateField(blank=True,null=True)
|
||||
email_display = models.CharField(blank=True, max_length=50)
|
||||
agenda = models.IntegerField(null=True, blank=True)
|
||||
# cur_state = models.ForeignKey(IDState, db_column='cur_state', related_name='docs')
|
||||
prev_state = models.ForeignKey(IDState, db_column='prev_state', related_name='docs_prev')
|
||||
assigned_to = models.CharField(blank=True, max_length=25)
|
||||
mark_by = models.ForeignKey('IESGLogin', db_column='mark_by', related_name='marked')
|
||||
# job_owner = models.ForeignKey(IESGLogin, db_column='job_owner', related_name='documents')
|
||||
event_date = models.DateField(null=True)
|
||||
area_acronym = models.ForeignKey('Area')
|
||||
cur_sub_state = BrokenForeignKey('IDSubState', related_name='docs', null=True, blank=True, null_values=(0, -1))
|
||||
prev_sub_state = BrokenForeignKey('IDSubState', related_name='docs_prev', null=True, blank=True, null_values=(0, -1))
|
||||
# returning_item = models.IntegerField(null=True, blank=True)
|
||||
# telechat_date = models.DateField(null=True, blank=True)
|
||||
via_rfc_editor = models.IntegerField(null=True, blank=True)
|
||||
# state_change_notice_to = models.CharField(blank=True, max_length=255)
|
||||
dnp = models.IntegerField(null=True, blank=True)
|
||||
dnp_date = models.DateField(null=True, blank=True)
|
||||
noproblem = models.IntegerField(null=True, blank=True)
|
||||
resurrect_requested_by = BrokenForeignKey('IESGLogin', db_column='resurrect_requested_by', related_name='docsresurrected', null=True, blank=True)
|
||||
approved_in_minute = models.IntegerField(null=True, blank=True)
|
||||
|
||||
class CheckListBallotInfo(models.Model):
|
||||
ballot = models.AutoField(primary_key=True, db_column='ballot_id')
|
||||
active = models.BooleanField()
|
||||
an_sent = models.BooleanField()
|
||||
an_sent_date = models.DateField(null=True, blank=True)
|
||||
an_sent_by = models.ForeignKey('IESGLogin', db_column='an_sent_by', related_name='ansent', null=True)
|
||||
defer = models.BooleanField(blank=True)
|
||||
defer_by = models.ForeignKey('IESGLogin', db_column='defer_by', related_name='deferred', null=True)
|
||||
defer_date = models.DateField(null=True, blank=True)
|
||||
approval_text = models.TextField(blank=True)
|
||||
last_call_text = models.TextField(blank=True)
|
||||
ballot_writeup = models.TextField(blank=True)
|
||||
ballot_issued = models.IntegerField(null=True, blank=True)
|
82
redesign/import-groups.py
Executable file
82
redesign/import-groups.py
Executable file
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
import sys, os
|
||||
|
||||
basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||
sys.path = [ basedir ] + sys.path
|
||||
|
||||
from ietf import settings
|
||||
from django.core import management
|
||||
management.setup_environ(settings)
|
||||
|
||||
|
||||
from redesign.group.models import *
|
||||
from redesign.name.models import *
|
||||
from ietf.idtracker.models import AreaGroup, IETFWG, Area, AreaGroup, Acronym, AreaWGURL, IRTF
|
||||
|
||||
# Group replaces IETFWG, Area, AreaGroup, Acronym, IRTF
|
||||
|
||||
# make sure we got the names
|
||||
GroupStateName.objects.get_or_create(slug="bof", name="BOF") # is this a state?
|
||||
GroupStateName.objects.get_or_create(slug="proposed", name="Proposed")
|
||||
GroupStateName.objects.get_or_create(slug="active", name="Active")
|
||||
GroupStateName.objects.get_or_create(slug="dormant", name="Dormant")
|
||||
GroupStateName.objects.get_or_create(slug="conclude", name="Concluded")
|
||||
GroupStateName.objects.get_or_create(slug="unknown", name="Unknown")
|
||||
|
||||
GroupTypeName.objects.get_or_create(slug="ietf", name="IETF")
|
||||
GroupTypeName.objects.get_or_create(slug="area", name="Area")
|
||||
GroupTypeName.objects.get_or_create(slug="wg", name="WG")
|
||||
GroupTypeName.objects.get_or_create(slug="rg", name="RG")
|
||||
GroupTypeName.objects.get_or_create(slug="team", name="Team")
|
||||
|
||||
# FIXME: what about AG (area group?)?
|
||||
|
||||
|
||||
# Area
|
||||
for o in Area.objects.all():
|
||||
group, _ = Group.objects.get_or_create(acronym=o.area_acronym.acronym)
|
||||
group.name = o.area_acronym.name
|
||||
if o.status.status == "Active":
|
||||
s = GroupStateName.objects.get(slug="active")
|
||||
elif o.status.status == "Concluded":
|
||||
s = GroupStateName.objects.get(slug="conclude")
|
||||
elif o.status.status == "Unknown":
|
||||
s = GroupStateName.objects.get(slug="unknown")
|
||||
group.state = s
|
||||
group.type = GroupTypeName.objects.get(slug="area")
|
||||
|
||||
# FIXME: missing fields from old: concluded_date, comments, last_modified_date, extra_email_addresses
|
||||
|
||||
group.save()
|
||||
|
||||
# IETFWG, AreaGroup
|
||||
for o in IETFWG.objects.all():
|
||||
group, _ = Group.objects.get_or_create(acronym=o.group_acronym.acronym)
|
||||
group.name = o.group_acronym.name
|
||||
# state
|
||||
if o.group_type.type == "BOF":
|
||||
s = GroupStateName.objects.get(slug="bof")
|
||||
elif o.group_type.type == "PWG": # FIXME: right?
|
||||
s = GroupStateName.objects.get(slug="proposed")
|
||||
elif o.status.status == "Active":
|
||||
s = GroupStateName.objects.get(slug="active")
|
||||
elif o.status.status == "Dormant":
|
||||
s = GroupStateName.objects.get(slug="dormant")
|
||||
elif o.status.status == "Concluded":
|
||||
s = GroupStateName.objects.get(slug="conclude")
|
||||
group.state = s
|
||||
# type
|
||||
if o.group_type.type == "team":
|
||||
group.type = GroupTypeName.objects.get(slug="team")
|
||||
else:
|
||||
group.type = GroupTypeName.objects.get(slug="wg")
|
||||
|
||||
if o.area:
|
||||
print "no area for", group.acronym, group.name, group.type, group.state
|
||||
group.parent = Group.objects.get(acronym=o.area.area.area_acronym.acronym)
|
||||
|
||||
# FIXME: missing fields from old: proposed_date, start_date, dormant_date, concluded_date, meeting_scheduled, email_address, email_subscribe, email_keyword, email_archive, comments, last_modified_date, meeting_scheduled_old
|
||||
|
||||
group.save()
|
||||
|
0
redesign/name/__init__.py
Normal file
0
redesign/name/__init__.py
Normal file
22
redesign/name/admin.py
Normal file
22
redesign/name/admin.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
from django.contrib import admin
|
||||
from models import *
|
||||
|
||||
class NameAdmin(admin.ModelAdmin):
|
||||
list_display = ["slug", "name", "desc", "used"]
|
||||
prepopulate_from = { "slug": ("name",) }
|
||||
|
||||
admin.site.register(GroupTypeName, NameAdmin)
|
||||
admin.site.register(GroupStateName, NameAdmin)
|
||||
admin.site.register(RoleName, NameAdmin)
|
||||
admin.site.register(DocStreamName, NameAdmin)
|
||||
admin.site.register(DocStateName, NameAdmin)
|
||||
admin.site.register(DocRelationshipName, NameAdmin)
|
||||
admin.site.register(WgDocStateName, NameAdmin)
|
||||
admin.site.register(IesgDocStateName, NameAdmin)
|
||||
admin.site.register(IanaDocStateName, NameAdmin)
|
||||
admin.site.register(RfcDocStateName, NameAdmin)
|
||||
admin.site.register(DocTypeName, NameAdmin)
|
||||
admin.site.register(DocInfoTagName, NameAdmin)
|
||||
admin.site.register(IntendedStatusName, NameAdmin)
|
||||
admin.site.register(StdStatusName, NameAdmin)
|
||||
admin.site.register(BallotPositionName, NameAdmin)
|
57
redesign/name/models.py
Normal file
57
redesign/name/models.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
from django.db import models
|
||||
|
||||
class NameModel(models.Model):
|
||||
slug = models.CharField(max_length=8, primary_key=True)
|
||||
name = models.CharField(max_length=32)
|
||||
desc = models.TextField(blank=True)
|
||||
used = models.BooleanField(default=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
class GroupStateName(NameModel):
|
||||
"""BOF, Proposed, Active, Dormant, Concluded"""
|
||||
class GroupTypeName(NameModel):
|
||||
"""IETF, Area, WG, RG, Team, etc."""
|
||||
class RoleName(NameModel):
|
||||
"""AD, Chair"""
|
||||
class DocStreamName(NameModel):
|
||||
"""IETF, IAB, IRTF, Independent Submission"""
|
||||
class DocStateName(NameModel):
|
||||
"""Active, Expired, RFC, Replaced, Withdrawn"""
|
||||
class DocRelationshipName(NameModel):
|
||||
"""Updates, Replaces, Obsoletes, Reviews, ... The relationship is
|
||||
always recorded in one direction.
|
||||
"""
|
||||
class WgDocStateName(NameModel):
|
||||
"""Not, Candidate, Active, Parked, LastCall, WriteUp, Submitted,
|
||||
Dead"""
|
||||
class IesgDocStateName(NameModel):
|
||||
"""Pub Request, Ad Eval, Expert Review, Last Call Requested, In
|
||||
Last Call, Waiting for Writeup, Waiting for AD Go-Ahead, IESG
|
||||
Evaluation, Deferred, Approved, Announcement Sent, Do Not Publish,
|
||||
Ad is watching, Dead """
|
||||
class IanaDocStateName(NameModel):
|
||||
""" """
|
||||
class RfcDocStateName(NameModel):
|
||||
"""Missref, Edit, RFC-Editor, Auth48, Auth, Published; ISR,
|
||||
ISR-Auth, ISR-Timeout;"""
|
||||
class DocTypeName(NameModel):
|
||||
"""Draft, Agenda, Minutes, Charter, Discuss, Guideline, Email,
|
||||
Review, Issue, Wiki"""
|
||||
class DocInfoTagName(NameModel):
|
||||
"""Waiting for Reference, IANA Coordination, Revised ID Needed,
|
||||
External Party, AD Followup, Point Raised - Writeup Needed"""
|
||||
class StdStatusName(NameModel):
|
||||
"""Proposed Standard, Draft Standard, Standard, Experimental,
|
||||
Informational, Best Current Practice, Historic, ..."""
|
||||
class IntendedStatusName(NameModel):
|
||||
"""Standards Track, Experimental, Informational, Best Current
|
||||
Practice, Historic, ..."""
|
||||
class BallotPositionName(NameModel):
|
||||
""" Yes, NoObjection, Abstain, Discuss, Recuse """
|
20
redesign/name/proxy.py
Normal file
20
redesign/name/proxy.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
from redesign.proxy_utils import TranslatingManager
|
||||
from models import *
|
||||
|
||||
class IDStatus(DocStateName):
|
||||
def __init__(self, base):
|
||||
for f in base._meta.fields:
|
||||
setattr(self, f.name, getattr(base, f.name))
|
||||
|
||||
#status_id = models.AutoField(primary_key=True)
|
||||
|
||||
#status = models.CharField(max_length=25, db_column='status_value')
|
||||
@property
|
||||
def status(self):
|
||||
return self.name
|
||||
|
||||
def __unicode__(self):
|
||||
return super(self.__class__, self).__unicode__()
|
||||
|
||||
class Meta:
|
||||
proxy = True
|
0
redesign/person/__init__.py
Normal file
0
redesign/person/__init__.py
Normal file
28
redesign/person/admin.py
Normal file
28
redesign/person/admin.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
from django.contrib import admin
|
||||
from models import *
|
||||
|
||||
class EmailAdmin(admin.ModelAdmin):
|
||||
list_display = ["address", "person", "time", "active", ]
|
||||
raw_id_fields = ["person", ]
|
||||
search_fields = ["address", "person__name", ]
|
||||
admin.site.register(Email, EmailAdmin)
|
||||
|
||||
class EmailInline(admin.TabularInline):
|
||||
model = Email
|
||||
|
||||
class AliasAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "person", ]
|
||||
search_fields = ["name",]
|
||||
raw_id_fields = ["person"]
|
||||
admin.site.register(Alias, AliasAdmin)
|
||||
|
||||
class AliasInline(admin.StackedInline):
|
||||
model = Alias
|
||||
|
||||
class PersonAdmin(admin.ModelAdmin):
|
||||
list_display = ["name", "short", "time", ]
|
||||
search_fields = ["name", "ascii"]
|
||||
inlines = [ EmailInline, AliasInline, ]
|
||||
# actions = None
|
||||
admin.site.register(Person, PersonAdmin)
|
||||
|
67
redesign/person/models.py
Normal file
67
redesign/person/models.py
Normal file
|
@ -0,0 +1,67 @@
|
|||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
from django.db import models
|
||||
|
||||
class Person(models.Model):
|
||||
time = models.DateTimeField(auto_now_add=True) # When this Person record entered the system
|
||||
name = models.CharField(max_length=255) # The normal unicode form of the name. This must be
|
||||
# set to the same value as the ascii-form if equal.
|
||||
ascii = models.CharField(max_length=255) # The normal ascii-form of the name.
|
||||
ascii_short = models.CharField(max_length=32, null=True, blank=True) # The short ascii-form of the name. Also in alias table if non-null
|
||||
address = models.TextField(max_length=255, blank=True)
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
def _parts(self, name):
|
||||
prefix, first, middle, last, suffix = "", "", "", "", ""
|
||||
parts = name.split()
|
||||
if parts[0] in ["Mr", "Mr.", "Mrs", "Mrs.", "Ms", "Ms.", "Miss", "Dr.", "Doctor", "Prof", "Prof.", "Professor", "Sir", "Lady", "Dame", ]:
|
||||
prefix = parts[0];
|
||||
parts = parts[1:]
|
||||
if len(parts) > 2:
|
||||
if parts[-1] in ["Jr", "Jr.", "II", "2nd", "III", "3rd", ]:
|
||||
suffix = parts[-1]
|
||||
parts = parts[:-1]
|
||||
if len(parts) > 2:
|
||||
first = parts[0]
|
||||
last = parts[-1]
|
||||
middle = " ".join(parts[1:-1])
|
||||
elif len(parts) == 2:
|
||||
first, last = parts
|
||||
else:
|
||||
last = parts[0]
|
||||
return prefix, first, middle, last, suffix
|
||||
def name_parts(self):
|
||||
return self._parts(self.name)
|
||||
def ascii_parts(self):
|
||||
return self._parts(self.ascii)
|
||||
def short(self):
|
||||
if self.ascii_short:
|
||||
return self.ascii_short
|
||||
else:
|
||||
prefix, first, middle, last, suffix = self.ascii_parts()
|
||||
return (first and first[0]+"." or "")+(middle or "")+" "+last+(suffix and " "+suffix or "")
|
||||
|
||||
class Alias(models.Model):
|
||||
"""This is used for alternative forms of a name. This is the
|
||||
primary lookup point for names, and should always contain the
|
||||
unicode form (and ascii form, if different) of a name which is
|
||||
recorded in the Person record.
|
||||
"""
|
||||
person = models.ForeignKey(Person)
|
||||
name = models.CharField(max_length=255)
|
||||
def __unicode__(self):
|
||||
return self.name
|
||||
class Meta:
|
||||
verbose_name_plural = "Aliases"
|
||||
|
||||
class Email(models.Model):
|
||||
address = models.CharField(max_length=64, primary_key=True)
|
||||
person = models.ForeignKey(Person, null=True)
|
||||
time = models.DateTimeField(auto_now_add=True)
|
||||
active = models.BooleanField(default=True) # Old email addresses are *not* purged, as history
|
||||
# information points to persons through these
|
||||
def __unicode__(self):
|
||||
return self.address
|
||||
|
||||
def get_name(self):
|
||||
return self.person.name if self.person else self.address
|
0
redesign/person/proxy.py
Normal file
0
redesign/person/proxy.py
Normal file
215
redesign/proxy_utils.py
Normal file
215
redesign/proxy_utils.py
Normal file
|
@ -0,0 +1,215 @@
|
|||
from django.db.models.manager import Manager
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
class TranslatingQuerySet(QuerySet):
|
||||
def translated_kwargs(self, kwargs):
|
||||
trans = self.translated_attrs
|
||||
return dict((trans[k], v) if k in trans else (k, v) for k, v in kwargs.iteritems())
|
||||
|
||||
# overridden methods
|
||||
def _clone(self, *args, **kwargs):
|
||||
c = super(self.__class__, self)._clone(*args, **kwargs)
|
||||
c.translated_attrs = self.translated_attrs
|
||||
return c
|
||||
|
||||
def dates(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).dates(*args, **kwargs)
|
||||
|
||||
def distinct(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).distinct(*args, **kwargs)
|
||||
|
||||
def extra(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).extra(*args, **kwargs)
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).get(*args, **kwargs)
|
||||
|
||||
def get_or_create(self, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).get_or_create(**kwargs)
|
||||
|
||||
def create(self, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).create(**kwargs)
|
||||
|
||||
def filter(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).filter(*args, **kwargs)
|
||||
|
||||
def aggregate(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).aggregate(*args, **kwargs)
|
||||
|
||||
def annotate(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).annotate(*args, **kwargs)
|
||||
|
||||
def complex_filter(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).complex_filter(*args, **kwargs)
|
||||
|
||||
def exclude(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).exclude(*args, **kwargs)
|
||||
|
||||
def in_bulk(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).in_bulk(*args, **kwargs)
|
||||
|
||||
def iterator(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).iterator(*args, **kwargs)
|
||||
|
||||
def latest(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).latest(*args, **kwargs)
|
||||
|
||||
def order_by(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).order_by(*args, **kwargs)
|
||||
|
||||
def select_related(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).select_related(*args, **kwargs)
|
||||
|
||||
def values(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).values(*args, **kwargs)
|
||||
|
||||
def values_list(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).values_list(*args, **kwargs)
|
||||
|
||||
def update(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).update(*args, **kwargs)
|
||||
|
||||
def reverse(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).reverse(*args, **kwargs)
|
||||
|
||||
def defer(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).defer(*args, **kwargs)
|
||||
|
||||
def only(self, *args, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self).only(*args, **kwargs)
|
||||
|
||||
def _insert(self, values, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return insert_query(self.model, values, **kwargs)
|
||||
|
||||
def _update(self, values, **kwargs):
|
||||
kwargs = self.translated_kwargs(kwargs)
|
||||
return super(self.__class__, self)._update(values, **kwargs)
|
||||
|
||||
class TranslatingManager(Manager):
|
||||
def __init__(self, trans):
|
||||
super(self.__class__, self).__init__()
|
||||
self.translated_attrs = trans
|
||||
|
||||
def get_query_set(self):
|
||||
qs = TranslatingQuerySet(self.model)
|
||||
qs.translated_attrs = self.translated_attrs
|
||||
return qs
|
||||
|
||||
# def dates(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().dates(*args, **kwargs)
|
||||
|
||||
# def distinct(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().distinct(*args, **kwargs)
|
||||
|
||||
# def extra(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().extra(*args, **kwargs)
|
||||
|
||||
# def get(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().get(*args, **kwargs)
|
||||
|
||||
# def get_or_create(self, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().get_or_create(**kwargs)
|
||||
|
||||
# def create(self, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().create(**kwargs)
|
||||
|
||||
# def filter(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().filter(*args, **kwargs)
|
||||
|
||||
# def aggregate(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().aggregate(*args, **kwargs)
|
||||
|
||||
# def annotate(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().annotate(*args, **kwargs)
|
||||
|
||||
# def complex_filter(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().complex_filter(*args, **kwargs)
|
||||
|
||||
# def exclude(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().exclude(*args, **kwargs)
|
||||
|
||||
# def in_bulk(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().in_bulk(*args, **kwargs)
|
||||
|
||||
# def iterator(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().iterator(*args, **kwargs)
|
||||
|
||||
# def latest(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().latest(*args, **kwargs)
|
||||
|
||||
# def order_by(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().order_by(*args, **kwargs)
|
||||
|
||||
# def select_related(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().select_related(*args, **kwargs)
|
||||
|
||||
# def values(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().values(*args, **kwargs)
|
||||
|
||||
# def values_list(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().values_list(*args, **kwargs)
|
||||
|
||||
# def update(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().update(*args, **kwargs)
|
||||
|
||||
# def reverse(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().reverse(*args, **kwargs)
|
||||
|
||||
# def defer(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().defer(*args, **kwargs)
|
||||
|
||||
# def only(self, *args, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set().only(*args, **kwargs)
|
||||
|
||||
# def _insert(self, values, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return insert_query(self.model, values, **kwargs)
|
||||
|
||||
# def _update(self, values, **kwargs):
|
||||
# kwargs = self.translated_kwargs(kwargs)
|
||||
# return self.get_query_set()._update(values, **kwargs)
|
45
redesign/util.py
Normal file
45
redesign/util.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
|
||||
def name(obj):
|
||||
if hasattr(obj, 'abbrev'):
|
||||
return obj.abbrev()
|
||||
elif hasattr(obj, 'name'):
|
||||
if callable(obj.name):
|
||||
return obj.name()
|
||||
else:
|
||||
return unicode(obj.name)
|
||||
else:
|
||||
return unicode(obj)
|
||||
|
||||
def admin_link(field, label=None, ordering="", display=name, suffix=""):
|
||||
if not label:
|
||||
label = field.capitalize().replace("_", " ").strip()
|
||||
if ordering == "":
|
||||
ordering = field
|
||||
def _link(self):
|
||||
obj = self
|
||||
for attr in field.split("__"):
|
||||
obj = getattr(obj, attr)
|
||||
if callable(obj):
|
||||
obj = obj()
|
||||
if hasattr(obj, "all"):
|
||||
objects = obj.all()
|
||||
elif callable(obj):
|
||||
objects = obj()
|
||||
if not hasattr(objects, "__iter__"):
|
||||
objects = [ objects ]
|
||||
elif hasattr(obj, "__iter__"):
|
||||
objects = obj
|
||||
else:
|
||||
objects = [ obj ]
|
||||
chunks = []
|
||||
for obj in objects:
|
||||
app = obj._meta.app_label
|
||||
model = obj.__class__.__name__.lower()
|
||||
id = obj.pk
|
||||
chunks += [ u'<a href="/admin/%(app)s/%(model)s/%(id)s/%(suffix)s">%(display)s</a>' %
|
||||
{'app':app, "model": model, "id":id, "display": display(obj), "suffix":suffix, } ]
|
||||
return u", ".join(chunks)
|
||||
_link.allow_tags = True
|
||||
_link.short_description = label
|
||||
_link.admin_order_field = ordering
|
||||
return _link
|
Loading…
Reference in a new issue