Gossamer Forum
Home : Products : Links 2.0 : Customization :

How to sort by custom field?

Quote Reply
How to sort by custom field?
I have a created a membership directory using Links 2, and have had requests to add a page that sorts members by birthday. The directory lists families, with up to seven names each, and birthdays are in a unique field for each of the names, so I know this can be done. I can get it to print just the birthday and name for each entry (mr, mrs, child 1 - 5), but I need it to combine all the birthday entries, then sort them numerically. Dates are entered month-day (4-27), so sorting on the numbers before the dash first, then after it, is needed. This can be a text file or an HTML page. I have been out of Perl for awhile, so what may be obvious is escaping me. Any help out there...?


Leonard
aka PerlFlunkie

Last edited by:

PerlFlunkie: Aug 31, 2008, 4:13 PM
Quote Reply
Re: [PerlFlunkie] How to sort by custom field? In reply to
The picture (attachment) shows entries sorted by date - by category and in alphabetical order.

I hope that the following code sniplets will help you.




db_utils.pl : sub build_sorthit

Code:

sub build_sorthit {
# --------------------------------------------------------
# This function sorts a list of links. It has been modified to sort
# new links first, then cool links, then the rest alphabetically. By modifying
# the sort function below, you can sort the links however you like (by date,
# or random, etc.).

my (@unsorted) = @_;
my ($num) = ($#unsorted+1) / ($#db_cols+1);
my (%sortby, %isnew, %iscool, $hit, $i, @sorted, $column, $type);

foreach $column (@db_cols) {
if ($db_sort_links == $db_def{$column}[0]) {
$type = $db_def{$column}[1];
last;
}
}
for ($i = 0; $i < $num; $i++) {
$sortby{$i} = $unsorted[$db_sort_links + ($i * ($#db_cols+1))];
($unsorted[$db_isnew + ($i * ($#db_cols+1))] eq "Ja") and ($isnew{$i} = 1);
($unsorted[$db_ratings + ($i * ($#db_cols+1))] eq "Ja") and ($iscool{$i} = 1);
}
if ($type eq "date") {
foreach $hit (sort {

&date_to_unix ($sortby{$b}) <=> &date_to_unix ($sortby{$a});
} (keys %sortby)) {
$first = ($hit * $#db_cols) + $hit;
$last = ($hit * $#db_cols) + $#db_cols + $hit;
push (@sorted, @unsorted[$first .. $last]);
}
}
elsif ($type eq "numer") {
foreach $hit (sort {
$sortby{$b} <=> $sortby{$a};
} (keys %sortby)) {
$first = ($hit * $#db_cols) + $hit;
$last = ($hit * $#db_cols) + $#db_cols + $hit;
push (@sorted, @unsorted[$first .. $last]);
}
}
else {
foreach $hit (sort {
($isnew{$b} and !$isnew{$a}) and return 1;
($isnew{$a} and !$isnew{$b}) and return -1;
($iscool{$b} and !$iscool{$a}) and return 1;
($iscool{$a} and !$iscool{$b}) and return -1;
($isnew{$a} and $isnew{$b}) and return lc($sortby{$a}) cmp lc($sortby{$b});
($iscool{$a} and $iscool{$b}) and return lc($sortby{$a}) cmp lc($sortby{$b});
return lc($sortby{$a}) cmp lc($sortby{$b});
} (keys %sortby)) {
$first = ($hit * $#db_cols) + $hit;
$last = ($hit * $#db_cols) + $#db_cols + $hit;
push (@sorted, @unsorted[$first .. $last]);
}
}
return @sorted;
}



nph-build.cgi - sub build_new_page

Code:
sub build_new_page {
# --------------------------------------------------------

my (%link_output, $category_clean, $long_date, $category, $date,
$number_links, $main_link_results, $main_total, %span_totals);
local ($total, $link_results, $title_linked);


if ($build_new_path =~ m,^$build_root_path/(.*)$,) {
&build_dir ($1);
}


$total = 0;
CATEGORY: foreach $category (sort keys %new_links) {
LINK: for ($i = 0; $i < ($#{$new_links{$category}}+1) / ($#db_cols + 1); $i++) {
$total++;
%tmp = &array_to_hash ($i, @{$new_links{$category}});
${$link_output{$tmp{'Anmeldedatum'}}}{$category} .= &site_html_link (%tmp) . "\n";
$span_totals{$tmp{'Anmeldedatum'}}++;
}
}


DATE: foreach $date (sort { &date_to_unix($b) <=> &date_to_unix($a) } keys %link_output) {
$long_date = &long_date ($date);
if ($build_new_span_pages) {
$link_results = "";
$total = $span_totals{$date};
$title_linked = &build_linked_title ("Neu/$long_date");
}
else {
$link_results .= "";
}
CATEGORY: foreach $category (sort keys %{$link_output{$date}}) {

$category_clean = &kategorie_title_mb ($category);
# Verändert für Listendarstellung ---------------------------------------------------------------------
$link_results .= qq|<tr style ="border : 1px solid #000000" bgcolor="#D4DFFB"><td style ="border : 1px solid #000000" colspan=6><font size=2>$long_date in: <A HREF="$build_root_url/$category/$build_index">$category_clean</A></font></td></tr>\n|;
# Ende Verändert für Listendarstellung ----------------------------------------------------------------
$link_results .= ${$link_output{$date}}{$category};
}

if ($build_new_span_pages) {
open (NEW, ">$build_new_path/$date$build_extension") or cgierr ("Kann die Datei bzw. Verzeichnis nicht öffnen: $build_new_path/$build_index. Grund: $!");
$use_html ?
print qq|\tNeue Links für <a href="$build_new_url/$date$build_extension" TARGET="_blank">$date</a>: $total\n| :
print qq|\tNeue Links für $date: $total\n|;
print NEW &site_html_new;
close NEW;
$main_link_results .= qq|<tr><td colspan=6><br><font size=2>Neuzug&auml;nge am <a href="$build_new_url/$date$build_extension"><b>$long_date</b></a> ($total)</font><hr noshade size=1></td></tr>|;
$main_total += $total;
}
else {
$link_results .= "";
}
}

if ($build_new_span_pages) {
$link_results = "$main_link_results";
$total = $main_total;
}
$title_linked = &build_linked_title ("Neu");


open (NEW, ">$build_new_path/$build_index") or cgierr ("Kann die Datei bzw. Verzeichnis nicht öffnen: $build_new_path/$build_index. Grund: $!");
print "\tNeue Einträge gesamt: $total\n";
print NEW &site_html_new(@new_links);
close NEW;
}

.


Andreas

Dr.Windows








Last edited by:

SevenSpirits: Sep 1, 2008, 11:21 AM
Quote Reply
Re: [SevenSpirits] How to sort by custom field? In reply to
Thanks, thats' a step in the right direction, but the complicated part is I need to pull info from seven fields (birthdays) for each link (member last name), then put all of that into an array and sort it by date, but also have the proper name with each birth date. Need to sit down with my collection of perl books and figure this out...


Leonard
aka PerlFlunkie
Quote Reply
Re: [PerlFlunkie] How to sort by custom field? In reply to
I think it would be easier to create an additional *.db (for example: singleperson.db)
similar to $db_url_name in nph-build.cgi ( sub build_url_index - see attachment) .

The new sub in nph-build.cgi (mod of sub build_url_index)
- ( sub build_single_person_index - for example ) -
should contain a counter which creates a different value
for each single person (existing combination : last name + first name) .
For each entry in links.db the number of persons must be proofed.

The values of the counter = The new $db_key ( $values[$db_key_pos] in sub build_url_index ) in singleperson.db.

Entries for each single person:

- The new $db_key
- last name
- first name
- birthday

and $db_key of links.sb because it could be that two persons have identic last names and first names.

Now it should be easy to sort singleperson.db by date and
create a text list or a html site using templates.

( If you need the exact code links.def, links.cfg would be useful )

Sorry for my bad english.

Code:

sub build_url_index {
# --------------------------------------------------------


my $time = time();
my @values = ();
my $count = 0;

open (DB, "<$db_file_name") or &cgierr("Kann die Datei bzw. Verzeichnis nicht öffnen: $db_file_name.\nGrund: $!");
open (URL, ">$db_url_name") or &cgierr("Kann die Datei bzw. Verzeichnis nicht öffnen: $db_url_name. Grund: $!");
if ($db_use_flock) { flock (URL, 2) or &cgierr ("Allgemeiner Fehler. Grund: $!"); }
LINE: while (<DB>) {
/^#/ and next LINE;
/^\s*$/ and next LINE;
chomp;
@values = &split_decode($_);
print URL "$values[$db_key_pos]$db_delim$values[$db_url]\n";
$count++;
}
close DB;
close URL;


open (CNT, ">$db_hits_path/index.count") or &cgierr("Kann die Datei bzw. Verzeichnis nicht öffnen: '$db_hits_path/index.count'. Grund: $!");
if ($db_use_flock) { flock (CNT, 2) or &cgierr ("Kann die Datei bzw. Verzeichnis nicht öffnen: $db_hits_path/index.count. Grund: $!"); }
print CNT $count;
close CNT;
}


.












.


Andreas

Dr.Windows








Last edited by:

SevenSpirits: Sep 1, 2008, 2:49 PM
Quote Reply
Re: [SevenSpirits] How to sort by custom field? In reply to
That may be the way to go, thanks for the idea. Not working on it today, but will this week, and will post if I find something that works.


Leonard
aka PerlFlunkie
Quote Reply
Re: [PerlFlunkie] How to sort by custom field? In reply to
Let me know if you have any problems.
Good Luck!


Andreas

Dr.Windows








Quote Reply
Re: [PerlFlunkie] How to sort by custom field? In reply to
I got it to do what I want,almost, though there is probably a better way. Here it is...

1. Two new subs in db_utils.pl:

Code:
sub build_birthday_list {
#-----------------------------
my @data;
my %seen;
my @mr_bday;
my @mr_bday_sorted;
my $path = "/big/dom/xvmcoc/www/members";
open (DB, "<$db_file_name") or &cgierr("unable to open database: $db_file_name.\nReason: $!");
open (DBBDAY, ">$path/birthday_list.txt") or &cgierr ("Unable to open output database.\nReason: $!");
while (<DB>) {
/^#/ and next; # Skip comment Lines.
/^\s*$/ and next; # Skip blank lines.
chomp;
@data = &split_decode($_);
if ($data[$db_mr_bday] ne '') {
$mr = ("$data[$db_mr_bday]|$data[$db_mr_name]|$data[$db_title]");
print DBBDAY "$mr\n";
}
if ($data[$db_mrs_bday] ne '') {
$mrs = ("$data[$db_mrs_bday]|$data[$db_mrs_name]|$data[$db_title]");
print DBBDAY "$mrs\n";
}
if ($data[$db_child_1] ne '') {
$child_1 = ("$data[$db_child_1_bday]|$data[$db_child_1]|$data[$db_title]");
print DBBDAY "$child_1\n";
}
if ($data[$db_child_2] ne '') {
$child_2 = ("$data[$db_child_2_bday]|$data[$db_child_2]|$data[$db_title]");
print DBBDAY "$child_2\n";
}
if ($data[$db_child_3] ne '') {
$child_3 = ("$data[$db_child_3_bday]|$data[$db_child_3]|$data[$db_title]");
print DBBDAY "$child_3\n";
}
if ($data[$db_child_4] ne '') {
$child_4 = ("$data[$db_child_4_bday]|$data[$db_child_4]$|data[$db_title]");
print DBBDAY "$child_4\n";
}
if ($data[$db_child_5] ne '') {
$child_5 = ("$data[$db_child_5_bday]|$data[$db_child_5]|$data[$db_title]");
print DBBDAY "$child_5\n";
}
}
close DB;
close DBBDAY;
}

sub build_birthday_list_2 {
#-----------------------------
my @data;
my %seen;
my $path = "/big/dom/xvmcoc/www/members";
open (DB, "<$path/birthday_list.txt") or &cgierr ("Unable to open output database.\nReason: $!"); open (DBBDAY, ">$path/birthday_list_2.txt") or &cgierr ("Unable to open output database.\nReason: $!");
@text = <DB>;
close (DB);
@text = sort{$a <=> $b}@text;
foreach $line(@text) {
$line =~ s/\*//g;
print DBBDAY "$line";
}
close DBBDAY;
}

2. Add the call for th e routines in nph-build.cgi, sub build_all:

Code:
# Backup the birthday_list database.
print "Updating bday list database . . .\n";
&build_birthday_list;
print "Done.\n\n";
# Backup the birthday_list_2 database.
print "Updating bday list 2 database . . .\n";
&build_birthday_list_2;
print "Done.\n\n";

3. Create the two new text files in the www directory.

----

The first routine gets the data from the links db, ignores any empty birthday fields, and puts it into a text file which is then read and sorted by the second routine. The sorting is a bit off; the months are fine, but it's not sorting the days consistantly. Probably the dash is messing it up. Any ideas...?

A sample of the outputs...

first
Quote:
10-10|Chip*|Alleman
1-20|Cindy*|Alleman
10-29|Malia*|Alleman
11-22|Joe*|Beaver
2-13|Jo-Ann*|Beaver

second
Quote:
1-6|Kaleb|Brown
1-20|Cindy|Alleman
1-7|Jane|Rollins
1-16|Colt|Brokaw
1-17|Arianna|Dick

Works for me!


Leonard
aka PerlFlunkie
Quote Reply
Re: [PerlFlunkie] How to sort by custom field? In reply to
I think the wrong sorted birthdays are simply caused by a logical problem.

sub build_birthday_list_2 handles the entries of birthday_list.txt
as alphanumeric characters and so they are sorted.

You can proove this by manipulating birthday_list.txt manually-
using an editor :

Quote:
Change (in birthday_list.txt)
1-20|Cindy|Alleman ===> Z|Cindy|Alleman
1-7|Jane|Rollins ===> A|Jane|Rollins
and the output will look like this:

Quote:
1-6|Kaleb|Brown
1-16|Colt|Brokaw
1-17|Arianna|Dick
A|Jane|Rollins
Z|Cindy|Alleman



You could use an new sub which changes birthday_list_2 to birthday_list_3 :

Quote:
1-6|Kaleb|Brown ===> 6|Kaleb|Brown|1- or 6|Kaleb|Brown|1

With this result you have several solutions to sort entries the way you want to.

One solution:

A new sub has to read in the characters after the last "|" --> Month
Now the results must be sorted similar to sub build_birthday_list_2.

Using an if-condition this must be done for each month - from 1 to 12.

The result is birthday_list_4 :

Quote:
days (1-31) | first name | last name | month (1)
...
days (1-31) | first name | last name | month (12)

Next step ist to change back birthday_list_4.



This is only one way to receive your favourite output.

Perhaps a less complex method does exist. Smile
.


Andreas

Dr.Windows








Quote Reply
Re: [SevenSpirits] How to sort by custom field? In reply to
After much searching and testing, found the answer at perlmonks.com. The second routine now has this:

Code:
sub build_birthday_list_2 {
#-----------------------------
my @data;
my %seen;
my $path = "/big/dom/xvmcoc/www/members";
open (DB, "<$path/birthday_list.txt") or &cgierr ("Unable to open output database.\nReason: $!"); open (DBBDAY, ">$path/birthday_list_2.txt") or &cgierr ("Unable to open output database.\nReason: $!");
@text = <DB>;
close (DB);
my @sorted = map { $_->[0] }
sort { $a->[0] <=> $b->[0] ||
$a->[1] <=> $b->[1];
}
map { [ $_, join "", reverse (split /-/) ] }
@text;
foreach $line(@sorted) {
$line =~ s/\*//g;
$line =~ s/\|/ /g;
print DBBDAY "$line";
}
close DBBDAY;
}


Which produces this:

Quote:

1-1 Julie Wakefield
1-3 Sarah Nichols
1-6 Kaleb Brown
1-7 Thelma Cole
1-7 Lorraine Brock
1-7 Jane Rollins



Leonard
aka PerlFlunkie

Last edited by:

PerlFlunkie: Sep 2, 2008, 10:48 PM
Quote Reply
Re: [SevenSpirits] How to sort by custom field? In reply to
The problem is solved ?

.


Andreas

Dr.Windows








Quote Reply
Re: [SevenSpirits] How to sort by custom field? In reply to
Yes. No doubt it could be done with fewer steps, but it works fine and I'm done with it! Thanks for your help. Smile


Leonard
aka PerlFlunkie