comp.lang.tcl

Small NNTP viewer

Location:
/packages/acs-subsite/www/comp.lang.tcl
Author:
Gustaf Neumann
Created:
2024-08-15

Related Files

[ hide source ] | [ make this the default ]

File Contents

ad_page_contract {
    Small NNTP viewer

    @author Gustaf Neumann
    @creation-date 2024-08-15
} {
    {thread_id:integer,notnull ""}
}

set nntp_group comp.lang.tcl

if {$thread_id ne ""} {
    set single_thread $thread_id
    set doc(title) "Thread from $nntp_group"
    
} else {
    set doc(title) "Recent Articles from $nntp_group"
}

set context [list $doc(title)]

lassign [acs::dc list dbqd..get_group_info {
    select nntp_id from nntp_groups where name = :nntp_group
}] nntp_id 

set tuples [acs::dc list_of_lists dbqd..get_articles {
    select article_id, dict from nntp_articles where nntp_id = :nntp_id
    order by article_id desc
}]

foreach tuple $tuples {
    lassign $tuple article_id article
    ns_log notice "work on $article_id"
    set msg_id [dict get $article MSG_ID]
    if {[info exists single_thread] && $single_thread == $article_id} {
        set single_thread_msg_id $msg_id
    }
    set secs [clock scan [dict get $article date]]
    lappend timeline_order $secs $msg_id
    dict set article POSTED_SECS $secs
    dict set article ARTICLE_ID $article_id
    dict set articles_dict $msg_id $article
    #if {![dict exists $article in-reply-to]} {
    #    ns_log notice "ARTICLE $article_id $msg_id '[dict get $article subject]' is not a reply"
    #} else {
    #    ns_log notice "ARTICLE $article_id $msg_id '[dict get $article subject]' is in reply to [dict exists $article in-reply-to]"
    #}
    if {![dict exists $article references]} {        
        #ns_log notice "ARTICLE $article_id $msg_id '[dict get $article subject]' does not reference something else"
    } else {
        foreach reference [dict get $article references] {
            dict lappend referenced_by_dict $reference $msg_id
            dict lappend references_dict $msg_id $reference 
        }
    }
}

#foreach key [dict keys $referenced_by_dict] { ns_log notice ... $key <- [dict get $referenced_by_dict $key]}
#foreach key [dict keys $references_dict] { ns_log notice ... $key -> [dict get $references_dict $key]}

#
# Make one iteration ot update "referenced" and "referenced_by"
# strutures for associating articles to threads. We get from the NNTP
# server not necessarily the begin of the thread.
#
foreach msg_id [dict keys $articles_dict] {
    set referenced_by [expr {[dict exists $referenced_by_dict $msg_id] ? [dict get $referenced_by_dict $msg_id] : {} }]
    set references    [expr {[dict exists $references_dict $msg_id]    ? [dict get $references_dict $msg_id] : {} }]
    
    #set article_id    [dict get $articles_dict $msg_id ARTICLE_ID]
    #ns_log notice "2nd work on $article_id msg_id $msg_id referenced_by_count [llength $referenced_by] references_count [llength $references]"

    dict set articles_dict $msg_id Referenced_by $referenced_by
    dict set articles_dict $msg_id References $references
}

