Project: Apps Library

Process to maintain my apps library

07 Sep 2022

Creating a list of all apps installed on my 2 Macs

Fetching all apps on my systems with Python, and listing them in Grist Grist | The Evolution of Spreadsheets

Plus copy each app's icon in its original format.

####################
# Import Apps to Grist

import grist_PE
from slugify import slugify

import glob
import os
import shutil

list_locations = [
    '/Applications/*.app', # fetches all apps in my Applications folder
    '/Applications/Setapp/*.app', # fetches all apps in my Setapp subscription
    '/Applications/Adobe Acrobat DC/*.app', # fetching Adobe apps within their folder
    '/Applications/Adobe Illustrator 2022/*.app', # fetching Adobe apps within their folder
    '/Applications/Adobe Photoshop 2022/*.app', # fetching Adobe apps within their folder
    '/Volumes/Macintosh HD-1/Applications/*.app', # fetching all apps from the Applications folder on my 2nd Mac on network
]

list_errors = []

count_file = 0

for location in list_locations:

    parts = location.split('*')
    prefix = len(parts[0])

    list_of_files = glob.glob(location)

    print()
    for file in list_of_files:
        count += 1

        app = file[prefix:-4]
        print(app)
        slug = slugify(app)

        for root, dirs, files in os.walk(file):
            for name in files:
                # copying app's logos to a central folder
                if name.endswith((".icns")):
                    count_file += 1

                    print(f"{root}/{name}")

                    try:
                        shutil.copy2(f'{root}/{name}', f"/path/to/folder/{slug}_{name}.icns")
                    except:
                        list_errors.append(name)
                        continue

        print()

        # adding to Grist
        grist_PE.Apps.add_records('Master', [
                                        {   
                                            'name': app,
                                            'type': ['L', 'macOS'],
                                            'status': ['L', 'using'],
                                            'slug': slug,
                                            }
                                    ])


for error in list_errors:
    print(error)

print(f"\nTotal of {len(list_errors)} errors")

Convert apps' logos to usable format

####################
# Convert .icns files to .png 

count_deleted = 0

import icnsutil

for root, dirs, files in os.walk("/path/to/folder/with/icns"): # path to folder with .icns files
    for name in files:
        if name.endswith((".icns")):
            count += 1
            file_path = f"{root}/{name}"
            print(file_path)
            slug = name[:-5]
            print(slug)

            img = icnsutil.IcnsFile(file_path)
            output_path = f"/path/to/output/folder/{slug}"
            os.makedirs(output_path)
            img.export(output_path, allowed_ext='png', convert_png=True)


## Cleaning

### move to apps folder

import shutil

path = "/path/to/folder/png/"
for root, dirs, files in os.walk(path):
    prefix = len(path)
    for name in files:
        if name.endswith((".png")):
            count += 1
            file_path = f"{root}/{name}"
            print(file_path)
            file_name = name
            print(file_name)
            slug = root[prefix:]
            print(slug)

            shutil.copy2(f'{root}/{name}', f'/path/to/folder/content/images/apps/{slug}_{file_name}')

### remove undesirables

blacklist = [
    '@2x',
    '_16x',
    '_32x',
]

path = "/path/to/folder/content/images/apps/"
for black in blacklist:
    for root, dirs, files in os.walk(path):
        prefix = len(path)
        for name in files:
            if name.endswith((".png")):
                count += 1
                file_path = f"{root}/{name}"

                if black in name:
                    os.remove(file_path)
                    count_deleted += 1

Import Apps list from Grist to Pelican Markdown files (notes)

22 Sep 2022

####################
# Import Apps list from Grist to Pelican markdown files

from datetime import date
from datetime import timedelta
from operator import attrgetter
from slugify import slugify

## Define Published Date as yesterday so library updates do not show up as Featured article
today = date.today()
yesterday = today - timedelta(days = 1)
publish_date = f"{yesterday.strftime('%Y-%m-%d')}"

