Tinderbox User-to-User Forum (for formal tech support please email: info@eastgate.com)
http://www.eastgate.com/Tinderbox/forum//YaBB.cgi
Tinderbox Users >> Agent, Actions, Rules & Automation >> Setting $StartDate from $Name
http://www.eastgate.com/Tinderbox/forum//YaBB.cgi?num=1350639286

Message started by David Bertenshaw on Oct 19th, 2012, 5:34am

Title: Setting $StartDate from $Name
Post by David Bertenshaw on Oct 19th, 2012, 5:34am

[edit]by admin: Split out as a separate topic from "So, umm..., what is this app actually for?"[/edit]

Right, caveat lector first of all: I've tried to answer this as an exercise to learn something myself. It works for me, but I've not done much testing and no doubt somebody more skilled will know better ways...

But, assuming you are rigid in your naming convention:

YYYY/MM/DD This is the note title

Then, if you put the following code into the Rule box of your note

$StartDate=format($Name.substr(8,2) + "/" + $Name.substr(5,2) + "/" +$Name.substr(0,4))

then it will populate the $StartDate attribute with the correct date. Note, I'm in the UK, so this will be DD/MM/YYYY. If you're in the US, then you'd obviously swap round the first two $Name.substr terms. (BTW, substr counting starts at 0, not 1.)

So, e.g. "2012/10/26 Desperately tried to work out what to do", $StartDate will become 26/10/2012 10:22. There are ways of stripping out the HH:MM if you want.

Of course you could put the expression into your Prototype.

Or you might want to use this as a one-off agent to populate existing notes, (and afterwards use the same substr function to strip out the date from the existing titles.)  Then for new ones enter the $StartDate in directly and use $DisplayExpression = $StartDate + " " + $Name to display the full Date + Title.

Now, one of the Marks will be along in 5 minutes to give you a much simpler and more logical way of doing this.... ;)

Title: Re: So, umm..., what is this app actually for?
Post by Martin Boycott-Brown on Oct 19th, 2012, 6:13am

Amazing! Many thanks for that. I'll try it out with some dummy data.

I'm in the UK, too, so I use the same date format.

I think one of the Marks is in the US, so I suspect it will take him a few hours to wake up ;-).

Thanks again,
Martin.

Title: Re: So, umm..., what is this app actually for?
Post by Mark Anderson on Oct 19th, 2012, 6:48am

Yes, your code does works though TB's actually being forces to do some guesswork and figuring out you want a date from a string as we're passing a string to a data attribute ($StartDate). Stepping back, I see you want to make date-type data from a literal string(i.e. the text representation of a date).

Sidenote: I'd concur your point re day & month order in dates. If I recall, everywhere bar the US & Philippines uses DMY order; the latter do MDY order (Canada does doth). Ah, and China uses neither but YMD instead. Plus the delimiter varies by country: dot, slash, hyphen, space, etc. Anyway, being British myself, I'll stick with day-month order for this example.

At source we have a $Name of "2012/10/26 Desperately tried to work out what to do" and we want to make a valid TB date from that. Your approach of using String.substr() to get parts of $Name is on track. But what then? The obvious candidate is date() which has two alternate input forms. These are date(year,month,day,[hour,min]) and date.(string). We can use either:

date(year,…): $StartDate = date(($Name.substr(0,4)),($Name.substr(5,2)),($Name.substr(8,2)))
date(string): $StartDate = date($Name.substr(8,2) + "/" + $Name.substr(5,2) + "/" +$Name.substr(0,4))

Note, the extra parentheses in the first example around each $Name call are because that form takes number inputs not strings. The extra () around $Name.substr(0,4) means that when TB is given string "2012" when it expects a number, it automatically co-erces it to number 2012. A clearer but more verbose way to do the same thing is to use $Name.substr(0,4).toNumber which won't need the extra parentheses. For example:

$StartDate = date($Name.substr(0,4).toNumber,$Name.substr(5,2).toNumber,$Name.substr(8,2).toNumber)

