displaying unique objects only in django templates - python

I have a list of objects. I want to display these objects in such a way that only the first unique date displays if the subsequent objects contain the same date. If the date is different than it should display. Here is an example.
data:
id: 2, date: "01/01/2010"
id: 3, date: "01/01/2010"
id: 4, date: "02/02/2010"
What I want to display:
id - 2, "01/01/2010"
id - 3,
id - 4, "02/02/2010"
See how id 3 shows nothing since the previous date was the same?
How do I do this with django templates? One thing I tried was creating a custom filter. The only problem is that it uses a global variable which is a no-no in my opinion. How can I maintain state in a function filter or in django templating language to be concious of the previous value?
__author__ = 'Dave'
#This works but isn't best practice
from django import template
register = template.Library()
a = ''
#register.filter()
def ensure_unique(value):
global a
if a == value:
return ''
else:
a = value
return value

Using a simple_tag made it much easier for me to save state and accomplish exactly what I needed to.
from django import template
register = template.Library()
#register.simple_tag(takes_context=True)
def stop_repeat(context, event):
"""
Finds various types of links embedded in feed text and creates links out of them.
"""
if event.date:
if (event.get_date_time_location(), event.id) in context:
return ''
else:
context[(event.get_date_time_location(), event.id)] = (event.get_date_time_location(), event.id)
return event.get_date_time_location()

Related

Flask Admin: Format the way relationships are displayed

I would like to change the way the relationships are displayed in the Flask-Admin Index view of a Model. I do have two models connected through a many-to-many relationship which get displayed in the admin index view as well. Unfortunately, the relationships are just separated using a comma, which thus the user might lose the overview quickly. Ideally, I would like to convert the relationships entries into a simple list (e.g. like with li in HTML).
Is there an easy way of achieving this?
Thanks a lot!
Instead of the comma, you can use a <br> tag as the separator.
see column_type_formatters.
Define default formatter and update a list type.
def lineby_list_formatter(view, values):
html = u'<br/> '.join(str(v) for v in values)
return Markup(html)
MY_DEFAULT_FORMATTERS = dict(typefmt.BASE_FORMATTERS)
MY_DEFAULT_FORMATTERS.update({
list: lineby_list_formatter
})
class EventView(ModelView):
...
column_type_formatters = MY_DEFAULT_FORMATTERS
Ok... I figured it out myself: You can manipulate the way the data is rendered with overwriting the function _get_list_value(). see code below
def _get_list_value(self, context, model, name, column_formatters,
column_type_formatters):
"""
Returns the value to be displayed.
:param context:
:py:class:`jinja2.runtime.Context` if available
:param model:
Model instance
:param name:
Field name
:param column_formatters:
column_formatters to be used.
:param column_type_formatters:
column_type_formatters to be used.
"""
column_fmt = column_formatters.get(name)
if column_fmt is not None:
value = column_fmt(self, context, model, name)
else:
value = self._get_field_value(model, name)
choices_map = self._column_choices_map.get(name, {})
if choices_map:
return choices_map.get(value) or value
type_fmt = None
for typeobj, formatter in column_type_formatters.items():
if isinstance(value, typeobj):
type_fmt = formatter
break
if type_fmt is not None:
value = type_fmt(self, value)
### overwritten here
if name == 'items':
html_string = '<ul>'
for item in value.split(','):
html_string += '<li> {} </li>'.format(item)
html_string += '</ul>'
value = Markup(html_string)
return value

Two forms of same class on one page in flask-wtforms