# Identify if cover image is available
list_existing_images = []
for root, dirs, files in os.walk("/path/to/folder/content/images/apps"):
    for name in files:
        list_existing_images.append(name)

### books/library 

apps_data = grist_PE.Apps.fetch_table('Master') # list of namedtuples
total = len(apps_data)
print(f"{total} Apps in Grist\n")

today = datetime.now()
date_today = today.strftime('%Y-%m-%d')

apps_sorted_by_title = sorted(apps_data, key=attrgetter('slug'))

# APPS NOTES

import glob

notes_path = '/path/to/folder/content/articles/apps/*.md'
list_of_apps_notes = [x[len(notes_path)-4:-3] for x in glob.glob(notes_path)]

# USING

count_using = 0

output_using = ''

for b in apps_sorted_by_title:

    count_row += 1

    if b.status != None:
        if 'using' in b.status:

            count_using += 1

            title = b.name
            slug = b.slug

            summary = b.summary
            if b.rating != None:
                rating = "⭐️" * b.rating
            else:
                rating = ''
            link = b.link

            types = b.type
            type = ''
            if types != None:
                for typ in types:
                    if typ != 'L':
                        if type != '':
                            type = f"{type}  <span class=\"tag\">{typ}</span>"
                        else:
                            type = f"<span class=\"tag\">{typ}</span>"
            else:
                type = ''

            categories = b.category
            category = ''
            if categories != None:
                for cat in categories:
                    if cat != 'L':
                        if category != '':
                            category = f"{category}  <span class=\"tag\">{cat}</span>"
                        else:
                            category = f"<span class=\"tag\">{cat}</span>"

            tags_raw = b.tags
            tags = ''
            if tags_raw != None:
                for tag in tags_raw:
                    if tag != 'L':
                        if tags != '':
                            tags = f"{tags}  <span class=\"tag\">{tag}</span>"
                        else:
                            tags = f"<span class=\"tag\">{tag}</span>"

            if slug in list_of_apps_notes:
                note = f'[[note](../../apps/{slug}/index.html)]'
            else:
                note = ''

            icon = f"<img class=\"app_icon\" src=\"https://ik.imagekit.io/vhucnsp9j1u/images/apps/{slug}_128x128.png\" alt=\"{slug}\"/>"

            if link != '':
                output_using = f"{output_using}\n {icon} | <a href=\"{link}\" target=\"_blank\">{title}</a> | {note} | {type} | {category} | {tags} | {summary} | {rating} | "
            else:
                output_using = f"{output_using}\n {icon} | {title} | {note} | {type} | {category} | {tags} | {summary} | {rating} | "

# Adding the header at the end to ensure proper books count
header = f"Title: My Apps Library\nDate: {publish_date}\nTags: apps\nSummary: keeping track of apps I currently use, have used and on my radar\n\n{total} apps total"

header_using = f"\n\n# Using: {count_using} apps  \n\napp | name/website | note | type | category | tags | summary | rating |\n---|---|---|---|---|---|---|---|"


# ## USED

count_used = 0

output_used = ''

