The model and controller does. The model handles all data entries, delete, and edit. The view does only view and the controller communicates with both. I know there are better ways of doing this but, it's helping me get my head around the mvc concept.
I've ran into a problem I thought I already fixed. Had every thing working and started to work on the add new character
method and going to use the same form for both create and edit. My problem came back. I have a delete and create method in the backend.py (where all the model function are). If I use both the delete and create together it does update when changing the guild class. If I change anything else seems to work fine. Just can't change the guild class.
I'll post all the updated code in hope maybe someone can see what I'm missing. If the don't mind looking through a ton of code.
Well I have it all working now. Still a few bugs when entering skills and weapons. converting a list to a dict. (in the convert function). Probably some bugs I haven't encountered yet but, overall seems to work ok.
I have updated the code. Just noticed that I have not added a window to create guilds.
card.py
import tkinter as tk
from tkinter.scrolledtext import ScrolledText
from tkinter import ttk, messagebox
import json
import backend
from os import sys
# The name of our database file
file = 'card.json'
class BasicModel:
'''
BasicModel controls all of the methods for calling backend functions that:
create, delete, and edit. It gets data for displaying in the tkinter view window.
'''
def __init__(self, file):
'''
Set the instance variables and create the database if it doesn't exist.
'''
self.file = file
backend._create_database(self.file)
def create_guild(self, guild):
'''
Method for creating guilds
'''
backend._create_guild(self.file, guild)
def create_character(self, character):
'''
Method for creating characters
'''
backend._create(self.file, character)
def delete_character(self, character, guild):
'''
Method for deleting characters
'''
backend._delete(self.file, character, guild)
def get_character(self, name):
'''
Method for getting character data by name
'''
return backend._get_character(self.file, name)
def get_all(self):
'''
Method for getting all character names
'''
return backend._get_all(self.file)
def get_guilds(self):
'''
Method for getting all guilds
'''
return backend.get_guilds(self.file)
def get_character_by_guild(self, guild):
'''
Method for getting a character by guild name
'''
return backend.get_character_by_guild(self.file, guild)
class View:
'''
This is the main view window. It displays all data and buttons
'''
def __init__(self, parent):
'''
Set some instance variables and build the view
'''
self.parent = parent
self.parent.title('Card Collection')
self.parent.columnconfigure(0, weight=1)
self.parent.rowconfigure(0, weight=1)
# Container
self.container = tk.Frame(self.parent)
self.container['borderwidth'] = 1
self.container['highlightbackground'] = 'lightgray'
self.container['highlightcolor'] = 'lightgray'
self.container['highlightthickness'] = 1
self.container.grid(column=0, row=0, sticky='news', padx=8, pady=8)
self.container.grid_columnconfigure(0, weight=3)
# Header
self.title_label = tk.Label(self.container, text='Card Collection')
self.title_label['bg'] = 'gray86'
self.title_label['font'] = ('"" 16 bold')
self.title_label['relief'] = 'groove'
self.title_label.grid(column=0, row=0, sticky='new', pady=4, padx=4, ipady=4)
# Search Frame
self.search_frame = tk.Frame(self.container, padx=4, pady=4)
self.search_frame['highlightcolor'] = 'gray80'
self.search_frame['highlightbackground'] = 'gray80'
self.search_frame['highlightthickness'] = 1
self.search_frame.grid(column=0, row=1, sticky='news', pady=4, padx=5)
self.search_frame.grid_columnconfigure(0, weight=0)
self.search_frame.grid_columnconfigure(1, weight=2)
self.search_frame.grid_columnconfigure(2, weight=3)
# Search Boxes
self.search_label = tk.Label(self.search_frame, text='Search by ...')
self.search_label.grid(column=0, row=0, sticky='new', padx=(2, 2))
# Create combobox
self.show_ = tk.StringVar()
self.menu = ttk.Combobox(self.search_frame, justify='left', state='readonly', \
cursor='hand2', textvariable=self.show_)
self.menu.grid(column=1, row=0, sticky='new', padx=(2, 1))
# Instrcution label
info_label = tk.Label(self.search_frame, bg='lightyellow')
info_label['text'] = 'Doubleclick a name to view stats.'
info_label['relief'] = 'groove'
info_label.grid(column=2, row=0, sticky='new', padx=(1, 0), ipady=1)
# Mid Frame Container
self.mid_container = tk.Frame(self.container)
self.mid_container.grid(column=0, row=2, sticky='new', pady=4, padx=4)
self.mid_container.grid_columnconfigure(0, weight=3, uniform='box')
self.mid_container.grid_columnconfigure(1, weight=3, uniform='box')
# Container for the listbox
self.list_container = tk.Frame(self.mid_container)
self.list_container.grid(column=0, row=0, sticky='news')
self.list_container.grid_columnconfigure(0, weight=3)
# listbox for left side
self.listbox = tk.Listbox(self.list_container, height=15, selectmode='single')
self.listbox.grid(column=0, row=0, sticky='news')
# Create a scrollbar for the list
self.scrollbar = tk.Scrollbar(self.list_container)
self.scrollbar.grid(column=1, row=0, sticky='ns')
self.listbox.configure(yscrollcommand = self.scrollbar.set)
self.scrollbar.config(command = self.listbox.yview)
# Button Conainer
self.btn_container = tk.Frame(self.container)
self.btn_container['highlightcolor'] = 'gray86'
self.btn_container['highlightbackground'] = 'gray86'
self.btn_container['highlightthickness'] = 1
self.btn_container.grid(column=0, row=3, sticky='news', padx=4, pady=4)
for i in range(4):
self.btn_container.grid_columnconfigure(i, weight=3, uniform='buttons')
# Create some buttons
self.add = tk.Button(self.btn_container, text='Add')
self.add['cursor'] = 'hand2'
self.add['bg'] = 'skyblue'
self.add.grid(column=0, row=0, sticky='new', padx=2, pady=4)
self.edit = tk.Button(self.btn_container, text='Edit')
self.edit['cursor'] = 'hand2'
self.edit['bg'] = 'orange'
self.edit.grid(column=1, row=0, sticky='new', padx=2, pady=4)
self.delete = tk.Button(self.btn_container, text='Delete')
self.delete['cursor'] = 'hand2'
self.delete['bg'] = 'orangered'
self.delete.grid(column=2, row=0, sticky='new', padx=2, pady=4)
self.exit = tk.Button(self.btn_container, text='Exit')
self.exit['bg'] = 'tomato'
self.exit['command'] = sys.exit
self.exit['cursor'] = 'hand2'
self.exit.grid(column=3, row=0, sticky='new', padx=2, pady=4)
class TopWindow:
def __init__(self):
'''
TopWindow contains the form and buttons for creating and editing characters
'''
self.window = tk.Toplevel()
self.window.geometry('480x208+200+200')
self.window.title(None)
self.window['padx'] = 8
self.window['pady'] = 4
self.window.resizable(False, False)
self.window.columnconfigure(0, weight=1)
self.window.rowconfigure(0, weight=1)
self.container = tk.Frame(self.window)
self.container.grid(column=0, row=0, sticky='new')
self.container.columnconfigure(0, weight=3)
self.header = tk.Label(self.container)
self.header['font'] = '"" 14 italic'
self.header['bg'] = 'silver'
self.header['relief'] = 'groove'
self.header.grid(column=0, row=0, sticky='new')
mid_frame = tk.Frame(self.container)
mid_frame.grid(column=0, row=1, sticky='new')
mid_frame.grid_columnconfigure(0, weight=1)
mid_frame.grid_columnconfigure(1, weight=2)
self.name_header = tk.Label(mid_frame, text='Name:', anchor='w')
self.name_header['font'] = '"" 10 bold'
self.name_header['highlightcolor'] = 'gray'
self.name_header['highlightbackground'] = 'gray'
self.name_header['highlightthickness'] = 1
self.name_header['borderwidth'] = 1
self.name_header.grid(column=0, row=0, sticky='new', padx=(1,2))
self.namevar = tk.StringVar()
self.name_label = tk.Entry(mid_frame)
self.name_label['textvariable'] = self.namevar
self.name_label['highlightcolor'] = 'gray'
self.name_label['highlightbackground'] = 'gray'
self.name_label['highlightthickness'] = 1
self.name_label['borderwidth'] = 1
self.name_label.grid(column=1, row=0, sticky='new', padx=(2,2))
age_header = tk.Label(mid_frame, text='Age:', anchor='w')
age_header['font'] = '"" 10 bold'
age_header['highlightcolor'] = 'gray'
age_header['highlightbackground'] = 'gray'
age_header['highlightthickness'] = 1
age_header['borderwidth'] = 1
age_header.grid(column=0, row=1, sticky='new', padx=(1,2), pady=(2,2))
self.agevar = tk.StringVar()
self.age = tk.Entry(mid_frame)
self.age['textvariable'] = self.agevar
self.age['highlightcolor'] = 'gray'
self.age['highlightbackground'] = 'gray'
self.age['highlightthickness'] = 1
self.age['borderwidth'] = 1
self.age.grid(column=1, row=1, sticky='new', padx=(2,2), pady=(2,2))
guild_header = tk.Label(mid_frame, text='Guild:', anchor='w')
guild_header['font'] = '"" 10 bold'
guild_header['highlightcolor'] = 'gray'
guild_header['highlightbackground'] = 'gray'
guild_header['highlightthickness'] = 1
guild_header['borderwidth'] = 1
guild_header.grid(column=0, row=2, sticky='new', padx=(1,2), pady=(2,2))
self.guildvar = tk.StringVar()
self.guild = ttk.Combobox(mid_frame, state='readonly', cursor='hand2')
self.guild['textvariable'] = self.guildvar
self.guild.grid(column=1, row=2, sticky='new', padx=(2,2), pady=(2,2))
skills_header = tk.Label(mid_frame, text='Skills:', anchor='w')
skills_header['font'] = '"" 10 bold'
skills_header['highlightcolor'] = 'gray'
skills_header['highlightbackground'] = 'gray'
skills_header['highlightthickness'] = 1
skills_header['borderwidth'] = 1
skills_header.grid(column=0, row=3, sticky='new', padx=(1,2), pady=(2,2))
self.skillsvar = tk.StringVar()
self.skills = tk.Entry(mid_frame)
self.skills['textvariable'] = self.skillsvar
self.skills['highlightcolor'] = 'gray'
self.skills['highlightbackground'] = 'gray'
self.skills['highlightthickness'] = 1
self.skills['borderwidth'] = 1
self.skills.grid(column=1, row=3, sticky='new', padx=(2,2), pady=(2,2))
weapons_header = tk.Label(mid_frame, text='Weapons:', anchor='w')
weapons_header['font'] = '"" 10 bold'
weapons_header['highlightcolor'] = 'gray'
weapons_header['highlightbackground'] = 'gray'
weapons_header['highlightthickness'] = 1
weapons_header['borderwidth'] = 1
weapons_header.grid(column=0, row=4, sticky='new', padx=(1,2), pady=(2,2))
self.weaponsvar = tk.StringVar()
self.weapons = tk.Entry(mid_frame)
self.weapons['textvariable'] = self.weaponsvar
self.weapons['highlightcolor'] = 'gray'
self.weapons['highlightbackground'] = 'gray'
self.weapons['highlightthickness'] = 1
self.weapons['borderwidth'] = 1
self.weapons.grid(column=1, row=4, sticky='new', padx=(2,2), pady=(2,2))
btn_container = tk.Frame(mid_frame)
btn_container.grid(column=0, columnspan=2, row=5, sticky='new')
btn_container.grid_columnconfigure(0, weight=3, uniform='buttons')
btn_container.grid_columnconfigure(1, weight=3, uniform='buttons')
self.save_button = tk.Button(btn_container)
self.save_button['text'] = 'Save'
self.save_button['bg'] = 'skyblue'
self.save_button['cursor'] = 'hand2'
self.save_button.grid(column=0, row=0, sticky='new', padx=(0,1), pady=(4.0))
self.cancel_button = tk.Button(btn_container)
self.cancel_button['text'] = 'Cancel'
self.cancel_button['cursor'] = 'hand2'
self.cancel_button['bg'] = 'orangered'
self.cancel_button.grid(column=1, row=0, sticky='new', padx=(1,0), pady=(4,0))
class Controller:
def __init__(self, model, view):
'''
Controller handles all communiction and data transfer between the View and BasicModel
'''
# Set some instance variables
self.model = model
self.view = view
# Give our buttons in view some functionality
self.view.edit['command'] = lambda: Controller2(self.model, self.view, target='edit', action='edit')
self.view.add['command'] = lambda: Controller2(self.model, self.view, target='add')
self.view.delete['command'] = lambda: self.delete_character()
def populate(self):
'''
populate does just that. It populates the combobox in View and sets a bind
to display character names in the listbox.
'''
items = ['Names']
items = items + self.model.get_guilds()
self.view.menu['values'] = items
self.view.menu.current(0)
self.view.menu.bind('<<ComboboxSelected>>', lambda x: self.get_guild())
for i, data in enumerate(self.model.get_all()):
self.view.listbox.insert(i, data.title())
self.view.listbox.select_set(0)
self.view.listbox.bind('<Double-Button-1>', lambda x: self.get_character())
# Call self.body to give the label_frame on the right labels and data display
self.body()
def get_guild(self):
'''
get_guild changes the listbox display by guild name in the menu combobox
'''
self.view.listbox.delete(0, tk.END)
guild = self.view.menu.get().lower()
if guild == 'names':
data = self.model.get_all()
else:
data = self.model.get_character_by_guild(guild)
for i, name in enumerate(data):
self.view.listbox.insert(i, name.title())
self.view.listbox.select_set(0)
self.body()
def get_character(self):
'''
Calls self.body to display labels and character info
'''
self.body()
def delete_character(self):
'''
Method calls BasicModel to delete a character
'''
try:
character = self.view.listbox.get(self.view.listbox.curselection()[0]).lower()
data = self.model.get_character(character)
confirm = messagebox.askyesno('Warning!',f'Are you sure you want to delete {character.title()}?')
if confirm:
self.model.delete_character(character, data['guild'])
self.view.listbox.delete(0, tk.END)
self.view.menu.current(0)
self.label_frame.destroy()
self.populate()
messagebox.showinfo('Character Deleted', f'{character.title()} has been deleted.')
else:
print('Action has been canceled.')
except IndexError:
messagebox.showerror('No Character', 'There is no character to delete.')
def frame(self):
'''
Creates a frame for self.body to have a container for the labels
'''
self.label_frame = tk.Frame(self.view.mid_container, pady=2, padx=4)
self.label_frame['highlightcolor'] = 'gray80'
self.label_frame['highlightbackground'] = 'gray80'
self.label_frame['highlightthickness'] = 1
self.label_frame.grid(column=1, row=0, sticky='news', padx=(4,0))
self.label_frame.grid_columnconfigure(0, weight=0)
self.label_frame.grid_columnconfigure(1, weight=3)
def body(self):
'''
Method for formatting and displaying character data
'''
error = False
try:
character = self.view.listbox.get(self.view.listbox.curselection()[0])
except IndexError:
error = True
if error:
pass
else:
data = self.model.get_character(character.lower())
self.frame()
name_label_list = []
ability_label_list = []
spell_data_list = []
weapons_data_list = []
for i, item in enumerate(data):
name_label_list.append(tk.Label(self.label_frame, anchor='w'))
ability_label_list.append(tk.Label(self.label_frame, anchor='w', wraplength=250))
if isinstance(data[item], dict) and item == 'skills':
for key, value in data[item].items():
spell_data_list.append(f'{key.title()}: {value}')
value = ', '.join(spell_data_list)
elif isinstance(data[item], dict) and item == 'weapons':
for key, value in data[item].items():
weapons_data_list.append(f'{key.title()}: {value}')
value = ', '.join(weapons_data_list)
else:
value = data[item].title() if isinstance(data[item], str) else data[item]
name_label_list[i]['text'] = f'{item.upper()}:'
name_label_list[i].grid(column=0, row=i, sticky='new')
ability_label_list[i]['text'] = value
ability_label_list[i].grid(column=1, row=i, sticky='new',padx=8)
class Controller2(Controller):
'''
Controller2 handles all communication between TopWindow and BasicModel
'''
def __init__(self, model, view, target=None, action=None):
'''
Set some instance variables
'''
super().__init__(model, view)
self.model = model
self.view = view
# self.action is a variable for determining if we want to create or edit a character
self.action = action
# Open TopWindow and fill the guild combobox and set functionality for the buttons
self.topwindow = TopWindow()
self.topwindow.guild['values'] = self.model.get_guilds()
self.topwindow.guild.current(0)
self.topwindow.cancel_button['command'] = self.cancel
self.topwindow.save_button['command'] = self.save
# Do we want to create or edit
if target == 'edit':
self.edit()
if target == 'add':
self.add()
def edit(self):
'''
Populates the TopWindow form with character data if editing.
Disables the name field if editing
'''
if self.action == 'edit':
self.topwindow.name_label['state'] = 'disabled'
self.topwindow.window.title('Edit Character')
character = self.view.listbox.get(self.view.listbox.curselection())
self.data = self.model.get_character(character)
self.topwindow.header['text'] = f'Editing {character}'
self.topwindow.namevar.set(self.data['name'])
self.topwindow.agevar.set(self.data['age'])
self.topwindow.agevar.trace_add('write', self.check_age)
self.topwindow.guildvar.set(self.data['guild'])
# try block to see if any data exist for skills. If not places unknown in the field
try:
skill_list = []
for skill, level in self.data['skills'].items():
skill_list.append(f'{skill.title()}: {level}')
self.topwindow.skillsvar.set(', '.join(skill_list))
except:
self.topwindow.skillsvar.set('Unknown')
# Same as above but with the weapons field
try:
weapons_list = []
for weapon, level in self.data['weapons'].items():
weapons_list.append(f'{weapon}: {level}')
self.topwindow.weaponsvar.set(', '.join(weapons_list))
except:
self.topwindow.weaponsvar.set('No Weapons')
def check_age(self, agevar, index, mode):
'''
Method for entering a valid age. Limited to three characters. e.g. 999 is max
'''
agevar = self.topwindow.agevar.get()
if agevar:
if len(agevar) > 3:
messagebox.showerror(title='Error!', message='Age can\'t be more than 3 digits long')
self.topwindow.agevar.set(self.data['age'])
try:
agevar = int(agevar)
except:
messagebox.showerror('Error!', 'Only numbers can be entered.')
self.topwindow.agevar.set(self.data['age'])
def cancel(self):
'''
We don't want to continue with editing/creating a character.
Destroy TopWindow
'''
self.topwindow.window.destroy()
def add(self):
'''
Sets window and header text
'''
self.topwindow.window.title('Add new character')
self.topwindow.header['text'] = 'Create a character'
def save(self):
'''
Method for saving a character
'''
# Get character data from TopWindow
name = self.topwindow.namevar.get()
guild = self.topwindow.guildvar.get().lower()
age = self.topwindow.agevar.get()
skills = self.topwindow.skillsvar.get()
weapons = self.topwindow.weaponsvar.get()
# If no name was entered display a message. Character must have a name to be created
if not name:
messagebox.showerror('No Name', 'Your character must have a name')
else:
# Set some variables
self.guild = guild.title()
self.name = name
# Creating a dict to be entered into the json file
character = {
'name': name,
'guild': guild,
'age': age,
'skills': self.convert(skills),
'weapons': self.convert(weapons)
}
# If there are any empty fields, fill with unknown
for key, value in character.items():
if len(character[key]) == 0:
character[key] = 'unknown'
# If editing delete the old character and create the new with updated fields.
# Else we are creating a new character and don't need to delete.
if self.action == 'edit':
old_guild = self.model.get_character(name)
self.model.delete_character(name, old_guild['guild'])
self.model.create_character(character)
messagebox.showinfo('Updated', f'{name.title()} has been updated')
else:
self.model.create_character(character)
messagebox.showinfo('Created', f'{name.title()} has been created')
# Call self.populate
self.populate()
# Destroy TopWindow
self.topwindow.window.destroy()
def get_character(self):
'''
Same as in Controller
'''
self.body()
def body(self):
'''
Same as in Controller. Populates the View and goes to the new or edited character
'''
error = False
# try block for checking if there is any characters in the listbox
try:
character = self.view.listbox.get(self.view.listbox.curselection()[0])
except IndexError:
error = True
# If there is an error, ignore
# Else no error format and populate the View
if error:
pass
else:
data = self.model.get_character(character.lower())
self.frame()
name_label_list = []
ability_label_list = []
spell_data_list = []
weapons_data_list = []
for i, item in enumerate(data):
name_label_list.append(tk.Label(self.label_frame, anchor='w'))
ability_label_list.append(tk.Label(self.label_frame, anchor='w', wraplength=250))
if isinstance(data[item], dict) and item == 'skills':
for key, value in data[item].items():
spell_data_list.append(f'{key.title()}: {value}')
value = ', '.join(spell_data_list)
elif isinstance(data[item], dict) and item == 'weapons':
for key, value in data[item].items():
weapons_data_list.append(f'{key.title()}: {value}')
value = ', '.join(weapons_data_list)
else:
value = data[item].title() if isinstance(data[item], str) else data[item]
name_label_list[i]['text'] = f'{item.upper()}:'
name_label_list[i].grid(column=0, row=i, sticky='new')
ability_label_list[i]['text'] = value
ability_label_list[i].grid(column=1, row=i, sticky='new',padx=8)
def convert(self, string):
'''
Converts a string to dict for weapons and skills
Has some bugs that need to be fixed
'''
if string == 'No Weapons':
return 'No Weapons'
words = []
for word in string.split(' '):
words.append(word.strip(':').strip(','))
it = iter(words)
result = dict(zip(it,it))
return result
def populate(self):
'''
Populates the View with data
'''
items = ['Names']
items = items + self.model.get_guilds()
self.view.menu['values'] = items
for i, guild in enumerate(items):
if self.guild == guild:
self.view.menu.current(i)
self.view.listbox.delete(0, tk.END)
self.get_guild(guild)
index = 0
for i, name in enumerate(self.view.listbox.get(0, tk.END)):
if name.lower() == self.name.lower():
index = i
self.view.listbox.select_set(index)
self.body()
def get_guild(self, guild):
'''
Populates the menu of View
'''
self.view.listbox.delete(0, tk.END)
guild = self.view.menu.get().lower()
if guild == 'names':
data = self.model.get_all()
else:
data = self.model.get_character_by_guild(guild)
for i, name in enumerate(data):
self.view.listbox.insert(i, name.title())
self.body()
def main():
app = tk.Tk()
app.geometry('600x400+200+200')
controller = Controller(BasicModel(file), View(app))
controller.populate()
app.mainloop()
if __name__ == '__main__':
main()
backend.py
from os import path
import json
from itertools import chain
from collections import OrderedDict
file = 'card.json'
def _create_database(file):
'''
Check for or create the database
'''
if path.exists(file):
pass
else:
with open(file, 'w') as out_file:
characters = {}
json.dump(characters, out_file, indent=4)
def _create_guild(file, guild):
'''
Function checks the json file for already existing guildss.
If one exist it return a message. If one does not exist it
will create the guild
'''
with open(file, 'r+') as json_file:
data = json.load(json_file)
if guild in data:
print('Guild already exists')
else:
data[guild] = []
json_file.seek(0)
json.dump(data, json_file, indent=4)
print(f'Guild {guild} created')
def _check_name(file, name):
'''
Checks to see if a character already exists in the database
'''
with open(file, 'r') as json_file:
data = json.load(json_file)
if name.lower() in [c.get('name') for c in chain(*data.values())]:
return True
return False
def _create(file, character):
'''
Check if character name already exists. If it does return true otherwise,
return false and create the character
'''
check = _check_name(file, character['name'])
if check:
print(f'{character["name"].title()} already exists in the database.')
else:
with open(file, 'r+') as json_file:
data = json.load(json_file)
data[character['guild']].append(character)
json_file.seek(0)
json.dump(data, json_file, indent=4)
print(f'{character["name"].title()} has been created.')
def _delete(file, character_name=None, guild=None):
'''
Function searches for a character by name and deletes the character
'''
ok = _check_name(file, character_name)
if not ok:
return 'Chacrater does not exists'
with open(file, 'r') as json_file:
data = OrderedDict(json.load(json_file))
glen = len(data[guild.lower()])
for i, name in enumerate(data[guild.lower()]):
if character_name in name.values():
data[guild.lower()].pop(i)
with open(file, 'w') as json_file:
json.dump(data, json_file, indent=4)
print(f'{character_name.title()} has been deleted')
def _get_character(file, name):
'''
This function will return all related data to a character entered.
'''
with open(file, 'r+') as json_file:
data = json.load(json_file)
for _ in data:
for i in range(len(data[_])):
if name.lower() == data[_][i]['name']:
character = data[_][i]
return character
return f'{name.title()} does not exist.'
def _get_all(file):
'''
Returns all characters
'''
with open(file, 'r') as json_file:
data = json.load(json_file)
characters = [c.get('name') for c in chain(*data.values())]
return characters
def get_guilds(file):
'''
Returns all guilds
'''
guilds = []
with open(file, 'r') as json_file:
data = json.load(json_file)
for _ in data:
guilds.append(_.title())
return guilds
def get_character_by_guild(file, guild):
'''
Returns characters by guild
'''
with open(file, 'r') as json_file:
data = json.load(json_file)
names = []
if guild.lower() in [character.get('guild') for character in chain(*data.values())]:
for character in data[guild]:
if character:
names.append(character['name'])
return names
card.json
{
"wizard": [
{
"name": "tabol",
"guild": "wizard",
"age": 23,
"skills": {
"fireball": 20,
"meteor": 15
}
},
{
"name": "zac",
"guild": "wizard",
"age": 25,
"skills": {
"fireball": 10,
"meteor": 5
}
},
{
"name": "tony",
"guild": "wizard",
"age": 25,
"skills": {
"fireball": 10,
"meteor": 5
}
},
{
"name": "ralph",
"guild": "wizard",
"age": 20,
"skills": {
"fireball": 15,
"meteor": 5
}
}
],
"cleric": [],
"warrior": [
{
"name": "randy",
"guild": "warrior",
"age": 27,
"skills": {
"hand to hand": 20,
"iron fist": 15
},
"weapons": {
"sword": 20,
"knife": 10
}
}
]
}