In my website, I have a model Experiment that contains many Activities. I have a view where people can add or remove Activities from an Experiment.
I show a table with Activities that are a part of this Experiment, and a table of Activities not a part of this Experiment. Users can check which Activities they want to add or remove and use a submit button under the table.
However, when I update the list of choices on one form, the list of choices on the other form reflects this. Am I doing something wrong?
For both adding and removing the Activities to/from the Experiment, I use the same form.
class MultiCheckboxField(SelectMultipleField):
widget = ListWidget(prefix_label=False)
option_widget = CheckboxInput()
class ActivityListForm(Form):
activities = MultiCheckboxField(validators=[DataRequired()], choices=[])
submit = SubmitField("Submit")
def populate_activities(self, activities_set):
activities_mapping = {}
for activity in activities_set:
activities_mapping[str(activity.id)] = activity
choice_tuple = (str(activity.id), activity.question)
self.activities.choices.append(choice_tuple)
return activities_mapping
Here is my view:
def settings_experiment(exp_id):
experiment = Experiment.query.get(exp_id)
remove_activities_form = ActivityListForm(prefix="remove")
add_activities_form = ActivityListForm(prefix="add")
remove_activities_mapping = remove_activities_form.populate_activities(
experiment.activities)
add_activities_mapping = add_activities_form.populate_activities(
Activity.query.\
filter(not_(Activity.experiments.any(id=experiment.id))).all())
return render_template("experiments/settings_experiment.html",
experiment=experiment,
update_experiment_form=update_experiment_form,
remove_activities_form=remove_activities_form,
add_activities_form=add_activities_form,
add_activities_mapping=add_activities_mapping,
remove_activities_mapping=remove_activities_mapping)
Although remove_activities_form and add_activities_form have their options set separately to different lists, they both end up containing a union of their two option lists, which messes up my template rendering. Is there a way to keep them separate or am I screwed?
https://github.com/wtforms/wtforms/issues/284
Solution in the above discussion.

Plone: Change default schemata of a content type

