Trying to make a captcha field for forms - django

I'm trying to create a custom captcha field for a form, and it seems to be working alright except for the fact that when the code is supposed to pick a random captcha to return to the end-user for them to solve, it returns a ValueError: too many values to unpack (expected 2). I think this is because the list isn't being randomised and python is selecting the entire list to use as the user's captcha. How can I fix this issue?
class CaptchaField(IntegerField):
widget = CaptchaInput
error_msgs = { 'incorrect': _('Captcha incorrect- try again'), }
def __init__(self):
captcha_array = (
('What is the product of fifteen and four?', 60),
('What is four plus four?', 8),
('What is nine times one?', 9),
('How many letters are in the word orange?', 6),
('What is the sum of ten and two?', 12),
('What is the difference of eighty-four and nineteen?', 65),
('How many letters are in the word forest?', 6),
('How many letter are in the word apple?', 5),
('If there are four palm trees and one dies, how many are alive?', 3),
('What is four divided by two?', 2),
('How many letters are in the name of the capital of France?', 5),
)
captcha = random.choice(captcha_array)
for (a,b) in captcha:
return a
def validate(self, value):
for (a,b) in captcha:
if value == b:
return value
else:
raise ValidationError(self.error_msgs['incorrect'], code = 'incorrect')

captcha = random.choice(captcha_array) returns a tuple (i.e. ('What is the product of fifteen and four?', 60)). If you only want to return a string 'What is the product of fifteen and four?' you can just do return captcha[0].
Otherwise your loop will raise ValueError:
for (a,b) in captcha:
return a
captcha being a tuple will supply only 1 value, not two - first string, then number, to the loop. To unpack values like you do captcha has to be an iterable of tuples (list, dict, ...).
Also, do you expect captcha do be accessible later in validate? If so you need to save captcha in self.

Related

Python3: Checking if a key word within a dictionary matches any part of a string

I'm having trouble converting my working code from lists to dictionaries. The basics of the code checks a file name for any keywords within the list.
But I'm having a tough time understanding dictionaries to convert it. I am trying to pull the name of each key and compare it to the file name like I did with lists and tuples. Here is a mock version of what i was doing.
fname = "../crazyfdsfd/fds/ss/rabbit.txt"
hollow = "SFV"
blank = "2008"
empty = "bender"
# things is list
things = ["sheep", "goat", "rabbit"]
# other is tuple
other = ("sheep", "goat", "rabbit")
#stuff is dictionary
stuff = {"sheep": 2, "goat": 5, "rabbit": 6}
try:
print(type(things), "things")
for i in things:
if i in fname:
hollow = str(i)
print(hollow)
if hollow == things[2]:
print("PERFECT")
except:
print("c-c-c-combo breaker")
print("\n \n")
try:
print(type(other), "other")
for i in other:
if i in fname:
blank = str(i)
print(blank)
if blank == other[2]:
print("Yes. You. Can.")
except:
print("THANKS OBAMA")
print("\n \n")
try:
print(type(stuff), "stuff")
for i in stuff: # problem loop
if i in fname:
empty = str(i)
print(empty)
if empty == stuff[2]: # problem line
print("Shut up and take my money!")
except:
print("CURSE YOU ZOIDBERG!")
I am able to get a full run though the first two examples, but I cannot get the dictionary to run without its exception. The loop is not converting empty into stuff[2]'s value. Leaving money regrettably in fry's pocket. Let me know if my example isn't clear enough for what I am asking. The dictionary is just short cutting counting lists and adding files to other variables.
A dictionary is an unordered collection that maps keys to values. If you define stuff to be:
stuff = {"sheep": 2, "goat": 5, "rabbit": 6}
You can refer to its elements with:
stuff['sheep'], stuff['goat'], stuff['rabbit']
stuff[2] will result in a KeyError, because the key 2 is not found in your dictionary. You can't compare a string with the last or 3rd value of a dictionary, because the dictionary is not stored in an ordered sequence (the internal ordering is based on hashing). Use a list or tuple for an ordered sequence - if you need to compare to the last item.
If you want to traverse a dictionary, you can use this as a template:
for k, v in stuff.items():
if k == 'rabbit':
# do something - k will be 'rabbit' and v will be 6
If you want to check to check the keys in a dictionary to see if they match part of a string:
for k in stuff.keys():
if k in fname:
print('found', k)
Some other notes:
The KeyError would be much easier to notice... if you took out your try/except blocks. Hiding python errors from end-users can be useful. Hiding that information from YOU is a bad idea - especially when you're debugging an initial pass at code.
You can compare to the last item in a list or tuple with:
if hollow == things[-1]:
if that is what you're trying to do.
In your last loop: empty == str(i) needs to be empty = str(i).

