Skip to content

Commit f16d025

Browse files
Improve equivalence data (#299)
* Create stackedIcon.py * Update stackedIcon.py * Update stackedIcon.py * Update stackedIcon.py * Update stackedIcon.py * Update stackedIcon.py * Update stackedIcon.py * Update stackedIcon.py * new markdown! * update changelog * force folder order * update changelog * color legend * update changelog * code improvements * update changelog * Update README.md * improve texts * update changelog * fix text copy * update changelog * Update generate_graph.py * update changelog * use missing name * update * update changelog * Update generate_graph.py * update maths * update changelog * new iteration * Update generate_graph.py * big math improvement * cleaning * update changelog * styling * update * Update generate_markdown.py * update changelog * cleaning * update missings * update * fix total concepts * fix spacings * fix spacing * global calculation in all columns * remove cache files --------- Co-authored-by: github-actions <github-actions[bot]@users.noreply.github.com>
1 parent fe59d16 commit f16d025

File tree

9 files changed

+320
-260
lines changed

9 files changed

+320
-260
lines changed
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
import os
2+
3+
PIPE = "|"
4+
SLASH = "/"
5+
SVG_EXTENSION = ".svg"
6+
PDF_EXTENSION = ".pdf"
7+
BREAK = "\n"
8+
9+
# Show or hide and reorder brands as needed
10+
brands = ["telefonica", "o2", "vivo-new", "blau"]
11+
12+
# Define the colors for the bar representation
13+
bar_colors = {
14+
"unique": "59C2C9",
15+
"all_equivalence": "0066FF",
16+
"some_equivalence": "EAC344",
17+
"missing": "D1D5E4"
18+
}
19+
20+
# List folders in icons directory
21+
def read_folder(folder):
22+
if os.path.isdir(folder):
23+
files = os.listdir(folder)
24+
if ".DS_Store" in files:
25+
files.remove(".DS_Store")
26+
return files
27+
return []
28+
29+
def preprocess_filename(filename):
30+
# Remove common style indicators from the filename and return
31+
return filename.replace("-filled.svg", "").replace("-light.svg", "").replace("-regular.svg", "")
32+
33+
# Function to recursively list all unique SVG filenames without their paths
34+
def list_concepts(folder):
35+
concepts = set() # Use a set to avoid duplicates
36+
for root, dirs, files in os.walk(folder):
37+
# Filter and preprocess SVG filenames before adding to the set
38+
for file in files:
39+
if file.endswith('.svg') and not file.startswith('.'):
40+
processed_file = preprocess_filename(file)
41+
concepts.add(processed_file)
42+
return list(concepts) # Convert set to list before returning
43+
44+
# List all .svg files in the directory, including subdirectories.
45+
def list_svg_files(folder):
46+
svg_files = []
47+
for root, dirs, files in os.walk(folder):
48+
svg_files.extend(os.path.join(root, file) for file in files if file.endswith('.svg') and not file.startswith('.'))
49+
return svg_files
50+
51+
52+
concepts = list_concepts("./icons")
53+
total_concepts = len(concepts)
54+
55+
# SVG List
56+
svg_files = list_svg_files("./icons")
57+
58+
# Total number of icons
59+
total_icons = len(svg_files)
60+
61+
def process_icon_sets(folders, all_concepts):
62+
"""Process each icon set to compute various metrics, update all_concepts with unique processed names."""
63+
data = {}
64+
for folder in folders:
65+
files = list_svg_files(folder)
66+
icons = {os.path.basename(file): file for file in files}
67+
68+
# no_processed_names are icons with -regular, -light, -filled
69+
no_processed_names = {(os.path.basename(file)): file for file in files}
70+
71+
# processed_names are icons without -regular, -light, -filled
72+
processed_names = {preprocess_filename(os.path.basename(file)): file for file in files}
73+
74+
data[folder] = {
75+
"total": len(files),
76+
"icons": set(icons.keys()),
77+
"no_processed_names": set(no_processed_names.keys()),
78+
"processed_names": set(processed_names.keys()),
79+
"unique": set(),
80+
"all_equivalence": set(),
81+
"some_equivalence": set(),
82+
"missing": set()
83+
}
84+
all_concepts.update(data[folder]["no_processed_names"])
85+
86+
for folder in folders:
87+
current_no_processed_names = data[folder]["no_processed_names"]
88+
data[folder]["unique"] = current_no_processed_names - set.union(*(data[f]["no_processed_names"] for f in folders if f != folder))
89+
data[folder]["all_equivalence"] = set.intersection(*(data[f]["no_processed_names"] for f in folders))
90+
for other_folder in folders:
91+
if other_folder != folder:
92+
data[folder]["some_equivalence"].update(current_no_processed_names & data[other_folder]["no_processed_names"])
93+
data[folder]["some_equivalence"] -= data[folder]["all_equivalence"]
94+
data[folder]["missing"] = all_concepts - (data[folder]["unique"] | data[folder]["all_equivalence"] | data[folder]["some_equivalence"])
95+
96+
return data
97+
98+
# Generate a color-coded bar representation for each icon set based on percentage data.
99+
def generate_bar_representation(data, folders, bar_width=400, bar_height=8):
100+
bar_output = []
101+
for folder, metrics in data.items():
102+
folder_data = data[folder]
103+
104+
# Total per brand
105+
total_brand_icons = folder_data['total']
106+
107+
all_equivalence_count = len(folder_data['all_equivalence'])
108+
some_equivalence_count = len(folder_data['some_equivalence'])
109+
unique_count = len(folder_data['unique'])
110+
missing_count = len(folder_data['missing'])
111+
112+
all_equivalence_percent = (all_equivalence_count * 100) / total_icons
113+
some_equivalence_percent = (some_equivalence_count * 100) / total_icons
114+
unique_percent = (unique_count * 100) / total_icons
115+
missing_percent = (missing_count * 100) / total_icons
116+
117+
all_equivalence_width = round(int((all_equivalence_percent / 100) * bar_width))
118+
if 0 < all_equivalence_percent < 1:
119+
all_equivalence_width = 1
120+
121+
some_equivalence_width = round(int((some_equivalence_percent / 100) * bar_width))
122+
if 0 < some_equivalence_percent < 1:
123+
some_equivalence_width = 1
124+
125+
unique_width = round(int((unique_percent / 100) * bar_width))
126+
if 0 < unique_percent < 1:
127+
unique_width = 1
128+
129+
missing_width = bar_width - (unique_width + all_equivalence_width + some_equivalence_width)
130+
if 0 < missing_percent < 1:
131+
missing_width = 1
132+
133+
bar_parts = []
134+
if all_equivalence_width > 0:
135+
bar_parts.append(f"<img src='https://dummyimage.com/{all_equivalence_width}x{bar_height}/{bar_colors['all_equivalence']}/000&text=+' alt='All Equivalence'>")
136+
if some_equivalence_width > 0:
137+
bar_parts.append(f"<img src='https://dummyimage.com/{some_equivalence_width}x{bar_height}/{bar_colors['some_equivalence']}/000&text=+' alt='Some Equivalence'>")
138+
if unique_width > 0:
139+
bar_parts.append(f"<img src='https://dummyimage.com/{unique_width}x{bar_height}/{bar_colors['unique']}/000&text=+' alt='Unique'>")
140+
if missing_width > 0:
141+
bar_parts.append(f"<img src='https://dummyimage.com/{missing_width}x{bar_height}/{bar_colors['missing']}/000&text=+' alt='Missing'>")
142+
143+
bar_representation = f"{os.path.basename(folder).title()} " + "\n" + "".join(bar_parts) + "\n"
144+
bar_output.append(bar_representation)
145+
return bar_output
146+
147+
def generate_markdown_table(data, folders):
148+
global total_concepts
149+
150+
"""Generate markdown table representation of the data."""
151+
markdown = f"| <sub><sup>ICON SET</sup></sub> | <sub><sup>CONCEPTS ({total_concepts})</sup></sub> | <sub><sup>TOTAL ({total_icons})</sup></sub> | <sub><sup>ALL EQUIVALENCE</sup></sub> | <sub><sup>SOME EQUIVALENCE</sup></sub> | <sub><sup>UNIQUE</sup></sub> | <sub><sup>MISSING</sup></sub> |\n"
152+
markdown += "| :--------- | --------: | -----: | ----------: | -------------------: | -------------------: | ------------: |\n"
153+
154+
for folder in folders:
155+
folder_name = os.path.basename(folder).title()
156+
folder_data = data[folder]
157+
158+
total_brand_icons = folder_data['total']
159+
160+
all_equivalence_count = len(folder_data['all_equivalence'])
161+
all_equivalence_percent = f"{all_equivalence_count} ({all_equivalence_count * 100 / (total_icons):.1f}%) ![All Equivalence](https://dummyimage.com/4x12/{bar_colors['all_equivalence']}/000&text=+)" if total_icons > 0 else "0 (0%)"
162+
some_equivalence_count = len(folder_data['some_equivalence'])
163+
some_equivalence_percent = f"{some_equivalence_count} ({some_equivalence_count * 100 / total_icons:.1f}%) ![Some Equivalence](https://dummyimage.com/4x12/{bar_colors['some_equivalence']}/000&text=+)" if total_icons > 0 else "0 (0%)"
164+
unique_count = len(folder_data['unique'])
165+
unique_percent = f"{unique_count} ({unique_count * 100 / total_icons:.1f}%) ![Unique](https://dummyimage.com/4x12/{bar_colors['unique']}/000&text=+)" if total_icons > 0 else "0 (0%)"
166+
167+
# ARCHIVE
168+
# missing_count = len(folder_data['missing'])
169+
# missing_percent = f"{missing_count}" #({missing_count * 100 / total_icons:.1f}%)" if total_icons > 0 else "0 (0%)"
170+
171+
# missing_percent = f"{missing_count} ({missing_count * 100 / total_icons:.1f}%) ![Missing](https://dummyimage.com/4x12/{bar_colors['missing']}/000&text=+)"
172+
missing_count = total_icons - total_brand_icons
173+
# print(missing_count)
174+
missing_percent = f"{total_icons - total_brand_icons} ({(missing_count * 100) / total_icons:.1f}%) ![Missing](https://dummyimage.com/4x12/{bar_colors['missing']}/000&text=+)"
175+
176+
markdown += f"| {folder_name} | {len(folder_data['processed_names'])} | {folder_data['total']} | {all_equivalence_percent} | {some_equivalence_percent} | {unique_percent} | {missing_percent} |\n"
177+
178+
# markdown += f"| | **{total_concepts}** | **{total_icons}** | | | | |\n"
179+
markdown += "\n"
180+
# markdown += f"<table><tr><th>Total concepts</th><td>{total_concepts}</td></tr><tr><th>Total icons</th><td>{total_icons}</td></tr></table>"
181+
182+
183+
return markdown
184+
185+
186+
def generate_icon_table(path): # Renombrar la función para que coincida con el nombre del módulo
187+
brands = [folder for folder in os.listdir(path) if os.path.isdir(os.path.join(path, folder))]
188+
root = os.path.basename(path)
189+
dictionary = {}
190+
file_content = BREAK + "| ---BRANDS--- | icon name |" + \
191+
BREAK + "| ---HEADER-BREAK--- |" + ":--- |" + BREAK
192+
for brand in brands:
193+
brand_folder = path + SLASH + brand
194+
styles = read_folder(brand_folder)
195+
for style in styles:
196+
style_folder = brand_folder + SLASH + style
197+
icons = read_folder(style_folder)
198+
for icon in icons:
199+
icon_name = os.path.splitext(icon)[0]
200+
file_path = root + SLASH + brand + SLASH + \
201+
style + SLASH + icon_name + SVG_EXTENSION
202+
if icon_name in dictionary:
203+
if style not in dictionary[icon_name]:
204+
dictionary[icon_name][style] = {brand: file_path}
205+
else:
206+
dictionary[icon_name][style][brand] = file_path
207+
else:
208+
dictionary[icon_name] = {
209+
style: {brand: file_path}
210+
}
211+
212+
brands.remove("telefonica")
213+
brands = ["telefonica"] + sorted(brands, reverse=True)
214+
separator = " " + PIPE + " "
215+
file_content = file_content.replace("---BRANDS---", separator.join(brands))
216+
file_content = file_content.replace("---HEADER-BREAK---", separator.join(
217+
[":---:"] * (len(brands)))) # add (len(brands) + 2) to add svg & pdf download
218+
219+
for icon_name in sorted(dictionary.keys()):
220+
icon = dictionary[icon_name]
221+
for style in sorted(icon.keys()):
222+
icon_images = []
223+
for brand in brands:
224+
icon_image = "![" + icon_name + \
225+
"](" + icon[style][brand] + \
226+
") " if brand in icon[style] else " "
227+
icon_images.append(icon_image)
228+
row = PIPE + PIPE.join(icon_images) + PIPE + \
229+
"<a id=" + "'" + icon_name + "'>"+ "`" + icon_name + "`" + "</a>" + \
230+
"[" + "![" + icon_name + "]" + \
231+
"(.github/resources/anchor.svg)" + \
232+
"]" + "(" + "#" + icon_name + ")" + PIPE
233+
file_content += row + BREAK
234+
235+
return file_content # Devolver el contenido de la tabla de iconos
236+
237+
238+
def main(root_folder):
239+
folders = [os.path.join(root_folder, brand) for brand in brands]
240+
all_concepts = set()
241+
icon_data = process_icon_sets(folders, all_concepts)
242+
bars = generate_bar_representation(icon_data, all_concepts)
243+
244+
markdown_content = ""
245+
246+
# Add documentation
247+
documentation = "![Mistica Icons](.github/resources/mistica-icons-light.svg#gh-light-mode-only)" + BREAK + "![Mistica Icons](.github/resources/mistica-icons-dark.svg#gh-dark-mode-only)" + BREAK + BREAK + "Mística Icons is a multibrand icon system that contains all icons that is working in [Mistica Design System](https://github.com/Telefonica/mistica) now. " + BREAK + BREAK + "Mistica support [Brand Factory icons](https://brandfactory.telefonica.com/document/1086#/nuestra-identidad/iconos). This set of icons are a big list of different icons and style that Brand Team worked to be used through Telefonica applications." + BREAK + BREAK + "If you have any question, please you can ask directly in the app of Microsoft Teams, in [Mistica Team](https://teams.microsoft.com/l/team/19%3ad2e3607a32ec411b8bf492f43cd0fe0c%40thread.tacv2/conversations?groupId=e265fe99-929f-45d1-8154-699649674a40&tenantId=9744600e-3e04-492e-baa1-25ec245c6f10). " + \
248+
BREAK + BREAK + "## Documentation" + BREAK + BREAK + "### Develop" + BREAK + BREAK + "#### iOS and Android" + BREAK + BREAK + "You can get .pdf or .svg files from this repo." + BREAK + BREAK + "#### Web" + BREAK + BREAK + \
249+
"Visit [Mistica Storybook](https://mistica-web.vercel.app/?path=/story/icons-catalog--catalog) to get all the detail about using Mistica Icons Library" + BREAK + BREAK + "### Design" + BREAK + BREAK + "Use Mística icons library in Figma!" + BREAK + BREAK
250+
markdown_content += documentation + BREAK
251+
252+
markdown_content += "## Equivalence status\n\n"
253+
for bar in bars:
254+
markdown_content += bar + "\n"
255+
markdown_content += " " + BREAK
256+
257+
# Add equivalence status table
258+
markdown_table = generate_markdown_table(icon_data, folders)
259+
markdown_content += markdown_table + "\n"
260+
261+
legend = (
262+
"<sub>**Concepts**: Counts the different names of icons in the set excluding any variations in style or weight.</sub> "
263+
+ BREAK +
264+
"<sub>**Total**: The total number of icons found in a brand set. Counting light, regular and filled weights.</sub> "
265+
+ BREAK +
266+
"<sub>**All Equivalence**: Icons that are present in all sets.</sub> "
267+
+ BREAK +
268+
"<sub>**Some Equivalence**: Icons that are present in some sets.</sub> "
269+
+ BREAK +
270+
"<sub>**Unique**: Icons that only exists in this set.</sub> "
271+
+ BREAK +
272+
"<sub>**Missing**: Missing icons with respect to other sets.</sub>"
273+
)
274+
275+
markdown_content += legend + "\n"
276+
277+
markdown_content += "## Icon equivalence\n\n"
278+
icon_table_output = generate_icon_table(root_folder)
279+
markdown_content += icon_table_output + "\n"
280+
281+
with open("./README.md", "w") as file:
282+
file.write(markdown_content)
283+
284+
if __name__ == "__main__":
285+
root_folder = "icons"
286+
main(root_folder)
287+

0 commit comments

Comments
 (0)