Task

Write a simple tool to track a small set of data.

The tool should have a command-line interface to enter at least two different values.

The entered data should be stored in a structured format and saved to disk.

It does not matter what kind of data is being tracked.   It could be a collection (CDs, coins, baseball cards, books), a diary, an electronic organizer (birthdays/anniversaries/phone numbers/addresses), etc.



You should track the following details:

 A description of the item. (e.g., title, name)
 A category or tag (genre, topic, relationship such as “friend” or “family”)
 A date (either the date when the entry was made or some other date that is meaningful, like the birthday); the date may be generated or entered manually
 Other optional fields


The command should support the following Command-line arguments to run:

 Add a new entry
 Print the latest entry
 Print the latest entry for each category
 Print all entries sorted by a date


The category may be realized as a tag or as structure (by making all entries in that category subitems)

The file format on disk should be human readable, but it need not be standardized.   A natively available format that doesn't need an external library is preferred.   Avoid developing your own format if you can use an already existing one.   If there is no existing format available, pick one of:

   JSON
   S-Expressions
   YAML
   others


Related task

   Take notes on the command line 


#!/usr/bin/env tclsh8.6
package require Tcl 8.6
namespace eval udb {
    variable db {}
 
    proc Load {filename} {
	variable db
	if {[catch {set f [open $filename]}]} {
	    set db {}
	    return
	}
	set db [read $f]
	close $f
    }
    proc Store {filename} {
	variable db
	if {[catch {set f [open $filename w]}]} return
	dict for {nm inf} $db {
	    puts $f [list $nm $inf]
	}
	close $f
    }
 
    proc add {title category {date "now"} args} {
	variable db
	if {$date eq "now"} {
	    set date [clock seconds]
	} else {
	    set date [clock scan $date]
	}
	dict set db $title [list $category $date $args]
	return
    }
    proc Rec {nm cat date xtra} {
	dict create description $nm category $cat date [clock format $date] \
	    {*}$xtra _names [dict keys $xtra]
    }
    proc latest {{category ""}} {
	variable db
	if {$category eq ""} {
	    set d [lsort -stride 2 -index {1 1} -integer -decreasing $db]
	    dict for {nm inf} $d break
	    return [list [Rec $nm {*}$inf]]
	}
	set latestbycat {}
	dict for {nm inf} [lsort -stride 2 -index {1 1} -integer $db] {
	    dict set latestbycat [lindex $inf 0] [list $nm {*}$inf]
	}
	return [list [Rec {*}[dict get $latestbycat $category]]]
    }
    proc latestpercategory {} {
	variable db
	set latestbycat {}
	dict for {nm inf} [lsort -stride 2 -index {1 1} -integer $db] {
	    dict set latestbycat [lindex $inf 0] [list $nm {*}$inf]
	}
	set result {}
	dict for {- inf} $latestbycat {
	    lappend result [Rec {*}$inf]
	}
	return $result
    }
    proc bydate {} {
	variable db
	set result {}
	dict for {nm inf} [lsort -stride 2 -index {1 1} -integer $db] {
	    lappend result [Rec $nm {*}$inf]
	}
	return $result
    }
 
    namespace export add latest latestpercategory bydate
    namespace ensemble create 
}
 
if {$argc < 2} {
    puts stderr "wrong # args: should be \"$argv0 dbfile subcommand ?args...?\""
    exit 1
}
udb::Load [lindex $argv 0]
set separator ""
if {[catch {udb {*}[lrange $argv 1 end]} msg]} {
    puts stderr [regsub "\"udb " $msg "\"$argv0 dbfile "]
    exit 1
}
foreach row $msg {
    puts -nonewline $separator
    apply {row {
	dict with row {
	    puts "Title: $description"
	    puts "Category: $category"
	    puts "Date: $date"
	    foreach v $_names {
		puts "${v}: [dict get $row $v]"
	    }
	}
    }} $row
    set separator [string repeat - 70]\n
}
 
udb::Store [lindex $argv 0]