I've been getting the hang of Discord.py lately. Managing dictionaries, bot arguments, all that jazz. To finish off point management in my bot, I want to make a leaderboard. Following this answer, i structured my code slightly differently from them.
#bot.command(pass_context=True)
async def testboard(ctx, top:int=10):
total=[]
total = sorted(total,reverse=True)
board=discord.Embed(title = f'Top {top} highest awarded monkes.')
index = 1
if index == top:
return
else:
index += 1
for str in points:
userid = points[str]
user= bot.get_user(userid)
if index==1:
badge="\U0001F947"
elif index==2:
badge="\U0001F948"
elif index==3:
badge="\U0001F949"
else:
badge="\U0001F539"
board.add_field(name=f"**{badge}{index}. {user}**",value=(f"{points[str]} points"), inline=False)
await ctx.send(embed=board)
While it does display the points stored in my database, it doesn't do it from greatest to least, and doesn't display the name of the user.
As seen here: Result
I suspect it's because how my dictionary is structured.
{"userid": 0, "userid": 8, "userid": 0, "userid": 35, "userid": 11, "userid": 6}
Perhaps I can grab the name of the variable? If so, how can I do that?
The reason the username is just showing up as "None" is this portion of the code:
userid = points[str]
user= bot.get_user(userid)
If the str variable from the for loop is the user id, then the userid variable you are creating here is the point score for that user, not the id itself, so bot.get_user returns None because it won't find a user with, for example, an ID of 0. Change those lines to the following:
user= bot.get_user(int(str))
Secondly, the points are not in order because dictionaries are not ordered at all, so you would have to sort the objects manually. One way to do that is to get the entries of the dictionary, including both the key (the userid) and the value (number of points), sort it by points, and then iterate through it in a for loop:
for (userid, score) in sorted(points.items(), key=lambda entry: entry[1], reverse=True):
user= bot.get_user(int(userid))
if index==1:
badge="\U0001F947"
elif index==2:
badge="\U0001F948"
elif index==3:
badge="\U0001F949"
else:
badge="\U0001F539"
board.add_field(name=f"**{badge}{index}. {user}**",value=(f"{score} points"), inline=False)
I changed a couple of variable names here, most importantly str which is the class name for the string type, so it could potentially cause conflicts. Also, per your comment, the get_user method seems to require an integer and won't do conversion automatically.
Related
I am creating a dictionary in python in which a user enters his information, such as name and role.
Regarding the last two keys, I would like the user to write a simple letter in the input that corresponds exactly to the options I provide.
Example:
`userData= dict()
userData["name"]=input("Insert your name and last name: ")
userData["role"]=input("What is your role? \nA)Professor \nB) Student [A/B]: ")
#print(userData)`
Then below I'd like to create if statements where if the user enters "A" in the role key, it saves the value as "Professor" in the dictionary, and if he/she/them enters "B" it saves the value as "Student".
I tried writing something like this:
if userData["role"]== "A": userData["role"]== "Professor"
Only, in the dictionary, the value that is saved is "A" and not "Professor".
How can I get the value I want by making the user type only one letter?
Thank you in advance
PS: i'm completely new in Python and this is only an exercise class, please be gentle.
Possible solution is the following:
userData= {}
userData["name"]=input("Insert your name and last name: ")
# start infinite loop until correct role will be entered
while True:
role=input("What is your role? \nA) Professor \nB) Student\n").upper()
if role == 'A':
userData["role"] = "Professor"
break
elif role == 'B':
userData["role"] = "Student"
break
else:
print(f"{role} is incorrect role. Please enter correct role A or B")
continue
print(userData)
Prints
Insert your name and last name: Gray
What is your role?
A) Professor
B) Student
B
{'name': 'Gray', 'role': 'Student'}
Another solution that does not require the use of if statements is using another dictionary for role entries.
# define roles dict
roles_dict = {"A": "Professor", "B":"Student"}
# get user data
userData= dict()
userData["name"]=input("Insert your name and last name: ")
role_letter=input("What is your role? \nA) Professor \nB) Student [A/B]: ")
# update dict
userData.update({"role": roles_dict[role_letter]})
print(userData)
Prints:
Insert your name and last name: Jayson
What is your role?
A)Professor
B) Student [A/B]: A
{'name': 'Jayson', 'role': 'Professor'}
I want to put a dictionary which contains user information into a new json file and i want it to give every user a number when i call a function 'new_user'. How can i do it?
from get_stars import rate_service
user_info = {
'user_number': int(),
'user_info': {
'username': 'username',
'user_location': 'user_location',
'used_application': 'used_application',
'stars': int()
}
}
def get_user_info():
new_user = user_info.copy()
new_user['user_info']['username'] = input(f"\nEnter your name: ")
new_user['user_info']['stars'] = rate_service()
return new_user
from userinfo import get_user_info
import json
def new_user():
user = get_user_info()
filename = f"user.json"
with open(filename, 'w'):
json.dump(filename, user)
For example i call that func and in my json file there is dict with user number 1, but when i call it next time this number increases by 1
If you want to avoid reading the file before assigning the number I would use a Guuid, that is basically a random number so big that is at all effects guaranteed not to collide with others.
If you want to assign a integer then you have two choices:
reading the files taking the max value and assign before setting
the user info
having another file with the "metadata" of the user
count which you will also read.
Normally this kind of operations are made using a database that will take care out of them.
You can also use a indexed dataframe a numerated dictionary or even a list of user as a middle data-structure that will help you if you go for the option 1.
Assign a uuid: https://docs.python.org/3/library/uuid.html
import uuid
new_user = {}
new_user['user_number'] = uuid.uuid1()
# output: {'user_number': UUID('d2586590-abb8-11ec-94de-acde48001122')}
print(new_user)
Whenever I updated my insert_one with a new field to use, I had to always delete the old posts in the collection. I know there are manual methods of updating such fields using update_many but I know it's inefficient.
For example:
posts.insert_one({
"id": random.randint(1,10000)
"value1": "value1",
"value2": "value2"
})
I use the following code to check if the document exists or not. How would this work for a field?
if posts.find({'id': 12312}).count() > 0:
I know I can easily overwrite the previous data but I know people won't enjoy having their data wiped every other month.
Is there a way to add the field to a document in Python?
How would this work for a field?
You can use $exists to check whether a field exists in a doc.
In your case, you can combine this with find
find({ 'id':1, "fieldToCheck":{$exists:"true"}})
It will return the doc if it exists with id = 1, fieldToCheck is present in doc with id = 1
You can skip id=1, in that case, it will return all docs where fieldToCheck exists
Is there a way to add the field to a document in Python?
You could use update with new field, it will update if it is present else it will insert.
update({"_id":1}, {field:"x"})
If field is present, it will set to x else it will add with field:x
Beware of update options like multi, upsert
Yes you can you use update command in mongoDB shell to do that. check here
This is the command to use...
db.collection.update({},{$set : {"newfield":1}},false,true)
The above will work in the mongoDB shell. It will add newfield in all the documents, if it is not present.
If you want to use Python, use pymongo.
For python, following command should work
db.collection.update({},{"$set" : {"newfield":1}},False, True)
Thanks to john's answer I have made an entire solution that automatically updates documents without the need to run a task meaning you don't update inactive documents.
import datetime
import pymongo
database = pymongo.MongoClient("mongodb://localhost:27017") # Mongodb connection
db = database.maindb # Database
posts = db.items # Collection within a database
# A schema equivalent function that returns the object
def user_details(name, dob):
return {
"username": name, # a username/id
"dob": dob, # some data
"level": 0, # some other data
"latest_update": datetime.datetime.fromtimestamp(1615640176)
# Must be kept to ensure you aren't doing it that often
}
# The first schema changed for example after adding a new feature
def user_details2(name, dob, cake):
return {
"username": name, # a username/id
"dob": dob, # Some data
"level": 0, # Some other data
"cake": cake, # Some new data that isn't in the document
"latest_update": datetime.datetime.utcnow() # Must be kept to ensure you aren't doing it that often
}
def check_if_update(find, main_document,
collection): # parameters: What you find a document with, the schema dictionary, then the mongodb collection
if collection.count_documents(find) > 0: # How many documents match, only proceed if it exists
fields = {} # Init a dictionary
for x in collection.find(find): # You only want one for this to work
fields = x
if "latest_update" in fields: # Just in case it doesn't exist yet
last_time = fields["latest_update"] # Get the time that it was last updated
time_diff = datetime.datetime.utcnow() - last_time # Get the time difference between the utc time now and the time it was last updated
if time_diff.total_seconds() < 3600: # If the total seconds of the difference is smaller than an hour
print("return")
return
db_schema = main_document # Better naming
db_schema["_id"] = 0 # Adds the _id schema_key into the dictionary
if db_schema.keys() != fields:
print("in")
for schema_key, schema_value in db_schema.items():
if schema_key not in fields.keys(): # Main key for example if cake is added and doesn't exist in db fetched fields
collection.update_one(find, {"$set": {schema_key: schema_value}})
else: # Everything exists and you want to check for if a dictionary within that dictionary is changed
try:
sub_dict = dict(schema_value) # Make the value of it a dictionary
# It exists in the schema dictionary but not in the db fetched document
for key2, value2 in sub_dict.items():
if key2 not in fields[schema_key].keys():
new_value = schema_value
new_value[
key2] = value2 # Adding the key and value from the schema dictionary that was added
collection.update_one(find,
{"$set": {schema_key: new_value}})
# It exists in the db fetched document but not in the schema dictionary
for key2, value2 in fields[schema_key].items():
if key2 not in sub_dict.keys():
new_dict = {} # Get all values, filter then so that only the schema existent ones are passed back
for item in sub_dict:
if item != key2:
new_dict[item] = sub_dict.get(item)
collection.update_one(find, {"$set": {schema_key: new_dict}})
except: # Wasn't a dict
pass
# You removed a value from the schema dictionary and want to update it in the db
for key2, value2 in fields.items():
if key2 not in db_schema:
collection.update_one(find, {"$unset": {key2: 1}})
else:
collection.insert_one(main_document) # Insert it because it doesn't exist yet
print("start")
print(posts.find_one({"username": "john"}))
check_if_update({"username": "john"}, user_details("john", "13/03/2021"), posts)
print("inserted")
print(posts.find_one({"username": "john"}))
check_if_update({"username": "john"}, user_details2("john", "13/03/2021", "Lemon drizzle"), posts)
print("Results:")
print(posts.find_one({"username": "john"}))
It is available as a gist
So I want to get the json keys and compare them with the variable q
Json file
{
"api_key": "YOUR AUTOBUY API KEY",
"prefix": "PREFIX FOR BOT COMMANDS (for e.x !redeem, ?claim, etc",
"redeem_message": "Thanks for redeeming your order for {0}, I have added the ROLE_NAME_HERE role.",
"role_id": "REPLACE THIS WITH THE ID OF THE ROLE YOU WANT TO GIVE UPON REDEEMING",
"redeem_channel_id": "REPLACE THIS WITH THE CHANNEL ID WHERE PEOPLE CAN USE THE REDEEM COMMAND",
"bot_token": "PUT YOUR DISCORD BOT TOKEN HERE"
}
Code
import json
def search(q):
with open("config.json") as f:
data = json.load(f)
for obj in data:
print(data[obj])
search(q="role_id")
Expected output: REPLACE THIS WITH THE ID OF THE ROLE YOU WANT TO GIVE UPON REDEEMING (cause q = role_id and I want it to return the value of the key)
Actual output:
YOUR AUTOBUY API KEY
PREFIX FOR BOT COMMANDS (for e.x !redeem, ?claim, etc
Thanks for redeeming your order for {0}, I have added the ROLE_NAME_HERE role.
REPLACE THIS WITH THE ID OF THE ROLE YOU WANT TO GIVE UPON REDEEMING
REPLACE THIS WITH THE CHANNEL ID WHERE PEOPLE CAN USE THE REDEEM COMMAND
PUT YOUR DISCORD BOT TOKEN HERE
Super simple, no need for a for loop if you just need one value:
import json
def search(q):
with open("config.json") as f:
data = json.load(f)
print(data[q])
search(q="role_id")
below
data = {'A':12,'B':13}
q = 'A2'
value = data.get(q)
if value is not None:
print(value)
else:
print('{} not found'.format(q))
I am restoring data for users' in a nested dictionary as follows:
user_dict = {user_name:{product:amount, product:amount}}
So, user_name is the user's user_name. The product is the what the user put in the basket(e.g. orange juice), and the amount is the amount of a specific product. So, for instance:
user_dict = {"sara" : {"orange juice": 2, "apple juice" : 4}, "ali": {"banana":3}}
what I want to do is that when a user buys some stuff and put them in the (user_dict) then logs out, when the same user logs in again, I want to get the user's basket as follows: let's say sara logged in and put some stuff in her basket(user_dict) then logged out, when sara logs in again, I want to make get sara's information from the (user_dict) in a new dictionary(called basket) like this:
basket = {"orange juice": 2, "apple juice" : 4}
Also, I want to make the basket empty if the user (user_name) who logged in does not have anything in the basket. So, I want to make users' things do not interfere and I do not want to lose any information. I have almost finished my project but this is what I am stuck at, so help me guys, please.
Here are my lines of code for this task:(but the code does not work as I want)
for key in user_dict.keys():
if user_name == key:
key_sto = " "
nested_dict = user_dict[user_name]
val = a.values()
val_in_nested = val[0]
key_in_nested = key_sto.join(a)
basket[key_in_nested] = val_in_nested
elif user_name != key :
basket = {}
I was able to solve the problem, so here is the code.
for key, amount in user_dict.items():
if key == user_name:
basket = amount
if user_name not in user_dict.keys():
basket = {}