I am looking for an efficient and maintainable way to create a table in Python that can be used to look up user readable strings for enumeration values.
Constraints:
I want it to work with an enumeration that supports bitwise operations. For example: passing in a value of enumeration values that has been bitmasked together will return a list of strings for each bitmasked value.
I want the user readable strings to be translated from the enumeration value names so I don't have to maintain a table that has to be updated every time the enumeration is modified.
I want it to be efficient. For example, I don't want a static function that will do the conversion every time it's called. I want to create a static table that is initialized once with the strings. For Example, I want to create a static dict that looks like this: {Privileges.CanAddPost: "can add post", Privileges.CanDeletePost: "can delete post", ...}
from enum import IntFlag, unique
#unique
class Privileges(IntFlag):
"""Privileges enum that supports bitwise operations"""
NoPrivileges = 0
CanAddPost = 1
CanDeletePost = 2
CanBanUser = 4
CanResetPasswords = 8
CanModerateDiscussions = 16
CanSuspendAccounts = 32
All = CanAddPost | CanDeletePost | CanBanUser |\
CanResetPasswords | CanModerateDiscussions | CanSuspendAccounts
#Instantiate the static variable
Privileges.strings_map = ... # How do initialize this table?
To accomplish this I created two functions. The first is a static function _toString(privilege) that will convert a enumeration value name to a user readable string. For example: CanAddPost becomes "can add post"
The 2nd function list_privileges() converts a bitmask of enumeration values to a list of strings. For Example: Privileges.CanAddPost|Privileges.CanDeletePost becomes ["can add post", "can delete post"]
I use a dictionary comprehension to create a static string_map lookup table.
from enum import IntFlag, unique
#unique
class Privileges(IntFlag):
"""Privileges enum that supports bitwise operations"""
NoPrivileges = 0
CanAddPost = 1
CanDeletePost = 2
CanBanUser = 4
CanResetPasswords = 8
CanModerateDiscussions = 16
CanSuspendAccounts = 32
All = CanAddPost | CanDeletePost | CanBanUser |\
CanResetPasswords | CanModerateDiscussions | CanSuspendAccounts
#staticmethod
def _toString(privilege) -> str:
"""Converts a Privileges enum value to a string"""
return "".join([char if char.islower() else " " + char.lower()\
for char in privilege.name]).lstrip()
def list_privileges(self) -> list:
"""Converts a bitmask of Privileges to a list of readable strings"""
return list(\
[Privileges.strings_map[privilege] for privilege in Privileges\
if privilege != Privileges.NoPrivileges\
and privilege != Privileges.All\
and (privilege & self) == privilege])
# Create static strings_map that will map a Privilege enum value to a
# readable string
Privileges.strings_map = { privilege: Privileges._toString(privilege) for privilege in Privileges}
user_privileges = Privileges.CanAddPost | Privileges.CanDeletePost
admin_privileges = Privileges.All
print(user_privileges.list_privileges())
print(admin_privileges.list_privileges())
Output:
['can add post', 'can delete post']
['can add post', 'can delete post', 'can ban user', 'can reset passwords', 'can moderate discussions', 'can suspend accounts']
Related
I have this model
from django.db import models
class TranslatedString(models.Model):
lang = models.CharField()
key = models.CharField()
value = models.CharField()
I have these instances of this model:
a = TranslatedString(lang="en_US", key="my-string", value="hello world")
b = TranslatedString(lang="en_AU", key="my-string", value="g'day world")
c = TranslatedString(lang="ja_JP", key="my-string", value="こんにちは世界")
And I have this list of languages a user wants
preferred_langs = ["en_CA", "en_US", "en_AU", "fr_CA"]
which is ordered by preference. I would like to return the value that matches the first item in that list. Even though both a and b would match a query like
TranslatedString.objects.filter(key="my-string", lang__in=preferred_langs).first()
I want it to be ordered by the list, so that I always get a.
I can make a query for each element in preferred_langs and return as soon as I find a matching value, but is there a better option? I'd like to do it in one query.
You can use a generator expression over preferred_langs to produce a mapping of preferred languages to their respective indices in the list as When objects for a Case object to be annotated as a field so that you can order the filtered result by it:
from django.db.models import Case, Value, When
TranslatedString.objects.filter(key="my-string", lang__in=preferred_langs).annotate(
preference=Case(*(When(lang=lang, then=Value(i)) for i, lang in preferred_langs))
).order_by('preference').first()
If you don't mind retrieving all preferred translations from the database, this may be accomplished tersely by sorting the models in Python:
preferred_langs = ["en_CA", "en_US", "en_AU", "fr_CA"]
strings = list(TranslatedString.objects.filter(key="my-string", lang__in=preferred_langs))
strings.sort(key=lambda s: preferred_langs.index(s))
first_choice = strings[0]
print(first_choice.lang) # outputs "en_US"
This will perform a single (but potentially large) query. However, if the sequence of preferred languages is fairly short, the sorting should occur in negligible time.
I'm trying to provide an API like interface in my Django python application that allows someone to input an id and then also include key/values with the request as form data.
For example the following field name and values for ticket 111:
ticket.subject = Hello World
ticket.group_id = 12345678
ticket.collaborators = [123, 4567, 890]
ticket.custom_fields: [{id: 32656147,value: "something"}]
On the backend, I have a corresponding Dict that should match this structure (and i'd do validation). Something like this:
ticket: {
subject: "some subject I want to change",
group_id: 99999,
collaborator_ids: [ ],
custom_fields: [
{
id: 32656147,
value: null
}
]
}
1) I'm not sure exactly the best way to parse the dot notation there, and
2) Assuming I am able to parse it, how would I be able to change the values of the Dict to match what was passed in. I'd imagine maybe something like a class with these inputs?
class SetDictValueFromUserInput(userDotNotation, userNewValue, originalDict)
...
SetDictValueFromUserInput("ticket.subject", "hello world", myDict)
Fastest way is probably splitting the string and indexing based on seperation. For example:
obj = "ticket.subject".split(".")
actual_obj = eval(obj[0]) # this is risky, they is a way around this if you just use if statements and predifined variables.
actual_obj[obj[1]] = value
To have further indexing where an object like ticket.subject.name might work try using a for loop as so.
for key in obj[1:-2]: # basically for all the values in between the object name and the defining key
actual_obj = actual_obj[key] # make the new object based on the value in-between.
actual_obj[obj[-1]] = value
I have encountered some problems with sqlite3 and sqlalchemy. From some time I try to make some specific query and in some way I failed. The database is composed from two tables users, and Properties. Those tables have schema as shown bellow.
sqlite> .schema users
CREATE TABLE users (
id INTEGER NOT NULL,
name VARCHAR(50) NOT NULL,
PRIMARY KEY (id)
);
sqlite> .schema properties
CREATE TABLE properties (
id INTEGER NOT NULL,
property_number INTEGER,
user_id INTEGER,
PRIMARY KEY (id),
FOREIGN KEY(user_id) REFERENCES users (id)
);
The content of users table is pretty straightforward, but properties deserves for some dose of explanations. In property_number column I store different properties, each with its unique number, for example: property bald has number 3 and property tan has number 4 etc. If the User have multiple properties, every one of them occupies one row in the properties table. I choosed this style for easy way to add new properties without messing with migrations and stuff like that.
The problem is, a do not know how to make query which consist of multiple properties. My current best solution is, ask for every single property in separate query. This gives mi list of sets, two different ones. One for the positive and one for the negative instance of given property (positive equals stuff I would like user to have, negative equals stuff I would not like user to have). And in next step I make difference of the two subsets, and get final list which contains users' ids who have interesting for me properties. Then I make query for those users' names. It seems to be very complicated, maybe it is, but for sure it is ugly. I also do not like make single query for every single property. Python code if somone is interested.
def prop_dicts():
"""Create dictionaries of properties
contained in table properties in db.
Returns:
touple:
prop_names (dict)
prom_values (dict)."""
prop_names = {'higins': 10000,
'tall': 1,
'fat': 2,
'bald': 3,
'tan': 4,
'hairry': 5}
prop_values = {1000: 'higins',
1: 'tal',
2: 'fat',
3: 'bald',
4: 'tan',
5: 'hairry'}
dictionaries = (prop_names, prop_values)
return dictionaries
def list_of_sets_intersection(set_list):
"""Makes intersection of all sets in list.
Args:
param1 (list): list containing sets to check.
Returns:
set (values): contains intersectred values."""
if not set_list:
return set()
result = set_list[0]
for s in set_list[1:]:
result &= s
return result
def list_of_sets_union(set_list):
"""Makes union of elements in all sets in list.
Args:
param1 (list): list containing sets to check.
Returns:
set (values): contains union values."""
if not set_list:
return set()
result = set_list[0]
for s in set_list[1:]:
result |= s
return result
def db_search():
"""Search database against positiv and negative values.
Returns:
list (sets): one set in list for every property in
table properties db."""
n, v = prop_dicts()
positive = [2, 3]
negative = [4, 5]
results_p = []
results_n = []
#Positive properties.
for element in xrange(0, len(positive)):
subresult = []
for u_id, in db.query(Property.user_id).\
filter_by(property_number = positive[element]):
subresult.append(u_id)
subresult = set(subresult)
results_p.append(subresult)
#Negative properties.
for element in xrange(0, len(negative)):
subresult = []
for u_id, in db.query(Property.user_id).\
filter_by(property_number = negative[element]):
subresult.append(u_id)
subresult = set(subresult)
results_n.append(subresult)
print 'positive --> ', results_p
print 'negative --> ', results_n
results_p = list_of_sets_intersection(results_p)
results_n = list_of_sets_union(results_n)
print 'positive --> ', results_p
print 'negative --> ', results_n
final_result = results_p.difference(results_n)
return list(final_result)
print db_search()
Is it a way for do it in one single query? I am new in the field of databases and sorry if the quality of the question seems to be lame. There is so many possibilities that I really do not know how to do it in the "right" way. I have searched the vast percent of the internet regarding this topic and best solution I have found was this containing "WHERE" Cause and "AND" Operator. But those two do not work if you connects two the same columns of the one table.
SELECT user_id FROM properties WHERE property_number=3 AND property_number=4;
Or in sqlalchemy.
db.query(User.user_id).join(Property).filter(and_(property_number=3, property_number=4)).all()
This sqlalchemy example may contain some error, because I have no preview for it, but for sure you will understand what is the point of this.
You can do this by using aggregation
SELECT user_id
FROM properties
WHERE property_number in (3, 4)
GROUP BY user_id
HAVING count(*) = 2
In SQLAlchemy
from sqlalchemy import func
properties = [3, 4]
db.session.query(Property.user_id)\
.filter(Property.property_number.in_(properties))\
.group_by(Property.user_id)\
.having(func.count()==len(properties))\
.all()
update
positive = [2, 3]
negative = [4, 5]
positive_query = db.session.query(Property.user_id)\
.filter(Property.property_number.in_(positive))\
.group_by(Property.user_id)\
.having(func.count()==len(positive))
negative_query = db.session.query(Property.user_id)\
.filter(Property.property_number.in_(negative))\
.distinct()
final_result = positive_query.except_(negative_query).all()
I have a list of of SQLAlchemy Model attributes. For example:
my_list = ['firstName', 'lastName']
I also have a person SQLAlchemy Object with firstName and lastName attributes.
I want to search query my database for people with a query as follows:
session.filter( Person.lastName.like(query+'%') | Person.firstName.like(query+'%')).all()
The tricky part is that I want to generate the above filter dynamically from the my_list list. For example if an emailAddress is added to the list I want the query to also search via the objects email property.
With SQLAlchemy this is pretty easy if you know about reduce; use getattr to get the dynamically named column from the Person class.
from functools import reduce # python 3
from operator import or_
columns = [ 'firstName', 'lastName' ]
# make a list of `Person.c.like(query+'%')`
likes = [ getattr(Person, c).like(query+'%') for c in column ]
# join them with | using reduce; does the same as likes[0]|likes[1]|....
final_filter = reduce(or_, likes)
session.filter(final_filter).all()
Though or_ also accepts any number of clauses to or together, so you can use argument unpacking too:
final_filter = or_(*likes)
This question already has answers here:
How can you dynamically create variables? [duplicate]
(8 answers)
Closed 8 years ago.
I am trying to automate populating a town by randomly generating households. I generate the name of the town, generate the number of households, the last name of each household and number of occupants in each. That much is fine. I am now, however, trying to create each individual, to generate a first name, a sex, an age and an occupation, and I'd like to store this data in a list as well, one list containing the attributes of each person. The problem I'm running into is that I want to use a for loop, something like:
#houseArray[currentFam][1] is the number of members in the current house.
for currentFam in range(houseArray[currentFam][1]):
uniquelyNamedArray[0] = genSex()
uniquelyNamedArray[1] = genFirstName()
uniquelyNamedArray[2] = genAge()
So... look at the data of the first household, use a for loop to iterate through each member assigning stats, then go to the next household and do the same, progressing through each household. My problem lies in not knowing how to assign a unique name to each array created by the for loop. It doesn't really matter what the name is, it could be anything as long as each person has their own uniquely named array storing their attributes.
Use a dictionary with the person's name as the key. Like:
people = {}
people["Billy Bloggs"] = ['23','Male','263 Evergreen Tce'] # store to dict
print ( people["Billy Bloggs"] ) # get stuff out of dict
Better still, give the attributes names by storing those as a dict as well:
people["Billy Bloggs"] = { 'Age':23, 'Gender':'M', 'Address':'263 Evergreen Tce' }
print ( people["Billy Bloggs"]['Age'] ) # Get billy's age
You can loop through the elements of a dictionary using the following syntax:
>>> mydict = {'a':'Apple', 'b':'Banana', 'c':'Cumquat'}
>>> for key, value in mydict.iteritems():
... print ('Key is :' + key + ' Value is:' + value)
...
Key is :a Value is:Apple
Key is :c Value is:Cumquat
Key is :b Value is:Banana
Note that there is no guarantee on the order of the data. You may insert data in the order A, B, C and get A, C, B back.
Note: The keys of a dict, in this case the person's name, are constrained to be unique. So if you store data to the same name twice, then the first key:value pair will be overwritten.
mydict["a"] = 5
mydict["a"] = 10
print (mydict["a"]) # prints 10
Sidenote: some of your gen*() functions could almost certainly be replaced by random.choice():
import random
first_names = ['Alice','Bob','Charlie','Dick','Eliza']
random_first_name = random.choice(first_names)
Keep data out of your variable names and just store them in a dict.
First, while you haven't shown us the surrounding code, you are probably relying too much on global variables. Rather than trying to create uniquely named arrays for each family member simply do something like this:
Don't really do this (I'll tell you why in a minute)
#houseArray[currentFam][1] is the number of members in the current house.
for currentFam in range(houseArray[currentFam][1]):
family_member_info = []
family_member_info[0] = genSex()
family_member_info[1] = genFirstName()
family_member_info[2] = genAge()
# Pretend 2 is where we are storing the family member information list
houseArray[currentFam][2].append(family_member_info)
A better way
Don't use an array for this sort of thing - it gets very difficult very quickly to tell what is actually stored in which index. Even in your example you have to note that houseArray[currentFam][1] is storing the number of members in the current house.
I would use either a dictionary or a named tuple and store your information in there. That way you can do something like this:
from collections import namedtuple
# Create a class called "household"
# with three fields, "owner", "size" and "members"
household = namedtuple("household", "owner size members")
househould_array = []
# Create some households and put them in the array
household_array.append(household("Family #1", 3, []))
household_array.append(household("Family #2", 1, []))
household_array.append(household("Family #3", 7, []))
# Loop over every household in the household_array
for family in household_array:
# Each `household` namedtulpe's values can be accessed by
# attribute as well as by index number
# family[1] == family.size == 3
# (for Family #1)
for member_number in range(family.size):
# family[2] == family.members == []
# (before we put anything in it)
family.members.append(generate_family_member())
You are mixing program data with variable names. It is okay to call a variable something generic; you do this all the time: e.g. in your for-loop, you use currentFam rather than the name of the family. Asking to uniquely name the array makes (no offense) as much sense as either asking what to name currentFam (it doesn't matter what you name it), or alternatively trying to do:
Andersons[0] = genSex()
Andersons[1] = genFirstName()
Andersons[2] = genAge()
Longs[0] = genSex()
Longs[1] = genFirstName()
Longs[2] = genAge()
Smiths[0] = genSex()
Smiths[1] = genFirstName()
Smiths[2] = genAge()
...
Variables are separate from program data.
You should just name your array person, and store it with other arrays. Even better would be to define a class Person(object): ..., so you could do things like x.name and x.age, but you don't need to do that. For example:
class Person(object):
def __init__(self, **kw):
self.data = kw
self.__dict__.update(kw)
def __repr__(self):
return str('Person(**{})'.format(self.data))
__str__ = __repr__
M = Person.M = 'm'
F = Person.F = 'f'
ALL_PEOPLE = set()
for ...:
person = Person(name=..., age=..., sex=...)
people.add(person)
Then to find people:
def findPeople(name=None, age=None, custom=set()):
matchers = custom
if name!=None:
matchers.add(lambda x:name.lower() in x.name.lower())
if age!=None:
matchers.add(lambda x:age==x.age)
return set(p for p in ALL_PEOPLE if all(m(p) for m in matchers))
Demo:
ALL_PEOPLE = set([
Person(name='Alex', age=5, sex=M),
Person(name='Alexander', age=33, sex=M),
Person(name='Alexa', age=21, sex=F)
])
>>> pprint.pprint( findPeople(name='alex', custom={lambda p: p.age>10}) )
{Person(**{'age': 33, 'name': 'Alexander', 'sex': 'm'}),
Person(**{'age': 21, 'name': 'Alexa', 'sex': 'f'})}
Wow, I really enjoyed reading all of the other answers.
So many great suggestions including, but not limited to:
#Sean Vieira suggests named-tuples -- an excellent, light-weight choice;
#ninjagecko uses a neat trick to dynamically assign instance attributes;
#Li-aung Yip mentions using the built-in sqlite3 module.
Much if not all of what's here has already been suggested.
If nothing else I hope this answer is an introduction to what classes may provide beyond what is provided by other data-structures.
Caveat: If performance is a huge concern, modeling each entity as a class might be overkill.
from __future__ import division, print_function
class Town(object):
def __init__(self, name=None, country=None, area_km2=0, population=0):
self.name = name
self.area_km2 = area_km2
self.area_mi2 = self.area_km2 * 0.38610217499077215
self.population = population
self.households = []
#property
def total_households(self):
return len(self.households)
#property
def population_density_per_km2(self):
try:
return self.population / self.area_km2
except ZeroDivisionError:
return 0
#property
def population_density_per_mi2(self):
try:
return self.population / self.area_mi2
except ZeroDivisionError:
return 0
class Household(object):
def __init__(self, primary_lang='Esperanto'):
self.primary_lang = primary_lang
self.members = []
#property
def total_members(self):
return len(self.members)
class Person(object):
def __init__(self, age=0, gender=None, first_name=None):
self.age = age
self.gender = gender
self.first_name = first_name
if __name__ == '__main__':
londontown = Town(name='London',
country='UK',
area_km2=1572,
population=7753600)
print(londontown.population_density_per_km2)
print(londontown.population_density_per_mi2)
a_household = Household()
a_household.members.append(
Person(age=10, gender='m', first_name='john'),
)
a_household.members.append(
Person(age=10, gender='f', first_name='jane')
)
londontown.households.append(a_household)
print(londontown.total_households)
print(a_household.total_members)