def make_fields(self, **kwargs): if self.localized: langs_dict = SortedDict(django_settings.LANGUAGES) default_code = django_settings.LANGUAGE_CODE default_name = langs_dict[default_code] langs_dict.insert(0, default_code, default_name) langs = langs_dict.keys() else: langs = (django_settings.LANGUAGE_CODE,) fields = list() for lang in langs: kwargs['language_code'] = lang fields.append(self.make_field(**kwargs)) #set initial values for field in fields: lang = field.language_code field.initial = self.get_editor_value(lang) if self.localized and len(django_settings.LANGUAGES) > 1: for field in fields: lang_name = unicode(langs_dict[field.language_code]) field.label += mark_safe(' <span class="lang">(%s)</span>' % lang_name) return fields
def build_pretty_data_view(form_instance, model_object, exclude=(), append=()): ''' Taken from: http://stackoverflow.com/questions/2170228/django-iterate-over-model-instance-field-names-and-values-in-template#comment4280740_3431104 @author: Alan Viars ''' i=0 sd=SortedDict() for j in append: try: sdvalue={'label':j.capitalize(), 'fieldvalue':model_object.__getattribute__(j)} sd.insert(i, j, sdvalue) i+=1 except(AttributeError): pass for k,v in form_instance.fields.items(): sdvalue={'label':"", 'fieldvalue':""} if not exclude.__contains__(k): if v.label is not None: sdvalue = {'label':v.label, 'fieldvalue': model_object.__getattribute__(k)} else: sdvalue = {'label':k, 'fieldvalue': model_object.__getattribute__(k)} sd.insert(i, k, sdvalue) i+=1 return sd
def build_pretty_data_view(form_instance, model_object, exclude=(), append=()): ''' function for displaying beautifully data from the database ''' i=0 sd=SortedDict() for j in append: try: sdvalue={'label':j.capitalize(), 'fieldvalue':model_object.__getattribute__(j)} sd.insert(i, j, sdvalue) i+=1 except(AttributeError): pass for k,v in form_instance.fields.items(): sdvalue={'label':"", 'fieldvalue':""} if not exclude.__contains__(k): if v.label is not None: sdvalue = {'label':v.label, 'fieldvalue': model_object.__getattribute__(k)} else: sdvalue = {'label':k, 'fieldvalue': model_object.__getattribute__(k)} sd.insert(i, k, sdvalue) i+=1 return sd
class FormDialog(wx.Dialog): def __init__(self,model_instance,form=None,parent=None,exclude=(),title=None): wx.Dialog.__init__(self,parent=parent,style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) self.title = title or model_instance.__class__.__name__ if form: self.form = form else: class MForm(ModelForm): class Meta: model = model_instance.__class__ self.form = MForm() self.model = model_instance self.build_interface(exclude) # find all the fields in the parent. # stick them in the form in a grid sizer flexsizer = wx.FlexGridSizer(rows=0,cols=2,vgap=10,hgap=10) for v in self.interface.values(): flexsizer.Add(wx.StaticText(self,label=v['label']),0,wx.ALIGN_RIGHT) flexsizer.Add(v['widget'].display,1,wx.EXPAND) self.sizer = wx.BoxSizer(wx.VERTICAL) self.sizer.Add(flexsizer,1,wx.EXPAND | wx.ALL,10) szCmd = self.CreateButtonSizer(wx.OK | wx.CANCEL) if szCmd: self.sizer.Add(wx.StaticLine(self),0,wx.EXPAND) self.sizer.Add(szCmd,0,wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT,10) self.SetSizer(self.sizer) self.sizer.Fit(self) self.SetTitle(self.title) self.Layout() def build_interface(self,exclude): self.interface = SortedDict() i = 0 for k,v in self.form.fields.items(): sdvalue = {'label':"",'widget':None} if not exclude.__contains__(k): if v.label is not None: sdvalue = {'label':v.label, 'widget': widget_lookup(v)(k,self.model,self)} else: sdvalue = {'label':k, 'widget': widget_lookup(v)(k,self.model,self)} self.interface.insert(i, k, sdvalue) i+=1 def save_model(self): for w in self.interface.values(): w['widget'].get_data() self.model.save() def show_and_save(self): if self.ShowModal()==wx.ID_OK: self.save_model()
def _build_keys(self, slug, date=None, granularity="all"): """Builds redis keys used to store metrics. * ``slug`` -- a slug used for a metric, e.g. "user-signups" * ``date`` -- (optional) A ``datetime.date`` or ``datetime.datetime`` objects used to generate the time period for the metric. If omitted, the current date will be used. * ``granularity`` -- Must be one of: "all" (default), "daily", "weekly", "monthly", "yearly". Returns a list of strings. """ slug = slugify(slug) # Make sure our slugs have a consistent format if date is None: date = datetime.date.today() # we want to keep the order, here: daily, weekly, monthly, yearly patterns = SortedDict() patterns.insert(0, "daily", "m:{0}:{1}".format(slug, date.strftime("%Y-%m-%d"))) patterns.insert(1, "weekly", "m:{0}:w:{1}".format(slug, date.strftime("%U"))) patterns.insert(2, "monthly", "m:{0}:m:{1}".format(slug, date.strftime("%Y-%m"))) patterns.insert(3, "yearly", "m:{0}:y:{1}".format(slug, date.strftime("%Y"))) if granularity == "all": return patterns.values() else: return [patterns[granularity]]
def prepare_idTransaction_dict(model_instance): fields = SortedDict() for field in model_instance._meta.fields: try: if getattr(model_instance, field.name) not in (None, ''): newfieldname ="%s" % (field.name) value = getattr(model_instance, field.name) #if a datetime sting, then turn into a datetime try: value = time.strptime(value, "%Y-%m-%d %H:%M:%S") except: pass try: value = time.strptime(value, "%Y-%m-%d") except: pass try: value = time.strptime(value, "%H:%M:%S") except: pass fields[newfieldname] = str(value) except: pass fields.insert(0,'_id', model_instance.transaction_id) if fields.has_key('extra_fields'): ef =json.loads(fields['extra_fields']) fields.update(ef) del fields['extra_fields'] if fields.has_key('tags'): fields['tags'] = json.loads(fields['tags']) #print "The tx type is ", fields['transaction_type'] if fields['transaction_type']=="omhe": if fields['text'] != "": p = parseomhe() d = p.parse(fields['text']) del d['transaction_id'], d['transaction_datetime'] fields.update(d) #for k in fields.keys(): # print k,"= ",fields[k] return fields
def insertFields(self): """Add ordered fields to self.fields. """ fields = SortedDict() survey_order = self.survey_content.getSurveyOrder() # first, insert dynamic survey fields for _, property in sorted(survey_order.items()): fields.insert(len(fields) + 1, property, self.survey_fields[property]) # add comment if field has one and this isn't an edit view property = COMMENT_PREFIX + property if property in self.survey_fields: fields.insert(len(fields) + 1, property, self.survey_fields[property]) return fields
def insertFields(self): """Add ordered fields to self.fields. """ fields = SortedDict() survey_order = self.survey_content.getSurveyOrder() # first, insert dynamic survey fields for position, property in sorted(survey_order.items()): fields.insert(len(fields) + 1, property, self.survey_fields[property]) # add comment if field has one and this isn't an edit view property = COMMENT_PREFIX + property if property in self.survey_fields: fields.insert(len(fields) + 1, property, self.survey_fields[property]) return fields
def build_blog_date_dict(): entries = Entry.objects.only('title', 'publish_date', 'internal_name')\ .order_by('publish_date') entry_dict = SortedDict() for e in entries: month = '{:0>2}'.format(e.publish_date.month) month_name = e.publish_date.strftime('%B') if e.publish_date.year not in entry_dict: entry_dict.insert(0, e.publish_date.year, SortedDict( (((month, month_name), [e]),) )) elif (month, month_name) not in entry_dict[e.publish_date.year]: entry_dict[e.publish_date.year][(month, month_name)] = [e] else: entry_dict[e.publish_date.year][(month, month_name)].append(e) return entry_dict
def get_choices(self): query = """SELECT DISTINCT djl2.id, djl1.object_id, djl1.object_repr, djl1.content_type_id, COUNT(djl1.content_type_id) AS num_items FROM django_admin_log AS djl1 INNER JOIN django_admin_log AS djl2 ON djl1.id=djl2.id WHERE djl1.content_type_id = %s GROUP BY djl1.object_repr, djl1.content_type_id ORDER BY num_items DESC LIMIT 20 """ % self.ctype logdict = SortedDict() results = LogEntry.objects.raw(query) for result in sorted(results, key=self.get_num_items): name, objid, num, mod = (result.object_repr, result.object_id, result.num_items, result.content_type_id) logdict.insert(0, "%s+%s" % (mod, objid), (num, name)) return logdict
def get_choices(self): query = """SELECT DISTINCT djl2.id, djl1.object_id, djl1.object_repr, djl1.content_type_id, COUNT(djl1.content_type_id) AS num_items FROM django_admin_log AS djl1 INNER JOIN django_admin_log AS djl2 ON djl1.id=djl2.id WHERE djl1.content_type_id = %s GROUP BY djl1.object_repr, djl1.content_type_id ORDER BY num_items DESC LIMIT 20 """ % self.ctype logdict = SortedDict() results = LogEntry.objects.raw(query) for result in sorted(results, key=self.get_num_items): name, objid, num, mod = (result.object_repr, result.object_id, result.num_items, result.content_type_id) logdict.insert(0,"%s+%s" % (mod, objid), (num, name)) return logdict
def exportExcelUnix(modeladmin, request, queryset): #qsHosts = unixuserlist.objects.all() entries = list(queryset.values()) new_entries = [] for e in entries: app_str = "" try: the_host = unixhost.objects.get(pk=int(e['host_id'])) except: e['hostname'] = e['host_id'] # no "try-finally" statement in python 2.4 :-( del(e['host_id']) del(e['id']) del(e['user_id']) else: #del(e['datedisabled']) e['hostname'] = the_host.name for app in the_host.apps.values(): app_str += app['name'] + ", " app_str = app_str.strip(", ") e['apps'] = app_str del(e['host_id']) del(e['id']) del(e['user_id']) # hack to put these entries at the end of the dictionary... e = SortedDict(e) e.insert(len(e), "retired", the_host.retired) # this one is probably better just for internal use... # e.insert(len(e), "accessible", the_host.accessible) new_entries.append(e) return ExcelResponse(new_entries)
class NexusSite(object): def __init__(self, name=None, app_name='nexus'): self._registry = {} self._categories = SortedDict() if name is None: self.name = 'nexus' else: self.name = name self.app_name = app_name def register_category(self, category, label, index=None): if index: self._categories.insert(index, category, label) else: self._categories[category] = label def register(self, module, namespace=None, category=None): module = module(self, category) if not namespace: namespace = module.get_namespace() if namespace: module.app_name = module.name = namespace self._registry[namespace] = (module, category) return module def unregister(self, namespace): if namespace in self._registry: del self._registry[namespace] def get_urls(self): try: from django.conf.urls import patterns, url, include except ImportError: # Django <1.4 from django.conf.urls.defaults import patterns, url, include base_urls = patterns( '', url(r'^media/(?P<module>[^/]+)/(?P<path>.+)$', self.media, name='media'), url(r'^$', self.as_view(self.dashboard), name='index'), url(r'^login/$', self.login, name='login'), url(r'^logout/$', self.as_view(self.logout), name='logout'), ), self.app_name, self.name urlpatterns = patterns( '', url(r'^', include(base_urls)), ) for namespace, module in self.get_modules(): urlpatterns += patterns( '', url(r'^%s/' % namespace, include(module.urls)), ) return urlpatterns def urls(self): return self.get_urls() urls = property(urls) def has_permission(self, request, extra_permission=None): """ Returns True if the given HttpRequest has permission to view *at least one* page in the admin site. """ permission = request.user.is_active and request.user.is_staff if extra_permission: permission = permission and request.user.has_perm(extra_permission) return permission def as_view(self, view, cacheable=False, extra_permission=None): """ Wraps a view in authentication/caching logic extra_permission can be used to require an extra permission for this view, such as a module permission """ def inner(request, *args, **kwargs): if not self.has_permission(request, extra_permission): # show login pane return self.login(request) return view(request, *args, **kwargs) # Mark it as never_cache if not cacheable: inner = never_cache(inner) # We add csrf_protect here so this function can be used as a utility # function for any view, without having to repeat 'csrf_protect'. if not getattr(view, 'csrf_exempt', False): inner = csrf_protect(inner) return update_wrapper(inner, view) def get_context(self, request): context = csrf(request) context.update({ 'request': request, 'nexus_site': self, 'nexus_media_prefix': conf.MEDIA_PREFIX.rstrip('/'), }) return context def get_modules(self): for k, v in self._registry.iteritems(): yield k, v[0] def get_module(self, module): return self._registry[module][0] def get_categories(self): for k, v in self._categories.iteritems(): yield k, v def get_category_label(self, category): return self._categories.get(category, category.title().replace('_', ' ')) def render_to_string(self, template, context, request, current_app=None): if not current_app: current_app = self.name else: current_app = '%s:%s' % (self.name, current_app) if request: context_instance = RequestContext(request, current_app=current_app) else: context_instance = None context.update(self.get_context(request)) return render_to_string(template, context, context_instance=context_instance) def render_to_response(self, template, context, request, current_app=None): "Shortcut for rendering to response and default context instances" if not current_app: current_app = self.name else: current_app = '%s:%s' % (self.name, current_app) if request: context_instance = RequestContext(request, current_app=current_app) else: context_instance = None context.update(self.get_context(request)) return render_to_response(template, context, context_instance=context_instance) ## Our views def media(self, request, module, path): """ Serve static files below a given point in the directory structure. """ if module == 'nexus': document_root = os.path.join(NEXUS_ROOT, 'media') else: document_root = self.get_module(module).media_root path = posixpath.normpath(urllib.unquote(path)) path = path.lstrip('/') newpath = '' for part in path.split('/'): if not part: # Strip empty path components. continue drive, part = os.path.splitdrive(part) head, part = os.path.split(part) if part in (os.curdir, os.pardir): # Strip '.' and '..' in path. continue newpath = os.path.join(newpath, part).replace('\\', '/') if newpath and path != newpath: return HttpResponseRedirect(newpath) fullpath = os.path.join(document_root, newpath) if os.path.isdir(fullpath): raise Http404("Directory indexes are not allowed here.") if not os.path.exists(fullpath): raise Http404('"%s" does not exist' % fullpath) # Respect the If-Modified-Since header. statobj = os.stat(fullpath) mimetype = mimetypes.guess_type( fullpath)[0] or 'application/octet-stream' if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]): return HttpResponseNotModified(content_type=mimetype) contents = open(fullpath, 'rb').read() response = HttpResponse(contents, content_type=mimetype) response["Last-Modified"] = http_date(statobj[stat.ST_MTIME]) response["Content-Length"] = len(contents) return response def login(self, request, form_class=None): "Login form" from django.contrib.auth import login as login_ from django.contrib.auth.forms import AuthenticationForm if form_class is None: form_class = AuthenticationForm if request.POST: form = form_class(request, request.POST) if form.is_valid(): login_(request, form.get_user()) request.session.save() return HttpResponseRedirect( request.POST.get('next') or reverse('nexus:index', current_app=self.name)) else: request.session.set_test_cookie() else: form = form_class(request) request.session.set_test_cookie() return self.render_to_response('nexus/login.html', { 'form': form, }, request) login = never_cache(login) def logout(self, request): "Logs out user and redirects them to Nexus home" from django.contrib.auth import logout logout(request) return HttpResponseRedirect( reverse('nexus:index', current_app=self.name)) def dashboard(self, request): "Basic dashboard panel" # TODO: these should be ajax module_set = [] for namespace, module in self.get_modules(): home_url = module.get_home_url(request) if hasattr(module, 'render_on_dashboard'): # Show by default, unless a permission is required if not module.permission or request.user.has_perm( module.permission): module_set.append( (module.get_dashboard_title(), module.render_on_dashboard(request), home_url)) return self.render_to_response('nexus/dashboard.html', { 'module_set': module_set, }, request)
def test_insert(self): d = SortedDict() with warnings.catch_warnings(record=True) as w: warnings.simplefilter("always") d.insert(0, "hello", "world") assert w[0].category is DeprecationWarning
class NexusSite(object): def __init__(self, name=None, app_name='nexus'): self._registry = {} self._categories = SortedDict() if name is None: self.name = 'nexus' else: self.name = name self.app_name = app_name def register_category(self, category, label, index=None): if index: self._categories.insert(index, category, label) else: self._categories[category] = label def register(self, module, namespace=None, category=None): module = module(self, category) if not namespace: namespace = module.get_namespace() if namespace: module.app_name = module.name = namespace self._registry[namespace] = (module, category) return module def unregister(self, namespace): if namespace in self._registry: del self._registry[namespace] def get_urls(self): from django.conf.urls.defaults import patterns, url, include base_urls = patterns('', url(r'^media/(?P<module>[^/]+)/(?P<path>.+)$', self.media, name='media'), url(r'^$', self.as_view(self.dashboard), name='index'), url(r'^login/$', self.login, name='login'), url(r'^logout/$', self.as_view(self.logout), name='logout'), ), self.app_name, self.name urlpatterns = patterns('', url(r'^', include(base_urls)), ) for namespace, module in self.get_modules(): urlpatterns += patterns('', url(r'^%s/' % namespace, include(module.urls)), ) return urlpatterns def urls(self): return self.get_urls() urls = property(urls) def has_permission(self, request, extra_permission=None): """ Returns True if the given HttpRequest has permission to view *at least one* page in the admin site. For EmployInsight, restricting this to just superusers. """ permission = (request.user.is_active and request.user.is_staff and request.user.is_superuser) if extra_permission: permission = permission and request.user.has_perm(extra_permission) return permission def as_view(self, view, cacheable=False, extra_permission=None): """ Wraps a view in authentication/caching logic extra_permission can be used to require an extra permission for this view, such as a module permission """ def inner(request, *args, **kwargs): if not self.has_permission(request, extra_permission): # show login pane return self.login(request) return view(request, *args, **kwargs) # Mark it as never_cache if not cacheable: inner = never_cache(inner) # We add csrf_protect here so this function can be used as a utility # function for any view, without having to repeat 'csrf_protect'. if not getattr(view, 'csrf_exempt', False): inner = csrf_protect(inner) return update_wrapper(inner, view) def get_context(self, request): context = csrf(request) context.update({ 'request': request, 'nexus_site': self, 'nexus_media_prefix': conf.MEDIA_PREFIX.rstrip('/'), }) return context def get_modules(self): for k, v in self._registry.iteritems(): yield k, v[0] def get_module(self, module): return self._registry[module][0] def get_categories(self): for k, v in self._categories.iteritems(): yield k, v def get_category_label(self, category): return self._categories.get(category, category.title().replace('_', ' ')) def render_to_string(self, template, context, request, current_app=None): if not current_app: current_app = self.name else: current_app = '%s:%s' % (self.name, current_app) if request: context_instance = RequestContext(request, current_app=current_app) else: context_instance = None context.update(self.get_context(request)) return render_to_string(template, context, context_instance=context_instance ) def render_to_response(self, template, context, request, current_app=None): "Shortcut for rendering to response and default context instances" if not current_app: current_app = self.name else: current_app = '%s:%s' % (self.name, current_app) if request: context_instance = RequestContext(request, current_app=current_app) else: context_instance = None context.update(self.get_context(request)) return render_to_response(template, context, context_instance=context_instance ) ## Our views def media(self, request, module, path): """ Serve static files below a given point in the directory structure. """ if module == 'nexus': document_root = os.path.join(NEXUS_ROOT, 'media') else: document_root = self.get_module(module).media_root path = posixpath.normpath(urllib.unquote(path)) path = path.lstrip('/') newpath = '' for part in path.split('/'): if not part: # Strip empty path components. continue drive, part = os.path.splitdrive(part) head, part = os.path.split(part) if part in (os.curdir, os.pardir): # Strip '.' and '..' in path. continue newpath = os.path.join(newpath, part).replace('\\', '/') if newpath and path != newpath: return HttpResponseRedirect(newpath) fullpath = os.path.join(document_root, newpath) if os.path.isdir(fullpath): raise Http404("Directory indexes are not allowed here.") if not os.path.exists(fullpath): raise Http404('"%s" does not exist' % fullpath) # Respect the If-Modified-Since header. statobj = os.stat(fullpath) mimetype = mimetypes.guess_type(fullpath)[0] or 'application/octet-stream' if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]): return HttpResponseNotModified(mimetype=mimetype) contents = open(fullpath, 'rb').read() response = HttpResponse(contents, mimetype=mimetype) response["Last-Modified"] = http_date(statobj[stat.ST_MTIME]) response["Content-Length"] = len(contents) return response def login(self, request, form_class=None): "Login form" from django.contrib.auth import login as login_ from django.contrib.auth.forms import AuthenticationForm if form_class is None: form_class = AuthenticationForm if request.POST: form = form_class(request, request.POST) if form.is_valid(): login_(request, form.get_user()) request.session.save() return HttpResponseRedirect(request.POST.get('next') or reverse('nexus:index', current_app=self.name)) else: request.session.set_test_cookie() else: form = form_class(request) request.session.set_test_cookie() return self.render_to_response('nexus/login.html', { 'form': form, }, request) login = never_cache(login) def logout(self, request): "Logs out user and redirects them to Nexus home" from django.contrib.auth import logout logout(request) return HttpResponseRedirect(reverse('nexus:index', current_app=self.name)) def dashboard(self, request): "Basic dashboard panel" # TODO: these should be ajax module_set = [] for namespace, module in self.get_modules(): home_url = module.get_home_url(request) if hasattr(module, 'render_on_dashboard'): # Show by default, unless a permission is required if not module.permission or request.user.has_perm(module.permission): module_set.append((module.get_dashboard_title(), module.render_on_dashboard(request), home_url)) return self.render_to_response('nexus/dashboard.html', { 'module_set': module_set, }, request)
def groups_form_factory(email=None, grouping_name=None, list_id=None): """ Form factory for selecting the interest groups. email An email address of a list member from which to set the initial values for the form. grouping_name The grouping name as defined in MailChimp. If not provided then the first grouping will be used. list_id The MailChimp list ID. If not provided, the value defined in the config settings will be used. """ if not hasattr(settings, 'MAILCHIMP_API_KEY'): raise ImproperlyConfigured(_("You need to specify MAILCHIMP_API_KEY" \ "in your Django settings file.")) ms = MailSnake(settings.MAILCHIMP_API_KEY) if not list_id: list_id = get_list_id() # get all groupings for the list grouping = None response = ms.listInterestGroupings(id=list_id) raise_if_error(response) # get the correct grouping if not grouping_name: grouping = response[0] grouping_name = grouping['name'] else: for try_grouping in response: if try_grouping['name'] == grouping_name: grouping = try_grouping if not grouping: errmsg = _("Grouping not found: '%s'") % grouping_name raise MailChimpGroupingNotFound(errmsg) if email: # get the user's group subscription to set initial field values response = ms.listMemberInfo(id=list_id, email_address=[email]) if not response['success']: raise MailChimpEmailNotFound user_groupings = response['data'][0]['merges']['GROUPINGS'] for try_grouping in user_groupings: if try_grouping['name'] == grouping_name: user_grouping = try_grouping # create the appropriate type of fields if grouping['form_field'] == 'checkboxes': fields = SortedDict() for i, group in enumerate(grouping['groups']): key = 'mailchimp_group_'+group['bit'] if email: initial=bool(user_grouping['groups'].find(group['name'])+1) else: initial=False fields.insert(i, key, forms.BooleanField(label=group['name'], required=False, initial=initial)) else: # radio or select fields = {} CHOICES = tuple((group['bit'], group['name']) for group in grouping['groups']) for group in grouping['groups']: initial=None if email: if bool(user_grouping['groups'].find(group['name'])+1): initial = group['bit'] else: initial = None if grouping['form_field'] == 'radio': widget = RadioSelect else: widget = Select fields['mailchimp_group'] = forms.ChoiceField(choices=CHOICES, label=grouping['name'], widget=widget, initial=initial) form = type('GroupsForm', (_GroupsForm,), {'base_fields': fields}) form._grouping = grouping return form
else: from django.forms.widgets import TextInput from django.forms import Field format_desc = SortedDict([ #('Y', 'years'), #('m', 'months'), ('D', 'days'), ('H', 'hours'), ('M', 'minutes'), ('S', 'seconds'), ('X', 'microseconds') ]) if HAVE_RELATIVEDELTA: format_desc.insert(0, 'Y', 'years') format_desc.insert(1, 'm', 'months') def check_format(format): for letter in format: if letter not in format_desc: raise ValueError("Format letter '%s' unknown" % letter) class IntervalWidget(TextInput): class Media: css = { 'all': ('interval.css', ), }