python docxtpl - cant create dynamic form - python

I have been trying to create a dynamic table in word but the created file has all the contents except the list inside the options tag. I have tried a lot of stuff but can't get it to work.
from docxtpl import DocxTemplate
from registration.models import stuff
def create_doc(options, model, file='resources/stuff.docx'):
doc = DocxTemplate(file)
log = stuff(options_object=model)
log.save()
l = []
for i in range(1, len(options) + 1):
x = {}
x['number'] = i
x['course'] = options[i - 1]
l.append(x)
context = {
'name': model.name,
'reg_no': model.regno,
'roll_no': model.rollno,
'rank': model.student_rank,
'course': model.alloted_to,
'cat': model.alloted_cat,
'options': l
}
doc.render(context)
doc.save(f'media/request_{model.rollno}.docx')
The template
The output

I solved it by using a different name than "options". I'm guessing it messed with the parameter that was being passed somehow and caused the issue.

Related

Return different value when querying dictionary

I am pulling from a dictionary in Python. I have it setup like this:
entries = [
{'website': 'yahoo','username': 'jblumberg','password': 'sdkljfhwe'},
{'website': 'google','username': 'jblumberg1','password': 'CoIushujSetu'}
]
I am asking the user to give me a website so I can return the password as so:
def lookup_password(website):
if website in entries.keys():
encrypted_password = entries[website]
return password_encrypt(encrypted_password, -encryption_key)
pass
However that won't give me what I want. How do I set it up to give me the password value for the provided website?
First let's restructure your dict like this so that it's a dict with each key as a website, like it seems you treat it in your code:
entries = {
'yahoo': {'username': 'jblumberg', 'password': 'sdkljfhwe'},
'google': {'username': 'jblumberg1', 'passwprd': 'CoIushujSetu'}
}
Now a couple of changes to your original code gets us to what should be working:
if website in entries:
encrypted_password = entries[website]['password']
I should note that website in entries and website in entries.keys() do the same thing here.
There are multiples small problems in your conception of your data :
First entries is a list. Therefore you won't be able to call ".keys()"
Also when using
encrypted_password = entries[website]
this will store the whole dictionnary. Meaning that you would then be able to access the password via ['password']
encrypted_password = entries[website]['password']
To resume : if you change you data to look like this
entries = {
'yahoo':{
'username': 'jblumberg','password': 'sdkljfhwe'
},
'google':{
'username': 'jblumberg1','password': 'CoIushujSetu'
},
}
def lookup_password(website):
if website in entries:
encrypted_password = entries[website]['password']
return password_encrypt(encrypted_password, -encryption_key)
pass
But if you keep the same data, it will have to look like this:
def lookup_password(website):
for record in entries:
if record['website'] == website:
encrypted_password = record['website']
return password_encrypt(encrypted_password, -encryption_key)
pass
You could use another dictionary:
entries = [
{'yahoo':{'username': 'jblumberg','password': 'sdkljfhwe'},
{'google':{'username': 'jblumberg1','password': 'CoIushujSetu'}
]
Then the following would happen:
>> website = 'yahoo'
>> entries[website]
>> {'username': 'jblumberg','password': 'sdkljfhwe'}
so if you wanted the password:
>> entires[website]['password']
>> 'sdkljfhwe'
Try:
def lookup_password(website):
for entry in entries:
if entry['website'] == website:
return entry['password']
Output:
In[2]: lookup_password('google')
Out[2]: 'CoIushujSetu'

List out auto scaling group names with a specific application tag using boto3

I was trying to fetch auto scaling groups with Application tag value as 'CCC'.
The list is as below,
gweb
prd-dcc-eap-w2
gweb
prd-dcc-emc
gweb
prd-dcc-ems
CCC
dev-ccc-wer
CCC
dev-ccc-gbg
CCC
dev-ccc-wer
The script I coded below gives output which includes one ASG without CCC tag.
#!/usr/bin/python
import boto3
client = boto3.client('autoscaling',region_name='us-west-2')
response = client.describe_auto_scaling_groups()
ccc_asg = []
all_asg = response['AutoScalingGroups']
for i in range(len(all_asg)):
all_tags = all_asg[i]['Tags']
for j in range(len(all_tags)):
if all_tags[j]['Key'] == 'Name':
asg_name = all_tags[j]['Value']
# print asg_name
if all_tags[j]['Key'] == 'Application':
app = all_tags[j]['Value']
# print app
if all_tags[j]['Value'] == 'CCC':
ccc_asg.append(asg_name)
print ccc_asg
The output which I am getting is as below,
['prd-dcc-ein-w2', 'dev-ccc-hap', 'dev-ccc-wfd', 'dev-ccc-sdf']
Where as 'prd-dcc-ein-w2' is an asg with a different tag 'gweb'. And the last one (dev-ccc-msp-agt-asg) in the CCC ASG list is missing. I need output as below,
dev-ccc-hap-sdf
dev-ccc-hap-gfh
dev-ccc-hap-tyu
dev-ccc-mso-hjk
Am I missing something ?.
In boto3 you can use Paginators with JMESPath filtering to do this very effectively and in more concise way.
From boto3 docs:
JMESPath is a query language for JSON that can be used directly on
paginated results. You can filter results client-side using JMESPath
expressions that are applied to each page of results through the
search method of a PageIterator.
When filtering with JMESPath expressions, each page of results that is
yielded by the paginator is mapped through the JMESPath expression. If
a JMESPath expression returns a single value that is not an array,
that value is yielded directly. If the result of applying the JMESPath
expression to a page of results is a list, then each value of the list
is yielded individually (essentially implementing a flat map).
Here is how it looks like in Python code with mentioned CCP value for Application tag of Auto Scaling Group:
import boto3
client = boto3.client('autoscaling')
paginator = client.get_paginator('describe_auto_scaling_groups')
page_iterator = paginator.paginate(
PaginationConfig={'PageSize': 100}
)
filtered_asgs = page_iterator.search(
'AutoScalingGroups[] | [?contains(Tags[?Key==`{}`].Value, `{}`)]'.format(
'Application', 'CCP')
)
for asg in filtered_asgs:
print asg['AutoScalingGroupName']
Elaborating on Michal Gasek's answer, here's an option that filters ASGs based on a dict of tag:value pairs.
def get_asg_name_from_tags(tags):
asg_name = None
client = boto3.client('autoscaling')
while True:
paginator = client.get_paginator('describe_auto_scaling_groups')
page_iterator = paginator.paginate(
PaginationConfig={'PageSize': 100}
)
filter = 'AutoScalingGroups[]'
for tag in tags:
filter = ('{} | [?contains(Tags[?Key==`{}`].Value, `{}`)]'.format(filter, tag, tags[tag]))
filtered_asgs = page_iterator.search(filter)
asg = filtered_asgs.next()
asg_name = asg['AutoScalingGroupName']
try:
asgX = filtered_asgs.next()
asgX_name = asg['AutoScalingGroupName']
raise AssertionError('multiple ASG\'s found for {} = {},{}'
.format(tags, asg_name, asgX_name))
except StopIteration:
break
return asg_name
eg:
asg_name = get_asg_name_from_tags({'Env':env, 'Application':'app'})
It expects there to be only one result and checks this by trying to use next() to get another. The StopIteration is the "good" case, which then breaks out of the paginator loop.
I got it working with below script.
#!/usr/bin/python
import boto3
client = boto3.client('autoscaling',region_name='us-west-2')
response = client.describe_auto_scaling_groups()
ccp_asg = []
all_asg = response['AutoScalingGroups']
for i in range(len(all_asg)):
all_tags = all_asg[i]['Tags']
app = False
asg_name = ''
for j in range(len(all_tags)):
if 'Application' in all_tags[j]['Key'] and all_tags[j]['Value'] in ('CCP'):
app = True
if app:
if 'Name' in all_tags[j]['Key']:
asg_name = all_tags[j]['Value']
ccp_asg.append(asg_name)
print ccp_asg
Feel free to ask if you have any doubts.
The right way to do this isn't via describe_auto_scaling_groups at all but via describe_tags, which will allow you to make the filtering happen on the server side.
You can construct a filter that asks for tag application instances with any of a number of values:
Filters=[
{
'Name': 'key',
'Values': [
'Application',
]
},
{
'Name': 'value',
'Values': [
'CCC',
]
},
],
And then your results (in Tags in the response) are all the times when a matching tag is applied to an autoscaling group. You will have to make the call multiple times, passing back NextToken every time there is one, to go through all the pages of results.
Each result includes an ASG ID that the matching tag is applied to. Once you have all the ASG IDs you are interested in, then you can call describe_auto_scaling_groups to get their names.
yet another solution, in my opinion simple enough to extend:
client = boto3.client('autoscaling')
search_tags = {"environment": "stage"}
filtered_asgs = []
response = client.describe_auto_scaling_groups()
for group in response['AutoScalingGroups']:
flattened_tags = {
tag_info['Key']: tag_info['Value']
for tag_info in group['Tags']
}
if search_tags.items() <= flattened_tags.items():
filtered_asgs.append(group)
print(filtered_asgs)

How to decode dataTables Editor form in python flask?

I have a flask application which is receiving a request from dataTables Editor. Upon receipt at the server, request.form looks like (e.g.)
ImmutableMultiDict([('data[59282][gender]', u'M'), ('data[59282][hometown]', u''),
('data[59282][disposition]', u''), ('data[59282][id]', u'59282'),
('data[59282][resultname]', u'Joe Doe'), ('data[59282][confirm]', 'true'),
('data[59282][age]', u'27'), ('data[59282][place]', u'3'), ('action', u'remove'),
('data[59282][runnerid]', u''), ('data[59282][time]', u'29:49'),
('data[59282][club]', u'')])
I am thinking to use something similar to this really ugly code to decode it. Is there a better way?
from collections import defaultdict
# request.form comes in multidict [('data[id][field]',value), ...]
# so we need to exec this string to turn into python data structure
data = defaultdict(lambda: {}) # default is empty dict
# need to define text for each field to be received in data[id][field]
age = 'age'
club = 'club'
confirm = 'confirm'
disposition = 'disposition'
gender = 'gender'
hometown = 'hometown'
id = 'id'
place = 'place'
resultname = 'resultname'
runnerid = 'runnerid'
time = 'time'
# fill in data[id][field] = value
for formkey in request.form.keys():
exec '{} = {}'.format(d,repr(request.form[formkey]))
This question has an accepted answer and is a bit old but since the DataTable module seems being pretty popular among jQuery community still, I believe this approach may be useful for someone else. I've just wrote a simple parsing function based on regular expression and dpath module, though it appears not to be quite reliable module. The snippet may be not very straightforward due to an exception-relied fragment, but it was only one way to prevent dpath from trying to resolve strings as integer indices I found.
import re, dpath.util
rxsKey = r'(?P<key>[^\W\[\]]+)'
rxsEntry = r'(?P<primaryKey>[^\W]+)(?P<secondaryKeys>(\[' \
+ rxsKey \
+ r'\])*)\W*'
rxKey = re.compile(rxsKey)
rxEntry = re.compile(rxsEntry)
def form2dict( frmDct ):
res = {}
for k, v in frmDct.iteritems():
m = rxEntry.match( k )
if not m: continue
mdct = m.groupdict()
if not 'secondaryKeys' in mdct.keys():
res[mdct['primaryKey']] = v
else:
fullPath = [mdct['primaryKey']]
for sk in re.finditer( rxKey, mdct['secondaryKeys'] ):
k = sk.groupdict()['key']
try:
dpath.util.get(res, fullPath)
except KeyError:
dpath.util.new(res, fullPath, [] if k.isdigit() else {})
fullPath.append(int(k) if k.isdigit() else k)
dpath.util.new(res, fullPath, v)
return res
The practical usage is based on native flask request.form.to_dict() method:
# ... somewhere in a view code
pars = form2dict(request.form.to_dict())
The output structure includes both, dictionary and lists, as one could expect. E.g.:
# A little test:
rs = jQDT_form2dict( {
'columns[2][search][regex]' : False,
'columns[2][search][value]' : None,
'columns[2][search][regex]' : False,
} )
generates:
{
"columns": [
null,
null,
{
"search": {
"regex": false,
"value": null
}
}
]
}
Update: to handle lists as dictionaries (in more efficient way) one may simplify this snippet with following block at else part of if clause:
# ...
else:
fullPathStr = mdct['primaryKey']
for sk in re.finditer( rxKey, mdct['secondaryKeys'] ):
fullPathStr += '/' + sk.groupdict()['key']
dpath.util.new(res, fullPathStr, v)
I decided on a way that is more secure than using exec:
from collections import defaultdict
def get_request_data(form):
'''
return dict list with data from request.form
:param form: MultiDict from `request.form`
:rtype: {id1: {field1:val1, ...}, ...} [fieldn and valn are strings]
'''
# request.form comes in multidict [('data[id][field]',value), ...]
# fill in id field automatically
data = defaultdict(lambda: {})
# fill in data[id][field] = value
for formkey in form.keys():
if formkey == 'action': continue
datapart,idpart,fieldpart = formkey.split('[')
if datapart != 'data': raise ParameterError, "invalid input in request: {}".format(formkey)
idvalue = int(idpart[0:-1])
fieldname = fieldpart[0:-1]
data[idvalue][fieldname] = form[formkey]
# return decoded result
return data

python lxml get the name of a node

This is my xml file:
<FuzzyComparison>
<Modules>
<Module>
<name>AutosoukModelMakeFuzzyComparisonModule</name>
<configurationLoader>DefaultLoader</configurationLoader>
<configurationFile>MakesModels.conf</configurationFile>
<settings></settings>
</Module>
<Module>
<name>DefaultFuzzyComparisonModule</name>
<configurationLoader>DefaultLoader</configurationLoader>
<configurationFile>Buildings.conf</configurationFile>
<settings>
<attribute>building</attribute>
</settings>
</Module>
</Modules>
</FuzzyComparison>
This is the code I've been trying to parse it with:
from lxml import etree
class AttributesXMLParser():
def __init__(self):
self.doc=etree.parse('Items.xml')
def getValueOfTag(self, tagName): #This function returns the value of a specific tag for exmaple, the tageName could be "FirstDate"
return self.doc.find(tagName).text
def loadFuzzySettings(self):
modulesDict = list()
modules = self.doc.findall('FuzzyComparison/Modules/Module')
for module in modules:
moduleDict = dict()
moduleName = module.find('name').text
moduleDict['name'] = moduleName
moduleConfigurationLoader = module.find('configurationLoader').text
moduleDict['configurationLoader'] = moduleConfigurationLoader
moduleConfigurationFile = module.find('configurationFile').text
moduleDict['moduleConfigurationFile'] = moduleConfigurationFile
settings = module.findall('settings')
settingsDict = dict()
for oneSetting in settings:
settingsDict[oneSetting] = oneSetting.text
moduleDict['settings'] = settingsDict
modulesDict.append(moduleDict)
return modulesDict
and this is the results:
[{'moduleConfigurationFile': 'MakesModels.conf', 'configurationLoader': 'Default
Loader', 'name': 'AutosoukModelMakeFuzzyComparisonModule', 'settings': {<Element
settings at 0x25257c8>: None}}, {'moduleConfigurationFile': 'Buildings.conf', '
configurationLoader': 'DefaultLoader', 'name': 'DefaultFuzzyComparisonModule', '
settings': {<Element settings at 0x2525e48>: '\n\t\t\t\t'}}]
My problem
I don't know how to get the name and value of the settings node, because as you see everything is working great except the settings, I need to have it like this:
"attribute": building
But my code gives me:
{<Element settings at 0x2525e48>: '\n\t\t\t\t'}}]
Could you help please to solve that?
Since findall() returns a list, you want to iterate over the contents of elements of that list, rather than the list itself. You also want to use the element's tag as a key, rather than using the element itself.
settingsDict = {}
for settingsNode in module.findall('settings'):
for setting in settingsNode:
settingsDict[setting.tag] = setting.text
Or, if you only have one settings tag,
settingsDict = {}
for setting in module.find('settings'):
settingsDict[setting.tag] = setting.text
Which can be simplified to:
settingsDict = {setting.tag: setting.text
for setting in module.find('settings')}

How to add multiple entries to Mongodb document at once using MongoEngine in Flask?

How can I add several entries to a document at once in Flask using MongoEngine/Flask-MongoEngine?
I tried to iterate over the dictionary that contains my entries. I simplified the example a bit, but originally the data is a RSS file that my Wordpress spits out and that I parsed via feedparser.
But the problem obviously is that I cannot dynamically generate variables that hold my entries before being saved to the database.
Here is what I tried so far.
How can I add the entries to my MongoDB database in bulk?
# model
class Entry(db.Document):
created_at = db.DateTimeField(
default=datetime.datetime.now, required=True),
title = db.StringField(max_length=255, required=True)
link = db.StringField(required=True)
# dictionary with entries
e = {'entries': [{'title': u'title1',
'link': u'http://www.me.com'
},
{'title': u'title2',
'link': u'http://www.me.com/link/'
}
]
}
# multiple entries via views
i = 0
while i<len(e['entries']):
post[i] = Entry(title=e['entries'][i]['title'], link=e['entries'][i]['title'])
post[i].save();
i += 1
Edit 1:
I thought about skipping the variables alltogether and translate the dictionary to the form that mongoengine can understand.
Because when I create a list manually, I can enter them in bulk into MongoDB:
newList = [RSSPost(title="test1", link="http://www.google.de"),
RSSPost(title="test2", link="http://www.test2.com")]
RSSPost.objects.insert(newList)
This works, but I could not translate it completely to my problem.
I tried
f = []
for x in e['entries']:
f.append("insert " + x['link'] + " and " + x['title'])
But as you see I could not recreate the list I need.
How to do it correctly?
# dictionary with entries
e = {'entries': [{'title': u'title1',
'link': u'http://www.me.com'
},
{'title': u'title2',
'link': u'http://www.me.com/link/'
}
]
}
How is your data/case different from the examples you posted? As long as I'm not missing something you should be able to instantiate Entry objects like:
entries = []
for entry in e['entries']:
new_entry = Entry(title=entry['title'], link=entry['link'])
entries.append(new_entry)
Entry.objects.insert(entries)
Quick and easy way:
for i in e['entries']:
new_e = Entry(**i)
new_e.save()

Categories