Python - values only being used if they are after the initial in a list

I'm trying to make a program that finds likely friends by finding similarities in user scores in different topics:
def similarity(user,d_list):
user_data=()
user_score=[]
for item in d_list:
if user in item:
user_data=item[1]
if item[0]!=user :
local_score=0
local_score=sum(a*b for a,b in zip(user_data,item[1]))
user_score.append(local_score)
print user_score
return
When using:
ratings=(("mark",[4,8,0,7]),("bob",[3,6,9,1]),("jim",[11,4,6,3]),("steve",[22,19,1,0]))
As the d_list and "mark" as the user, the program works as expected, giving:
[67, 97, 240]
When bob is used, the comparison with mark is set to 0.
[0, 114, 189]
When steve is used the set is just zeros. I'm at a loss.
Got it. The calculation was still nested inside the first iteration through d_list.
def similarity(user,d_list):
user_data=()
user_score=[]
for item in d_list:
if user in item:
user_data=item[1]
for item in d_list:
if item[0]!=user :
local_score=0
local_score=sum(a*b for a,b in zip(user_data,item[1]))
user_score.append(local_score)
print user_score
return

Custom Django count filtering

A lot of websites will display:
"1.8K pages" instead of "1,830 pages"
or
"43.2M pages" instead of "43,200,123 pages"
Is there a way to do this in Django?
For example, the following code will generate the quantified amount of objects in the queryset (i.e. 3,123):
Books.objects.all().count()
Is there a way to add a custom count filter to return "3.1K pages" instead of "3,123 pages?
Thank you in advance!
First off, I wouldn't do anything that alters the way the ORM portion of Django works. There are two places this could be done, if you are only planning on using it in one place - do it on the frontend. With that said, there are many ways to achieve this result. Just to spout off a few ideas, you could write a property on your model that calls count then converts that to something a little more human readable for the back end. If you want to do it on the frontend you might want to find a JavaScript lib that could do the conversion.
I will edit this later from my computer and add an example of the property.
Edit: To answer your comment, the easier one to implement depends on your skills in python vs in JavaScript. I prefer python so I would probably do it in there somewhere on the model.
Edit2: I have wrote an example to show you how I would do a classmethod on a base model or on the model that you need these numbers on. I found a python package called humanize and I took its function that converts these to readable and modified it a bit to allow for thousands and took out some of the super large number conversion.
def readable_number(value, short=False):
# Modified from the package `humanize` on pypy.
powers = [10 ** x for x in (3, 6, 9, 12, 15, 18)]
human_powers = ('thousand', 'million', 'billion', 'trillion', 'quadrillion')
human_powers_short = ('K', 'M', 'B', 'T', 'QD')
try:
value = int(value)
except (TypeError, ValueError):
return value
if value < powers[0]:
return str(value)
for ordinal, power in enumerate(powers[1:], 1):
if value < power:
chopped = value / float(powers[ordinal - 1])
chopped = format(chopped, '.1f')
if not short:
return '{} {}'.format(chopped, human_powers[ordinal - 1])
return '{}{}'.format(chopped, human_powers_short[ordinal - 1])
class MyModel(models.Model):
#classmethod
def readable_count(cls, short=True):
count = cls.objects.all().count()
return readable_number(count, short=short)
print(readable_number(62220, True)) # Returns '62.2K'
print(readable_number(6555500)) # Returns '6.6 million'
I would stick that readable_number in some sort of utils and just import it in your models file. Once you have that, you can just stick that string wherever you would like on your frontend.
You would use MyModel.readable_count() to get that value. If you want it under MyModel.objects.readable_count() you will need to make a custom object manager for your model, but that is a bit more advanced.