I am using the Products.FacultyStaffDirectory product to manage contacts in my website.
One of the content types, i.e. FacultyStaffDirectory, has the description field in the 'categorization' tab when you are in edit mode. Now I need to remove the description field and put it in the default tab.
To do this, I'm using archetypes.schemaextender product to edit the field. In particular, I am using ISchemaModifier.
I have implemented the code but the field is still being shown in the categorization tab. May be something I missed. Below is the code:
This is the class that contains the description field that I want to modify:
# -*- coding: utf-8 -*-
__author__ = """WebLion <support#weblion.psu.edu>"""
__docformat__ = 'plaintext'
from AccessControl import ClassSecurityInfo
from Products.Archetypes.atapi import *
from zope.interface import implements
from Products.FacultyStaffDirectory.interfaces.facultystaffdirectory import IFacultyStaffDirectory
from Products.FacultyStaffDirectory.config import *
from Products.CMFCore.permissions import View, ManageUsers
from Products.CMFCore.utils import getToolByName
from Products.ATContentTypes.content.base import ATCTContent
from Products.ATContentTypes.content.schemata import ATContentTypeSchema, finalizeATCTSchema
from Products.membrane.at.interfaces import IPropertiesProvider
from Products.membrane.utils import getFilteredValidRolesForPortal
from Acquisition import aq_inner, aq_parent
from Products.FacultyStaffDirectory import FSDMessageFactory as _
schema = ATContentTypeSchema.copy() + Schema((
LinesField('roles_',
accessor='getRoles',
mutator='setRoles',
edit_accessor='getRawRoles',
vocabulary='getRoleSet',
default = ['Member'],
multiValued=1,
write_permission=ManageUsers,
widget=MultiSelectionWidget(
label=_(u"FacultyStaffDirectory_label_FacultyStaffDirectoryRoles", default=u"Roles"),
description=_(u"FacultyStaffDirectory_description_FacultyStaffDirectoryRoles", default=u"The roles all people in this directory will be granted site-wide"),
i18n_domain="FacultyStaffDirectory",
),
),
IntegerField('personClassificationViewThumbnailWidth',
accessor='getClassificationViewThumbnailWidth',
mutator='setClassificationViewThumbnailWidth',
schemata='Display',
default=100,
write_permission=ManageUsers,
widget=IntegerWidget(
label=_(u"FacultyStaffDirectory_label_personClassificationViewThumbnailWidth", default=u"Width for thumbnails in classification view"),
description=_(u"FacultyStaffDirectory_description_personClassificationViewThumbnailWidth", default=u"Show all person thumbnails with a fixed width (in pixels) within the classification view"),
i18n_domain="FacultyStaffDirectory",
),
),
))
FacultyStaffDirectory_schema = OrderedBaseFolderSchema.copy() + schema.copy() # + on Schemas does only a shallow copy
finalizeATCTSchema(FacultyStaffDirectory_schema, folderish=True)
class FacultyStaffDirectory(OrderedBaseFolder, ATCTContent):
"""
"""
security = ClassSecurityInfo()
implements(IFacultyStaffDirectory, IPropertiesProvider)
meta_type = portal_type = 'FSDFacultyStaffDirectory'
# Make this permission show up on every containery object in the Zope instance. This is a Good Thing, because it easy to factor up permissions. The Zope Developer's Guide says to put this here, not in the install procedure (http://www.zope.org/Documentation/Books/ZDG/current/Security.stx). This is because it isn't "sticky", in the sense of being persisted through the ZODB. Thus, it has to run every time Zope starts up. Thus, when you uninstall the product, the permission doesn't stop showing up, but when you actually remove it from the Products folder, it does.
security.setPermissionDefault('FacultyStaffDirectory: Add or Remove People', ['Manager', 'Owner'])
# moved schema setting after finalizeATCTSchema, so the order of the fieldsets
# is preserved. Also after updateActions is called since it seems to overwrite the schema changes.
# Move the description field, but not in Plone 2.5 since it's already in the metadata tab. Although,
# decription and relateditems are occasionally showing up in the "default" schemata. Move them
# to "metadata" just to be safe.
if 'categorization' in FacultyStaffDirectory_schema.getSchemataNames():
FacultyStaffDirectory_schema.changeSchemataForField('description', 'categorization')
else:
FacultyStaffDirectory_schema.changeSchemataForField('description', 'metadata')
FacultyStaffDirectory_schema.changeSchemataForField('relatedItems', 'metadata')
_at_rename_after_creation = True
schema = FacultyStaffDirectory_schema
# Methods
security.declarePrivate('at_post_create_script')
def at_post_create_script(self):
"""Actions to perform after a FacultyStaffDirectory is added to a Plone site"""
# Create some default contents
# Create some base classifications
self.invokeFactory('FSDClassification', id='faculty', title='Faculty')
self.invokeFactory('FSDClassification', id='staff', title='Staff')
self.invokeFactory('FSDClassification', id='grad-students', title='Graduate Students')
# Create a committees folder
self.invokeFactory('FSDCommitteesFolder', id='committees', title='Committees')
# Create a specialties folder
self.invokeFactory('FSDSpecialtiesFolder', id='specialties', title='Specialties')
security.declareProtected(View, 'getDirectoryRoot')
def getDirectoryRoot(self):
"""Return the current FSD object through acquisition."""
return self
security.declareProtected(View, 'getClassifications')
def getClassifications(self):
"""Return the classifications (in brains form) within this FacultyStaffDirectory."""
portal_catalog = getToolByName(self, 'portal_catalog')
return portal_catalog(path='/'.join(self.getPhysicalPath()), portal_type='FSDClassification', depth=1, sort_on='getObjPositionInParent')
security.declareProtected(View, 'getSpecialtiesFolder')
def getSpecialtiesFolder(self):
"""Return a random SpecialtiesFolder contained in this FacultyStaffDirectory.
If none exists, return None."""
specialtiesFolders = self.getFolderContents({'portal_type': 'FSDSpecialtiesFolder'})
if specialtiesFolders:
return specialtiesFolders[0].getObject()
else:
return None
security.declareProtected(View, 'getPeople')
def getPeople(self):
"""Return a list of people contained within this FacultyStaffDirectory."""
portal_catalog = getToolByName(self, 'portal_catalog')
results = portal_catalog(path='/'.join(self.getPhysicalPath()), portal_type='FSDPerson', depth=1)
return [brain.getObject() for brain in results]
security.declareProtected(View, 'getSortedPeople')
def getSortedPeople(self):
""" Return a list of people, sorted by SortableName
"""
people = self.getPeople()
return sorted(people, cmp=lambda x,y: cmp(x.getSortableName(), y.getSortableName()))
security.declareProtected(View, 'getDepartments')
def getDepartments(self):
"""Return a list of FSDDepartments contained within this site."""
portal_catalog = getToolByName(self, 'portal_catalog')
results = portal_catalog(portal_type='FSDDepartment')
return [brain.getObject() for brain in results]
security.declareProtected(View, 'getAddableInterfaceSubscribers')
def getAddableInterfaceSubscribers():
"""Return a list of (names of) content types marked as addable using the
IFacultyStaffDirectoryAddable interface."""
return [type['name'] for type in listTypes() if IFacultyStaffDirectoryAddable.implementedBy(type['klass'])]
security.declarePrivate('getRoleSet')
def getRoleSet(self):
"""Get the roles vocabulary to use."""
portal_roles = getFilteredValidRolesForPortal(self)
allowed_roles = [r for r in portal_roles if r not in INVALID_ROLES]
return allowed_roles
#
# Validators
#
security.declarePrivate('validate_id')
def validate_id(self, value):
"""Ensure the id is unique, also among groups globally."""
if value != self.getId():
parent = aq_parent(aq_inner(self))
if value in parent.objectIds():
return _(u"An object with id '%s' already exists in this folder") % value
groups = getToolByName(self, 'portal_groups')
if groups.getGroupById(value) is not None:
return _(u"A group with id '%s' already exists in the portal") % value
registerType(FacultyStaffDirectory, PROJECTNAME)
Below is the portion of code from the class that I have implemented to change the description field's schemata:
from Products.Archetypes.public import ImageField, ImageWidget, StringField, StringWidget, SelectionWidget, TextField, RichWidget
from Products.FacultyStaffDirectory.interfaces.facultystaffdirectory import IFacultyStaffDirectory
from archetypes.schemaextender.field import ExtensionField
from archetypes.schemaextender.interfaces import ISchemaModifier, ISchemaExtender, IBrowserLayerAwareExtender
from apkn.templates.interfaces import ITemplatesLayer
from zope.component import adapts
from zope.interface import implements
class _ExtensionImageField(ExtensionField, ImageField): pass
class _ExtensionStringField(ExtensionField, StringField): pass
class _ExtensionTextField(ExtensionField, TextField): pass
class FacultyStaffDirectoryExtender(object):
"""
Adapter to add description field to a FacultyStaffDirectory.
"""
adapts(IFacultyStaffDirectory)
implements(ISchemaModifier, IBrowserLayerAwareExtender)
layer = ITemplatesLayer
fields = [
]
def __init__(self, context):
self.context = context
def getFields(self):
return self.fields
def fiddle(self, schema):
desc_field = schema['description'].copy()
desc_field.schemata = "default"
schema['description'] = desc_field
And here is the code from my configure.zcml:
<adapter
name="apkn-FacultyStaffDirectoryExtender"
factory="apkn.templates.extender.FacultyStaffDirectoryExtender"
provides="archetypes.schemaextender.interfaces.ISchemaModifier"
/>
Is there something that I'm missing in this?
def fiddle(self, schema):
schema['description'].schemata = 'default'
should be sufficient. The copy() operation does not make any sense here.
In order to check if the fiddle() method is actually used: use pdb or add debug print statements.
It turns out that there was nothing wrong with the code.
I got it to work by doing the following:
Restarted the plone website
Re-installed the product
Clear and rebuilt my catalog
This got it working somehow.

