Musings of a dad with too much time on his hands and not enough to do. Wait. Reverse that.

Tag: powershell (Page 2 of 7)

Iterating over a date range

I leverage a number of different programming and scripting tools. Recently, I found myself in a situation where I had to write code to loop through a range of dates to do some operations, by month, in not one, not two…but three different languages: Scala, Python, and Bash. The coding principles are the same across the technologies, but the syntax sure is different.

Here are code examples in four technologies–I threw in PowerShell for good measure–for looping through a range of dates. I loop by month, but these could easily be adapted to loop by day or year or whatever increment fits your needs.

Scala

import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.Date

val start = LocalDate.of(2020, 1, 1) // inclusive in loop
val end = LocalDate.of(2020, 9, 1) // excluded from loop

val template = "This loop is for Year %1$d and Month (zero padded) %2$s \n"

val date_range = Iterator.iterate(start) { _.plusMonths(1) }.takeWhile(_.isBefore(end))
while(date_range.hasNext){
	val d = date_range.next
	val s = template.format(d.getYear, d.format(DateTimeFormatter.ofPattern("MM")))
	print(s)
}

Python

import datetime
import calendar

start = datetime.date(2020, 1, 1)
end = datetime.date(2020, 9, 1)
template = "This loop is for Year {0} and Month (zero padded) {1:%m}"

while start != end:
	s = template.format(start.year, start)
	print(s)
	days_in_month = calendar.monthrange(start.year, start.month)[1]
	start = start + datetime.timedelta(days=days_in_month)
	

Bash

start=2020-1-1
end=2020-9-1

while [ "$start" != "$end" ]; do
	s="`date -d "$start" +"This loop is for Year %Y and Month (zero padded) %m"`"
	echo s
	start=$(date -I -d "$start + 1 month")
done

PowerShell

$start = get-date "2020-1-1"
$end = Get-Date "2020-9-1"

while($start -ne $end){
    "This loop is for Year {0:yyyy} and Month (zero padded) {0:MM}" -f $start
    $start = $start.AddMonths(1)
}

Logging in PowerShell

As far as I can tell, there are no logging APIs built into the PowerShell framework. So, when I have to write a code solution in PowerShell, I just add this snippet to meet some of my basic logging needs.

Set up some helper variables

I need to set the name and location of my log file. In addition, I like to add a “run id” to my logs so that I can easily group all the log entries associated with a given execution together for analysis or performance reporting:

$ExecutionDir = Split-Path $MyInvocation.MyCommand.Path
$RunId = ([guid]::NewGuid()).ToString().Replace("-", "")
$logFile = "$ExecutionDir\my_log.log"

Add my helper function

This simple helper function meets my basic logging needs:

function Write-Log($logMsg){
    ("{0}|{1}|{2}" -f ("{0:yyyy-MM-dd HH:mm:ss}" -f (Get-Date).ToUniversalTime()), $RunId, $logMsg) | Out-File $logFile -Append
}

Now, log away

Write-Log -logMsg "Starting up some script"
# do some work here
Sleep 2
Write-Log -logMsg "Finishing some script"

This produces a simple log like below. You can see my “run id” in the second column that helps me group log lines together by unique executions:

Sure, there are no log levels, no file rotations, no easy way to change the format…but in lieu of a built in API, this does the trick. One way it could be improved would be to stick this work into a module that I could simply import from script-to-script instead of copy/pasting this code around.

Music to drive by, Part 4

Another post in my continuing saga to collect quality, offline music to play in my car. For more details, see my previous posts:

I’ve made a few revisions lately to the code I wrote to inventory my music collection–over 300 albums and over 4000 songs–and write select songs to a USB stick for playing in my car.

Dealing with odd characters unfriendly to PowerShell

Several of my filepaths including characters with brackets, ampersands, and the like that don’t seem particularly friendly to some of the PowerShell commands I use. In particular Select-Object -Unique does not seem fond of strings with these types of characters in them. To circumvent this issue, I’ve found that if I encode those strings before performing the Unique operation on them, I can avoid a certain amount of pain. However, I’ve also found that Shell’s Namespace method is not fond of escaped characters. So, immediately after I produce my unique set of folders to process, I unencode those strings:

$mp3_folders = dir "$music_folder\*.mp3" -Recurse | where {$dirs_to_exclude -notcontains $_.Directory.FullName} | select Directory | %{[RegEx]::Escape($_.Directory)} | select -Unique | %{[RegEx]::Unescape($_)}

Adding additional fields

There are a lot of ID3 tag fields to filter on

Previously, I was only collecting the “contributing artist”; however, there is also an “album artist” field, so I added that to my inventory, as well. Often, there might be multiple people or bands listed as contributing artists for a single song, however, usually, the album artist is just the band or the singer who released the CD. Differentiating these two values can be helpful when you start to filter down just what music you want on your thumb drive.

Writing my inventory to a CSV file instead of a JSON file

In my first version of the inventory script, I produced a JSON file representing my music inventory. That’s not necessarily a bad approach; however, if I write my inventory out as a CSV file instead, I can then easily open that file in Excel and start to quality check the metadata in my music files.

Making sure to encode my inventory file as UTF8

I have a fair amount of foreign artists in my collection using accented letters and the like. Encoding my inventory as UTF8 will properly preserve these characters.

Filtering on both album artists and albums

My music collection includes music my children enjoy–much of which I find annoying. For example, I don’t enjoy singing along to the Smurfs soundtrack while stuck in rush hour traffic. So, I’ve added logic to my “copy” script to not only filter out particular artists I don’t want to hear in the car but also filter out specific albums, as well.

Filtering on spoken word audio

I have a few CDs that include speeches or interviews of the artists, such as The Beatles Anthology. Personally, I’d rather hear The Beatles’s music over a speech or interview, so I do my best to filter such audio out of my playlist by excluding any song who’s title includes the word speech. I should probably extend that logic to include the word interview, also.

$mp3s_to_write_to_drive = $music_collection | where {$genres_to_include -contains $_.genre} | where {$album_artists_to_exclude -notcontains $_.album_artist} | 
    where {$albums_to_exclude -notcontains $_.album} | where {$_.song -notlike "*speech*"} | sort {random}

For these improvements and more, check out my new versions here.

« Older posts Newer posts »

© 2024 DadOverflow.com

Theme by Anders NorenUp ↑