Jump to content

Creating 'new projects' in OSX


Recommended Posts

Hi all --

 

I'm brand new to Alfred and this community, so apologies if some of these questions have already been answered.

 

I work on a lot of new projects and manage a LOT of files. 

 

Here, in a nutshell, is something I'd love to be able to do programmatically:

 

* Initiate a workflow with the naming convention "Client - Project"

* Have Alfred create a folder with that name in a set directory

* Have Alfred scan a separate directory of recent downloads for the "client" and/or "project" keywords and then move all matching files to the directory created above

 

Is this the kind of thing you can do with Alfred? And if so, can someone point me to a few appropriate links?

 

Thanking you!

Link to comment

A similar topic was brought up a little while ago about creating projects from file structure templates and naming them according to a fixed standard.

 

This is something you can do with Alfred fairly easily if you want a bespoke workflow. If you want to abstract it and release it, then it'll be a bit harder.

 

Basically, you could do this with a few bash commands fairly easily.

 

Make a new blank workflow. Add in an input keyword, argument required, space. Link that to a "run script" action. Use Bash. Then drop the below into it, but change the three directory variables near the top.

#!/bin/sh
# Check to make sure that the syntax is followed.
if [[ "{query}" =~ " - " ]]; then
  echo "Invalid entry. Please use 'Client - Project' syntax."
  exit
fi


# Set the base directory for all of the projects.
# If you don't want it in the home directory, then
# replace $HOME with the __FULL__ path to the base
# directory.
basedir="$HOME/path/to/dir" 
template="$HOME/path/to/template/files"
downloads="$HOME/Downloads"

# IFS is the internal field separator. If you want
# something else, then change it. For now it's:
# " - "
while IFS=' - ' read -ra string; do
  # since there are only two arguments in this array,
  # we don't need to loop through it.
  client="${string[0]}"
  project="${string[1]}"
done <<< "{query}"

# Make the directories
if [ ! -d "$basedir" ]; then
  mkdir "$basedir"
fi
if [ ! -d "$basedir/$client" ]; then
  mkdir "$basedir/$client"
fi
if [ ! -d "$basedir/$client/$project" ]; then
  mkdir "$basedir/$client/$project"
fi

# Copy all the files from the template directory
cp -fR "$template/"* "$basedir/$client/$project"

# Scan the downloads folder for files
files=`find "$downloads" -iname '*"$client"*'  -o -iname '*"$project"*' \
-maxdepth 0 -type f

for file in "${files[@]}"
do
  mv "$file" "$basedir/$client/$project/"
done

echo "Project created."

After that, link an "output" notification to it.

 

So, the script above should work, but I didn't test it, so you might have to tweak it or something. When you do this, it'll create a "client/project" directory structure... if that's not what you want, then change the directory creation and the directory variables (wherever it says "$client/$project" change to "$client - $project").

 

Make sure that neither the client nor the project has the " - " string because that will cut some stuff off.

 

Cheers.

 

Shawn

Link to comment

Hey Shawn -

 

Thanks a ton for your help. I'm still learning, so any answers you might be able to provide to the following questions would be amazing:

 

1. What if I'd like for my argument to be Client - Project where either client or project could be multiple words?

 

2. Is there a way to force the directory name to be created in all caps?

 

3. I've included my amended bash script is below. The debugger is still yielding the following error whenever I use it, though 

 

[ERROR: alfred.workflow.action.script] Code 2: /bin/bash: -c: line 32: unexpected EOF while looking for matching ``'

/bin/bash: -c: line 41: syntax error: unexpected end of file

 

Any tips?

#!/bin/sh
# Check to make sure that the syntax is followed.
if [[ "{query}" =~ " - " ]]; then
  echo "Invalid entry. Please use 'Client - Project' syntax."
  exit
fi


# Set the base directory for all of the projects.
# If you don't want it in the home directory, then
# replace $HOME with the __FULL__ path to the base
# directory.
basedir="/Users/redacted/Dropbox/New Jobs/" 
downloads="/Users/redeacted/Downloads/"

# IFS is the internal field separator. If you want
# something else, then change it. For now it's:
# " - "
while IFS=' - ' read -ra string; do
  # since there are only two arguments in this array,
  # we don't need to loop through it.
  client="${string[0]}"
  project="${string[1]}"
done <<< "{query}"


# Make the directories
if [ ! -d "$basedir/$client - $project" ]; then
  mkdir "$basedir/$client - $project"
fi

#Scan the downloads folder for files
files=`find "$downloads" -iname '*"$client"*'  -o -iname '*"$project"*' \
-maxdepth 0 -type f

