Overlap is the largest city in central Iowa right on the border, a rural metropolis and the smallest ghost town in the United States. It is known for its sprawling fields, bustling suburbs, desolate swamps, congested coastlines, abandoned territories, and for absolutely nothing at all.
The reason for its paradoxically muddled description is that Overlap, Iowa is the specific convergence point of all timelines, plot lines, parallel universes, and alternate realities.
There is no continuity in Overlap whatsoever. A particular person might be a sheriff one day and a custodian at a secret government research facility the next. Come next week, they might not even have ever existed.
The population of Overlap are not aware of their unique cosmic positioning. They simply go about their days as they always have, never realizing that their yesterday had no bearing at all on today, while tomorrow is a completely random concept.
Generation X Has a Mortgage and Car Payments
Saturday, April 27, 2024
Overlap, Iowa
Saturday, April 20, 2024
A Dewey Decimal System of My Own
Since I abandoned the social media platform previously known as, well, as not a single-letter name, I've been using and enjoying Mastodon, a decentralized version of microblogging. In a nutshell, anyone can start a Mastodon server, and there's no corporate overlord making crappy decisions designed to tank its stock value on purpose. Also, there's no stock value.
Mastodon is part of what's been named the Fediverse, which is the umbrella term for all sorts of decentralized versions of popular web sites. Even better, these federated sites are designed to work in concert with each other, which in my opinion is one of the main goals of an altruistic version of what the Internet can be.
Another part of the Fediverse is BookWyrm, a federated version of Goodreads. I used to try to use Goodreads to track my books and my reading, but I fell out of using it for some reason. With an exploration of the Fediverse and my discovery of BookWyrm, I've found myself back in the world of obsessively tracking the books I own, and which ones I've read or not.
I think part of what caused me to drift away from Goodreads was that I felt like I was being too obsessive about tracking everything: what was wrong with just reading and enjoying a good book? Did I have to enter it into a database and categorize it and rate it and review it?
Getting back into tracking my books has made me realize I think I was growing embarrassed about how much I enjoyed sorting and updating my own library. It was weird, right? But now, I don't think I care. At least, I'm trying not to care.
Taking things that are disorganized (or poorly organized) and applying some kind of logical order to them actual gives me joy. I can admit that now. I like tidying up when my office gets overly cluttered. It not only gives me a sense of satisfaction, but I feel I've accomplished something by making it better. In this case, keeping better track of books I've read and books I want to read, and books I own or that I once owned and can't remember if I got rid of them for some reason.
It's kind of a large undertaking, since I possess far too many books (especially now that I've started using my Kindle more and more). But as long as I only do maybe a shelf a night, and only then if I feel like it and not because I have to keep at it until it's done, it's been pretty enjoyable.
And regarding the title of this post: I don't actually have any kind of self-created system for categorizing my books. I just liked the sound of it as the title of a blog entry. Again: possibly showing my weird to the world, but I care less and less about that. And that's good.
Sunday, April 14, 2024
Off.
I finally get it.
I never really understood why people who were on medication would ever intentionally stop taking them, short of the medication making them feel worse than they did without it.
But what about making them feel less than they did without it?
I've noticed during the past few years that I wasn't crying at movies anymore, even movies that were once guaranteed to make me at least tear up, no matter how many times I'd seen them. I could sense the emotions present in YouTube videos of animals rescued from abusive situations or military personnel surprising their kids by returning from overseas and showing up at some school event. Both of those used to be automatic waterworks triggers but now, nothing.
I missed it. I missed the emotional release of letting tears flow, then gathering myself with a few breaths and moving on.
Wednesday, April 3, 2019
Creating and Updating Jira Issues with PowerShell
This is my basic function for getting a user's name and password.
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 function IdentifyUser { # Prompt user for logon credentials. $script:MyCredential = Get-Credential -Message "Please enter your network user ID and password:" -UserName $env:UserName if ($script:MyCredential) { $script:attemptConnection = $true $script:username = $script:MyCredential.Username $script:password = $script:MyCredential.GetNetworkCredential().password } else { Write-Host "" Write-Host $script:equalsLine Write-Host "Logon canceled by user." Write-Host $script:equalsLine } } #IdentifyUser
This function contains many of the fields associated with a Jira issue. This is where values would be altered for creating or updating a specific case.
function InitializeVariables { $script:target = "https://[your Jira base URL]" $script:projectKey = "[your Jira project key]"; #=================================== # Available variables/Jira fields #----------------------------------- $script:createOrUpdate = "update" $script:issueKey = "$script:projectKey-1" # this is ignored if creating an issue $script:acceptanceCriteria = "If you build it, they will come." $script:assignee = "[a Jira user name]" $script:description = "This issue was created from PowerShell using Jira REST API." $script:dueDate = "2020-01-01" $script:issueType = "Task" $script:label_1 = "Development" $script:label_2 = "Request" $script:priority = "Lowest" $script:summary = "Jira workflow examination" #=================================== $script:attemptConnection = $false $script:equalsLine = "=" * 130 if ($script:createOrUpdate.ToLower() -eq "create") { $script:method = "POST" $script:requestUri = "$script:target/jira/rest/api/latest/issue" } elseif ($script:createOrUpdate.ToLower() -eq "update") { $script:method = "PUT" $script:requestUri = "$script:target/jira/rest/api/latest/issue/$script:issueKey" } else { $script:method = "[ error ]" $script:requestUri = "[ error ]" } } #InitializeVariables
Here I create the JSON structure containing the field values and submit it to my Jira instance. The REST method invoked is POST for creating an issue, and PUT for updating one (this has been established in the InitializeVariables function.).
function PerformOperation { if ($script:attemptConnection) { $bodyString = @{ update = @{ } fields=@{ project=@{key="$script:projectKey"} # can be omitted when updating an issue; or it can just "change" it to the value it already has #assignee = @{name = "$script:assignee"} description = "$script:description" duedate = "$script:dueDate" issuetype = @{name = "$script:issueType"} labels = [string[]]"$script:label_1" priority = @{name = "$script:priority"} summary = "$script:summary" # comment out a line to omit it from item creation/update }} $bodyJSON = $bodyString | ConvertTo-Json -Depth 2 #-Compress try { $basicAuth = "Basic " + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("$($script:Username):$script:password")) $headers = @{ "Authorization" = $basicAuth "Content-Type"="application/json" } $response = Invoke-RestMethod -Uri $script:requestUri -Method $script:method -Headers $headers -Body $bodyJSON -UseBasicParsing if ($script:createOrUpdate.ToLower() -eq "create") { Write-Output "ID: $($response.id)" Write-Output "Key: $($response.key)" Write-Output "Self: $($response.self)" } } catch { Write-Warning "Remote Server Response: $($_.Exception.Message)" Write-Output "Status Code: $($_.Exception.Response.StatusCode)" } } } #PerformOperation
All that's left is to call these functions in sequence.
InitializeVariables IdentifyUser PerformOperation
Thursday, February 21, 2019
Discovering Active Directory groups with PowerShell
In most programs I write, you'll probably find an InitializeVariables function. While some variables sometimes need to be set at the top of the program, this function will usually hold the majority of my variables' starting values.
function InitializeVariables { $script:input_file = "" $script:output_file = "" $script:not_found_file = "" # Set the input + output files, depending on the presence of invocation parameters. if (-not $script:in) { $script:input_file = $script:default_input_file } else { $script:input_file = $script:in } if (-not $script:out) { $script:output_file = $script:default_output_file } else { $script:output_file = $script:out } if (-not $script:lost) { $script:not_found_file = $script:default_not_found_file } else { $script:not_found_file = $script:lost } } #InitializeVariables
In the interest of flexibility, I give the user the option of overwriting the output of previous runs or retaining them with a timestamp.
function CleanOutputFiles { # Remove existing output files, first preserving them if instructed. $overwrite_time = Get-Date -Format "yyyyMMdd-HHmmss" if ($script:preserve_previous_output -and (Test-Path -LiteralPath $script:output_file)) { $old_output_file = $script:output_file.Replace(".csv", "-$overwrite_time.csv") Rename-Item $script:output_file $old_output_file } elseif (Test-Path -LiteralPath $script:output_file) { Remove-Item $script:output_file } if ($script:preserve_previous_output -and (Test-Path -LiteralPath $script:not_found_file)) { $old_not_found_file = $script:not_found_file.Replace(".txt", "-$overwrite_time.txt") Rename-Item $script:not_found_file $old_not_found_file } elseif (Test-Path -LiteralPath $script:not_found_file) { Remove-Item $script:not_found_file } } #CleanOutputFiles
A fairly standard reading of the input file.
function ReadUserList { $script:user_list = Get-Content $script:input_file if (-not $script:suppress_screen_output) { Write-Host "" Write-Host "user_list is" $script:user_list.count "lines long." } } #ReadUserList
This is the meat of the program. The users are stored internally in a multidimensional arraylist--an arraylist whose members are arraylists--as a method of keeping users together with their groups, even if at some point in the future I add sorting functionality.
function CreateUserGroupList { # Create the main/outer arraylist that will hold user arraylists. $script:user_group_list = New-Object System.Collections.ArrayList forEach ($user in $script:user_list) { # Create an arraylist for each user. $single_user = New-Object System.Collections.ArrayList # The first element in a user's arraylist is the user's name. [void]$single_user.Add("$user") # Add the user's arraylist as an element of the main arraylist. [void]$script:user_group_list.Add($single_user) } if ($script:strip_header_from_input) { [void]$script:user_group_list.Remove($script:user_group_list[0]) } if (-not $script:suppress_screen_output) { Write-Host "user_group_list is" $script:user_group_list.Count "items long" -NoNewline if ($script:strip_header_from_input) { Write-Host " (the header line has been stripped)." Write-Host "" } else { Write-Host "." Write-Host "" } } for($loopCounter = 0; $loopCounter -lt $script:user_group_list.Count; $loopCounter++) { try { $AD_entry = (New-Object System.DirectoryServices.DirectorySearcher("(&(objectCategory=User)(samAccountName=$($script:user_group_list[$loopCounter][0])))")).FindOne().GetDirectoryEntry().memberOf forEach ($line in $AD_entry) { # Extract the Common Name from each line, which is structured as: # CN=[Common Name],OU=[Organizational Unit],OU=[Organizational Unit],DC=[Domain Component],[Domain Component],[Domain Component] [void]$script:user_group_list[$loopCounter].Add($line.Substring(3, $line.IndexOf(",") - 3)) } if (-not $script:suppress_screen_output) { $user_number = $loopCounter + 1 $user_name = $script:user_group_list[$loopCounter][0] $group_count = $script:user_group_list[$loopCounter].Count - 1 $output_line = "${user_number}: $user_name, $group_count groups." Write-Host $output_line Write-Host "" } } #try catch [System.Management.Automation.RuntimeException] { # The user was not found in the Active Directory search. if (-not $script:suppress_screen_output) { $user_number = $loopCounter + 1 $user_name = $script:user_group_list[$loopCounter][0] $output_line = "${user_number}: $user_name was not found." Write-Host $output_line Write-Host "" } Write-Output $script:user_group_list[$loopCounter][0] >> $script:not_found_file } #catch } #loopCounter } #CreateUserGroupList
Constructing the output file. In this case, the specs required that each line start with a user name, followed by one user group name.
function WriteOutputFile { if (-not $script:suppress_screen_output) { Write-Host "" Write-Host "Writing output file..." Write-Host "" } forEach($user_group in $script:user_group_list) { for($loopCounter = 1; $loopCounter -lt $user_group.Count; $loopCounter++) { # Each line in the output file will be formatted as: # [user name], [one user group] $output_line = $user_group[0] + "," + $user_group[$loopCounter] Write-Output $output_line >> $script:output_file } } } #WriteOutputFile
Here is the structure of the remainder of the program, beginning with (optional) parameters and concluding with the trail of function calls that do the actual work of the program
param ( [string]$in, [string]$out, [string]$lost ) #******************************************************************************** # # This program can be invoked with or without file name parameters: # # powershell -File AD_user_groups.ps1 # powershell -File AD_user_groups.ps1 myinput.csv myoutput.csv not_found.txt # powershell -File AD_user_groups.ps1 -in myinput.csv -out myoutput.csv -lost not_found.txt # powershell -File AD_user_groups.ps1 -in myinput.csv # powershell -File AD_user_groups.ps1 -out myoutput.csv # powershell -File AD_user_groups.ps1 -lost not_found.txt # # A default file will be used if a file name is not specified. # #******************************************************************************** # Set to $true to rename previous output files before overwriting them. $script:preserve_previous_output = $true # Set to $true if the input file contains a header row. $script:strip_header_from_input = $true # Set to $false to see program progress on the screen. $script:suppress_screen_output = $true # These are the file names that will be used if file names are not provided as program parameters. $script:default_input_file = "$PSScriptRoot\users.csv" $script:default_output_file = "$PSScriptRoot\user_groups.csv" $script:default_not_found_file = "$PSScriptRoot\users_not_found.txt" #******************************************************************************** # Function definitions appear here. #******************************************************************************** InitializeVariables CleanOutputFiles ReadUserList CreateUserGroupList WriteOutputFile
Wednesday, March 25, 2015
"Magic is a drug. Careful how you use it."
Jaye Wells has created a world (continued in Cursed Moon and Deadly Spells) with a tremendous amount of depth and a wealth of enticing characters. Magic exists, but unfortunately can be as addictive and detrimental as drugs. The lead character is Kate Prospero, a magic adept and police officer. As the trilogy is named "Prospero's War", you know you'll be delving into her life for at least the duration of the series, and you won't be disappointed. However, even the supporting characters are so well fleshed out that it's difficult to think of them as "supporting". I wanted to learn so much more about Kate's colleagues, and I think there's at least a novel's worth of exploration for each and every character Wells devised. And that's only if the author gets bored of the world of Babylon, Ohio. She could do a series featuring any one of the members of the Magical Enforcement Agency task force and I'd be all in, each and every time!
Both the author and the main character are women. The only reason this is noteworthy is because it appears that women are still given short shrift in the world of fantasy/science fiction literature, even after so many strong female characters and so many incredibly talented female authors.It is my hope that the works created by brilliant authors such as Jaye Wells help to dissolve this issue once and for all. Personally, I didn't consider NOT purchasing Dirty Magic because of the gender of the author nor of the hero. It was an intriguing premise set in a fascinating world--that's why I bought it. I have no reservations about recommending these novels to anyone interested in supernatural fiction, urban fantasy, or crime stories, simply because the writing is "too feminine"--whatever that might mean. It is flat out a great series written by an incredible author.
I don't pre-order many books these days, simply because the "to be read" stack on my bookshelf has grown so large that it is now spawning little offspring stacks. But in this case, I bought Cursed Moon and pre-ordered Deadly Spells at the same time, because I knew I'd be anxious to read the third installment ASAP after finishing the second. Jaye published the novella Fire Water between books two and three as an e-book. I purchased that one as well; it was a nice diversion while waiting for the third book's release date (but it made the wait for a longer adventure that much more difficult!)
I'm now making my way through some of the author's other series, and what I've read so far is as engrossing as this one. However, "Prospero's War" will always be the series that introduced Jaye Wells to my bookshelves, and so I'll always feel a special affinity for the gritty world of Babylon, Ohio.