Hindi or Farsi numbers in django templating engine

I want to print {{forloop.counter}} with persian or Hindi encoding means to have "۱ ۲ ۳ ۴ .." instead of "1 2 3 4 ...". I searched a lot but I couldn't find any related functions. Would you mind helping me?
Regards
You could use a custom template filter. I'm not familiar enough with Django's l10n library to know if they do this for you.
def devanagari_int(arabic_int):
""" Converts an arabic numeral (ex. 5817) to Devanagari (ex. ५८१७) """
devanagari_nums = ('०','१','२','३','४','५','६','७','८','९')
# arabic_nums = ('۰','١','۲'....)
# farsi_nums = (...)
number = str(arabic_int)
return ''.join(devanagari_nums[int(digit)] for digit in number)
# register your filter, and then:
{{forloop.counter|devanagari_int}}
Make sure you save your filter file as UTF-8 (or instead use the appropriate unicode representations).
You can create your own template filter.
#register.filter(name='persian_int')
def persian_int(english_int):
devanagari_nums = ('۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹')
number = str(english_int)
return ''.join(devanagari_nums[int(digit)] for digit in number)
#register.filter(name='hindi_int')
def hindi_int(english_int):
devanagari_nums = ('०','१','२','३','४','५','६','७','८','९')
number = str(english_int)
return ''.join(devanagari_nums[int(digit)] for digit in number)
You could make a templatefilter that converts a number to the appropriate encoding. You could then use it as such:
{{ forloop.counter|convert_to_hindi }}
You can use the django internationalization there is a library l10n is define in django
You can create your own custom template tag.
To do so you need to create the following files assuming your app is called "myapp"
myapp/
__init__.py
models.py
templatetags/
__init__.py
extra_tags.py
views.py
Then add the following code in the file extra_tags.py
from django import template
register = template.Library()
#register.filter(name='persianize_digits')
def persian_int(string):
persianize = dict(zip("0123456789",'۰۱۲۳۴۵۶۷۸۹'))
return ''.join(persianize[digit] if digit in persianize else digit for digit in str(string))
Now you can simply do {{ value|persianize_digits }} in your templates and the digits will become Persian. This method also works for strings that are not numbers as a whole but include some digits in them, e.g. 10:27:33 which is a time duration.
You might try the unicode version of template filter as follows:
register = template.Library()
to_arabic_number_trans = dict(
zip((ord(s) for s in u'0123456789'),
u'\u0660\u0661\u0662\u0663\u0664\u0665\u0666\u0667\u0668\u0669')
)
to_english_number_trans = dict(
zip((ord(s) for s in u'٠١٢٣٤٥٦٧٨٩'),
u'\u2080\u2081\u2082\u2083\u2084\u2085\u2086\u2087\u2088\u2089')
)
#register.filter
def format_number(value):
cur_lang = get_language()
if hasattr(value, 'translate'):
if cur_lang == 'ar':
return value.translate(to_arabic_number_trans)
return value.translate(to_english_number_trans)
return value
This works well for stand alone numbers, however any ideas about numbers in date?
like in
2 يونيو، 2020
how can i get it right without messing with datetime objects!
This worked for me to turn english numbers to persian numbers
from django import template
register = template.Library()
#register.filter(name='persian_int')
def persian_int(english_int):
persian_nums = {'0':'۰', '1':'۱', '2':'۲', '3':'۳', '4':'۴', '5':'۵', '6':'۶', '7':'۷', '8':'۸', '9':'۹'}
number = str(english_int)
persian_dict = number.maketrans(persian_nums)
result = number.translate(persian_dict)
return result

