importos# Import os module for cls and executing commands through python
importjson# Import json so we can handle our data.json file
importpprint# Pretty print for debugging
importre# Not used atm
importunicodedata# Not used atm
fromoperatorimportitemgetter# Used for sorting our results after a specific option.
# ---- IMPORTS ---- #
# Opens a file and reads it so it's contents can be used as data for the program
defload(filename):
try:
# Opens file with R (read) with UTF 8 encoding as file
withopen(filename,'r',encoding='utf-8')asfile:
data=json.load(file)# Assigns the file contents to data as json
file.close()# Close the file
returndata
except:
returnNone
# WIP
defsave(data):
withopen('data.json','w',encoding='utf-8')asfile:
json.dump(data,file,ensure_ascii=False,indent=4)
file.close()
# Reload data
load()
# Get project count
defget_project_count(data):
returnlen(data)# Returns the len of the data list which is equivalent to the amount of projects
# Get project by ID
defget_project(data,id):
# Loops over a range with the length of the amount of projects present and gets a project if it matches the passed ID.
forninrange(0,get_project_count(data)):
ifdata[n]['project_id']==id:# If the current projects ID corresponds to the ID we're looking for then...
returndata[n]# Return the project at that index
# Get all unique techniques from project
defget_techniques(data):
techniques=[]# A list which will store the techniques found
forprojectindata:# For every project in our data
fortechinproject['techniques_used']:# For every technique in that project
iftechnotintechniques:# If it's not in the previously techniques, it's unique.
techniques.append(tech)# Append it to the list.
techniques.sort()# Sort it so it'll look nice.
returntechniques
# Gets all unique techniques from all projects. ( NOT USED? )
defget_technique_stats(data):
technique_list=get_techniques(data)# Get all the unique techniques so we know what techniques we can expect.
technique_stats={}# Creates a dictionary which will contain the id as a key and the name as a value.
current_techniques=[]# A list which will contain the found techniques.
fortechniqueintechnique_list:# For each unique technique we found with get_techniques()...
forprojectindata:# For every project in data, as we need to look over them all for each technique.
iftechniqueinproject['techniques_used']:# If the technique we're currently looking for is in the used techniques of the project we're iterating over.
current_techniques.append({'id':project['project_id'],'name':project['project_name']})# Create a new dict INSIDE the list with the project ID and project name.
technique_stats.update({technique:current_techniques.copy()})# Updates the dictionary with the list WITH a dict inside.
current_techniques.clear()# Clears the buffer so a new key value pair can be created.
returntechnique_stats# Return a dict with all techniques and the projects which uses them.
# Fetches and sorts projects matching criteria from the specified list.
# If search str belongs to search and if so, make search lower case.
ifisinstance(search,str):
search=search.lower()
results=[]
# get it
forprojectindata:# For each project in data
results.append(project)# Add all projects in data
# sort it
results=sorted(results,key=itemgetter(sort_by))# Sorts our results based on a specific variable by using a lambda function with itemgetter.
# order it
ifsort_order=='desc':# If the sort order is set to desc, then reverse the list.
results.reverse()
# filter it (by techniques)
iftechniquesisnotNone:# If there are techniques which are going to be filtered by..
fortechniqueinget_techniques(data):# For each unique technique..
forprojectinresults:# And for each project we've found so far..
ifall(ninproject['techniques_used']fornintechniques):# If all the techniques we're looking for are inside the projects technique_used field..
pass# Then do nothing..
else:
results.pop(results.index(project))# Otherwise remove it from our results.
# search for it
ifsearchisnotNone:# If the search field isn't set to None..
search_results=[]# Create a list for our search results..
forprojectinresults:# For every project we've found so far..
ifsearch_fieldsisnotNoneandsearch_fields!=""andsearch!=""andsearch_fields!=['None']:# If the searchf ield is not set to None AND search_fields is not empty AND search_fields is not an empty list..
forfieldinsearch_fields:# For each field in search fields..
substring=project[field]# Save the current field in substring so we can do operations on it..
# Check types before calling lower()
ifisinstance(substring,str):
substring=substring.lower()# Make case insensitive
ifsubstring.find(search)!=-1:# If found
search_results.append(project)
break
elifisinstance(substring,int):
ifstr(substring).find(search)!=-1:
search_results.append(project)
break
elifisinstance(substring,list):# If it's a list..
forsubsubstringinsubstring:# We'll need to make another substring so we can iterate over each index of the list instead of just for multiple lists.
ifisinstance(subsubstring,str):
ifsubsubstring.lower().find(search)!=-1:
search_results.append(project)
break
elifisinstance(subsubstring,int):
ifstr(subsubstring).find(search)!=-1:
search_results.append(project)
break
elifsearch_fields=="":# And if the search_fields is empty..
results.clear()# Clear all results
break
else:
forsubstringinlist(project.values()):
# Check type before calling lower()
ifisinstance(substring,str):
ifsubstring.lower().find(search)!=-1:
search_results.append(project)
break
elifisinstance(substring,int):
ifstr(substring).find(search)!=-1:
search_results.append(project)
break
elifisinstance(substring,list):
forsubsubstringinsubstring:
ifisinstance(subsubstring,str):
ifsubsubstring.lower().find(search)!=-1:
search_results.append(project)
break
elifisinstance(subsubstring,int):
ifstr(subsubstring).find(search)!=-1:
search_results.append(project)
break
results=search_results# Make results into search_results as they should be the same
returnresults# Returns our results.
# WIP
defcls():
os.system('cls'ifos.name=='nt'else'clear')
pass
# WIP
defnew_project(data):
cls()
# ---- COLLECT INFO ----
project_title=input("Project title: ")
project_id=get_project_count(data)+1
techniques=input("\nWhat techniques does your project use? Write them out in the following format: python, java, html, css\n\nTechniques: ").replace("","").lower().split(",")
description=input("Provide a description of your project: ")
url=input("Provide a link to the source code/demo of your project: ")
img_url=input("Image source (ex: logo.jpg): ")
# ---- COLLECT INFO ----
# lexicographical order sort aka alphabetical
techniques.sort()
new_project={
"project_name":project_title,
"project_id":project_id,
"used_techniques":techniques,
"long_description":description,
"img_url":img_url,
"url":url
}
cls()
print("\n\nProject preview:\n")
pprint.pp(new_project)
option=int(input("\n1: Create\n2: Cancel\n> "))
ifoption==1:
data.append(new_project)
save(data)
pass
# WIP
deflist_projects(data):
cls()
forprojectindata:
pprint.pp(project)
print("\n")
# WIP
defedit_project(data,id):
whileTrue:
ifid>get_project_count(data)orid<0:
print("Project ID doesn't exist.\n")
id=int(input("Project_ID to edit: "))
else:
cls()
project=get_project(data,id)
project.pop('project_id')# Project ID shouldn't be changed
print(f"Editing project: {project['title']}\n")
pprint.pp(project)
print("")
forfieldinenumerate(project):
print(f"{field[0]}: {field[1]}")
input("\nField to edit: ")
# WIP
defdelete_project(data):
pass
# WIP
defmenu(data):
menu_items=["Add new project","List projects","Edit existing project","Delete project","Quit"]