|
| 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}%) " 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}%) " 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}%) " 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}%) " |
| 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}%) " |
| 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 = " " 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 = "" + BREAK + "" + 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