Python OOP Project Organization

I'm a bit new to Python dev -- I'm creating a larger project for some web scraping. I want to approach this as "Pythonically" as possible, and would appreciate some help with the project structure. Here's how I'm doing it now:
Basically, I have a base class for an object whose purpose is to go to a website and parse some specific data on it into its own array, jobs[]
minion.py
class minion:
# Empty getJobs() function to be defined by object pre-instantiation
def getJobs(self):
pass
# Constructor for a minion that requires site authorization
# Ex: minCity1 = minion('http://portal.com/somewhere', 'user', 'password')
# or minCity2 = minion('http://portal.com/somewhere')
def __init__(self, title, URL, user='', password=''):
self.title = title
self.URL = URL
self.user = user
self.password = password
self.jobs = []
if (user == '' and password == ''):
self.reqAuth = 0
else:
self.reqAuth = 1
def displayjobs(self):
for j in self.jobs:
j.display()
I'm going to have about 100 different data sources. The way I'm doing it now is to just create a separate module for each "Minion", which defines (and binds) a more tailored getJobs() function for that object
Example: minCity1.py
from minion import minion
from BeautifulSoup import BeautifulSoup
import urllib2
from job import job
# MINION CONFIG
minTitle = 'Some city'
minURL = 'http://www.somewebpage.gov/'
# Here we define a function that will be bound to this object's getJobs function
def getJobs(self):
page = urllib2.urlopen(self.URL)
soup = BeautifulSoup(page)
# For each row
for tr in soup.findAll('tr'):
tJob = job()
span = tr.findAll(['span', 'class="content"'])
# If row has 5 spans, pull data from span 2 and 3 ( [1] and [2] )
if len(span) == 5:
tJob.title = span[1].a.renderContents()
tJob.client = 'Some City'
tJob.source = minURL
tJob.due = span[2].div.renderContents().replace('<br />', '')
self.jobs.append(tJob)
# Don't forget to bind the function to the object!
minion.getJobs = getJobs
# Instantiate the object
mCity1 = minion(minTitle, minURL)
I also have a separate module which simply contains a list of all the instantiated minion objects (which I have to update each time I add one):
minions.py
from minion_City1 import mCity1
from minion_City2 import mCity2
from minion_City3 import mCity3
from minion_City4 import mCity4
minionList = [mCity1,
mCity2,
mCity3,
mCity4]
main.py references minionList for all of its activities for manipulating the aggregated data.
This seems a bit chaotic to me, and was hoping someone might be able to outline a more Pythonic approach.
Thank you, and sorry for the long post!
Instead of creating functions and assigning them to objects (or whatever minion is, I'm not really sure), you should definitely use classes instead. Then you'll have one class for each of your data sources.
If you want, you can even have these classes inherit from a common base class, but that isn't absolutely necessary.

Categories