for file in "${files[@]}"
do
  copy "$file" "$basedir/$client - $project"
done

echo "Project created."
Link to comment

(1) The number of words shouldn't matter at all; you just need to make sure that the string " - " is not in either the client or the project name (otherwise, you'll get unexpected results).

 

(2) We just need to change it; the easiest way to do so in bash is just to take the variable that's already been set and do this:

VAR=`echo "$VAR" | tr '[:lower:]' '[:upper:]'`

The ` marks mean to evaluate the code and make that the output; so we're just echoing it but piping it through a "tr" command that uses a small regex to just switch lower to upper.

 

(3) If you look at the error, it says that the script was still waiting for something in ` marks to be closed because, in my initial script, I forgot to close it. So,

#Scan the downloads folder for files
files=`find "$downloads" -iname '*"$client"*'  -o -iname '*"$project"*' \
-maxdepth 0 -type f

should have had a ` mark after it.

 

Anyway, fixing the problem in (3) and putting in the logic for (2) yields this:

#!/bin/sh
# Check to make sure that the syntax is followed.
if [[ "{query}" =~ " - " ]]; then
  echo "Invalid entry. Please use 'Client - Project' syntax."
  exit
fi

# Set the base directory for all of the projects.
# If you don't want it in the home directory, then
# replace $HOME with the __FULL__ path to the base
# directory.
basedir="/Users/redacted/Dropbox/New Jobs/"
downloads="/Users/redeacted/Downloads/"

# IFS is the internal field separator. If you want
# something else, then change it. For now it's:
# " - "
while IFS=' - ' read -ra string; do
  # since there are only two arguments in this array,
  # we don't need to loop through it.
  client="${string[0]}"
  project="${string[1]}"
done <<< "{query}"

client=`echo $client | tr '[:lower:]' '[:upper:]'`
project=`echo $project | tr '[:lower:]' '[:upper:]'`

# Make the directories
if [ ! -d "$basedir/$client - $project" ]; then
  mkdir "$basedir/$client - $project"
fi

#Scan the downloads folder for files
files=`find "$downloads" -iname '*"$client"*'  -o -iname '*"$project"*' \
-maxdepth 0 -type f`

for file in "${files[@]}"
do
  copy "$file" "$basedir/$client - $project"
done

echo "Project created."

Again, it __should__ work, but there might be another problem or two in it.

 

I can't really test it because it's such a bespoke workflow.

Edited by Shawn Rice
Link to comment

Thanks Shawn! Still two things. (I REALLY appreciate all your help on this.)

 

1. If I enter "[keyword] client - project" where project has one word, everything works fine. But if I enter "[keyword] client - project name" where project name has two words, the script will return the error "Invalid entry. Please use 'Client - Project' syntax." Is that because I'm using the "-"? My understanding was that the " - " in my query was just a delimiter to differentiate the client string from the project name string?

 

2. It doesn't look like the copy functionality is working at all. Using the code you supplied, I got the error "Code 0: /bin/bash: line 38: copy: command not found", so I changed copy to cp, and now it says "Code 0: cp: fts_open: No such file or directory" when there definitely is?

 

Any ideas? (And again, THANK YOU.)

Link to comment

Whoops. Two problems from writing it to fast: (1) I forgot to negate the error, so valid attempts should work, but others shouldn't. (2) I accidentally wrote copy rather than cp.

 