^^ unavoidable line wrap - code has no line break.

So, I'd go with the second option here - date(string). It's simpler, though do remember alter day/month order and delimiter type as pertinent to your locale.

Agents? Yes, for instance find all notes of a given prototype (assumption: we're certain all these start their title with a date) you could do this.  So to set $StartDate all "pBattle" prototyped notes:

Query: $Prototype=="pBattle"
Action: $StartDate = date($Name.substr(8,2) + "/" + $Name.substr(5,2) + "/" +$Name.substr(0,4))

Why "pBattle" as the prototype name?  Not required, but I've found it useful to use prefixes for my prototype (and template, etc.) names that I'll never use as a normal note name to avoid accidental name confusion. I might just want a note called "Battle".

Sorry for the long post, but rather than give just the answer I hope having the worked method helps bootstrap your learning, even if it does mean a bit more reading.

Title: Re: Setting $StartDate from $Name
Post by Martin Boycott-Brown on Oct 19th, 2012, 7:00am

Thank you -- that's very much appreciated -- and very clear. I hope others find it as useful as I do. I would guess that it is an operation that others might want to perform -- perhaps even to format dates in alternative ways for display purposes. And it never hurts to know more than one way to do things. And I realise it's also a useful demonstration of the basic technique of extracting data from one place and re-using it in another. Very valuable.

Best, Martin.

Title: Re: Setting $StartDate from $Name
Post by Mark Anderson on Oct 19th, 2012, 10:01am

Here's a more advanced version of the above technique for an agent, using regular expressions. First, let's state some assumptions:
  • Date in $Name is in format YYYY/MM/DD.
  • $Name starts with date (i.e. first character is first Y digit).
  • day and month always have 2 digits, i.e. '04' for April and not '4'.
Agent code:

Query:  $Name.contains("^(\d{4})/(\d{2})/(\d{2})")
Action:  $MyDate = date($3+"/"+$2+"/"+$1")

Of course, the query could have other terms to restrict the scope of search. as written here it searches all notes. A break-down of the regular expression:
- From the start of the string ^
- a string of four digits & return as backreference $1
- a single forward slash
- a string of two digits & return as backreference $2
- a single forward slash
- a string of two digits & return as backreference $3
- any character matched any number of times (i.e. the rest of the title)

The () within the regular expression is what defines the back-reference. To match those parenthesis characters literally use \( and \) instead. By searching for NNNN/NN/NN at the beginning of the $Name we don't have to worry about whatever else is in the title - even another YYYY/MM/DD sequence.

The action then uses the contents of $1, $2 and $3 in string it creates. We insert the delimiters as literal string, quoted. The back-references, like references to string attribute values don't need quoting; think of $! as if it were $SomeString.

The date() used string is for general world Day-Month order, alter for US-style MD order:
  $MyDate = date ($2+"/"+$3+"/"+$1)
…or for a a system using YMD:
  $MyDate = date($1+"/"+$2+"/"+$3)

This isn't beginner stuff. But it's worth bearing in mind for one good reason. See how this technique never touched any note's $Rule leaving it free to do other stuff? Neat. Anyway, try it out. Once you see how it can work it may help give confidence to try the technique for yourself.  You don't have to use back references. This would find the same notes without creating the references:

 $Name.contains("^\d{4}/\d{2}/\d{2}")

Indeed, if you didn't always zero-pad days and months:

 $Name.contains("^\d{4}/\d{1,2}/\d{1,2}")

…but in the latter case you'd need in the action to check $@ and $3 were double-character before using for a date.

More on: back-references, regular expressions.

Title: Re: Setting $StartDate from $Name
Post by David Bertenshaw on Oct 19th, 2012, 11:25am

Thanks Mark,

As I said, I only tried to answer Martin's query as a learning exercise. I was chuffed enough that I got format() to work....

I tried to use date() but encountered all sorts of issues -- and your post explains that I should have used additional brackets, so that's useful.

I actually already use agents to set $StartDate automatically in my Journal -- each day's $Name is in the form "DD/MM/YYYY" anyway, so the new bit for me here was learning how to rearrange a reverse date for Martin's case.

In fact, I also use the prototype rule to set the $StartDate and $EndDate to HH:MM = 00:00 / 23:59, so that each note fills a day display nicely in timelines.

$StartDate = time($Name,00,00);$EndDate = time($Name,23,59)

or, for Martin's case, adapting your second option:

$StartDate = time($Name.substr(8,2) + "/" + $Name.substr(5,2) + "/" +$Name.substr(0,4),00,00)

The regex stuff looks interesting, but will take some time to assimilate...

Thanks again - fascinating stuff...

David

Title: Re: Setting $StartDate from $Name
Post by Mark Anderson on Oct 19th, 2012, 12:07pm

Glad you reminded me of time. If making lots of events - especially in the past - time is irrelevant. As you've shown, just add the info. For a 00:01 time you might use ,00,01 as the last two input or append the string or " 00:01" depending on which date type you use.

Hmm, what if you have titles starting DD/MM/YYYY and wanted then to start YYYY/MM/DD for easier sorting on $Name? Answer in a new thread.

Title: Re: Setting $StartDate from $Name
Post by Martin Boycott-Brown on Oct 20th, 2012, 3:06am

Many thanks for all of this -- amazing what you can do, and it raises all sorts of thoughts. I suppose it is technically possible to identify a date (provided you know what format it is) anywhere in the note. I'm thinking that you could probably find all notes that have DD/MM/YYYY somewhere in the content, for example.

I dabbled with regex in a couple of programs before -- powerful, but an awful lot of braces, dots and slashes. Gets confusing.

Cheers, Martin.

Title: Re: Setting $StartDate from $Name
Post by Mark Bernstein on Oct 20th, 2012, 8:27am

Regular expressions do get confusing -- but it's a kind of confusion you can't get around.  It's not notational, just a matter of perverse punctuation.  

Image that you were going to reinvent regular expressions!  You're going to use something to mean "this matches any character", right?  That can either be some special character, or it could be something that isn't a character.  But if it's not a character, how do you enter it from your keyboard?  So, we'll use some character we don't need often in prose or verse:

    +:  matches any character

But we might have some crazy text that actually uses a '+' sign.  So we have another character that means, "Yeah: I mean THAT character":

    \+: matches a '+' character
    \\: matches a '\' character

And so we proceed.  It does get complicated because generalized formats can get complicated: how exactly, for example, do we know at a glance that $7,420.98  is an amount of money and that 74$.98,20 is not?  

In fact, the range of things that regular expressions can match (and the range of things that they cannot) is really one of the more beautiful results of basic computer science.

It's not just cooked up to vex you.

Title: Re: Setting $StartDate from $Name
Post by Mark Anderson on Oct 20th, 2012, 11:39am

As Mark B points out regex are complex - but with a genuine purpose.

Meanwhile, here are some queries to find a YYYY/MM/DD sequence in $Text. First, match the sequence anywhere in $Text:

$Text.contains("(\d{4})/(\d{2})/(\d{2})")

Now only at the very beginning of $Text:

$Text.contains("^(\d{4})/(\d{2})/(\d{2})")

Now only at the very end of $Text:

$Text.contains("(\d{4})/(\d{2})/(\d{2})$")

All the above create separate backreferences to each of the YYYY MM and DD sequences. If you don't need these, simpler versions of all three of the regex above:

$Text.contains("\d{4}/\d{2}/\d{2}")
$Text.contains("^\d{4}/\d{2}/\d{2}")
$Text.contains("\d{4}/\d{2}/\d{2}$")

Want to match DD and MM where thay my be 1 or 2 digits? Then simply replace \d{2}, i.e. match only a 2 character sequence digits, with \d{1,2} which matches a sequence of 1 or 2 digits.

Tinderbox User-to-User Forum (for formal tech support please email: info@eastgate.com) » Powered by YaBB 2.2.1!
YaBB © 2000-2008. All Rights Reserved.