if {[info exists single_thread]} {
    #
    # Build the collection of articles of a single thread. Order should be chronological
    #    
    lappend timestamps [dict get $articles_dict $single_thread_msg_id POSTED_SECS] $single_thread_msg_id

    set replies [dict get $articles_dict $single_thread_msg_id Referenced_by]
    if {[llength $replies] == 0} {
        append doc(title) " (no replies yet)" 
    } elseif {[llength $replies] == 1} {
        append doc(title) " (1 reply)"
    } else {
        append doc(title) " ([llength $replies] replies)"
    }
    foreach r [dict get $articles_dict $single_thread_msg_id Referenced_by] {
        lappend timestamps [dict get $articles_dict $r POSTED_SECS] $r
    }
    set timestamps [lsort -increasing -stride 2 $timestamps]
    
} else {
    #
    # The begin of the posting overview are articles, that have no
    # references by themselves. For these compute the threads and the
    # "last_modified" entry (normally the newest response in a thread).
    #
    foreach msg_id [dict keys $articles_dict] {
        set article_id    [dict get $articles_dict $msg_id ARTICLE_ID]
        set referenced_by [dict get $articles_dict $msg_id Referenced_by]
        set references    [dict get $articles_dict $msg_id References]
        
        if {[llength $references] != 0} {        
            continue
        }
        set last_modified_secs [dict get $articles_dict $msg_id POSTED_SECS]
        if {[llength $referenced_by] > 0} {
            set last_modified [dict get $articles_dict $msg_id date]
            foreach r $referenced_by {
                set modified_secs [dict get $articles_dict $r POSTED_SECS]
                if {$modified_secs > $last_modified_secs} {
                    set last_modified_secs $modified_secs
                }
            }
        }
        lappend timestamps $last_modified_secs $msg_id
    }
    
    #
    # Sort the toplevel threads by the last_modified dates
    #
    set timestamps [lsort -decreasing -stride 2 $timestamps]
}


template::multirow create articles \
    article_id date pretty_posted_date last_modified pretty_last_modified \
    from subject \
    body nr_replies last_contrib

if {[info exists single_thread]} {
    set single_thread_msg_ids [dict get $articles_dict $single_thread_msg_id Referenced_by]
    lappend single_thread_msg_ids $single_thread_msg_id

    ns_log notice "MSGIDS single_thread_msg_ids $single_thread_msg_ids"
    
    foreach {timestamp msg_id} $timestamps {
        ns_log notice "... $msg_id ni $single_thread_msg_ids"
        if {$msg_id ni $single_thread_msg_ids} {
            continue
        }
        ns_log notice "NOT SKIPPING $msg_id"
        set article            [dict get $articles_dict $msg_id]
        set posted_secs        [dict get $article POSTED_SECS]
        set pretty_posted_date [::xowiki::utility pretty_age -levels 2 -timestamp $posted_secs]
        
        dict with article {
            template::multirow append articles  \
                [dict get $article ARTICLE_ID] \
                [dict get $article date] \
                $pretty_posted_date \
                last_modified pretty_last_modified \
                $from $subject \
                $BODY \
                [llength $Referenced_by] last_contrib
        }
    }
    ad_return_template nntp-thread
    
} else {
    
    template::multirow create replies article_id msg_id from date pretty_date 

    foreach {timestamp msg_id} $timestamps {
        set article       [dict get $articles_dict $msg_id]
        set article_id    [dict get $article ARTICLE_ID]
        set referenced_by [dict get $article Referenced_by]
        set references    [dict get $article References]
        #ns_log notice "2nd work on $article_id msg_id $msg_id referenced_by_count [llength $referenced_by] references_count [llength $references]"
        if {[llength $references] != 0} {
            continue
        }
        set posted_secs [dict get $article POSTED_SECS]
        set pretty_posted_date [::xowiki::utility pretty_age -levels 2 -timestamp $posted_secs]
        set pretty_last_modified $pretty_posted_date
        set last_contrib $article_id
        set last_modified [dict get $article date]
        set last_modified_secs $posted_secs
        if {[llength $referenced_by] > 0} {
            set last_modified_date [dict get $article date]
            foreach r [dict get $referenced_by_dict $msg_id] {
                set modified_secs [dict get $articles_dict $r POSTED_SECS]
                template::multirow append replies \
                    $article_id $r \
                    [dict get $articles_dict $r from] \
                    [dict get $articles_dict $r date] \
                    [::xowiki::utility pretty_age -levels 2 -timestamp [dict get $articles_dict $r POSTED_SECS]]
                if {$modified_secs > $last_modified_secs} {
                    set last_modified_secs $modified_secs
                    set last_contrib [dict get $articles_dict $r ARTICLE_ID]
                    set last_modified [dict get $articles_dict $r date]
                    set pretty_last_modified [::xowiki::utility pretty_age -levels 2 -timestamp $last_modified_secs]
                }
            }
        }

        dict with article {
            template::multirow append articles  \
                $article_id $date $pretty_posted_date $last_modified $pretty_last_modified \
                $from $subject $BODY [llength $referenced_by$last_contrib
        }
    }
    ad_return_template nntp-view
}