Not sure what's happening with the last error you reported. If could be that there are no matching files in the downloads directory, so it's failing. But if there are files that match in that directory, then that's a different problem.

 

So, let's try these fixes:

 

(1) Change

if [[ "{query}" =~ " - " ]]; then
  echo "Invalid entry. Please use 'Client - Project' syntax."
  exit
fi

to

if [[ ! "{query}" =~ " - " ]]; then
  echo "Invalid entry. Please use 'Client - Project' syntax."
  exit
fi

(2) Keep the "cp" rather than "copy"

 

(3) We'll add error checking around the for loop; so overwrite the for loop with:

if [[ "${#files[@]}" -gt 0 ]]; then
	for file in "${files[@]}"
	do
	  copy "$file" "$basedir/$client - $project"
	done
fi
Link to comment

Yeah, no joy, unfortunately!

 

I've made these changes but a query of "[keyword] client - project word word word" still only creates a directory called "client - project"

 

I've also got a file called project.pdf in the downloads directory and it's not being copied over.

 

Oh well -- I'll keep playing with it.

#!/bin/sh
# Check to make sure that the syntax is followed.
if [[ ! "{query}" =~ " - " ]]; then
  echo "Invalid entry. Please use 'Client - Project' syntax."
  exit
fi

# Set the base directory for all of the projects.
# If you don't want it in the home directory, then
# replace $HOME with the __FULL__ path to the base
# directory.
basedir="/Users/redacted/Dropbox/CURRENT JOBS/NEW/"
downloads="/Users/redacted/Desktop/"

# IFS is the internal field separator. If you want
# something else, then change it. For now it's:
# " - "
while IFS=' - ' read -ra string; do
  # since there are only two arguments in this array,
  # we don't need to loop through it.
  client="${string[0]}"
  project="${string[1]}"
done <<< "{query}"

client=`echo $client | tr '[:lower:]' '[:upper:]'`
project=`echo $project | tr '[:lower:]' '[:upper:]'`

# Make the directories
if [ ! -d "$basedir/$client - $project" ]; then
  mkdir "$basedir/$client - $project"
fi

#Scan the downloads folder for files
files=`find "$downloads" -iname '*"$client"*'  -o -iname '*"$project"*' \
-maxdepth 0 -type f`

if [[ "${#files[@]}" -gt 0 ]]; then
	for file in "${files[@]}"
	do
	  cp "$file" "$basedir/$client - $project"
	done
fi

echo "Project created."
Link to comment

Try breaking down the individual parts into separate shell scripts and running them from the command line.

 

So, do something like 

downloads="/path/to/folder"
client="client"
project="project name"


#Scan the downloads folder for files
files=`find "$downloads" -iname '*"$client"*'  -o -iname '*"$project"*' \
-maxdepth 0 -type f`

for file in "${files[@]}"
do
   echo "$file"
done

and see what that spits out. Then, tweak it until you can make sure that the right files are found when in the downloads folder.

Link to comment

Okay, for the more than one word problem... I'm not sure why we have to do this, but replace

# IFS is the internal field separator. If you want
# something else, then change it. For now it's:
# " - "
while IFS=' - ' read -ra string; do
  # since there are only two arguments in this array,
  # we don't need to loop through it.
  client="${string[0]}"
  project="${string[1]}"
done <<< "{query}"

client=`echo $client | tr '[:lower:]' '[:upper:]'`
project=`echo $project | tr '[:lower:]' '[:upper:]'`

with

# IFS is the internal field separator. If you want
# something else, then change it. For now it's:
# " - "
count=0
project=""
while IFS=' - ' read -ra string; do
  # since there are only two arguments in this array,
  # we don't need to loop through it.
  for s in "${string[@]}"; do
    if [ "$count" = 0 ]; then
      client="${string[$count]}"
    else
      if [ -z "$project" ]; then
        project="${string[$count]}"
      else
        project="$project ${string[$count]}"
      fi
    fi
    ((count++))
  done
