I'm trying to select all objects in the articles table, and have them grouped by their date. I'm thinking it would look similar to this:
articles = Article.objects.filter(pub_date__lte=datetime.date.today()).group_by(pub_date.day)
articles = {'2010-01-01': (articleA, articleB, articleC...),
'2010-01-02': (article1, article2, article3...)...}
Here's a working example of ignacio's suggestion to use itertools.groupby.
class Article(object):
def __init__(self, pub_date):
self.pub_date = pub_date
if __name__ == '__main__':
from datetime import date
import itertools
import operator
# You'll use your Article query here instead:
# a_list = Article.objects.filter(pub_date__lte = date.today())
a_list = [
Article(date(2010, 1, 2)),
Article(date(2010, 2, 3)),
Article(date(2010, 1, 2)),
Article(date(2011, 3, 2)),
]
keyfunc = operator.attrgetter('pub_date')
a_list = sorted(a_list, key = keyfunc)
group_list = [{ k.strftime('%Y-%m-%d') : list(g)}
for k, g in itertools.groupby(a_list, keyfunc)]
print group_list
Output:
[{'2010-01-02': [<__main__.Article object at 0xb76c4fec>, <__main__.Article object at 0xb76c604c>]}, {'2010-02-03': [<__main__.Article object at 0xb76c602c>]}, {'2011-03-02': [<__main__.Article object at 0xb76c606c>]}]
itertools.groupby()
MAybe you should do it at template level? If so you only need this : regroup tempalte tag
Related
I'm using the recipe_scraper python package to grab recipes from a website and create a single df with a column for each recipe under which recipe ingredient amounts are stored. I have managed to create the df but nested for loop in the create_df() function writes out the ingredients for each recipe for as many times as there are ingredients in the recipe e.g., if the recipe has 13 ingredients then the loop writes out all 13 ingredients 13 times instead of just once. Can someone please help me spot where I should be moving parts of my nested for loop such that each recipe contains only 1 copy of its ingredients?
Here's the code I've gotten thus far:
import pandas as pd
from recipe_scrapers import scrape_me
def replace_measurement_symbols(ingredients):
"""
Description:
Converts measurement symbols to numbers that will later serve as floats
Arguments:
* ingredients: this is the ingredient list object
"""
ingredients = [i.replace('¼', '0.25') for i in ingredients]
ingredients = [i.replace('½', '0.5') for i in ingredients]
ingredients = [i.replace('¾', '0.75') for i in ingredients]
return ingredients
def create_df(recipes):
"""
Description:
Creates one df with all recipes and their ingredients
Arguments:
* recipes: list of recipe URLs provided by user
"""
df_list = []
for recipe in recipes:
scraper = scrape_me(recipe)
recipe_details = replace_measurement_symbols(scraper.ingredients())
recipe_name = recipe.split("https://www.hellofresh.nl/recipes/", 1)[1]
recipe_name = recipe_name.rsplit('-', 1)[0]
print(recipe_name)
df_temp = pd.DataFrame(columns=['Ingredients', 'Measurement'])
df_temp[str(recipe_name)] = recipe_name
for ingredient in recipe_details:
try:
ing_1 = ingredient.split("2 * ", 1)[1]
ing_1 = ing_1.split(" ", 2)
item = ing_1[2]
measurement = ing_1[1]
quantity = float(ing_1[0]) * 2
df_temp.loc[len(df_temp)] = [item, measurement, quantity]
df_list.append(df_temp)
except ValueError:
pass
df = pd.concat(df_list)
return df
def main():
"""
Description:
Runs above functions to create one df with all recipes provided by user in a list
"""
recipes = [
'https://www.hellofresh.nl/recipes/luxe-burger-met-truffeltapenade-en-portobello-63ad875558b39f3da6083acd',
'https://www.hellofresh.nl/recipes/chicken-parmigiana-623c51bd7ed5c074f51bbb10',
'https://www.hellofresh.nl/recipes/quiche-met-broccoli-en-oude-kaas-628665b01dea7b8f5009b248',
]
df = create_df(recipes)
if __name__ == "__main__":
main()
I've solved it! In the create_df() function just needed to move df_temp() within the nested for loop:
def create_df(recipes):
"""
Description:
Creates one df with all recipes and their ingredients
Arguments:
* recipes: list of recipe URLs provided by user
"""
df_list = []
for recipe in recipes:
scraper = scrape_me(recipe)
recipe_details = replace_measurement_symbols(scraper.ingredients())
recipe_name = recipe.split("https://www.hellofresh.nl/recipes/", 1)[1]
recipe_name = recipe_name.rsplit('-', 1)[0]
print(recipe_name)
for ingredient in recipe_details:
try:
df_temp = pd.DataFrame(columns=['Ingredients', 'Measurement'])
df_temp[str(recipe_name)] = recipe_name
ing_1 = ingredient.split("2 * ", 1)[1]
ing_1 = ing_1.split(" ", 2)
item = ing_1[2]
measurement = ing_1[1]
quantity = float(ing_1[0]) * 2
df_temp.loc[len(df_temp)] = [item, measurement, quantity]
df_list.append(df_temp)
except ValueError:
pass
df = pd.concat(df_list)
return df
I have a code like this:
item = CitytalkItem() #scrapy item
event = self.driver.find_elements_by_xpath("...")
for d in event:
event_date = d.find_element_by_xpath("td[1]")
event_time_type = d.find_element_by_xpath("td[2]")
event_locate = d.find_element_by_xpath("td[3]")
event_date_c = event_date.text
event_time_type_c = event_time_type.text
event_locate_c = event_locate.text
#save like {"event_date": "0808", "event_tim_type": AM "event_locate": "LA"}
and I want to save it to a list
The form like this:
item['event']:
[{"event_date": "0808", "event_tim_type": AM "event_locate": "LA"},
{"event_date": "0708", "event_tim_type": PM "event_locate": "BA"},
{"event_date": "0810", "event_tim_type": AM "event_locate": "LA"}, ]
Please teach me
Thank you
You can use a list comprehension:
events = self.driver.find_elements_by_xpath("...")
item['event'] = [{'event_date': event.find_element_by_xpath("td[1]").text,
'event_time_type': event.find_element_by_xpath("td[2]").text,
'event_locate': event.find_element_by_xpath("td[3]").text}
for event in events]
I am trying to create a nested dictionary from a mysql query but I am getting a key error
result = {}
for i, q in enumerate(query):
result['data'][i]['firstName'] = q.first_name
result['data'][i]['lastName'] = q.last_name
result['data'][i]['email'] = q.email
error
KeyError: 'data'
desired result
result = {
'data': {
0: {'firstName': ''...}
1: {'firstName': ''...}
2: {'firstName': ''...}
}
}
You wanted to create a nested dictionary
result = {} will create an assignment for a flat dictionary, whose items can have any values like "string", "int", "list" or "dict"
For this flat assignment
python knows what to do for result["first"]
If you want "first" also to be another dictionary you need to tell Python by an assingment
result['first'] = {}.
otherwise, Python raises "KeyError"
I think you are looking for this :)
>>> from collections import defaultdict
>>> mydict = lambda: defaultdict(mydict)
>>> result = mydict()
>>> result['Python']['rules']['the world'] = "Yes I Agree"
>>> result['Python']['rules']['the world']
'Yes I Agree'
result = {}
result['data'] = {}
for i, q in enumerate(query):
result['data']['i'] = {}
result['data'][i]['firstName'] = q.first_name
result['data'][i]['lastName'] = q.last_name
result['data'][i]['email'] = q.email
Alternatively, you can use you own class which adds the extra dicts automatically
class AutoDict(dict):
def __missing__(self, k):
self[k] = AutoDict()
return self[k]
result = AutoDict()
for i, q in enumerate(query):
result['data'][i]['firstName'] = q.first_name
result['data'][i]['lastName'] = q.last_name
result['data'][i]['email'] = q.email
result['data'] does exist. So you cannot add data to it.
Try this out at the start:
result = {'data': []};
You have to create the key data first:
result = {}
result['data'] = {}
for i, q in enumerate(query):
result['data'][i] = {}
result['data'][i]['firstName'] = q.first_name
result['data'][i]['lastName'] = q.last_name
result['data'][i]['email'] = q.email
I have this class:
class Contract(db.Model):
book_number = db.IntegerProperty(required = True)
initial_page = db.IntegerProperty(required = True)
final_page = db.IntegerProperty(required = True)
contract_type = db.StringProperty(required = True)
parties = db.ListProperty(int)
date = db.DateTimeProperty (required = True, auto_now = True, auto_now_add = True)
class ContractingParty(db.Model):
person = db.ReferenceProperty(Person, required=True, collection_name="party_to_contracts")
contract = db.ReferenceProperty(Contract, required=True)
condition = db.StringProperty()
I use this code to create an entity named Contract which parties property is an empty list:
parties = []
contract_record = Contract(book_number = int(numBook),
initial_page = int(numInitialPage),
final_page = int(numFinalPage),
contract_type = choosed_contract_type,
parties = parties)
contract_record.put()
But I have to populate the parties property with IDs of ContractingPerson entitty. So, in other part of the code, I uptade the parties property of Contract entity, this way:
contracting_party_record = ContractingParty(person = person_key,
contract = contract_key,
condition = get_condition)
contracting_party_record.put()
contracting_party_id = contracting_party_record.key().id()
contract = Contract.get_by_id(contract_id)
contract.parties.append(contracting_party_id)
#contract.parties.extend(contracting_party_id)
contract.put()
But when I query datastore, this way:
parties = db.GqlQuery("SELECT * FROM ContractingParty WHERE contract =:c_key", c_key = contract_key).fetch(20)
I got only the last ContractingParty entity I've create. It seems that contract.parties.append(contracting_party_id) is replacing the old list with the new one. Then, I tried to use contract.parties.extend(contracting_party_id). But, now, I got this error:
File "C:\Users\Py\Desktop\contract\main.py", line 363, in post
contract.parties.extend(contracting_party_id)
TypeError: 'long' object is not iterable
How to fix that?
appending items on the list like contract.parties.append(contracting_party_id) does not replace the previous list it shouldn't do it in your case too.
what you want to do is using append instead of extend.
while extend expects an iterable like another list, append adds an item to the list.
>>> l = [1,2,3]
>>> l.append(4)
>>> l
[1, 2, 3, 4]
>>> l = [1,2,3]
>>> l.extend([4,5,6])
>>> l
[1, 2, 3, 4, 5, 6]
>>>
here the docs: http://docs.python.org/tutorial/datastructures.html
I've got two models like these:
class Schedule(models.Model):
name = models.CharField(_('name'), blank=True, max_length=15)
class Day(models.Model):
DAYS_OF_THE_WEEK = (
(0, _('Monday')),
(1, _('Tuesday')),
(2, _('Wednesday')),
(3, _('Thursday')),
(4, _('Friday')),
(5, _('Saturday')),
(6, _('Sunday')),
)
schedule = models.ForeignKey(Schedule, blank=True, null=True, verbose_name=_('schedule'))
day = models.SmallIntegerField(_('day'), choices=DAYS_OF_THE_WEEK)
opening = models.TimeField(_('opening'), blank=True)
closing = models.TimeField(_('closing'), blank=True)
It's possible that a schedule can have two Day objects like so:
Day(schedule=1, day=0, opening=datetime.time(7, 30), closing=datetime.time(10, 30))
Day(schedule=1, day=0, opening=datetime.time(12, 30), closing=datetime.time(15, 30))
like different shifts on the same day.
If I iterate them now i'll get two entries of day 0, like so
[day for day in schedule]
[0, 0, 1, 2, 3, 4, 5, 6]
How can I create a queryset so it'll group same days together and keep their attributes?
[day for day in schedule]
[0 (two entries), 1, 3, 4, 5, 6]
Maybe something like
[id: [day], id: [day]]
The code I ended up using is this:
from itertools import groupby
day_set = store.schedule_set.all()[0].day_set.all()
schedule = dict()
for k, v in groupby(day_set, lambda x: x.day):
schedule[k] = list(v)
and sending schedule to the template for rendering, which works like a charm.
You can group them at template level, using {% regroup %} or {% for %}-loop with {% ifchanged %} tag.
In Python code use groupby.