for b in apps_sorted_by_title:

    if b.status != None:
        if 'used' in b.status:

            count_used += 1

            title = b.name
            slug = b.slug

            summary = b.summary
            if b.rating != None:
                rating = "⭐️" * b.rating
            else:
                rating = ''
            link = b.link

            types = b.type
            type = ''
            if types != None:
                for typ in types:
                    if typ != 'L':
                        if type != '':
                            type = f"{type}  <span class=\"tag\">{typ}</span>"
                        else:
                            type = f"<span class=\"tag\">{typ}</span>"
            else:
                type = ''

            categories = b.category
            category = ''
            if categories != None:
                for cat in categories:
                    if cat != 'L':
                        if category != '':
                            category = f"{category}  <span class=\"tag\">{cat}</span>"
                        else:
                            category = f"<span class=\"tag\">{cat}</span>"

            tags_raw = b.tags
            tags = ''
            if tags_raw != None:
                for tag in tags_raw:
                    if tag != 'L':
                        if tags != '':
                            tags = f"{tags}  <span class=\"tag\">{tag}</span>"
                        else:
                            tags = f"<span class=\"tag\">{tag}</span>"

            if slug in list_of_apps_notes:
                note = f'[[note](../../apps/{slug}/index.html)]'
            else:
                note = ''

            icon = f"<img class=\"app_icon\" src=\"https://ik.imagekit.io/vhucnsp9j1u/images/apps/{slug}_128x128.png\" alt=\"{slug}\"/>"

            if link != '':
                output_used = f"{output_used}\n {icon} | <a href=\"{link}\" target=\"_blank\">{title}</a> | {note} | {type} | {category} | {tags} | {summary} | {rating} | "
            else:
                output_used = f"{output_used}\n {icon} | {title} | {note} | {type} | {category} | {tags} | {summary} | {rating} | "

header_used = f"\n\n# Used: {count_used} apps  \n\napp | name/website | note | type | category | tags | summary | rating |\n---|---|---|---|---|---|---|---|"


# ## RADAR

count_radar = 0

output_radar = ''

for b in apps_sorted_by_title:

    if b.status != None:
        if 'radar' in b.status:

            count_radar += 1

            title = b.name
            slug = b.slug

            summary = b.summary

            link = b.link

            types = b.type
            type = ''
            if types != None:
                for typ in types:
                    if typ != 'L':
                        if type != '':
                            type = f"{type}  <span class=\"tag\">{typ}</span>"
                        else:
                            type = f"<span class=\"tag\">{typ}</span>"
            else:
                type = ''

            categories = b.category
            category = ''
            if categories != None:
                for cat in categories:
                    if cat != 'L':
                        if category != '':
                            category = f"{category}  <span class=\"tag\">{cat}</span>"
                        else:
                            category = f"<span class=\"tag\">{cat}</span>"

            tags_raw = b.tags
            tags = ''
            if tags_raw != None:
                for tag in tags_raw:
                    if tag != 'L':
                        if tags != '':
                            tags = f"{tags}  <span class=\"tag\">{tag}</span>"
                        else:
                            tags = f"<span class=\"tag\">{tag}</span>"

            if slug in list_of_apps_notes:
                note = f'[[note](../../apps/{slug}/index.html)]'
            else:
                note = ''

            icon = f"<img class=\"app_icon\" src=\"https://ik.imagekit.io/vhucnsp9j1u/images/apps/{slug}_128x128.png\" alt=\"{slug}\"/>"

            if link != '':
                output_radar = f"{output_radar}\n <a href=\"{link}\" target=\"_blank\">{title}</a> | {note} | {type} | {category} | {tags} | {summary} |  "
            else:
                output_radar = f"{output_radar}\n {title} | {note} | {type} | {category} | {tags} | {summary} |  "

header_radar_table = f"\n\n# Radar: {count_radar} apps  \n\nname/website | note | type | category | tags | summary | \n---|---|---|---|---|---|"

output = f"{header}{header_using}{output_using}{header_used}{output_used}{header_radar_table}{output_radar}\n\n"

with open(f"notes/content/articles/apps/library.md", 'w') as file:
    file.write(output)


### apps/radar

# Adding the header at the end to ensure proper books count
header_radar = f"Title: Apps on my radar\nDate: {publish_date}\nTags: apps\nSummary: keeping track of apps I might want to look into...\n\n{count_radar} apps on radar"

output_radar = f"{header_radar}{header_radar_table}{output_radar}\n\n"

with open(f"notes/content/articles/apps/radar.md", 'w') as file:
    file.write(output_radar)

########################################################################################################

Next

generate note for B2B Sales apps only

as b2b-sales/apps.

links

social