done <<< "$query"

client=`echo $client | tr '[:lower:]' '[:upper:]'`
project=`echo $project | tr '[:lower:]' '[:upper:]'`
Link to comment

Try to implement these techniques to debug the workflow.

 

(And this is how I usually debug what I'm working on.)

 

You need to make sure that the variables are recording the right values. So, if you want to test this from the command line, then change the {query} to $1. Drop that into a file called "test.sh"; and then invoke it by using

sh test.sh "client - project project words"

in the terminal.

 

So, you won't see the right outputs unless you output them at the right places. So, look at parts of the scripts where it might be messing up, and then just add some echo "$variable" lines in places and see what they spit out. Once they get the right outputs, then change them back to implement the proper commands.

 

If you want to debug this in Alfred itself, then do the same thing above (except keep it in Alfred and don't change {query} to $1). Add in those echo statements, but make sure out link the script to some sort of output, either Large Text or Post Notification. You'll see the contents of the echo statements there, and you'll also see any error messages that can come out.

 

So, that's a place that you can find the places in the script that are broken.

Link to comment
  • 1 month later...

ive just spotted this. asd was wondering whether someone know about how i can add pre-built files to the new created folder

im keeping the pre-built files in the workflow source. but dont know how to implement them in. they are LUA files, something to start new projects off without having to do the typing as much

 

here is what i have done. slightly different to what your working on, but is aimed at the same way to create and add files to the created project

 

right ive gotten pretty far. which is good.

the one and only problem i need help with, is adding the pre-built files. but im close

 

so here is the first parts

 

file action

QUERY={query}

mkdir "$QUERY/untitled folder"
open "$QUERY"

then have keyword to activzate the process

source includes.sh

#Query from user (folder's name)
QUERY={query}

if [ ! "$QUERY" ] ; then
    QUERY="untitled folder"
fi

> "$FILE"

echo "$QUERY" > "$FILE"

which has a search folder script to save to attach to the previous one

tell application "Alfred 2" to search "folder "

lastly this is the desitination of where it will be saved

source includes.sh

#Query from user
QUERY={query}

#Read line from file
folder_name=$(head -1 "$FILE")

destination="$QUERY/$folder_name"
destination_check="$destination"

#Count of existing folders with same name
count=1

if [ "$folder_name" ] ; then

    while [ -e "$destination_check" ]
    do
        echo "incrementing count"
        #increment counter
        count=`expr $count + 1`

        echo "current query: $destination_check"
        destination_check="$destination $count"

    done < "$destination_check"

    mkdir -p "$destination_check"

    echo "final query: $destination"
    open "$QUERY"
    > "$FILE"

else

    open "$QUERY"

fi

now i have the files in the workflow itself, with an 'include.sh'. but cant figure out how to link those files with the created folder. once i get that. its done, and i wont bother whomever for some time  ;)

include.sh

#Working Directories
DATAPATH="$HOME/Library/Application Support/Alfred 2/Workflow Data/smokingbunny.makeLOVE"
FILE="$DATAPATH/main.lua"
FILE="$DATAPATH/conf.lua"

#Enable aliases for this script
shopt -s expand_aliases

#Case-insensitive matching
shopt -s nocasematch

#First run check
if [ ! -e "$FILE" ]; then
    mkdir "$DATAPATH"
    touch "$DATAPATH/main.lua"
    touch "$DATAPATH/conf.lua"
fi

so this is what i have, just need help with the files being added

 

thanks if someone can help

Edited by smoking bunny
Link to comment
  • 2 weeks later...

(1): 

FILE="$DATAPATH/main.lua"
FILE="$DATAPATH/conf.lua"

The second line overwrites the first line, so that's no good. Either add it as an array or have another variable.

 

(2): If the files are hardcoded (in that they're always the same files, which the above indicates), why not just do something like

cp "$DATAPATH/filename.extension" "destination_path"

for each file that you need to move?

 

If you have different structures, then you could define them in other files that would do the same sort of thing....

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...