Compare each item in a list with all previous items, print only unique items

I am using the following regexp to match all occurrences of a special kind of number:
^([0-57-9]|E)[12][0-9]{3}[A-Z]?[A-Z]([0-9]{3}|[0-9]{4})
Let's assume that this regex matches the following five numbers:
31971R0974
11957E075
31971R0974-A01P2
31971R0974-A05
51992PC0405
These matches are then printed using the following code. This prints each item in the list and if the item contains a dash, everything after the dash is discarded.
def number_function():
for x in range(0, 10):
print("Number", number_variable[x].split('-', 1)[0])
However, this would print five lines where lines 1, 3 and 4 would be the same.
I need your help to write a script which compares each item with all previous items and only prints the item if it does not already exist.
So, the desired output would be the following three lines:
31971R0974
11957E075
51992PC0405
EDIT 2:
I solved it! I just needed to do some moving around. Here's the finished product:
def instrument_function():
desired = set()
for x in range(0, 50):
try:
instruments_celex[x]
except IndexError:
pass
else:
before_dash = instruments_celex[x].split('-', 1)[0]
desired.add(before_dash)
for x in desired:
print("Cited instrument", x)
I've done practically no python up until now, but this might do what you're after
def number_function():
desired = set()
for x in range(0, 10):
before_hyphen = number_variable[x].split('-', 1)[0]
desired.add(before_hyphen)
for x in desired:
print("Number", x)
Here is a version of your "finished" function that is more reaonable.
# Don't use instruments_celex as a global variable, that's terrible.
# Pass it in to the function instead:
def instrument_function(instruments_celex):
unique = set()
# In Python you don't need an integer loop variable. This is not Java.
# Just loop over the list:
for entry in instruments_celex:
unique.add(entry.split('-', 1)[0])
for entry in unique:
print("Cited instrument", entry)
You can also make use of generator expressions to make this shorter:
def instrument_function(instruments_celex): 
unique = set(entry.split('-', 1)[0] for entry in instruments_celex)
for entry in set:
print("Cited instrument", entry)
That's it. It's so simple in fact that I wouldn't make a separate function of it unless I do it at least two times in the program.

Errors in Decimal Calcs within def clean method?

I'm attempting a few simple calculations in a def clean method following validation (basically spitting out a euro conversion of retrieved uk product price on the fly). I keep getting a TypeError.
Full error reads:
Cannot convert {'product': , 'invoice': , 'order_discount': Decimal("0.00"), 'order_price': {...}, 'order_adjust': None, 'order_value': None, 'DELETE': False, 'id': 92, 'quantity': 8} to Decimal
so I guess django is passing through the entire cleaned_data form to Decimal method. Not sure where I'm going wrong - the code I'm working with is:
def clean_order_price(self):
cleaned_data = self.cleaned_data
data = self.data
order_price = cleaned_data.get("order_price")
if not order_price:
try:
existing_price = ProductCostPrice.objects.get(supplier=data['supplier'], product_id=cleaned_data['product'], is_latest=True)
except ProductCostPrice.DoesNotExist:
existing_price = None
if not existing_price:
raise forms.ValidationError('No match found, please enter new price')
else:
if data['invoice_type'] == 1:
return existing_price.cost_price_gross
elif data['invoice_type'] == 2:
exchange = EuroExchangeRate.objects.latest('exchange_date')
calc = exchange.exchange_rate * float(existing_price.cost_price_gross)
calc = Decimal(str(calc))
return calc
return cleaned_data
If the invoice is of type 2 (a euro invoice) then the system should grab the latest exchange rate and apply that to the matching UK pound price pulled through to get euro result.
Should performing a decimal conversion be a problem within def clean method?
Thanks
I'm going to assume you've made an indentation error on pasting, and the lines from if data['invoice_type'] == 1: should actually be indented one level back - otherwise, as Alex says, the code will never get to the Decimal conversion.
There are multiple other problems with this code, but the biggest is that the last line returns the whole cleaned_data dictionary, rather than the value of this particular field - I suspect this is the cause of the error you are seeing.
Other than that, there is a big error where you calculate calc by multiplying cost_price_gross by exchange. Here exchange is an instance of EuroExchangeRate, rather than a number, so this calculation will not work.