Update '_main.py'

This commit is contained in:
Darmstadtium 2022-12-29 18:48:05 +01:00
parent 1d9ef3e31a
commit 48df454f16

View File

@ -1,355 +1,355 @@
#!/usr/bin/python3.10 #!/usr/bin/python3.10
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""************************ """************************
AnkIdentification - A project to improve your plant identifcation skills through Anki. AnkIdentification - A project to improve your plant identifcation skills through Anki.
Aurélien VALENTIN - from 06/11/2022 to 29/12/2022 Aurélien VALENTIN - from 06/11/2022 to 29/12/2022
************************""" ************************"""
# Default parameters, feel free to change them as you want. # Default parameters, feel free to change them as you want.
NB_SPECIES = 100 # Number of species wanted by the user. Can't exceed 100 with the GBIF method. NB_SPECIES = 100 # Number of species wanted by the user. Can't exceed 100 with the GBIF method.
LANGUAGE = "fr" # Please choose "fr" or "en" to get respectively French or English vernacular names. LANGUAGE = "fr" # Please choose "fr" or "en" to get respectively French or English vernacular names.
SPECIE_LIST_PATH_IF_GBIF_METHOD = "" # A csv file from https://identify.plantnet.org/prediction. Only if you've chosen the GBIF method. SPECIE_LIST_PATH_IF_GBIF_METHOD = "" # A csv file from https://identify.plantnet.org/prediction. Only if you've chosen the GBIF method.
MY_OWN_LIST_PATH = "" # A txt file with one specie by line. Be careful to write the scientific name used in Pl@ntNet and change NB_SPECIES if needed. MY_OWN_LIST_PATH = "" # A txt file with one specie by line. Be careful to write the scientific name used in Pl@ntNet and change NB_SPECIES if needed.
# Importation and other initialisations # Importation and other initialisations
import json, sys import json, sys
import genanki #https://github.com/kerrickstaley/genanki.git import genanki #https://github.com/kerrickstaley/genanki.git
from random import shuffle from random import shuffle
dict_common_plants = {} dict_common_plants = {}
with open("_dict_species.json", "r") as fpi : dict_common_names = json.load(fpi) # From the 3-1 and 3-2 supplementary codes with open("_dict_species.json", "r") as fpi : dict_common_names = json.load(fpi) # From the 3-1 and 3-2 supplementary codes
# Definition of the progressbar function, from https://gist.github.com/ChesterChowWOV/2b35c551b339adbf459363322aac5b4b # Definition of the progressbar function, from https://gist.github.com/ChesterChowWOV/2b35c551b339adbf459363322aac5b4b
def progressbar(it, prefix = "", size = 60, file = sys.stdout): def progressbar(it, prefix = "", size = 60, file = sys.stdout):
count = len(it) count = len(it)
def show(j): def show(j):
x = int(size*j/count) x = int(size*j/count)
file.write("{}[{}{}] {}/{} {}%\r".format(prefix, ""*x, "."*(size-x), j, count, round(j / count * 100, 2))) file.write("{}[{}{}] {}/{} {}%\r".format(prefix, ""*x, "."*(size-x), j, count, round(j / count * 100, 2)))
file.flush() file.flush()
show(0) show(0)
for i, item in enumerate(it): for i, item in enumerate(it):
yield item yield item
show(i + 1) show(i + 1)
file.write("\n") file.write("\n")
file.flush() file.flush()
"""************************ """************************
Step 1 : Listing the most common species (if without a manual list or a GBIF file). Step 1 : Listing the most common species (if without a manual list or a GBIF file).
************************""" ************************"""
if SPECIE_LIST_PATH_IF_GBIF_METHOD == MY_OWN_LIST_PATH: if SPECIE_LIST_PATH_IF_GBIF_METHOD == MY_OWN_LIST_PATH:
print("Listing the most common species") print("Listing the most common species")
# Loading of the whole list # Loading of the whole list
with open("_dict_species_by_rarety.json") as fpi : dict_plants_sorted = json.load(fpi) # From the first supplementary code with open("_dict_species_by_rarety.json") as fpi : dict_plants_sorted = json.load(fpi) # From the first supplementary code
# Writing of the user's list # Writing of the user's list
with open("Selected_species.txt", "w") as fpo: with open("Selected_species.txt", "w") as fpo:
for species in list(dict_plants_sorted.keys())[:NB_SPECIES]: for species in list(dict_plants_sorted.keys())[:NB_SPECIES]:
fpo.write(species + "\n") fpo.write(species + "\n")
"""************************ """************************
Step 2 : Parsing data for each species. Step 2 : Parsing data for each species.
************************""" ************************"""
# Initialisation of the dictionary (and addition of the family) from a file from the step 1... # Initialisation of the dictionary (and addition of the family) from a file from the step 1...
if SPECIE_LIST_PATH_IF_GBIF_METHOD == "": if SPECIE_LIST_PATH_IF_GBIF_METHOD == "":
with open("_dict_classification.json", "r") as fpi : dict_classification = json.load(fpi) # From the second supplementary code with open("_dict_classification.json", "r") as fpi : dict_classification = json.load(fpi) # From the second supplementary code
try: fpi = open(MY_OWN_LIST_PATH) try: fpi = open(MY_OWN_LIST_PATH)
except : fpi = open("Selected_species.txt") except : fpi = open("Selected_species.txt")
for line in progressbar(fpi.readlines(), "Adding families", 40): for line in progressbar(fpi.readlines(), "Adding families", 40):
specie = line.split(" ")[0] + " " + line.split(" ")[1].lower().strip() specie = line.split(" ")[0] + " " + line.split(" ")[1].lower().strip()
dict_common_plants[specie] = {"Occurrence": {"leaf": 0, "flower": 0, "fruit": 0, "bark": 0}} dict_common_plants[specie] = {"Occurrence": {"leaf": 0, "flower": 0, "fruit": 0, "bark": 0}}
try: dict_common_plants[specie]["Family"] = dict_classification[specie] try: dict_common_plants[specie]["Family"] = dict_classification[specie]
except: dict_common_plants[specie]["Family"] = "Family not found" except: dict_common_plants[specie]["Family"] = "Family not found"
dict_common_plants[specie]["Links"] = {"leaf": "", "flower": "", "fruit": "", "bark": ""} dict_common_plants[specie]["Links"] = {"leaf": "", "flower": "", "fruit": "", "bark": ""}
dict_common_plants[specie]["Authors"] = {"leaf": "", "flower": "", "fruit": "", "bark": ""} dict_common_plants[specie]["Authors"] = {"leaf": "", "flower": "", "fruit": "", "bark": ""}
# ...or from a GBIF file from here : https://identify.plantnet.org/prediction # ...or from a GBIF file from here : https://identify.plantnet.org/prediction
else: else:
with open(SPECIE_LIST_PATH_IF_GBIF_METHOD) as fpi: with open(SPECIE_LIST_PATH_IF_GBIF_METHOD) as fpi:
fpi.readline() fpi.readline()
for line in progressbar(fpi.readlines(), "Adding families", 40): for line in progressbar(fpi.readlines(), "Adding families", 40):
specie = line.split(",")[1].split(" ")[0] + " " + line.split(",")[1].split(" ")[1] specie = line.split(",")[1].split(" ")[0] + " " + line.split(",")[1].split(" ")[1]
dict_common_plants[specie] = {"Occurrence": {"leaf": 0, "flower": 0, "fruit": 0, "bark": 0}} dict_common_plants[specie] = {"Occurrence": {"leaf": 0, "flower": 0, "fruit": 0, "bark": 0}}
dict_common_plants[specie]["Family"] = line.split(",")[2] dict_common_plants[specie]["Family"] = line.split(",")[2]
dict_common_plants[specie]["Links"] = {"leaf": "", "flower": "", "fruit": "", "bark": ""} dict_common_plants[specie]["Links"] = {"leaf": "", "flower": "", "fruit": "", "bark": ""}
dict_common_plants[specie]["Authors"] = {"leaf": "", "flower": "", "fruit": "", "bark": ""} dict_common_plants[specie]["Authors"] = {"leaf": "", "flower": "", "fruit": "", "bark": ""}
# Addition of vernacular name(s) # Addition of vernacular name(s)
for specie in progressbar(dict_common_plants.keys(), "Adding common names", 40): for specie in progressbar(dict_common_plants.keys(), "Adding common names", 40):
try: try:
dict_common_plants[specie]["Common names"] = dict_common_names[specie][LANGUAGE] dict_common_plants[specie]["Common names"] = dict_common_names[specie][LANGUAGE]
except: dict_common_plants[specie]["Common names"] = "No common name found." except: dict_common_plants[specie]["Common names"] = "No common name found."
# Addition of photo links and author names # Addition of photo links and author names
with open("multimedia.txt", encoding="utf-8") as fpi: with open("multimedia.txt", encoding="utf-8") as fpi:
fpi.readline() fpi.readline()
for line in progressbar(fpi.readlines(), "Adding links and common names", 40): for line in progressbar(fpi.readlines(), "Adding links and common names", 40):
line_split = line.split("\t") line_split = line.split("\t")
specie = line_split[5].split(" ")[0] + " " + line_split[5].split(" ")[1] specie = line_split[5].split(" ")[0] + " " + line_split[5].split(" ")[1]
link = line_split[3].strip(" ").split("o/")[1] link = line_split[3].strip(" ").split("o/")[1]
type = line_split[5].split(":")[1].strip(" ") type = line_split[5].split(":")[1].strip(" ")
author = line_split[len(line_split) - 1].strip() author = line_split[len(line_split) - 1].strip()
if specie in dict_common_plants.keys() and type in ["leaf", "flower", "fruit", "bark"]: if specie in dict_common_plants.keys() and type in ["leaf", "flower", "fruit", "bark"]:
if dict_common_plants[specie]["Occurrence"][type] == 0: if dict_common_plants[specie]["Occurrence"][type] == 0:
dict_common_plants[specie]["Links"][type] += link dict_common_plants[specie]["Links"][type] += link
dict_common_plants[specie]["Authors"][type] += author dict_common_plants[specie]["Authors"][type] += author
dict_common_plants[specie]["Occurrence"][type] += 1 dict_common_plants[specie]["Occurrence"][type] += 1
elif dict_common_plants[specie]["Occurrence"][type] < 101: elif dict_common_plants[specie]["Occurrence"][type] < 101:
dict_common_plants[specie]["Links"][type] += "," + link dict_common_plants[specie]["Links"][type] += "," + link
dict_common_plants[specie]["Authors"][type] += "," + author dict_common_plants[specie]["Authors"][type] += "," + author
dict_common_plants[specie]["Occurrence"][type] += 1 dict_common_plants[specie]["Occurrence"][type] += 1
"""************************ """************************
Step 3 : Generation of the Anki deck. Step 3 : Generation of the Anki deck.
************************""" ************************"""
print("Preparing the Anki deck") print("Preparing the Anki deck")
# Definition of the Anki card model # Definition of the Anki card model
my_model = genanki.Model( my_model = genanki.Model(
1091735104, 1091735104,
'Model for AnkIdentification', 'Model for AnkIdentification',
fields=[ fields=[
{'name': 'Name'}, {'name': 'Name'},
{'name': 'Common names'}, {'name': 'Common names'},
{'name': 'Family'}, {'name': 'Family'},
{'name': 'Leaf'}, {'name': 'Leaf'},
{'name': 'Flower'}, {'name': 'Flower'},
{'name': 'Fruit'}, {'name': 'Fruit'},
{'name': 'Bark'}, {'name': 'Bark'},
{'name': 'Leaf authors'}, {'name': 'Leaf authors'},
{'name': 'Flower authors'}, {'name': 'Flower authors'},
{'name': 'Fruit authors'}, {'name': 'Fruit authors'},
{'name': 'Bark authors'} {'name': 'Bark authors'}
], ],
templates=[ templates=[
{ {
'name': 'Card 1', 'name': 'Card 1',
'qfmt': """<style> 'qfmt': """<style>
.card { .card {
font-family: arial; font-family: arial;
font-size: 20px; font-size: 20px;
text-align: center; text-align: center;
color: black; color: black;
background-color: white; background-color: white;
} }
body{ body{
margin:0; margin:0;
} }
input{ input{
position:absolute;top:0;left:0; position:absolute;top:0;left:0;
} }
#fullscreen{ #fullscreen{
position:fixed; position:fixed;
top:0;bottom:0; top:0;bottom:0;
left:0;right:0; left:0;right:0;
background-color:rgba(0,0,0,0.7); background-color:rgba(0,0,0,0.7);
display:none; display:none;
} }
#fImg{ #fImg{
position:absolute; position:absolute;
top:0; top:0;
bottom:0;left:0;right:0; bottom:0;left:0;right:0;
margin:auto; margin:auto;
max-width:100%; max-width:100%;
max-height:100%; max-height:100%;
} }
#container{ #container{
display: flex; display: flex;
width: 100vw; width: 100vw;
height: 98vh; height: 98vh;
padding: 1vh 0 1vh 0vh; padding: 1vh 0 1vh 0vh;
flex-wrap: wrap; flex-wrap: wrap;
background-color:black; background-color:black;
} }
td{ td{
border: 1px solid; border: 1px solid;
} }
#container>img { #container>img {
width: 49%; width: 49%;
height:49%; height:49%;
padding:1px; padding:1px;
object-fit: contain; object-fit: contain;
} }
.half{ .half{
display: block; display: block;
height: 35vh; height: 35vh;
} }
</style> </style>
<input type="button" id="qButton" value="" onclick="qSwitch()"> <input type="button" id="qButton" value="" onclick="qSwitch()">
<div id="fullscreen"><img id="fImg"/></div> <div id="fullscreen"><img id="fImg"/></div>
<div id=container></div> <div id=container></div>
<script> <script>
//once //once
if(typeof o=='undefined'){ if(typeof o=='undefined'){
var o={}; var o={};
var quality=3; var quality=3;
var prevName=''; var prevName='';
var isRecto=true; var isRecto=true;
var qTab=["s","m","o","a"]; var qTab=["s","m","o","a"];
var qTab2=["150x150","600x600","Original","Automatic"]; var qTab2=["150x150","600x600","Original","Automatic"];
var lastRelease=0; var lastRelease=0;
var lastPress=0; var lastPress=0;
//Quality switcher //Quality switcher
function qSwitch(){ function qSwitch(){
quality=(quality+1)%4; quality=(quality+1)%4;
for (const k in o) { for (const k in o) {
chgImg(o[k],0); chgImg(o[k],0);
qButton.value=qTab2[quality]; qButton.value=qTab2[quality];
} }
} }
//fullscreen //fullscreen
function fullscreenImg(img){ function fullscreenImg(img){
fImg.src='https://bs.plantnet.org/image/o/'+img.t[img.i]; fImg.src='https://bs.plantnet.org/image/o/'+img.t[img.i];
fImg.title="Author :\\n"+img.a[img.i]; fImg.title="Author :\\n"+img.a[img.i];
fullscreen.style.display='block'; fullscreen.style.display='block';
} }
function deFullscreenImg(){ function deFullscreenImg(){
fImg.src=""; fImg.src="";
fullscreen.style.display='none'; fullscreen.style.display='none';
} }
//click handling //click handling
function mDown(e){ function mDown(e){
setTimeout(function(){ setTimeout(function(){
if(Date.now()-lastRelease>=500){ if(Date.now()-lastRelease>=500){
chgImg(e.target,-1); chgImg(e.target,-1);
} }
},500); },500);
lastPress=Date.now(); lastPress=Date.now();
} }
function mUp(e){ function mUp(e){
if(e.button==0){ if(e.button==0){
if(Date.now()-lastPress<500){ if(Date.now()-lastPress<500){
chgImg(e.target,1); chgImg(e.target,1);
} }
}else if(e.button==2){ }else if(e.button==2){
if(Date.now()-lastPress<500){ if(Date.now()-lastPress<500){
fullscreenImg(e.target); fullscreenImg(e.target);
} }
} }
lastRelease=Date.now(); lastRelease=Date.now();
} }
function chgImg(img,n){ function chgImg(img,n){
img.i=(img.i+n)%img.t.length; img.i=(img.i+n)%img.t.length;
if(img.i<0){img.i=img.t.length-1;} if(img.i<0){img.i=img.t.length-1;}
img.title="Author :\\n"+img.a[img.i]; img.title="Author :\\n"+img.a[img.i];
if(qTab[quality]!='a'){ if(qTab[quality]!='a'){
img.srcset=''; img.srcset='';
img.src='https://bs.plantnet.org/image/'+qTab[quality]+'/'+img.t[img.i]; img.src='https://bs.plantnet.org/image/'+qTab[quality]+'/'+img.t[img.i];
}else{ }else{
img.srcset='https://bs.plantnet.org/image/s/'+img.t[img.i]+' 600w,'+'https://bs.plantnet.org/image/m/'+img.t[img.i]+' 2400w,'+'https://bs.plantnet.org/image/o/'+img.t[img.i]+' 4000w'; img.srcset='https://bs.plantnet.org/image/s/'+img.t[img.i]+' 600w,'+'https://bs.plantnet.org/image/m/'+img.t[img.i]+' 2400w,'+'https://bs.plantnet.org/image/o/'+img.t[img.i]+' 4000w';
} }
if(img.complete==true){ if(img.complete==true){
img.style.opacity=1; img.style.opacity=1;
}else{ }else{
img.style.opacity=0.5; img.style.opacity=0.5;
} }
} }
function imgLoad(e){ function imgLoad(e){
e.target.style.opacity=1; e.target.style.opacity=1;
} }
//main show //main show
function prepImg(list,author,id){ function prepImg(list,author,id){
if(isRecto){ if(isRecto){
img=document.createElement('img'); img=document.createElement('img');
img.t=list.replace(/<[^>]*>?/gm,'').split(','); img.t=list.replace(/<[^>]*>?/gm,'').split(',');
img.a=author.replace(/<[^>]*>?/gm,'').split(','); img.a=author.replace(/<[^>]*>?/gm,'').split(',');
img.i=Math.floor(Math.random()*img.t.length); img.i=Math.floor(Math.random()*img.t.length);
chgImg(img,0); chgImg(img,0);
img.addEventListener('mousedown',mDown); img.addEventListener('mousedown',mDown);
img.addEventListener('mouseup',mUp); img.addEventListener('mouseup',mUp);
img.addEventListener('contextmenu',function(e){e.preventDefault();}); img.addEventListener('contextmenu',function(e){e.preventDefault();});
img.addEventListener('load',imgLoad); img.addEventListener('load',imgLoad);
o[id]=img; o[id]=img;
} }
container.appendChild(o[id]); container.appendChild(o[id]);
} }
} }
//init objects (mist be re-done because recto/verso reload) //init objects (mist be re-done because recto/verso reload)
var fImg=document.getElementById('fImg'); var fImg=document.getElementById('fImg');
var fullscreen=document.getElementById('fullscreen'); var fullscreen=document.getElementById('fullscreen');
var qButton=document.getElementById('qButton'); var qButton=document.getElementById('qButton');
var container=document.getElementById('container'); var container=document.getElementById('container');
fullscreen.addEventListener('click',deFullscreenImg); fullscreen.addEventListener('click',deFullscreenImg);
//recto or verso ? //recto or verso ?
if(prevName=='{{Name}}'){ if(prevName=='{{Name}}'){
isRecto=!isRecto; isRecto=!isRecto;
}else{ }else{
isRecto=true; isRecto=true;
} }
prevName="{{Name}}"; prevName="{{Name}}";
if(isRecto){ if(isRecto){
qButton.value=qTab2[quality]; qButton.value=qTab2[quality];
}else{ }else{
container.style.height="55vh"; container.style.height="55vh";
qButton.style.display='none'; qButton.style.display='none';
} }
prepImg("{{Leaf}}","{{Leaf authors}}",'Leaf'); prepImg("{{Leaf}}","{{Leaf authors}}",'Leaf');
prepImg("{{Flower}}","{{Flower authors}}",'Flower'); prepImg("{{Flower}}","{{Flower authors}}",'Flower');
prepImg("{{Fruit}}","{{Fruit authors}}",'Fruit'); prepImg("{{Fruit}}","{{Fruit authors}}",'Fruit');
prepImg("{{Bark}}","{{Bark authors}}",'Bark'); prepImg("{{Bark}}","{{Bark authors}}",'Bark');
</script>""", </script>""",
'afmt': """<div class="half"> 'afmt': """<div class="half">
<h1><i>{{Name}}</i></h1> <h1><i>{{Name}}</i></h1>
<br> <br>
<b><i>{{Family}}</i></b> <b><i>{{Family}}</i></b>
<br> <br>
{{Common names}} {{Common names}}
</div> </div>
{{FrontSide}}""", {{FrontSide}}""",
'bfont': 'Arial' 'bfont': 'Arial'
#'Styles': ".card {\n font-family: arial;\n font-size: 20px;\n text-align: center;\n color: black;\n background-color: white;\n}" #'Styles': ".card {\n font-family: arial;\n font-size: 20px;\n text-align: center;\n color: black;\n background-color: white;\n}"
}, },
], ],
css='.card {\n font-family: arial;\n font-size: 20px;\n text-align: center;\n color: black;\n background-color: white;\n}\n', css='.card {\n font-family: arial;\n font-size: 20px;\n text-align: center;\n color: black;\n background-color: white;\n}\n',
) )
# Creation of the deck # Creation of the deck
my_deck = genanki.Deck( my_deck = genanki.Deck(
2059400110, 2059400110,
'AnkIdentifaction') 'AnkIdentifaction')
# Randomization of the species order # Randomization of the species order
species_shuffled = list(dict_common_plants.keys()) species_shuffled = list(dict_common_plants.keys())
shuffle(species_shuffled) shuffle(species_shuffled)
# Generation of all notes # Generation of all notes
for specie in progressbar(species_shuffled, "Creating notes", 40): for specie in progressbar(species_shuffled, "Creating notes", 40):
dict_specie = dict_common_plants[specie] dict_specie = dict_common_plants[specie]
my_note = genanki.Note( my_note = genanki.Note(
model = my_model, model = my_model,
fields = [specie, dict_specie["Common names"], dict_specie["Family"], dict_specie["Links"]["leaf"], dict_specie["Links"]["flower"], dict_specie["Links"]["fruit"], dict_specie["Links"]["bark"], dict_specie["Authors"]["leaf"], dict_specie["Authors"]["flower"], dict_specie["Authors"]["fruit"], dict_specie["Authors"]["bark"]], fields = [specie, dict_specie["Common names"], dict_specie["Family"], dict_specie["Links"]["leaf"], dict_specie["Links"]["flower"], dict_specie["Links"]["fruit"], dict_specie["Links"]["bark"], dict_specie["Authors"]["leaf"], dict_specie["Authors"]["flower"], dict_specie["Authors"]["fruit"], dict_specie["Authors"]["bark"]],
tags = ["AnkIdentification::" + dict_specie["Family"]]) tags = ["AnkIdentification::" + dict_specie["Family"]])
my_deck.add_note(my_note) my_deck.add_note(my_note)
# Creation of the final file # Creation of the final file
print("Almost done!") print("Almost done!")
genanki.Package(my_deck).write_to_file('AnkIdentification.apkg') genanki.Package(my_deck).write_to_file('AnkIdentification.apkg')
print("Enjoy ^^") print("Enjoy ^^")