Merge lp:~felmas/mvhub/structure_fix into lp:mvhub

Proposed by Ferhat Elmas
Status: Merged
Merged at revision: 602
Proposed branch: lp:~felmas/mvhub/structure_fix
Merge into: lp:mvhub
Diff against target: 2893 lines (+1895/-538)
28 files modified
app-mvhub/DocumentRoot/cgi-bin/mvhub/admin/structure.pl (+6/-470)
app-mvhub/conf/create_sqlite.lib (+69/-6)
app-mvhub/conf/sql_delete.lib (+1/-1)
app-mvhub/conf/sql_insert.lib (+68/-4)
app-mvhub/conf/sql_select.lib (+2/-1)
app-mvhub/conf/templates/html/admin.tmpl (+3/-2)
app-mvhub/t/debian_packages_installed.t (+1/-0)
app-mvhub/t/mech/admin/display_structure_menu.t (+79/-0)
app-mvhub/t/mech/admin/process_synonym_form.t (+111/-0)
lib-mvhub/lib/MVHub/Headings.pm (+6/-9)
lib-mvhub/lib/MVHub/Structure.pm (+471/-0)
lib-mvhub/lib/MVHub/Synonyms.pm (+11/-21)
lib-mvhub/t/00-setup.t (+8/-1)
lib-mvhub/t/Headings/categories.t (+31/-0)
lib-mvhub/t/Headings/heading_names.t (+22/-0)
lib-mvhub/t/Notifications/set_reminder_level_in_db.t (+1/-1)
lib-mvhub/t/Structure/display_category_chooser.t (+70/-0)
lib-mvhub/t/Structure/display_structure_menu.t (+60/-0)
lib-mvhub/t/Structure/fetch_existing_category_and_dependent_data_from_db.t (+95/-0)
lib-mvhub/t/Structure/get_all_headings.t (+19/-0)
lib-mvhub/t/Structure/get_all_programs.t (+26/-0)
lib-mvhub/t/Structure/is_category_name_taken.t (+33/-0)
lib-mvhub/t/Structure/process_category_form.t (+152/-0)
lib-mvhub/t/Structure/process_synonym_form.t (+130/-0)
lib-mvhub/t/Synonyms/fetch_synonyms_from_db.t (+29/-0)
lib-mvhub/t/Utils/clean_cgi_params.t (+4/-2)
lib-mvhub/t/lib/TestData.pm (+328/-18)
lib-mvhub/t/lib/TestHelper.pm (+59/-2)
To merge this branch: bzr merge lp:~felmas/mvhub/structure_fix
Reviewer Review Type Date Requested Status
Dan MacNeil Needs Fixing
Review via email: mp+67376@code.launchpad.net

Description of the change

fix of structure.pl and its related modules

To post a comment you must log in.
Revision history for this message
Ferhat Elmas (felmas) wrote :

Changes getting bigger so I submitted to check whether I am on the right path.

Revision history for this message
Dan MacNeil (omacneil) wrote :

120 - my $invocant = shift;
121 - my $class = ref($invocant) || $invocant;
122 -
123 - my $dbh = MVHub::Utils::DB::get_dbh();
124 -
125 +
126 + my $class = shift;
127 my $self = { HEADING_CATEGORIES_HREF => _fetch_heading_categories() };
128 -

I'm honestly a little vague about what the original code does. I'm pretty sure it handles calling things in a class context vs a object context, in any event it doesn't need to change ( ? ) for getting the file working again.

Revision history for this message
Dan MacNeil (omacneil) wrote :

109 [SYNONYM_STEM_X_ALL]
110 -DELETE FROM ?
111 +DELETE FROM synonym_stem

Looks like this was broken in the move to SQL::Library which never really tested the synonyms code

Good catch / fix

Revision history for this message
Dan MacNeil (omacneil) wrote :

basically goods good on code inspection

Revision history for this message
Ferhat Elmas (felmas) wrote :

120 - my $invocant = shift;
121 - my $class = ref($invocant) || $invocant;
122 -
123 - my $dbh = MVHub::Utils::DB::get_dbh();
124 -
125 +
126 + my $class = shift;
127 my $self = { HEADING_CATEGORIES_HREF => _fetch_heading_categories() };
128 -

Line 120-121 is shortened to 126 because $invocant is always sufficient

Line 123 is unnecessary because _fetch_heading_catgories() creates its own handle.

Revision history for this message
Dan MacNeil (omacneil) wrote :

this is FYI / thing to think of in future only:

this code and the orginal code put the 'view' (HTML ) in the same file as the 'model' (database) and controller ( Perl glue between them). Ideally, maybe over-engineered, model/view/controller should be in separate files to help us think of keeping them separate.

Revision history for this message
Dan MacNeil (omacneil) wrote :

(optional / suggestion )

479 +[005_drop_category]
482 +[006_create_category]

maybe slightly clearer as
[005_drop_category_table]
[006_create_category_table]

also for other table dropping / creating

Revision history for this message
Ferhat Elmas (felmas) wrote :

I had thought firstly that I should get it worked then separation can be done, I am already here for especially refactor.

After finishing tests, I will do it.

> this is FYI / thing to think of in future only:
>
> this code and the orginal code put the 'view' (HTML ) in the same file as the
> 'model' (database) and controller ( Perl glue between them). Ideally, maybe
> over-engineered, model/view/controller should be in separate files to help us
> think of keeping them separate.

Revision history for this message
Dan MacNeil (omacneil) wrote :

(optional / suggested)

734 +# TODO:
[...]
773 +

A lot of these comments can safely be removed. particularly the ones marked DONE. this is only a a suggestion as leaving them in is no worse than the original.

Revision history for this message
Dan MacNeil (omacneil) wrote :

(suggestion / optional )

81 +# TODO: trouble installing:
782 +#use Text::Capitalize;

remove these lines as they have done nothing for years.

Revision history for this message
Dan MacNeil (omacneil) wrote :

(stronger suggestion / optional if you really want)

784 +use MVHub::Utils;
785 +use MVHub::HTMLTemplateUtils;
786 +
787 +use MVHub::Common;
788 +use MVHub::Utils::DB;
789 +use MVHub::Headings;
790 +use MVHub::HTML;
791 +use MVHub::Page;
792 +use MVHub::Synonyms;
793 +
794 +use MVHub::Wrap::ConfigSimple;

See:

http://wiki.thecsl.org/mediawiki/index.php/Frequently_flagged_code#use_.3Clibrary.3E_statements_ordered_by_type_.283rd_party_vs_ours.29_and_then_alphabetical

Revision history for this message
Dan MacNeil (omacneil) wrote :

comment / FYI only

728 === added file 'lib-mvhub/lib/MVHub/Structure.pm'

most of these routines don't:
  http://wiki.thecsl.org/mediawiki/index.php/Frequently_flagged_code#Params::Validate_for_sub_routine_parameter_validation

but this was the case for the original code, so it is no worse.

Also many routines don't sanitize / check $cgi input for evil. This is also the case in the old code, so no worse, no action required for now. Also to get to structure.pl people need to authenticate. Still in long term if their password is compromised or they put in stupid as opposed to evil input we are (potentially) screwed.

Revision history for this message
Ferhat Elmas (felmas) wrote :

I was wondering the authentication because on mvhub.com I can access
structure.pl which can be dangerous in some weird ways.

On 07/29/2011 07:37 PM, Dan MacNeil wrote:
> comment / FYI only
>
> 728 === added file 'lib-mvhub/lib/MVHub/Structure.pm'
>
> most of these routines don't:
> http://wiki.thecsl.org/mediawiki/index.php/Frequently_flagged_code#Params::Validate_for_sub_routine_parameter_validation
>
> but this was the case for the original code, so it is no worse.
>
> Also many routines don't sanitize / check $cgi input for evil. This is also the case in the old code, so no worse, no action required for now. Also to get to structure.pl people need to authenticate. Still in long term if their password is compromised or they put in stupid as opposed to evil input we are (potentially) screwed.

Revision history for this message
Dan MacNeil (omacneil) wrote :

> I had thought firstly that I should get it worked then separation can be done,
> I am already here for especially refactor.

yes. exactly.

Revision history for this message
Dan MacNeil (omacneil) wrote :

.t files should be executable so they can be run one at a time as scripts.

find . -name '*.t' -and -not -executable

./lib-mvhub/t/Headings/heading_names.t
./lib-mvhub/t/Headings/categories.t
./lib-mvhub/t/Structure/display_structure_menu.t
./lib-mvhub/t/Structure/process_synonym_form.t
./lib-mvhub/t/Structure/fetch_existing_category_and_dependent_data_from_db.t
./lib-mvhub/t/Structure/get_all_headings.t
./lib-mvhub/t/Structure/is_category_name_taken.t
./lib-mvhub/t/Structure/get_all_programs.t

# to fix in one go.
find . -name '*.t' -and -not -executable -exec chmod +x {} \;

Revision history for this message
Dan MacNeil (omacneil) wrote :

1309 +#save config file
1310 +my $config_file = $ENV{MV_CONFIG_FILE};
1311 +$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};

All this save/restore config file stuff is not needed. %ENV gets reset for each test

review: Needs Fixing
Revision history for this message
Dan MacNeil (omacneil) wrote :

More precisely, we need to set $MV_CONFIG_FILE but we don't need to restore it.

$ENV{MV_CONFIG_FILE} =$ENV{MV_TEST_CONFIG_FILE};

Revision history for this message
Dan MacNeil (omacneil) wrote :

lib-mvhub/t/Structure/display_structure_menu.t

lib-mvhub/t/Structure/process_synonym_form.t

should probably move to

  app-mvhub/t/mech/admin

They are more integration tests (test whole app like the user) than they are unit tests (test each sub separately )

These tests will not effect test coverage because the code isn't being instrumented ( modified by Devel::Cover to measure coverage) , the code runs on the webserver, which could be in Siberia

It should not be hard to get test coverage

both routines take a CGI object as a parameter. It is pretty straight forward to dummy up a CGI object to pass in. see:

lib-mvhub/t/Utils/clean_cgi_params.t

The problem is that both routines send to stdout instead of returning a value (ugly I know)

Two options:
 a. take as given that code is tested by integration tests

b. redirect stdout to a variable and then do pattern matching on that variable to check if the expected data is there.

http://perldoc.perl.org/functions/open.html

http://www.nntp.perl.org/group/perl.beginners/2007/05/msg91387.html

review: Needs Fixing
Revision history for this message
Dan MacNeil (omacneil) wrote :

Generally should get this stuff from config file $cfg object , see:

perldoc MVHub::Wrap::ConfigSimple

1381 +@site_codes = qw( mvh nsp );

# just use
my $site_code = $CFG->param('SITE.website_code');

no need to test both sites, code is same.

1382 +$domain = 'testing123.net';

review: Needs Fixing
Revision history for this message
Dan MacNeil (omacneil) wrote :

> 1382 +$domain = 'testing123.net';

# see:

TestHelper::get_test_website_name()

# aka:

lib-mvhub/t/lib/TestHelper.pm

Revision history for this message
Dan MacNeil (omacneil) wrote :

1623 +is( $result, 0, 'is_category_name_taken_with_category_name_and_id' );

1629 +is( $result, 1, 'is_category_name_taken_with_category_name_and_id' );

testing two different things, test name should be different. maybe:

$msg='YES is_category_name_taken_with_category_name_and_id '
$msg='NO is_category_name_taken_with_category_name_and_id '

review: Needs Fixing
Revision history for this message
Dan MacNeil (omacneil) wrote :

1384 +$username = 'test';
1385 +$password = 'test';

1658 +$username = 'test';
1659 +$password = 'test';

better:

my ($username, $password)=TestHelper::get_admin_username_password();

# now
sub get_admin_username_password {

return qw/test test/
}

# later
sub get_admin_username_password {

# code to stick temp values in
# /var/www/mvhub/omacneil/conf/auth_apache_users
# and code to remove them in cleanup
}

advantage is we don't have to change $user/$password in more than one place and can (eventually) get a more secure setup.

review: Needs Fixing
lp:~felmas/mvhub/structure_fix updated
632. By Ferhat Elmas <<email address hidden>>

new test for structure

Revision history for this message
Ferhat Elmas (felmas) wrote :

 In short changes;

 I moved tests that use Test::WWW::Mechanize into app-mvhub/t/mech/admin and modified them according to reviews.

 I added new unit tests into lib-mvhub/t/Structure.

 Now, display_structure_menu mode
  normal get ok
  success get ok

 process_synonym_form
  normal get written but doesn't work due to postgre specific query
  submit TODO

 display_category_chooser
  normal get ok
  success get ok

 process_category_form
  new category get ok
  edit category get ok
  new category submit TODO
  edit category submit TODO

lp:~felmas/mvhub/structure_fix updated
633. By Ferhat Elmas <<email address hidden>>

non-numeric category id test for edit category mode in process_category_form.t

634. By Ferhat Elmas <<email address hidden>>

edited mech/admin/ tests

635. By Ferhat Elmas <<email address hidden>>

edited lib-mvhub/t/Structure tests

636. By Ferhat Elmas <<email address hidden>>

merged trunk

637. By Ferhat Elmas <<email address hidden>>

added TODOs into new broken tests

638. By Ferhat Elmas <<email address hidden>>

merged trunk

639. By Ferhat Elmas <<email address hidden>>

refactor of new tests

640. By Ferhat Elmas <<email address hidden>>

merged trunk

641. By Ferhat Elmas <<email address hidden>>

removed todos, added new tests

642. By Ferhat Elmas <<email address hidden>>

merged trunk

643. By Ferhat Elmas <<email address hidden>>

made executable expand_synonyms.t

644. By Ferhat Elmas <<email address hidden>>

merged trunk

645. By Ferhat Elmas <<email address hidden>>

merged trunk

646. By Ferhat Elmas <<email address hidden>>

merged trunk

647. By Ferhat Elmas <<email address hidden>>

merged trunk

648. By Ferhat Elmas <<email address hidden>>

merged lp:~omacneil/mvhub/strucure_fix_dan

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app-mvhub/DocumentRoot/cgi-bin/mvhub/admin/structure.pl'
2--- app-mvhub/DocumentRoot/cgi-bin/mvhub/admin/structure.pl 2011-01-01 04:43:01 +0000
3+++ app-mvhub/DocumentRoot/cgi-bin/mvhub/admin/structure.pl 2011-09-11 23:59:29 +0000
4@@ -1,72 +1,12 @@
5 #!/usr/bin/perl
6
7-# TODO:
8-# STRUCTURE MENU PAGE
9-# Create. DONE
10-# Beautify & Better Explanations.
11-# Link from Admin Menu Frame. DONE.
12-# CATEGORY SELECTING & EDITING
13-# Selecting Functionality. DONE.
14-# Editing Functionality. DONE
15-# Use template for Form. DONE
16-# Test user errors and responses. DONE.
17-# Explain to the user what an alias is. Distinguish from synonym.
18-# Make sure they're not re-entering a category that already exists (case insensitive): DONE
19-# Possibly order the programs so that selected ones are first? HAVE ORDERED BY AGENCY NAME INSTEAD.
20-# Look into Sharing code with scripts/guide/category_loader.pl
21-# SYNONYM SELECTING & EDITING:
22-# - Create Synonyms.pm from code out of synonym_loader.pl DONE
23-# - Refactor synonym_loader.pl to use MVHub::Synonyms.pm DONE
24-# - Use Synonyms.pm in this script. DONE
25-# - Create template form. DONE
26-# - Recreate synonym_stem table with submitted word column, and load data into it. DONE.
27-# - on dsiegal db. DONE.
28-# - on production db. DONE.
29-# - Create new function & aggregates on production database. DONE.
30-# - Check http://archives.postgresql.org/pgsql-sql/ for answer to question.
31-# - Possibly put in a check to catch (and drop) any duplicated stem within a stem set
32-# - Any limitations on input?
33-# Might want to drop at least commas, and possibly only keep alphas, hyphens, apostrophes.
34-# HEADINGS
35-# Everything. TODO.
36-#
37-# MISC
38-# Possibly, edit the Admin Home Page with the same links as in the top frame, but also explanations.
39-# Check other TODOs.
40-# Change Common to get only a specific version of FormBuilder: DONE
41-# Add spell checking. Probably want to refactor how we incorporate it (here as well as in rest of application).
42-# Test on IE
43-# Throw out scripts/guide/add_category.pl.
44-# Document IN PROGRESS
45-# Usability Testing
46-
47 use strict;
48 use warnings;
49
50 use CGI;
51-use CGI::Carp;
52-use DBI;
53-
54-# TODO: trouble installing:
55-#use Text::Capitalize;
56-
57+
58+use MVHub::Structure;
59 use MVHub::Utils;
60-use MVHub::HTMLTemplateUtils;
61-
62-use MVHub::Common;
63-use MVHub::Utils::DB;
64-use MVHub::Headings;
65-use MVHub::HTML;
66-use MVHub::Page;
67-use MVHub::Synonyms;
68-
69-use MVHub::Wrap::ConfigSimple;
70-
71-my $cfg = MVHub::Wrap::ConfigSimple->new();
72-my $template_dir = $cfg->param('ABSOLUTE_PATH.template_html_dir');
73-my $EDIT_SYNONYMS_TEMPLATE = "$template_dir/edit_synonyms.tmpl";
74-my $EDIT_CATEGORIES_TEMPLATE = "$template_dir/edit_categories_template";
75-my $STRUCTURE_MENU_TEMPLATE = "$template_dir/structure_menu.tmpl";
76
77 {
78 my $cgi = CGI->new();
79@@ -74,422 +14,18 @@
80 my $mode = $cgi->param('mode');
81 $cgi->delete('mode');
82 if ( !$mode || $mode eq 'structure_menu' ) {
83- display_structure_menu($cgi);
84+ MVHub::Structure::display_structure_menu($cgi);
85 }
86 elsif ( $mode eq 'category_chooser' ) {
87- display_category_chooser($cgi);
88+ MVHub::Structure::display_category_chooser($cgi);
89 }
90 elsif ( $mode eq 'edit_category' ) {
91- process_category_form($cgi);
92+ MVHub::Structure::process_category_form($cgi);
93 }
94 elsif ( $mode eq 'edit_synonyms' ) {
95- process_synonym_form($cgi);
96+ MVHub::Structure::process_synonym_form($cgi);
97 }
98 else {
99 MVHub::Utils::assert( 0, "Unknown mode requested of $0: $mode" );
100 }
101 }
102-
103-sub process_synonym_form {
104- my $cgi = shift;
105-
106- if ( $cgi->param('synonyms') ) {
107-
108- # Form was submitted; update the database
109- my $synonyms_string = $cgi->param('synonyms');
110- my @synonyms = split( '\n', $synonyms_string );
111- Synonyms::submit_synonyms_to_db(@synonyms);
112- my $redirect_url
113- = $ENV{SCRIPT_NAME} . '?mode=structure_menu&success=1';
114- print $cgi->redirect($redirect_url);
115- exit(0);
116- }
117- else {
118-
119- # Display the form with pre-existing values from database
120- my @synonyms = Synonyms::fetch_synonyms_from_db();
121- my $synonyms_string = join( "\n", @synonyms );
122-
123- # FYI: Using Cgi::Formbuilder is overkill for a form with just one field,
124- # so we've just layed out the form explicitly in the template.
125- HTMLTemplateUtils::format_template(
126- filename => $EDIT_SYNONYMS_TEMPLATE,
127- param_href => {
128- synonyms => $synonyms_string,
129- rows => ( @synonyms + 2 )
130- },
131- print => 1,
132- doctype => 1
133- );
134- }
135-}
136-
137-sub display_structure_menu {
138- my $cgi = shift;
139- MVHub::HTMLTemplateUtils::format_template(
140- filename => $STRUCTURE_MENU_TEMPLATE,
141- param_href => { $cgi->Vars() },
142- print => 1,
143- doctype => 1
144- );
145-}
146-
147-sub display_category_chooser {
148- my $cgi = shift;
149- my $headings = new Headings();
150- my @heading_names = $headings->heading_names();
151-
152- my @output = ('<br>');
153- if ( $cgi->param('success') ) {
154- push( @output,
155- CGI::p('<font color="red">Your changes have been saved.</font>')
156- );
157- }
158- push( @output,
159- '<p>',
160- '<button onclick=',
161- qq|location.href="$ENV{SCRIPT_NAME}?mode=edit_category"|,
162- '> <strong>Create a New Category</strong></button>',
163- '</p>',
164- '<p>',
165- '<strong>or Click a Category to Edit:</strong>',
166- '</p>' );
167-
168- my $url = $ENV{SCRIPT_NAME} . '?mode=edit_category&category_id=';
169- foreach (@heading_names) {
170- push( @output, "<h2>$_</h2>" );
171- my $categories_aref = $headings->categories($_);
172-
173- # Copied from Guide.pm. Consider refactoring.
174- # Generate an html table of category links
175- my @links;
176- foreach (@$categories_aref) {
177- my $link = $url . $_->{category_id};
178- my $label = CGI::escapeHTML( $_->{category_name} );
179-
180- # add space around forward slashes so a label like
181- # "Translating/Interpreting" will wrap nicely
182- $label =~ s|\s*/\s*| / |g;
183- push( @links, CGI::a( { href => $link }, $label ) );
184- }
185-
186- my $table = HTML::make_html_table(
187- columns => 4,
188- content_aref => \@links,
189- table_attributes => 'width="100%" cellspacing="5"'
190- );
191- push( @output, $table );
192- }
193- print( $cgi->header('text/html'),
194- $cgi->start_html(
195- -style => { -src => '/guide/styles/admin.css' },
196- -title => 'Directory Structure Administration'
197- ),
198- join( "\n", @output ),
199- $cgi->end_html()
200- );
201-}
202-
203-sub process_category_form {
204- my $cgi = shift;
205- my $category_id = $cgi->param('category_id');
206- if ( defined $category_id ) {
207- MVHub::Utils::assert( $category_id !~ /\D/,
208- 'Category id must be numeric only' );
209- }
210-
211- my $preloaded_values_href;
212- my $form = make_category_editing_form($cgi);
213-
214- if ( $form->submitted() ) {
215- if ( $form->validate() && validate_more($form) ) {
216- submit_category_form_data_to_db( scalar $form->field() );
217- my $redirect_url
218- = $ENV{SCRIPT_NAME} . '?mode=structure_menu&success=1';
219- print $cgi->redirect($redirect_url);
220- exit(0);
221- }
222- else {
223-
224- # TODO: add announcement?
225- }
226- }
227- elsif ($category_id) {
228- $preloaded_values_href
229- = fetch_existing_category_and_dependent_data_from_db(
230- $category_id);
231- }
232- print $form->render( header => 1, values => $preloaded_values_href );
233-}
234-
235-# Returns a CGI::Formbuilder object representing our form. The form is blank (i.e.
236-# the fields are defined, but are not pre-populated with any data.)
237-# This CGI::Formbuilder object has no associated page header.
238-sub make_category_editing_form {
239- my ($cgi) = @_;
240-
241- my %category_field_defs = (
242- category_id => { type => 'hidden' },
243- category_name => { required => 1 },
244- aliases => {
245- type => 'textarea',
246- rows => 3,
247- comment => 'Optional. One per line.'
248- },
249- headings => {
250- type => 'checkbox',
251- options => get_all_headings(),
252- columns => 3,
253- required => 1,
254- comment => 'Select one or more.'
255- },
256- programs => {
257- type => 'checkbox',
258- options => get_all_programs(),
259- columns => 1,
260- comment => "Optional. Alternatively, you can assign categories"
261- . " to programs via their program forms."
262- }
263- );
264-
265- my $form = MVHub::Common::prepare_form(
266- form_attributes_href => {
267- name => 'category_editor_form',
268- params => $cgi,
269-
270-# TODO: listing the columns here is only while we're not using an html template,
271-# so that CGI:FB knows what order to display 'em in:
272-# fields => [qw/category_name aliases headings programs/]
273-# TODO: remove the above 3 lines.
274- template => $EDIT_CATEGORIES_TEMPLATE
275- },
276- fields_href => \%category_field_defs
277- );
278-
279- return $form;
280-}
281-
282-# Returns a reference to an array of headings, e.g.:
283-# [ "Arts/Culture/Entertainment", "Financial Education/Credit", ... ]
284-sub get_all_headings {
285- my $dbh = MVHub::Utils::DB::get_dbh();
286- my $sql = MVHub::Utils::DB::get_sql_select_statement(
287- 'HEADING_X_HEADING_NAME');
288- my $headings_aref = $dbh->selectcol_arrayref($sql);
289- return $headings_aref;
290-}
291-
292-# Returns a reference to an array of program hashrefs, e.g.:
293-# [
294-# { 12345 => 'Youth Volleyball (YMCA)' },
295-# { 156745 => 'Building Blocks (North Shore Arc)' },
296-# ...
297-# ]
298-sub get_all_programs {
299- my $dbh = MVHub::Utils::DB::get_dbh();
300-
301- my $sql = MVHub::Utils::DB::get_sql_select_statement(
302- 'PROGRAM_X_PROGRAM_AGENCY_ID');
303-
304- my $result_aref = $dbh->selectall_arrayref($sql);
305-
306- # see end of 'perldoc -f map' on why the '+' in the following:
307- my @headings = map( +{ $_->[0] => $_->[1] }, @$result_aref );
308- return \@headings;
309-}
310-
311-# The return statement tells all.
312-sub fetch_existing_category_and_dependent_data_from_db {
313- my $category_id = shift;
314- my $dbh = MVHub::Utils::DB::get_dbh();
315-
316- my $sql = MVHub::Utils::DB::get_sql_select_statement(
317- 'CATEGORY_X_CATEGORY_NAME');
318- my @bind_values = ($category_id);
319-
320- my @result
321- = @{ $dbh->selectall_arrayref( $sql, { Slice => {} }, @bind_values )
322- };
323- my $category_name = $result[0]->{category_name};
324-
325- $sql = MVHub::Utils::DB::get_sql_select_statement('ALIAS_X_ALIAS_NAME');
326- my $aliases_aref
327- = $dbh->selectcol_arrayref( $sql, { Slice => {} }, $category_id );
328- my $aliases = join( "\n", @$aliases_aref );
329-
330- $sql = MVHub::Utils::DB::get_sql_select_statement(
331- 'HEADING_CATEGORY_X_HEADING_NAME');
332- my $headings_aref
333- = $dbh->selectcol_arrayref( $sql, { Slice => {} }, $category_id );
334- MVHub::Utils::assert( @$headings_aref,
335- "Expected at least one heading for this category (#$category_id)" );
336-
337- $sql = MVHub::Utils::DB::get_sql_select_statement(
338- 'PROGRAM_CATEGORY_X_PROGRAM_ID');
339- my $programs_aref
340- = $dbh->selectcol_arrayref( $sql, { Slice => {} }, $category_id );
341- return {
342- category_name => $category_name,
343- aliases => $aliases,
344- headings => $headings_aref,
345- programs => $programs_aref
346- };
347-}
348-
349-sub submit_category_form_data_to_db {
350- my $values_href = shift;
351-
352-# Pre-processing data destined for database here:
353-# TODO: get T:C installed or remove this line
354-#$values_href->{category_name} = Text::Capitalize::$values_href->{category_name};
355-
356- my $dbh = MVHub::Utils::DB::get_dbh();
357- $dbh->begin_work();
358-
359-# If we have a category id, we must be updating an existing category.
360-# In this case, in addition to updating the category record, we need to
361-# get rid of the dependent alias, heading_category and program_category records
362-# the user wants removed, update any changed ones, and add any new ones.
363-# That's a lot of work. The easiest way is to just delete the existing
364-# category record, whcih will cascade to delete the dependent records (because
365-# of the way the foreign keys are defined on the those tables).
366-# Then we insert everything, just as if we were creating a new category. The only
367-# difference is that for an update we re-use the existing category_id, and
368-# for a new category we'll make a new one.
369- if ( $values_href->{category_id} ) {
370- my $sql = MVHub::Utils::DB::get_sql_delete_statement(
371- 'CATEGORY_X_CATEGORY_ID');
372- $dbh->do( $sql, undef, $values_href->{category_id} );
373- }
374- else {
375- $values_href->{category_id}
376- = MVHub::Utils::DB::get_next_in_sequence( $dbh,
377- 'category_id_sequence' );
378- }
379- insert_category_and_dependent_data( dbh => $dbh, %$values_href );
380- $dbh->commit();
381-}
382-
383-sub insert_category_and_dependent_data {
384- my %args = @_;
385- my $dbh = delete $args{dbh};
386- my $category_id = delete $args{category_id};
387- my $category_name = delete $args{category_name};
388- my $aliases_string = delete $args{aliases};
389- my $heading_names_aref = delete $args{headings};
390- my $program_ids_aref = delete $args{programs};
391- MVHub::Utils::assert( ( scalar keys %args ) == 0,
392- "Unknown arguments: @{[%args]}" );
393-
394- MVHub::Utils::DB::insert_one_record(
395- dbh => $dbh,
396- table => 'category',
397- values_href => {
398- 'category_name' => $category_name,
399- 'stems_name' => Common::get_stems($category_name),
400- 'category_id' => $category_id
401- }
402- );
403-
404- if ($aliases_string) {
405-
406- # TODO: are we guaranteed that the line separator is a \n ?
407- my @aliases = split( '\n', $aliases_string );
408-
409- foreach my $alias (@aliases) {
410- $alias = MVHub::Utils::trim($alias);
411- next unless $alias; # ignore blank lines
412- Common::insert_one_record(
413- dbh => $dbh,
414- table => 'alias',
415- values_href => {
416- 'category_id' => $category_id,
417- 'alias_name' => $alias,
418- 'stems_name' => Common::get_stems($alias)
419- }
420- );
421- }
422- }
423-
424- foreach my $heading_name (@$heading_names_aref) {
425- Common::insert_one_record(
426- dbh => $dbh,
427- table => 'heading_category',
428- values_href => {
429- 'category_id' => $category_id,
430- 'heading_name' => $heading_name
431- }
432- );
433- }
434-
435- foreach my $program_id (@$program_ids_aref) {
436- Common::insert_one_record(
437- dbh => $dbh,
438- table => 'program_category',
439- values_href => {
440- 'category_id' => $category_id,
441- 'program_id' => $program_id
442- }
443- );
444- }
445-}
446-
447-# This is the place for error checking that can't be done within
448-# CGI::FormBuilder->validate(), e.g.:
449-# - complex validations that require access to other variables
450-# - validations that need to produce a custom error message
451-# Returns 0 iff any validation fails, supplying an error message
452-# for each failed validation in the custom_error-category_name template
453-# variable on the form.
454-sub validate_more {
455- my $form = shift;
456- my $return_val = 1;
457-
458- if (is_category_name_taken(
459- category_name => $form->field('category_name'),
460- category_id => $form->field('category_id')
461- )
462- )
463- {
464- my $msg = "The category name you entered is "
465- . "already being used by another category. Please enter a different one:";
466- $form->tmpl_param( 'custom_error-category_name', $msg );
467-
468- $return_val = 0;
469- }
470-
471- return $return_val;
472-}
473-
474-# Returns true iff the given category name is already in use
475-# (without regard to case) in the database by another category. If a
476-# a category_id argument is supplied, then any record with that ID
477-# will be excluded from the search. Returns false otherwise.
478-sub is_category_name_taken {
479- my %args = @_;
480- my $category_name = delete $args{'category_name'};
481- my $category_id = delete $args{'category_id'};
482- MVHub::Utils::assert( defined $category_name && $category_name ne "",
483- "Missing category_name argument" );
484- MVHub::Utils::assert( ( scalar keys %args ) == 0,
485- "Unknown arguments: @{[%args]}" );
486- my $dbh = MVHub::Utils::DB::get_dbh( $ENV{MV_CONFIG_FILE} );
487-
488- # The SELECT EXISTS (SELECT 1 ...) syntax is generally faster than the
489- # SELECT count(*) syntax, since the former only scans until it finds
490- # the first match.
491- my @bind_variables = ( lc($category_name) );
492- my $sql = MVHub::Utils::DB::get_sql_select_statement(
493- 'CATEGORY_X_EXISTS_ONE_CATEGORY_NAME');
494-
495- if ($category_id) {
496- @bind_variables = ( lc($category_name), $category_id );
497- $sql = MVHub::Utils::DB::get_sql_select_statement(
498- 'CATEGORY_X_EXISTS_ONE_CATEGORY_NAME_NOT_ID');
499- }
500-
501- my @row
502- = $dbh->selectall_arrayref( $sql, { Slice => {} }, @bind_variables );
503-
504- return ( $row[0] );
505-}
506
507=== modified file 'app-mvhub/conf/create_sqlite.lib'
508--- app-mvhub/conf/create_sqlite.lib 2011-08-25 15:05:33 +0000
509+++ app-mvhub/conf/create_sqlite.lib 2011-09-11 23:59:29 +0000
510@@ -1,4 +1,4 @@
511-[001_drop_agency]
512+[001_drop_agency_table]
513 DROP TABLE IF EXISTS agency;
514
515 -- This:
516@@ -7,7 +7,7 @@
517 -- postgresql CREATE TABLE agency
518 -- but for existing tests it doesn't have to
519
520-[002_create_agency]
521+[002_create_agency_table]
522 CREATE TABLE agency (
523 agency_id INTEGER PRIMARY KEY,
524 last_updated DATE not null,
525@@ -54,15 +54,14 @@
526 date_created DATE,
527 quick_login_passwd TEXT
528 );
529-SQL
530
531-[003_drop_program]
532+[003_drop_program_table]
533 DROP TABLE IF EXISTS program;
534
535-[004_create_program]
536+[004_create_program_table]
537 CREATE TABLE program (
538 program_id INTEGER PRIMARY KEY,
539- agency_id INTEGER not null REFERENCES agency ON DELETE CASCADE,
540+ agency_id INTEGER not null REFERENCES agency(agency_id) ON DELETE CASCADE,
541 last_updated DATE not null,
542 hidden_reason TEXT,
543
544@@ -130,3 +129,67 @@
545
546 CONSTRAINT area_check CHECK(((area_data IS not null) AND area_data <> '') or area_type = 'EVERYWHERE')
547 );
548+
549+[005_drop_category_table]
550+ DROP TABLE IF EXISTS category;
551+
552+[006_create_category_table]
553+CREATE TABLE category (
554+ category_id INTEGER PRIMARY KEY,
555+ category_name TEXT not null CHECK(category_name <> '') UNIQUE,
556+ stems_name TEXT not null CHECK(stems_name <> '')
557+);
558+
559+[007_drop_program_category_table]
560+ DROP TABLE IF EXISTS program_category;
561+
562+[008_create_program_category_table]
563+CREATE TABLE program_category (
564+ category_id INTEGER not null REFERENCES category(category_id) ON DELETE CASCADE,
565+ program_id INTEGER not null REFERENCES program(program_id) on DELETE CASCADE,
566+
567+ PRIMARY KEY(category_id, program_id)
568+);
569+
570+[009_drop_alias_table]
571+ DROP TABLE IF EXISTS alias;
572+
573+[010_create_alias_table]
574+CREATE TABLE alias (
575+ category_id INTEGER not null REFERENCES category(category_id) ON DELETE CASCADE,
576+ alias_name TEXT not null CHECK(alias_name <> ''),
577+ stems_name TEXT not null CHECK(stems_name <> ''),
578+
579+ PRIMARY KEY(category_id, alias_name)
580+);
581+
582+[011_drop_synonym_stem_table]
583+ DROP TABLE IF EXISTS synonym_stem;
584+
585+[012_create_synonym_stem_table]
586+CREATE TABLE synonym_stem (
587+ synonym_id INTEGER not null,
588+ word_stem TEXT not null CHECK(word_stem <> ''),
589+ submitted_word TEXT not null CHECK(submitted_word <> ''),
590+
591+ PRIMARY KEY(synonym_id, word_stem)
592+);
593+
594+[013_drop_heading_table]
595+ DROP TABLE IF EXISTS heading;
596+
597+[014_create_heading_table]
598+CREATE TABLE heading (
599+ heading_name TEXT PRIMARY KEY
600+);
601+
602+[015_drop_heading_category_table]
603+ DROP TABLE IF EXISTS heading_category;
604+
605+[016_create_heading_category_table]
606+CREATE TABLE heading_category (
607+ category_id INTEGER not null REFERENCES category(category_id) ON DELETE CASCADE,
608+ heading_name TEXT not null REFERENCES heading(heading_name) ON UPDATE CASCADE ON DELETE CASCADE,
609+
610+ PRIMARY KEY(category_id, heading_name)
611+);
612
613=== modified file 'app-mvhub/conf/sql_delete.lib'
614--- app-mvhub/conf/sql_delete.lib 2010-11-26 02:34:44 +0000
615+++ app-mvhub/conf/sql_delete.lib 2011-09-11 23:59:29 +0000
616@@ -44,4 +44,4 @@
617 WHERE program_id = ?
618
619 [SYNONYM_STEM_X_ALL]
620-DELETE FROM ?
621+DELETE FROM synonym_stem
622
623=== modified file 'app-mvhub/conf/sql_insert.lib'
624--- app-mvhub/conf/sql_insert.lib 2010-06-29 18:02:33 +0000
625+++ app-mvhub/conf/sql_insert.lib 2011-09-11 23:59:29 +0000
626@@ -24,16 +24,30 @@
627 'contact_first_name','contact_last_name','contact_phone','contact_email_available'
628 );
629
630+[CATEGORY_X_REQUIRED_FIELDS]
631+INSERT INTO category
632+(
633+ category_id,
634+ category_name,
635+ stems_name
636+)
637+VALUES
638+(
639+ ?,
640+ ?,
641+ ?
642+);
643+
644 [PROGRAM_CATEGORY_X_REQUIRED_FIELDS]
645 INSERT INTO program_category
646 (
647- program_id,
648- category_id
649+ program_id,
650+ category_id
651 )
652 VALUES
653 (
654- ?,
655- ?
656+ ?,
657+ ?
658 );
659
660 [PROGRAM_X_REQUIRED_FIELDS]
661@@ -69,3 +83,53 @@
662 'area_type','online_registration','area_type','area_data','resource library','meeting space',
663 'contact_first_name','contact_last_name','contact_phone', 'contact_email_available'
664 );
665+
666+[ALIAS_X_REQUIRED_FIELDS]
667+INSERT INTO alias
668+(
669+ category_id,
670+ alias_name,
671+ stems_name
672+)
673+VALUES
674+(
675+ ?,
676+ ?,
677+ ?
678+);
679+
680+[SYNONYM_STEM_X_REQUIRED_FIELDS]
681+INSERT INTO synonym_stem
682+(
683+ synonym_id,
684+ word_stem,
685+ submitted_word
686+)
687+VALUES
688+(
689+ ?,
690+ ?,
691+ ?
692+);
693+
694+[HEADING_X_REQUIRED_FIELDS]
695+INSERT INTO heading
696+(
697+ heading_name
698+)
699+VALUES
700+(
701+ ?
702+);
703+
704+[HEADING_CATEGORY_X_REQUIRED_FIELDS]
705+INSERT INTO heading_category
706+(
707+ category_id,
708+ heading_name
709+)
710+VALUES
711+(
712+ ?,
713+ ?
714+);
715
716=== modified file 'app-mvhub/conf/sql_select.lib'
717--- app-mvhub/conf/sql_select.lib 2011-07-12 20:50:24 +0000
718+++ app-mvhub/conf/sql_select.lib 2011-09-11 23:59:29 +0000
719@@ -629,6 +629,7 @@
720 WHERE lower(category_name) = ?
721 AND category_id <> ?
722 )
723+
724 [CATEGORY_X_IN_CATEGORY_NAME]
725 SELECT category_name
726 FROM category
727@@ -958,7 +959,7 @@
728 ORDER BY categories DESC, subq.program_and_agency_name
729
730 [PROGRAM_X_PROGRAM_AGENCY_ID]
731-SELECT p.program_id, a.agency_name || ': ' || program_name
732+SELECT p.program_id, a.agency_name || ': ' || p.program_name
733 AS agency_program_name
734 FROM agency a,program p
735 WHERE a.agency_id = p.agency_id
736
737=== modified file 'app-mvhub/conf/templates/html/admin.tmpl'
738--- app-mvhub/conf/templates/html/admin.tmpl 2011-06-29 15:01:03 +0000
739+++ app-mvhub/conf/templates/html/admin.tmpl 2011-09-11 23:59:29 +0000
740@@ -35,8 +35,9 @@
741 <a target="main_frame" href="/html/admin/home.shtml">Home</a> |
742 <a target="main_frame" href="/cgi-bin/mvhub/admin/admin_reports.pl">Reports</a> |
743 <a target="main_frame" href="/cgi-bin/mvhub/agency_form.pl">Add a New Agency</a> |
744- <a target="main_frame" href="/cgi-bin/mvhub/admin/call_manager.pl">Call Manager</a>
745- </td>
746+ <a target="main_frame" href="/cgi-bin/mvhub/admin/call_manager.pl">Call Manager</a> |
747+ <a target="main_frame" href="/cgi-bin/mvhub/admin/structure.pl">Structure</a>
748+ </td>
749 <td align="right"><a href="/cgi-bin/mvhub/logout.pl" target="_top" onclick="alert('You must close all your browser windows to fully logout.')">Log out</a><br/>
750 </td></tr>
751 </table>
752
753=== modified file 'app-mvhub/t/debian_packages_installed.t'
754--- app-mvhub/t/debian_packages_installed.t 2011-08-15 08:04:54 +0000
755+++ app-mvhub/t/debian_packages_installed.t 2011-09-11 23:59:29 +0000
756@@ -47,6 +47,7 @@
757 'libtest-mockobject-perl' => [],
758 'libtest-nowarnings-perl' => [],
759 'libtest-warn-perl' => [],
760+ 'libtest-trap-perl' => [],
761 'libtext-template-perl' => [],
762 'liburi-perl' => [],
763 'perltidy' => [],
764
765=== added file 'app-mvhub/t/mech/admin/display_structure_menu.t'
766--- app-mvhub/t/mech/admin/display_structure_menu.t 1970-01-01 00:00:00 +0000
767+++ app-mvhub/t/mech/admin/display_structure_menu.t 2011-09-11 23:59:29 +0000
768@@ -0,0 +1,79 @@
769+#!/usr/bin/perl
770+
771+use strict;
772+use warnings;
773+
774+use Test::Deep;
775+use Test::More tests => 20;
776+use Test::WWW::Mechanize;
777+
778+use MVHub::Wrap::ConfigSimple;
779+use TestHelper;
780+
781+my ( $cfg, $site_code, $website, $address, $mech );
782+my ( $base, $mode, $success, $param_message );
783+my ( $username, $password ) = TestHelper::get_admin_username_password();
784+
785+$cfg = MVHub::Wrap::ConfigSimple->new();
786+$site_code = $cfg->param('SITE.website_code');
787+$website = TestHelper::get_test_website_name($site_code);
788+
789+$base = '/cgi-bin/mvhub/admin/structure.pl';
790+$mode = 'mode=structure_menu';
791+$success = 'success=1';
792+
793+$mech = Test::WWW::Mechanize->new();
794+$mech->credentials( $username, $password );
795+
796+$address = "http://$website$base";
797+$param_message = 'without mode and succes parameters';
798+check_page_title_and_links( $mech, $address, $param_message );
799+
800+$address = "$base?$mode";
801+$param_message = 'with mode parameter';
802+check_page_title_and_links( $mech, $address, $param_message );
803+
804+$address = "$base?$success";
805+$param_message = 'with success parameter';
806+check_page_title_and_links( $mech, $address, $param_message );
807+
808+$address = "$base?$mode&$success";
809+$param_message = 'with mode and success parameters';
810+check_page_title_and_links( $mech, $address, $param_message );
811+
812+sub check_page_title_and_links {
813+
814+ my $mech = shift;
815+ my $address = shift;
816+ my $param_message = shift;
817+ my $title = 'Add/Edit Synonyms';
818+ my $success_message = 'Your changes have been saved';
819+ my ( @real_links, @content_links );
820+
821+ @content_links = ();
822+ @real_links = (
823+ '/all/css/admin.css',
824+ '/html/admin/help.shtml#search_config',
825+ '/cgi-bin/mvhub/admin/structure.pl?mode=category_chooser',
826+ '/cgi-bin/mvhub/admin/structure.pl?mode=edit_synonyms'
827+ );
828+
829+ $mech->get_ok( $address, "page is got successfully $param_message" );
830+ $mech->title_is( $title, "title is correct $param_message" );
831+ foreach ( $mech->followable_links() ) {
832+ push @content_links, $_->url();
833+ }
834+ if ( $param_message =~ /success/ ) {
835+ $mech->text_contains( $success_message,
836+ "success message is show successfully $param_message" );
837+ }
838+ else {
839+ $mech->text_lacks( $success_message,
840+ "success message is not shown $param_message" );
841+ }
842+
843+ cmp_deeply( \@content_links, \@real_links,
844+ "all links found are correct $param_message" );
845+ $mech->page_links_ok("all links are checked $param_message");
846+
847+}
848
849=== added file 'app-mvhub/t/mech/admin/process_synonym_form.t'
850--- app-mvhub/t/mech/admin/process_synonym_form.t 1970-01-01 00:00:00 +0000
851+++ app-mvhub/t/mech/admin/process_synonym_form.t 2011-09-11 23:59:29 +0000
852@@ -0,0 +1,111 @@
853+#!/usr/bin/perl
854+
855+use strict;
856+use warnings;
857+
858+use Test::Deep;
859+use Test::More tests => 13;
860+use Test::WWW::Mechanize;
861+
862+use MVHub::Wrap::ConfigSimple;
863+use TestHelper;
864+
865+my ( $cfg, $site_code, $website, $uri, $title, $mech );
866+my ( $username, $password ) = TestHelper::get_admin_username_password();
867+my ( $synonyms, $new_synonym, $success_message );
868+
869+$cfg = MVHub::Wrap::ConfigSimple->new();
870+$site_code = $cfg->param('SITE.website_code');
871+$website = TestHelper::get_test_website_name($site_code);
872+$uri = '/cgi-bin/mvhub/admin/structure.pl?mode=edit_synonyms';
873+$title = 'Add/Edit Synonyms';
874+$new_synonym = 'zoo animal park';
875+$synonyms = <<END;
876+ADD ADHD HADD
877+alcohol drinking drunk
878+assist help
879+cessation quit
880+child children kid youth
881+class lesson
882+computer technology internet
883+counseling therapy
884+detoxification detox
885+disabled handicapped
886+drug substance
887+elder elderly senior
888+fitness exercise
889+health medical healthcare
890+infant baby
891+job career vocation employment
892+literacy reading writing language
893+marital marriage
894+methadone heroine
895+money finance
896+nurse rna
897+nutrition nutritionist
898+pregnant pregnancy
899+register registration
900+teen adolescent
901+television tv
902+tenant rent renter rental
903+tobacco smoking
904+utility electricity energy fuel heat gas oil
905+vote voter elect
906+END
907+$success_message = 'Your changes have been saved';
908+
909+$mech = Test::WWW::Mechanize->new();
910+$mech->credentials( $username, $password );
911+
912+#first get the synonyms in the database and test their existence and links
913+$mech->get_ok( "http://$website$uri",
914+ 'page is got successfully before any processing' );
915+$mech->title_is( $title, 'title is correct before any processing' );
916+check_links($mech);
917+$mech->text_contains( $synonyms,
918+ 'all synonyms are shown correctly before any processing' );
919+$mech->text_lacks( $new_synonym,
920+ 'new synonym is not shown before any processing' );
921+
922+#second submit new synonym and then check whether it can be shown
923+$mech->form_number(1); #form number starts from 1
924+$mech->field( 'synonyms', "$synonyms\n$new_synonym" );
925+$mech->submit();
926+$mech->text_contains( $success_message,
927+ 'new synonym is added, redirect is successful' );
928+
929+#third get the synonyms again to see new synonym and delete it
930+$mech->get_ok( "http://$website$uri",
931+ 'page is got successfully after addition of new synonym' );
932+$mech->text_contains( "$synonyms$new_synonym",
933+ 'new synonym is shown successfully after addition' );
934+$mech->form_number(1);
935+$mech->field( 'synonyms', $synonyms );
936+$mech->submit();
937+$mech->text_contains( $success_message,
938+ 'new synonym is deleted, redirect is successful' );
939+
940+#finally get the synonyms to see whether new synonyms is deleted
941+$mech->get_ok( "http://$website$uri",
942+ 'page is got successfully after deletion of new synonym' );
943+$mech->text_contains( $synonyms,
944+ 'all synonyms are shown correctly after deletion' );
945+$mech->text_lacks( $new_synonym, 'new synonyms is deleted and not shown' );
946+
947+sub check_links {
948+
949+ my $mech = shift;
950+ my ( @real_links, @content_links );
951+
952+ @content_links = ();
953+ @real_links
954+ = ( '/all/css/admin.css', '/html/admin/help.shtml#search_config', );
955+ foreach ( $mech->followable_links() ) {
956+ push @content_links, $_->url();
957+ }
958+
959+ cmp_deeply( \@content_links, \@real_links,
960+ 'all links found are correct' );
961+ $mech->page_links_ok('all links are checked');
962+
963+}
964
965=== modified file 'lib-mvhub/lib/MVHub/Headings.pm'
966--- lib-mvhub/lib/MVHub/Headings.pm 2010-09-17 16:42:35 +0000
967+++ lib-mvhub/lib/MVHub/Headings.pm 2011-09-11 23:59:29 +0000
968@@ -8,8 +8,6 @@
969
970 use MVHub::Common;
971
972-our ($VERSION) = '$Revision: 1215 $' =~ /([.\d]+)/;
973-
974 =head2 new()
975
976 Initializes a Heading object from the database.
977@@ -17,13 +15,9 @@
978 =cut
979
980 sub new {
981- my $invocant = shift;
982- my $class = ref($invocant) || $invocant;
983-
984- my $dbh = MVHub::Utils::DB::get_dbh();
985-
986+
987+ my $class = shift;
988 my $self = { HEADING_CATEGORIES_HREF => _fetch_heading_categories() };
989-
990 bless( $self, $class );
991 return $self;
992 }
993@@ -76,7 +70,7 @@
994 # }
995 sub _fetch_heading_categories {
996
997- my $dbh = MVHub::Utils::DB::get_dbh();
998+ my $dbh = MVHub::Utils::DB::get_dbh( $ENV{MV_CONFIG_FILE} );
999 my $sql = MVHub::Utils::DB::get_sql_select_statement(
1000 "CATEGORY_HEADING_CATEGORY_X_FETCH_HEADING_CATEGORY");
1001
1002@@ -89,3 +83,6 @@
1003 }
1004 return \%heading_categories;
1005 }
1006+
1007+# all good modules return 1
1008+1;
1009
1010=== added file 'lib-mvhub/lib/MVHub/Structure.pm'
1011--- lib-mvhub/lib/MVHub/Structure.pm 1970-01-01 00:00:00 +0000
1012+++ lib-mvhub/lib/MVHub/Structure.pm 2011-09-11 23:59:29 +0000
1013@@ -0,0 +1,471 @@
1014+package MVHub::Structure;
1015+
1016+# TODO:
1017+# STRUCTURE MENU PAGE
1018+# Beautify & Better Explanations.
1019+# CATEGORY SELECTING & EDITING
1020+# Possibly order the programs so that selected ones are first? HAVE ORDERED BY AGENCY NAME INSTEAD.
1021+# Look into Sharing code with scripts/guide/category_loader.pl
1022+# SYNONYM SELECTING & EDITING:
1023+# - Check http://archives.postgresql.org/pgsql-sql/ for answer to question.
1024+# - Possibly put in a check to catch (and drop) any duplicated stem within a stem set
1025+# - Any limitations on input?
1026+# Might want to drop at least commas, and possibly only keep alphas, hyphens, apostrophes.
1027+# HEADINGS
1028+# Everything. TODO.
1029+#
1030+# MISC
1031+# Possibly, edit the Admin Home Page with the same links as in the top frame, but also explanations.
1032+# Check other TODOs.
1033+# Add spell checking. Probably want to refactor how we incorporate it (here as well as in rest of application).
1034+# Test on IE
1035+# Throw out scripts/guide/add_category.pl.
1036+# Document IN PROGRESS
1037+# Usability Testing
1038+
1039+use strict;
1040+use warnings;
1041+
1042+use CGI;
1043+use CGI::Carp;
1044+use DBI;
1045+
1046+use MVHub::Common;
1047+use MVHub::Headings;
1048+use MVHub::HTML;
1049+use MVHub::HTMLTemplateUtils;
1050+use MVHub::Page;
1051+use MVHub::Synonyms;
1052+use MVHub::Utils;
1053+use MVHub::Utils::DB;
1054+use MVHub::Wrap::ConfigSimple;
1055+
1056+use base 'Exporter';
1057+our @EXPORT_OK = qw(
1058+ display_structure_menu
1059+ display_category_chooser
1060+ process_category_form
1061+ process_synonym_form
1062+ make_categort_editing_form
1063+ get_all_headings
1064+ get_all_programs
1065+ fetch_existing_category_and_dependent_data_from_db
1066+ submit_category_form_data_to_db
1067+ insert_category_and_dependent_data
1068+ validate_more
1069+ is_category_name_taken
1070+);
1071+
1072+my $cfg = MVHub::Wrap::ConfigSimple->new();
1073+my $template_dir = $cfg->param('ABSOLUTE_PATH.template_html_dir');
1074+my $EDIT_SYNONYMS_TEMPLATE = "$template_dir/edit_synonyms.tmpl";
1075+my $EDIT_CATEGORIES_TEMPLATE = "$template_dir/edit_categories.tmpl";
1076+my $STRUCTURE_MENU_TEMPLATE = "$template_dir/structure_menu.tmpl";
1077+
1078+sub process_synonym_form {
1079+ my $cgi = shift;
1080+
1081+ if ( $cgi->param('synonyms') ) {
1082+
1083+ # Form was submitted; update the database
1084+ my $synonyms_string = $cgi->param('synonyms');
1085+ my @synonyms = split( '\n', $synonyms_string );
1086+ MVHub::Synonyms::submit_synonyms_to_db(@synonyms);
1087+ my $redirect_url
1088+ = $ENV{SCRIPT_NAME} . '?mode=structure_menu&success=1';
1089+ print $cgi->redirect($redirect_url);
1090+ exit(0);
1091+ }
1092+ else {
1093+
1094+ # Display the form with pre-existing values from database
1095+ my @synonyms = MVHub::Synonyms::fetch_synonyms_from_db();
1096+ my $synonyms_string = join( "\n", @synonyms );
1097+
1098+ # FYI: Using Cgi::Formbuilder is overkill for a form with just one field,
1099+ # so we've just layed out the form explicitly in the template.
1100+ MVHub::HTMLTemplateUtils::format_template(
1101+ filename => $EDIT_SYNONYMS_TEMPLATE,
1102+ param_href => {
1103+ synonyms => $synonyms_string,
1104+ rows => ( @synonyms + 2 )
1105+ },
1106+ print => 1,
1107+ doctype => 1
1108+ );
1109+ }
1110+}
1111+
1112+sub display_structure_menu {
1113+ my $cgi = shift;
1114+ MVHub::HTMLTemplateUtils::format_template(
1115+ filename => $STRUCTURE_MENU_TEMPLATE,
1116+ param_href => { $cgi->Vars() },
1117+ print => 1,
1118+ doctype => 1
1119+ );
1120+}
1121+
1122+sub display_category_chooser {
1123+ my $cgi = shift;
1124+ my $headings = MVHub::Headings->new();
1125+ my @heading_names = $headings->heading_names();
1126+
1127+ my @output = ('<br>');
1128+ if ( $cgi->param('success') ) {
1129+ push( @output,
1130+ CGI::p('<font color="red">Your changes have been saved.</font>')
1131+ );
1132+ }
1133+ push( @output,
1134+ '<p>',
1135+ '<button onclick=',
1136+ qq|location.href="$ENV{SCRIPT_NAME}?mode=edit_category"|,
1137+ '> <strong>Create a New Category</strong></button>',
1138+ '</p>',
1139+ '<p>',
1140+ '<strong>or Click a Category to Edit:</strong>',
1141+ '</p>' );
1142+
1143+ my $url = $ENV{SCRIPT_NAME} . '?mode=edit_category&category_id=';
1144+ foreach (@heading_names) {
1145+ push( @output, "<h2>$_</h2>" );
1146+ my $categories_aref = $headings->categories($_);
1147+
1148+ # Copied from Guide.pm. Consider refactoring.
1149+ # Generate an html table of category links
1150+ my @links;
1151+ foreach (@$categories_aref) {
1152+ my $link = $url . $_->{category_id};
1153+ my $label = CGI::escapeHTML( $_->{category_name} );
1154+
1155+ # add space around forward slashes so a label like
1156+ # "Translating/Interpreting" will wrap nicely
1157+ $label =~ s|\s*/\s*| / |g;
1158+ push( @links, CGI::a( { href => $link }, $label ) );
1159+ }
1160+
1161+ my $table = MVHub::HTML::make_html_table(
1162+ columns => 4,
1163+ content_aref => \@links,
1164+ table_attributes => 'width="100%" cellspacing="5"'
1165+ );
1166+ push( @output, $table );
1167+ }
1168+
1169+ print( $cgi->header('text/html'),
1170+ $cgi->start_html(
1171+ -style => { -src => '/all/css/admin.css' },
1172+ -title => 'Directory Structure Administration'
1173+ ),
1174+ join( "\n", @output ),
1175+ $cgi->end_html()
1176+ );
1177+}
1178+
1179+sub process_category_form {
1180+ my $cgi = shift;
1181+ my $category_id = $cgi->param('category_id');
1182+
1183+ if ( defined $category_id ) {
1184+ MVHub::Utils::assert( $category_id !~ /\D/,
1185+ 'Category id must be numeric only' );
1186+ }
1187+
1188+ my $preloaded_values_href;
1189+ my $form = make_category_editing_form($cgi);
1190+
1191+ if ( $form->submitted() ) {
1192+ if ( $form->validate() && validate_more($form) ) {
1193+ submit_category_form_data_to_db( scalar $form->field() );
1194+ my $redirect_url
1195+ = $ENV{SCRIPT_NAME} . '?mode=structure_menu&success=1';
1196+ print $cgi->redirect($redirect_url);
1197+ exit(0);
1198+ }
1199+ else {
1200+
1201+ # TODO: add announcement?
1202+ }
1203+ }
1204+ elsif ($category_id) {
1205+ $preloaded_values_href
1206+ = fetch_existing_category_and_dependent_data_from_db(
1207+ $category_id);
1208+ }
1209+ print $form->render( header => 1, values => $preloaded_values_href );
1210+}
1211+
1212+# Returns a CGI::Formbuilder object representing our form. The form is blank (i.e.
1213+# the fields are defined, but are not pre-populated with any data.)
1214+# This CGI::Formbuilder object has no associated page header.
1215+sub make_category_editing_form {
1216+ my ($cgi) = @_;
1217+
1218+ my %category_field_defs = (
1219+ category_id => { type => 'hidden' },
1220+ category_name => { required => 1 },
1221+ aliases => {
1222+ type => 'textarea',
1223+ rows => 3,
1224+ comment => 'Optional. One per line.'
1225+ },
1226+ headings => {
1227+ type => 'checkbox',
1228+ options => get_all_headings(),
1229+ columns => 3,
1230+ required => 1,
1231+ comment => 'Select one or more.'
1232+ },
1233+ programs => {
1234+ type => 'checkbox',
1235+ options => get_all_programs(),
1236+ columns => 1,
1237+ comment => "Optional. Alternatively, you can assign categories"
1238+ . " to programs via their program forms."
1239+ }
1240+ );
1241+
1242+ my $form = MVHub::Common::prepare_form(
1243+ form_attributes_href => {
1244+ name => 'category_editor_form',
1245+ params => $cgi,
1246+
1247+# TODO: listing the columns here is only while we're not using an html template,
1248+# so that CGI:FB knows what order to display 'em in:
1249+# fields => [qw/category_name aliases headings programs/]
1250+# TODO: remove the above 3 lines.
1251+ template => $EDIT_CATEGORIES_TEMPLATE
1252+ },
1253+ fields_href => \%category_field_defs
1254+ );
1255+
1256+ return $form;
1257+}
1258+
1259+# Returns a reference to an array of headings, e.g.:
1260+# [ "Arts/Culture/Entertainment", "Financial Education/Credit", ... ]
1261+sub get_all_headings {
1262+ my $dbh = MVHub::Utils::DB::get_dbh( $ENV{MV_CONFIG_FILE} );
1263+ my $sql = MVHub::Utils::DB::get_sql_select_statement(
1264+ 'HEADING_X_HEADING_NAME');
1265+ my $headings_aref = $dbh->selectcol_arrayref($sql);
1266+ return $headings_aref;
1267+}
1268+
1269+# Returns a reference to an array of program hashrefs, e.g.:
1270+# [
1271+# { 12345 => 'Youth Volleyball (YMCA)' },
1272+# { 156745 => 'Building Blocks (North Shore Arc)' },
1273+# ...
1274+# ]
1275+sub get_all_programs {
1276+ my $dbh = MVHub::Utils::DB::get_dbh( $ENV{MV_CONFIG_FILE} );
1277+
1278+ my $sql = MVHub::Utils::DB::get_sql_select_statement(
1279+ 'PROGRAM_X_PROGRAM_AGENCY_ID');
1280+
1281+ my $result_aref = $dbh->selectall_arrayref($sql);
1282+
1283+ # see end of 'perldoc -f map' on why the '+' in the following:
1284+ my @headings = map( +{ $_->[0] => $_->[1] }, @$result_aref );
1285+ return \@headings;
1286+}
1287+
1288+# The return statement tells all.
1289+sub fetch_existing_category_and_dependent_data_from_db {
1290+ my $category_id = shift;
1291+ my $dbh = MVHub::Utils::DB::get_dbh( $ENV{MV_CONFIG_FILE} );
1292+
1293+ my $sql = MVHub::Utils::DB::get_sql_select_statement(
1294+ 'CATEGORY_X_CATEGORY_NAME');
1295+ my @bind_values = ($category_id);
1296+
1297+ my @result
1298+ = @{ $dbh->selectall_arrayref( $sql, { Slice => {} }, @bind_values )
1299+ };
1300+ my $category_name = $result[0]->{category_name};
1301+
1302+ $sql = MVHub::Utils::DB::get_sql_select_statement('ALIAS_X_ALIAS_NAME');
1303+ my $aliases_aref
1304+ = $dbh->selectcol_arrayref( $sql, { Slice => {} }, $category_id );
1305+ my $aliases = join( "\n", @$aliases_aref );
1306+
1307+ $sql = MVHub::Utils::DB::get_sql_select_statement(
1308+ 'HEADING_CATEGORY_X_HEADING_NAME');
1309+ my $headings_aref
1310+ = $dbh->selectcol_arrayref( $sql, { Slice => {} }, $category_id );
1311+ MVHub::Utils::assert( @$headings_aref,
1312+ "Expected at least one heading for this category (#$category_id)" );
1313+
1314+ $sql = MVHub::Utils::DB::get_sql_select_statement(
1315+ 'PROGRAM_CATEGORY_X_PROGRAM_ID');
1316+ my $programs_aref
1317+ = $dbh->selectcol_arrayref( $sql, { Slice => {} }, $category_id );
1318+ return {
1319+ category_name => $category_name,
1320+ aliases => $aliases,
1321+ headings => $headings_aref,
1322+ programs => $programs_aref
1323+ };
1324+}
1325+
1326+sub submit_category_form_data_to_db {
1327+ my $values_href = shift;
1328+
1329+# Pre-processing data destined for database here:
1330+# TODO: get T:C installed or remove this line
1331+#$values_href->{category_name} = Text::Capitalize::$values_href->{category_name};
1332+
1333+ my $dbh = MVHub::Utils::DB::get_dbh( $ENV{MV_CONFIG_FILE} );
1334+ $dbh->begin_work();
1335+
1336+# If we have a category id, we must be updating an existing category.
1337+# In this case, in addition to updating the category record, we need to
1338+# get rid of the dependent alias, heading_category and program_category records
1339+# the user wants removed, update any changed ones, and add any new ones.
1340+# That's a lot of work. The easiest way is to just delete the existing
1341+# category record, whcih will cascade to delete the dependent records (because
1342+# of the way the foreign keys are defined on the those tables).
1343+# Then we insert everything, just as if we were creating a new category. The only
1344+# difference is that for an update we re-use the existing category_id, and
1345+# for a new category we'll make a new one.
1346+ if ( $values_href->{category_id} ) {
1347+ my $sql = MVHub::Utils::DB::get_sql_delete_statement(
1348+ 'CATEGORY_X_CATEGORY_ID');
1349+ $dbh->do( $sql, undef, $values_href->{category_id} );
1350+ }
1351+ else {
1352+ $values_href->{category_id}
1353+ = MVHub::Utils::DB::get_next_in_sequence( $dbh,
1354+ 'category_id_sequence' );
1355+ }
1356+ insert_category_and_dependent_data( dbh => $dbh, %$values_href );
1357+ $dbh->commit();
1358+}
1359+
1360+sub insert_category_and_dependent_data {
1361+ my %args = @_;
1362+ my $dbh = delete $args{dbh};
1363+ my $category_id = delete $args{category_id};
1364+ my $category_name = delete $args{category_name};
1365+ my $aliases_string = delete $args{aliases};
1366+ my $heading_names_aref = delete $args{headings};
1367+ my $program_ids_aref = delete $args{programs};
1368+ MVHub::Utils::assert( ( scalar keys %args ) == 0,
1369+ "Unknown arguments: @{[%args]}" );
1370+
1371+ MVHub::Utils::DB::insert_one_record(
1372+ dbh => $dbh,
1373+ table => 'category',
1374+ values_href => {
1375+ 'category_name' => $category_name,
1376+ 'stems_name' => MVHub::Common::get_stems($category_name),
1377+ 'category_id' => $category_id
1378+ }
1379+ );
1380+
1381+ if ($aliases_string) {
1382+
1383+ # TODO: are we guaranteed that the line separator is a \n ?
1384+ my @aliases = split( '\n', $aliases_string );
1385+
1386+ foreach my $alias (@aliases) {
1387+ $alias = MVHub::Utils::trim($alias);
1388+ next unless $alias; # ignore blank lines
1389+ MVHub::Utils::DB::insert_one_record(
1390+ dbh => $dbh,
1391+ table => 'alias',
1392+ values_href => {
1393+ 'category_id' => $category_id,
1394+ 'alias_name' => $alias,
1395+ 'stems_name' => MVHub::Common::get_stems($alias)
1396+ }
1397+ );
1398+ }
1399+ }
1400+
1401+ foreach my $heading_name (@$heading_names_aref) {
1402+ MVHub::Utils::DB::insert_one_record(
1403+ dbh => $dbh,
1404+ table => 'heading_category',
1405+ values_href => {
1406+ 'category_id' => $category_id,
1407+ 'heading_name' => $heading_name
1408+ }
1409+ );
1410+ }
1411+
1412+ foreach my $program_id (@$program_ids_aref) {
1413+ MVHub::Utils::DB::insert_one_record(
1414+ dbh => $dbh,
1415+ table => 'program_category',
1416+ values_href => {
1417+ 'category_id' => $category_id,
1418+ 'program_id' => $program_id
1419+ }
1420+ );
1421+ }
1422+}
1423+
1424+# This is the place for error checking that can't be done within
1425+# CGI::FormBuilder->validate(), e.g.:
1426+# - complex validations that require access to other variables
1427+# - validations that need to produce a custom error message
1428+# Returns 0 iff any validation fails, supplying an error message
1429+# for each failed validation in the custom_error-category_name template
1430+# variable on the form.
1431+sub validate_more {
1432+ my $form = shift;
1433+ my $return_val = 1;
1434+
1435+ if (is_category_name_taken(
1436+ category_name => $form->field('category_name'),
1437+ category_id => $form->field('category_id')
1438+ )
1439+ )
1440+ {
1441+ my $msg = "The category name you entered is "
1442+ . "already being used by another category. Please enter a different one:";
1443+ $form->tmpl_param( 'custom_error-category_name', $msg );
1444+
1445+ $return_val = 0;
1446+ }
1447+
1448+ return $return_val;
1449+}
1450+
1451+# Returns true iff the given category name is already in use
1452+# (without regard to case) in the database by another category. If a
1453+# a category_id argument is supplied, then any record with that ID
1454+# will be excluded from the search. Returns false otherwise.
1455+sub is_category_name_taken {
1456+ my %args = @_;
1457+ my $category_name = delete $args{'category_name'};
1458+ my $category_id = delete $args{'category_id'};
1459+ MVHub::Utils::assert( defined $category_name && $category_name ne "",
1460+ "Missing category_name argument" );
1461+ MVHub::Utils::assert( ( scalar keys %args ) == 0,
1462+ "Unknown arguments: @{[%args]}" );
1463+ my $dbh = MVHub::Utils::DB::get_dbh( $ENV{MV_CONFIG_FILE} );
1464+
1465+ # The SELECT EXISTS (SELECT 1 ...) syntax is generally faster than the
1466+ # SELECT count(*) syntax, since the former only scans until it finds
1467+ # the first match.
1468+ my @bind_variables = ( lc($category_name) );
1469+ my $sql = MVHub::Utils::DB::get_sql_select_statement(
1470+ 'CATEGORY_X_EXISTS_ONE_CATEGORY_NAME');
1471+
1472+ if ($category_id) {
1473+ @bind_variables = ( lc($category_name), $category_id );
1474+ $sql = MVHub::Utils::DB::get_sql_select_statement(
1475+ 'CATEGORY_X_EXISTS_ONE_CATEGORY_NAME_NOT_ID');
1476+ }
1477+
1478+ my @row = $dbh->selectrow_array( $sql, { Slice => {} }, @bind_variables );
1479+
1480+ return ( $row[0] );
1481+}
1482+
1483+# all good modules return 1;
1484+1;
1485
1486=== modified file 'lib-mvhub/lib/MVHub/Synonyms.pm'
1487--- lib-mvhub/lib/MVHub/Synonyms.pm 2010-09-13 00:58:44 +0000
1488+++ lib-mvhub/lib/MVHub/Synonyms.pm 2011-09-11 23:59:29 +0000
1489@@ -3,22 +3,10 @@
1490 use strict;
1491 use warnings;
1492
1493-# ---------------------------------------------------------------
1494-# Loads synonyms from the $SYNONYM_FILE (defined below) into the
1495-# database.
1496-#
1497-# This script must be run from the installed site (i.e. not from
1498-# your cvs projects path).
1499-#
1500-# Each line in the synonym file is a space-delimited list of one-word
1501-# synonyms
1502-#
1503-# Since the $SYNONYM_TABLE (below) has no dependents (i.e.
1504-# other tables that reference its data), this script may be re-run on
1505-# on an existing site.
1506-# The $SYNONYM_SEQUENCE (below) is reset to its initial value.
1507-#
1508-# ---------------------------------------------------------------
1509+use base 'Exporter';
1510+our @EXPORT_OK = qw(
1511+ fetch_synonyms_from_db
1512+);
1513
1514 use MVHub::Common;
1515
1516@@ -34,7 +22,7 @@
1517 # Returns a list of lines, where each line is a space delimited list of
1518 # one word synonyms of each other.
1519 sub fetch_synonyms_from_db {
1520- my $dbh = MVHub::Utils::DB::get_dbh();
1521+ my $dbh = MVHub::Utils::DB::get_dbh( $ENV{MV_CONFIG_FILE} );
1522
1523 my $sql = MVHub::Utils::DB::get_sql_select_statement(
1524 'SYNONYM_STEM_X_SUBMITTED_WORDS');
1525@@ -50,17 +38,19 @@
1526 # one word synonyms of each other.
1527 # Submits them into the database table, $SYNONYM_TABLE.
1528 # Dies on any database error.
1529+
1530+# **** DESTROYS ***** existing table
1531+
1532 sub submit_synonyms_to_db {
1533 my @synonym_lines = @_;
1534- my $dbh = MVHub::Utils::DB::get_dbh();
1535+ my $dbh = MVHub::Utils::DB::get_dbh( $ENV{MV_CONFIG_FILE} );
1536 $dbh->{AutoCommit} = 0;
1537
1538- my @bind_variables = ($SYNONYM_TABLE);
1539 my $sql
1540 = MVHub::Utils::DB::get_sql_delete_statement('SYNONYM_STEM_X_ALL');
1541- $dbh->do( $sql, undef, @bind_variables );
1542+ $dbh->do( $sql, undef, () );
1543
1544- @bind_variables = ( $SYNONYM_SEQUENCE, $SYNONYM_SEQUENCE_INIT_VAL );
1545+ my @bind_variables = ( $SYNONYM_SEQUENCE, $SYNONYM_SEQUENCE_INIT_VAL );
1546 $sql = MVHub::Utils::DB::get_sql_select_statement('SETVAL_X_SYNONYM_SEQ');
1547 $dbh->do( $sql, undef, @bind_variables );
1548
1549
1550=== modified file 'lib-mvhub/t/00-setup.t'
1551--- lib-mvhub/t/00-setup.t 2010-10-08 19:44:05 +0000
1552+++ lib-mvhub/t/00-setup.t 2011-09-11 23:59:29 +0000
1553@@ -9,7 +9,14 @@
1554
1555 use MVHub::Utils::DB qw/ get_dbh /;
1556
1557-use TestData qw/@agency @program/;
1558+use TestData qw/@agency
1559+ @program
1560+ @category
1561+ @program_category
1562+ @alias
1563+ @synonym_stem
1564+ @heading
1565+ @heading_category/;
1566 use TestHelper qw/ add_test_data create_sqlite_db /;
1567
1568 eval ' use Test::NoWarnings; ';
1569
1570=== modified file 'lib-mvhub/t/GuideSearch/expand_synonyms.t' (properties changed: -x to +x)
1571=== added directory 'lib-mvhub/t/Headings'
1572=== added file 'lib-mvhub/t/Headings/categories.t'
1573--- lib-mvhub/t/Headings/categories.t 1970-01-01 00:00:00 +0000
1574+++ lib-mvhub/t/Headings/categories.t 2011-09-11 23:59:29 +0000
1575@@ -0,0 +1,31 @@
1576+#!/usr/bin/perl
1577+
1578+use strict;
1579+use warnings;
1580+
1581+use Test::Deep;
1582+use Test::More tests => 3;
1583+
1584+use MVHub::Headings;
1585+
1586+use_ok('MVHub::Headings');
1587+
1588+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
1589+
1590+my $headings = MVHub::Headings->new();
1591+isa_ok( $headings, 'MVHub::Headings' );
1592+
1593+my $heading_name = 'Arts/Culture/Entertainment';
1594+my $real_categories = [
1595+ { category_id => 1006, category_name => 'Arts, Youth' },
1596+ { category_id => 1003, category_name => 'Arts/Entertainment' },
1597+ { category_id => 1001, category_name => 'Cultural Heritage' },
1598+ { category_id => 1007, category_name => 'Culture' },
1599+ { category_id => 1005, category_name => 'Museum' },
1600+ { category_id => 1002, category_name => 'Music' },
1601+ { category_id => 1004, category_name => 'Theater/Performance Art' }
1602+];
1603+
1604+my $categories = $headings->categories($heading_name);
1605+
1606+cmp_deeply( $categories, $real_categories, 'category arrays are same' );
1607
1608=== added file 'lib-mvhub/t/Headings/heading_names.t'
1609--- lib-mvhub/t/Headings/heading_names.t 1970-01-01 00:00:00 +0000
1610+++ lib-mvhub/t/Headings/heading_names.t 2011-09-11 23:59:29 +0000
1611@@ -0,0 +1,22 @@
1612+#!/usr/bin/perl
1613+
1614+use strict;
1615+use warnings;
1616+
1617+use Test::Deep;
1618+use Test::More tests => 3;
1619+
1620+use MVHub::Headings;
1621+
1622+use_ok('MVHub::Headings');
1623+
1624+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
1625+
1626+my $headings = MVHub::Headings->new();
1627+isa_ok( $headings, 'MVHub::Headings' );
1628+
1629+my $real_heading_names
1630+ = [ 'Arts/Culture/Entertainment', 'City/Town Services' ];
1631+my @heading_names = $headings->heading_names();
1632+
1633+is_deeply( \@heading_names, $real_heading_names, 'heading arrays are same' );
1634
1635=== modified file 'lib-mvhub/t/Notifications/set_reminder_level_in_db.t'
1636--- lib-mvhub/t/Notifications/set_reminder_level_in_db.t 2010-10-16 00:29:43 +0000
1637+++ lib-mvhub/t/Notifications/set_reminder_level_in_db.t 2011-09-11 23:59:29 +0000
1638@@ -64,7 +64,7 @@
1639 'PROGRAM_X_REMINDERS_SENT' );
1640 is_deeply(
1641 $dbh->selectcol_arrayref("SELECT reminders_sent FROM program"),
1642- [ 1, 2, 3, 4 ],
1643+ [ 1, 2, 3, 4, 4 ],
1644 $test_message
1645 );
1646
1647
1648=== added directory 'lib-mvhub/t/Structure'
1649=== added file 'lib-mvhub/t/Structure/display_category_chooser.t'
1650--- lib-mvhub/t/Structure/display_category_chooser.t 1970-01-01 00:00:00 +0000
1651+++ lib-mvhub/t/Structure/display_category_chooser.t 2011-09-11 23:59:29 +0000
1652@@ -0,0 +1,70 @@
1653+#!/usr/bin/perl
1654+
1655+use strict;
1656+use warnings;
1657+
1658+use CGI;
1659+use Test::More tests => 4;
1660+
1661+my ( @subs, $out, $result, $cgi );
1662+
1663+@subs = qw ( display_category_chooser );
1664+
1665+use_ok( 'MVHub::Structure', @subs );
1666+can_ok( 'MVHub::Structure', 'display_category_chooser' );
1667+
1668+close(STDOUT);
1669+open( STDOUT, ">", \$out ) or die "Can't open STDOUT: $!";
1670+
1671+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
1672+$ENV{SCRIPT_NAME} = 'structure.pl';
1673+
1674+$cgi = CGI->new();
1675+MVHub::Structure::display_category_chooser($cgi);
1676+
1677+#make output one line
1678+$out =~ s/\n//g;
1679+
1680+$result = check_categories_and_headings($out);
1681+ok( $result,
1682+ 'found expected strings in category chooser without success parameter' );
1683+$out = '';
1684+
1685+$cgi->param( 'success', '1' );
1686+MVHub::Structure::display_category_chooser($cgi);
1687+
1688+#make output one line
1689+$out =~ s/\n//g;
1690+
1691+$result = check_categories_and_headings($out);
1692+$result = $result && $out =~ /Your changes have been saved\./;
1693+ok( $result,
1694+ 'found expected strings in category chooser with success parameter' );
1695+
1696+sub check_categories_and_headings {
1697+
1698+ my $out = shift;
1699+
1700+ #categories
1701+ my $result = $out =~ /category_id=1001\">Cultural Heritage/;
1702+ $result = $result && $out =~ /category_id=1002\">Music/;
1703+ $result = $result && $out =~ /category_id=1003\">Arts \/ Entertainment/;
1704+ $result
1705+ = $result && $out =~ /category_id=1004\">Theater \/ Performance Art/;
1706+ $result = $result && $out =~ /category_id=1005\">Museum/;
1707+ $result = $result && $out =~ /category_id=1006\">Arts, Youth/;
1708+ $result = $result && $out =~ /category_id=1007\">Culture/;
1709+ $result = $result && $out =~ /category_id=1008\">Hotlines, City Services/;
1710+ $result = $result && $out =~ /category_id=1009\">Waste Management/;
1711+ $result = $result && $out =~ /category_id=1010\">Code Enforcement/;
1712+ $result = $result && $out !~ /Dance/; #Nonexist category
1713+
1714+ #headings
1715+ $result = $result && $out =~ /<h2>Arts\/Culture\/Entertainment<\/h2>/;
1716+ $result = $result && $out =~ /<h2>City\/Town Services<\/h2>/;
1717+ $result
1718+ = $result && $out !~ /<h2>Community\/Civi<\/h2>/; #Nonexist heading
1719+
1720+ return $result;
1721+
1722+}
1723
1724=== added file 'lib-mvhub/t/Structure/display_structure_menu.t'
1725--- lib-mvhub/t/Structure/display_structure_menu.t 1970-01-01 00:00:00 +0000
1726+++ lib-mvhub/t/Structure/display_structure_menu.t 2011-09-11 23:59:29 +0000
1727@@ -0,0 +1,60 @@
1728+#!/usr/bin/perl
1729+
1730+use strict;
1731+use warnings;
1732+
1733+use CGI;
1734+use Test::More tests => 4;
1735+
1736+my ( @subs, $out, $cgi, $result );
1737+
1738+@subs = qw ( display_structure_menu );
1739+
1740+use_ok( 'MVHub::Structure', @subs );
1741+can_ok( 'MVHub::Structure', 'display_structure_menu' );
1742+
1743+close(STDOUT);
1744+open( STDOUT, ">", \$out ) or die "Can't open STDOUT: $!";
1745+
1746+$cgi = CGI->new();
1747+MVHub::Structure::display_structure_menu($cgi);
1748+
1749+#make output one line
1750+$out =~ s/\n//g;
1751+
1752+$result = check_links_and_titles($out);
1753+ok( $result, 'found expected titles and links without success parameter' );
1754+$out = '';
1755+
1756+$cgi->param( 'success', '1' );
1757+MVHub::Structure::display_structure_menu($cgi);
1758+
1759+#make output one line
1760+$out =~ s/\n//g;
1761+
1762+$result = check_links_and_titles($out);
1763+$result = $result and $out =~ /Your changes have been saved\./;
1764+ok( $result, 'found expected titles and links with success parameter' );
1765+
1766+sub check_links_and_titles {
1767+
1768+ my $out = shift;
1769+
1770+ my $result = $out =~ /start structure_menu\.tmpl/;
1771+ $result = $result and $out =~ /Add\/Edit Synonyms/;
1772+ $result = $result and $out =~ /\/all\/css\/admin\.css/;
1773+ $result = $result and $out =~ /Configure the Directory\'s Structure/;
1774+ $result = $result and $out =~ /\/html\/admin\/help\/.shtml#search_config/;
1775+ $result = $result
1776+ and $out =~ /How to Configure the Program Category Search Engine/;
1777+ $result = $result
1778+ and $out
1779+ =~ /\/cgi-bin\/mvhub\/admin\/structure\.pl\?mode=category_chooser/;
1780+ $result = $result and $out =~ /Categories/;
1781+ $result = $result
1782+ and $out
1783+ =~ /\/cgi-bin\/mvhub\/admin\/structure\.pl\?mode=edit_synonyms/;
1784+ $result = $result and $out =~ /Headings/;
1785+
1786+ return $result;
1787+}
1788
1789=== added file 'lib-mvhub/t/Structure/fetch_existing_category_and_dependent_data_from_db.t'
1790--- lib-mvhub/t/Structure/fetch_existing_category_and_dependent_data_from_db.t 1970-01-01 00:00:00 +0000
1791+++ lib-mvhub/t/Structure/fetch_existing_category_and_dependent_data_from_db.t 2011-09-11 23:59:29 +0000
1792@@ -0,0 +1,95 @@
1793+#!/usr/bin/perl
1794+
1795+use strict;
1796+use warnings;
1797+
1798+use Test::Deep;
1799+use Test::More tests => 6;
1800+
1801+my @subs = qw( fetch_existing_category_and_dependent_data_from_db );
1802+
1803+use_ok( 'MVHub::Structure', @subs );
1804+can_ok( 'MVHub::Structure',
1805+ 'fetch_existing_category_and_dependent_data_from_db' );
1806+
1807+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
1808+
1809+my $category_id = 1001;
1810+my $data
1811+ = MVHub::Structure::fetch_existing_category_and_dependent_data_from_db(
1812+ $category_id);
1813+
1814+my $category_name = 'Cultural Heritage';
1815+my $aliases = join( "\n", (qw(History)) );
1816+my $headings = [qw(Arts/Culture/Entertainment)];
1817+my $programs = [qw(1001 1002 1003 1004 1005)];
1818+
1819+my $real_data = {
1820+ category_name => $category_name,
1821+ aliases => $aliases,
1822+ headings => $headings,
1823+ programs => $programs
1824+};
1825+
1826+cmp_deeply( $data, $real_data,
1827+ 'category and dependent data(with aliases and programs) hashes are same'
1828+);
1829+
1830+$category_id = 1010;
1831+$data = MVHub::Structure::fetch_existing_category_and_dependent_data_from_db(
1832+ $category_id);
1833+
1834+$category_name = 'Code Enforcement';
1835+$aliases = join( "\n", ('Code Violations') );
1836+$headings = ['City/Town Services'];
1837+$programs = [];
1838+
1839+$real_data = {
1840+ category_name => $category_name,
1841+ aliases => $aliases,
1842+ headings => $headings,
1843+ programs => $programs
1844+};
1845+
1846+cmp_deeply( $data, $real_data,
1847+ 'category and dependent data(with only aliases) hashes are same' );
1848+
1849+$category_id = 1004;
1850+$data = MVHub::Structure::fetch_existing_category_and_dependent_data_from_db(
1851+ $category_id);
1852+
1853+$category_name = 'Theater/Performance Art';
1854+$aliases = join( "\n", () );
1855+$headings = ['Arts/Culture/Entertainment'];
1856+$programs = [qw(1001 1002)];
1857+
1858+$real_data = {
1859+ category_name => $category_name,
1860+ aliases => $aliases,
1861+ headings => $headings,
1862+ programs => $programs
1863+};
1864+
1865+cmp_deeply( $data, $real_data,
1866+ 'category and dependent data(with only programs) hashes are same' );
1867+
1868+$category_id = 1008;
1869+$data = MVHub::Structure::fetch_existing_category_and_dependent_data_from_db(
1870+ $category_id);
1871+
1872+$category_name = 'Hotlines, City Services';
1873+$aliases = join( "\n", () );
1874+$headings = ['City/Town Services'];
1875+$programs = [];
1876+
1877+$real_data = {
1878+ category_name => $category_name,
1879+ aliases => $aliases,
1880+ headings => $headings,
1881+ programs => $programs
1882+};
1883+
1884+cmp_deeply( $data, $real_data,
1885+ 'category and dependent data(without aliases and programs) hashes are same'
1886+);
1887+
1888
1889=== added file 'lib-mvhub/t/Structure/get_all_headings.t'
1890--- lib-mvhub/t/Structure/get_all_headings.t 1970-01-01 00:00:00 +0000
1891+++ lib-mvhub/t/Structure/get_all_headings.t 2011-09-11 23:59:29 +0000
1892@@ -0,0 +1,19 @@
1893+#!/usr/bin/perl
1894+
1895+use strict;
1896+use warnings;
1897+
1898+use Test::Deep;
1899+use Test::More tests => 3;
1900+
1901+my @subs = qw( get_all_headings );
1902+
1903+use_ok( 'MVHub::Structure', @subs );
1904+can_ok( 'MVHub::Structure', 'get_all_headings' );
1905+
1906+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
1907+
1908+my @true_heading = ( 'Arts/Culture/Entertainment', 'City/Town Services' );
1909+my $heading = MVHub::Structure::get_all_headings();
1910+
1911+cmp_deeply( $heading, \@true_heading, 'all headings are ok and in order' );
1912
1913=== added file 'lib-mvhub/t/Structure/get_all_programs.t'
1914--- lib-mvhub/t/Structure/get_all_programs.t 1970-01-01 00:00:00 +0000
1915+++ lib-mvhub/t/Structure/get_all_programs.t 2011-09-11 23:59:29 +0000
1916@@ -0,0 +1,26 @@
1917+#!/usr/bin/perl
1918+
1919+use strict;
1920+use warnings;
1921+
1922+use Test::Deep;
1923+use Test::More tests => 3;
1924+
1925+my @subs = qw( get_all_programs );
1926+
1927+use_ok( 'MVHub::Structure', @subs );
1928+can_ok( 'MVHub::Structure', 'get_all_programs' );
1929+
1930+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
1931+
1932+my @true_program = (
1933+ { 1001 => 'Test Agency 1: Test Program 1' },
1934+ { 1002 => 'Test Agency 2: Test Program 2' },
1935+ { 1003 => 'Test Agency 3: Test Program 3' },
1936+ { 1004 => 'Test Agency 4: Test Program 4' },
1937+ { 1005 => 'Test Agency 4: Test Program 5' }
1938+);
1939+my $program = MVHub::Structure::get_all_programs();
1940+
1941+cmp_deeply( $program, \@true_program, 'all programs are ok and in order' );
1942+
1943
1944=== added file 'lib-mvhub/t/Structure/is_category_name_taken.t'
1945--- lib-mvhub/t/Structure/is_category_name_taken.t 1970-01-01 00:00:00 +0000
1946+++ lib-mvhub/t/Structure/is_category_name_taken.t 2011-09-11 23:59:29 +0000
1947@@ -0,0 +1,33 @@
1948+#!/usr/bin/perl
1949+
1950+use strict;
1951+use warnings;
1952+
1953+use Test::More tests => 6;
1954+
1955+my @subs = qw( is_category_name_taken );
1956+
1957+use_ok( 'MVHub::Structure', @subs );
1958+can_ok( 'MVHub::Structure', 'is_category_name_taken' );
1959+
1960+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
1961+
1962+my $result
1963+ = MVHub::Structure::is_category_name_taken( category_name => 'Culture' );
1964+is( $result, 1, 'is_category_name_taken_by_only_existing_category_name' );
1965+
1966+$result = MVHub::Structure::is_category_name_taken(
1967+ category_name => 'Non_existing_category_name' );
1968+is( $result, 0, 'is_category_name_taken_by_only_nonexisting_category_name' );
1969+
1970+$result = MVHub::Structure::is_category_name_taken(
1971+ category_name => 'Culture',
1972+ category_id => '1007'
1973+);
1974+is( $result, 0, 'NO, is_category_name_taken_with_category_name_and_id' );
1975+
1976+$result = MVHub::Structure::is_category_name_taken(
1977+ category_name => 'Culture',
1978+ category_id => '1008'
1979+);
1980+is( $result, 1, 'YES, is_category_name_taken_with_category_name_and_id' );
1981
1982=== added file 'lib-mvhub/t/Structure/process_category_form.t'
1983--- lib-mvhub/t/Structure/process_category_form.t 1970-01-01 00:00:00 +0000
1984+++ lib-mvhub/t/Structure/process_category_form.t 2011-09-11 23:59:29 +0000
1985@@ -0,0 +1,152 @@
1986+#!/usr/bin/perl
1987+
1988+use strict;
1989+use warnings;
1990+
1991+use CGI;
1992+use Test::Exception;
1993+use Test::More tests => 7;
1994+use Test::Trap;
1995+
1996+my ( $result, $cgi, $test_msg );
1997+
1998+use_ok( 'MVHub::Structure', qw/ process_category_form / );
1999+can_ok( 'MVHub::Structure', 'process_category_form' );
2000+
2001+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
2002+$ENV{SCRIPT_NAME} = 'structure.pl';
2003+
2004+#####
2005+$test_msg
2006+ = 'process_category_form() with no inputs returns existing categories';
2007+#####
2008+
2009+$cgi = CGI->new();
2010+trap {
2011+ MVHub::Structure::process_category_form($cgi);
2012+};
2013+$result = category_page_has_existing_categories( $trap->stdout() );
2014+ok( $result, $test_msg );
2015+
2016+#####
2017+$test_msg
2018+ = 'process_category_form() gets category_id parameter returns expected result';
2019+#####
2020+
2021+$cgi->param( 'category_id', '1001' );
2022+trap { MVHub::Structure::process_category_form($cgi); };
2023+
2024+$result = category_page_has_existing_headings_and_programs_aliseses(
2025+ $trap->stdout() );
2026+ok( $result, $test_msg );
2027+
2028+#####
2029+$test_msg = 'category form dies with non-numeric id:';
2030+####
2031+
2032+$cgi->delete('category_id');
2033+$cgi->param( 'category_id', 'non-numeric id' );
2034+dies_ok( sub { MVHub::Structure::process_category_form($cgi) }, $test_msg );
2035+
2036+####
2037+$test_msg = 'found expected string in successful new category creation';
2038+####
2039+TODO: {
2040+ local $TODO = "wait to resolve SEQUENCE / nextval() issue in sqlite ";
2041+
2042+ $cgi = prepare_cgi_for_successful_creation($cgi);
2043+ trap { MVHub::Structure::process_category_form($cgi); };
2044+
2045+ ok( $trap->stdout() =~ /Your changes have been saved\./xms, $test_msg );
2046+}
2047+
2048+####
2049+$test_msg = 'found redirect URL after successful edit category';
2050+####
2051+$cgi = prepare_cgi_for_successful_edit($cgi);
2052+trap { MVHub::Structure::process_category_form($cgi) };
2053+
2054+my $expected = q{Location:\s+structure.pl\?mode=structure_menu&success=1};
2055+ok( $trap->stdout() =~ /$expected/xms, $test_msg );
2056+
2057+##########
2058+
2059+sub prepare_cgi_for_successful_edit {
2060+
2061+ my $cgi = shift;
2062+ $cgi->delete_all();
2063+ $cgi->param( 'category_id', '1001' );
2064+ $cgi->param( 'category_name', 'Cultural Heritage' );
2065+ $cgi->param( 'aliases', 'History' );
2066+ $cgi->param( 'headings', 'Arts/Culture/Entertainment' );
2067+ $cgi->param( 'programs', '1001', '1002' );
2068+ $cgi->param( '_submitted_category_editor_form', '2' );
2069+
2070+ return $cgi;
2071+}
2072+
2073+sub prepare_cgi_for_successful_creation {
2074+
2075+ my $cgi = shift;
2076+ $cgi->delete_all();
2077+ $cgi->param( 'category_name', 'Dance' );
2078+ $cgi->param( 'aliases', 'dance' );
2079+ $cgi->param( 'headings', 'Arts/Culture/Entertainment' );
2080+ $cgi->param( 'programs', '1001', '1002' );
2081+ $cgi->param( '_submitted_category_editor_form', '2' );
2082+
2083+ return $cgi;
2084+}
2085+
2086+sub category_page_has_existing_headings_and_programs_aliseses {
2087+
2088+ my $out = shift;
2089+ my $match_str;
2090+ my $result = $out =~ /Edit a Category/;
2091+
2092+ #category
2093+ $result
2094+ &= $out =~ /category_name\" size.*type.*value=\"Cultural Heritage\"/;
2095+
2096+ #alias
2097+ $result &= $out =~ /History/;
2098+
2099+ #headings
2100+ $match_str = q{name="headings".*value="Arts/Culture/Entertainment};
2101+ $result &= $out =~ m|$match_str|xms;
2102+
2103+ #programs
2104+ $result &= $out
2105+ =~ /input checked=\"checked\" name=\"programs\".*value=\"1001\" \/> Test Agency 1: Test Program 1/;
2106+
2107+ $result &= $out
2108+ =~ /input checked=\"checked\" name=\"programs\".*value=\"1002\" \/> Test Agency 2: Test Program 2/;
2109+
2110+ $result &= $out =~ /name=\"programs\".*value=\"1003\"\s*\/>/xms;
2111+
2112+ $match_str = q{name="programs".*value="1004"\s*\/>\s*Test\s+Agency\s+4};
2113+ $result &= $out =~ /$match_str/xms;
2114+
2115+ return $result;
2116+
2117+}
2118+
2119+sub category_page_has_existing_categories {
2120+
2121+ my $out = shift;
2122+ my $result = $out =~ /Create a New Category/;
2123+
2124+ #headings
2125+ $result = $result && $out =~ /Arts\/Culture\/Entertainment/;
2126+ $result = $result && $out =~ /City\/Town Services/;
2127+
2128+ #programs
2129+ $result = $result && $out =~ /Test Program 1/;
2130+ $result = $result && $out =~ /Test Program 2/;
2131+ $result = $result && $out =~ /Test Program 3/;
2132+ $result = $result && $out =~ /Test Program 4/;
2133+ $result = $result && $out !~ /Test Porgram 5/; # Nonexist program
2134+
2135+ return $result;
2136+
2137+}
2138
2139=== added file 'lib-mvhub/t/Structure/process_synonym_form.t'
2140--- lib-mvhub/t/Structure/process_synonym_form.t 1970-01-01 00:00:00 +0000
2141+++ lib-mvhub/t/Structure/process_synonym_form.t 2011-09-11 23:59:29 +0000
2142@@ -0,0 +1,130 @@
2143+#!/usr/bin/perl
2144+
2145+use strict;
2146+use warnings;
2147+
2148+use CGI;
2149+use Test::More tests => 7;
2150+
2151+my ( @subs, $out, $cgi, $result, @synonyms, $new_synonym, $success_message );
2152+
2153+@subs = qw ( process_synonym_form );
2154+
2155+use_ok( 'MVHub::Structure', @subs );
2156+can_ok( 'MVHub::Structure', 'process_synonym_form' );
2157+
2158+close(STDOUT);
2159+open( STDOUT, ">", \$out ) or die "Can't open STDOUT: $!";
2160+
2161+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
2162+$ENV{SCRIPT_NAME} = 'structure.pl';
2163+
2164+@synonyms = (
2165+ 'ADD ADHD HADD',
2166+ 'alcohol drinking drunk',
2167+ 'assist help',
2168+ 'cessation quit',
2169+ 'child children kid youth',
2170+ 'class lesson',
2171+ 'computer internet technology',
2172+ 'counseling'
2173+);
2174+$new_synonym = 'search bing google';
2175+$success_message = qr /Your changes have been saved/;
2176+$cgi = CGI->new();
2177+
2178+MVHub::Structure::process_synonym_form($cgi);
2179+
2180+#make output one line
2181+$out =~ s/\n//g;
2182+$result = check_synonyms( $out, @synonyms );
2183+$result = $result && $out !~ qr/$new_synonym/;
2184+
2185+ok( $result, 'found expected synonyms before any processing' );
2186+
2187+TODO: {
2188+
2189+ local $TODO = 'Remove TODO block after sqlite sequence problem is solved';
2190+
2191+ $cgi = prepare_cgi( $cgi, @synonyms, $new_synonym );
2192+
2193+ eval {
2194+ MVHub::Structure::process_synonym_form($cgi);
2195+
2196+ $out =~ s/\n//g;
2197+ $result = $out =~ $success_message;
2198+ };
2199+ $TODO = 'fails because sequence query is used and not supported by sqlite'
2200+ if $@;
2201+ $result = 0 if $@;
2202+
2203+ ok( $result, 'submit synonyms with new synonym and see success message' );
2204+ $out = '';
2205+
2206+ $cgi->delete_all();
2207+
2208+ eval {
2209+ MVHub::Structure::process_synonym_form($cgi);
2210+
2211+ $out =~ s/\n//g;
2212+ $result = check_synonyms( $out, @synonyms );
2213+ $result = $result && $out =~ qr /$new_synonym/;
2214+ };
2215+
2216+ $TODO = 'fails because addition could not be done due to sequences';
2217+
2218+ ok( $result, 'found expected synonyms after addition' );
2219+ $out = '';
2220+
2221+ $cgi = prepare_cgi( $cgi, @synonyms );
2222+
2223+ eval {
2224+ MVHub::Structure::process_synonym_form($cgi);
2225+
2226+ $out =~ s/\n//g;
2227+ $result = $out =~ $success_message;
2228+ };
2229+ $TODO = 'fails because sequence query is used and not supported by sqlite'
2230+ if $@;
2231+
2232+ ok( $result,
2233+ 'submit synonyms without new synonym and see success message' );
2234+ $out = '';
2235+}
2236+
2237+#####
2238+$cgi->delete_all();
2239+MVHub::Structure::process_synonym_form($cgi);
2240+
2241+$out =~ s/\n//g;
2242+$result = check_synonyms( $out, @synonyms );
2243+$result = $result && $out !~ qr/$new_synonym/;
2244+
2245+ok( $result, 'found expected synonyms after deletion' );
2246+
2247+sub prepare_cgi {
2248+
2249+ my $cgi = shift;
2250+ my $new_synonym = shift;
2251+ $cgi->delete_all();
2252+ my $synonyms = join( "\n", @synonyms );
2253+ if ($new_synonym) {
2254+ $synonyms .= "\n" . $new_synonym;
2255+ }
2256+ $cgi->param( 'synonyms', $synonyms );
2257+ return $cgi;
2258+
2259+}
2260+
2261+sub check_synonyms {
2262+
2263+ my $out = shift;
2264+ my @synonyms = shift;
2265+
2266+ my $result = 1;
2267+ foreach my $synonym (@synonyms) {
2268+ $result = $result && $out =~ qr/$synonym/;
2269+ }
2270+
2271+ return $result;
2272+}
2273
2274=== added directory 'lib-mvhub/t/Synonyms'
2275=== added file 'lib-mvhub/t/Synonyms/fetch_synonyms_from_db.t'
2276--- lib-mvhub/t/Synonyms/fetch_synonyms_from_db.t 1970-01-01 00:00:00 +0000
2277+++ lib-mvhub/t/Synonyms/fetch_synonyms_from_db.t 2011-09-11 23:59:29 +0000
2278@@ -0,0 +1,29 @@
2279+#!/usr/bin/perl
2280+
2281+use strict;
2282+use warnings;
2283+use Test::More tests => 3;
2284+use Test::Deep;
2285+
2286+use MVHub::Synonyms;
2287+
2288+my @subs = qw( fetch_synonyms_from_db );
2289+
2290+use_ok( 'MVHub::Synonyms', @subs );
2291+can_ok( __PACKAGE__, 'fetch_synonyms_from_db' );
2292+
2293+$ENV{MV_CONFIG_FILE} = $ENV{MV_TEST_CONFIG_FILE};
2294+
2295+my @real_synonyms = (
2296+ 'ADD ADHD HADD',
2297+ 'alcohol drinking drunk',
2298+ 'assist help',
2299+ 'cessation quit',
2300+ 'child children kid youth',
2301+ 'class lesson',
2302+ 'computer internet technology',
2303+ 'counseling',
2304+);
2305+my @synonyms = MVHub::Synonyms::fetch_synonyms_from_db();
2306+
2307+cmp_deeply( \@synonyms, \@real_synonyms, 'synonym arrays are same' );
2308
2309=== modified file 'lib-mvhub/t/Utils/clean_cgi_params.t'
2310--- lib-mvhub/t/Utils/clean_cgi_params.t 2010-09-27 13:28:16 +0000
2311+++ lib-mvhub/t/Utils/clean_cgi_params.t 2011-09-11 23:59:29 +0000
2312@@ -1,8 +1,10 @@
2313 #!/usr/bin/perl -w
2314
2315 use strict;
2316+
2317+use CGI;
2318 use Test::More tests => 7;
2319-use CGI;
2320+
2321 use MVHub::Utils;
2322
2323 my $q = CGI->new();
2324@@ -64,7 +66,7 @@
2325
2326 ####
2327 $test_name
2328- = "multitple mixed parameters with same name are selectively cleaned";
2329+ = "multiple mixed parameters with same name are selectively cleaned";
2330 ####
2331 $q->param(
2332 mixed_multi => "clean",
2333
2334=== modified file 'lib-mvhub/t/lib/TestData.pm'
2335--- lib-mvhub/t/lib/TestData.pm 2010-10-16 00:29:43 +0000
2336+++ lib-mvhub/t/lib/TestData.pm 2011-09-11 23:59:29 +0000
2337@@ -9,7 +9,16 @@
2338 use Exporter ();
2339 our (@EXPORT_OK);
2340
2341- @EXPORT_OK = qw(@agency @program notifications_href);
2342+ @EXPORT_OK = qw(@agency
2343+ @program
2344+ notifications_href
2345+ @category
2346+ @program_category
2347+ @alias
2348+ @synonym_stem
2349+ @heading
2350+ @heading_category);
2351+
2352 }
2353 our @EXPORT_OK;
2354
2355@@ -17,7 +26,8 @@
2356 my $dev_user = $test_data_cfg->param('NOTIFICATION.dev_email');
2357
2358 our @agency = (
2359- { agency_id => '1001',
2360+ {
2361+ agency_id => '1001',
2362 agency_name => 'Test Agency 1',
2363 record_name => 'Test Agency 1 (Main Agency Record)',
2364 contact_first_name => 'Dan',
2365@@ -27,7 +37,8 @@
2366 last_updated => '2009-02-15',
2367 reminders_sent => '0',
2368 },
2369- { agency_id => '1002',
2370+ {
2371+ agency_id => '1002',
2372 agency_name => 'Test Agency 2',
2373 record_name => 'Test Agency 2 (Main Agency Record)',
2374 contact_first_name => 'Jane',
2375@@ -37,7 +48,8 @@
2376 last_updated => '2008-12-02',
2377 reminders_sent => '1',
2378 },
2379- { agency_id => '1003',
2380+ {
2381+ agency_id => '1003',
2382 agency_name => 'Test Agency 3',
2383 record_name => 'Test Agency 3 (Main Agency Record)',
2384 contact_first_name => 'Jack',
2385@@ -47,7 +59,8 @@
2386 last_updated => '2008-06-05',
2387 reminders_sent => '2',
2388 },
2389- { agency_id => '1004',
2390+ {
2391+ agency_id => '1004',
2392 agency_name => 'Test Agency 4',
2393 record_name => 'Test Agency 4 (Main Agency Record)',
2394 contact_first_name => 'Sue',
2395@@ -57,7 +70,8 @@
2396 last_updated => '2007-08-20',
2397 reminders_sent => '1',
2398 },
2399- { agency_id => '1005',
2400+ {
2401+ agency_id => '1005',
2402 agency_name => 'Test Agency 5',
2403 record_name => 'Test Agency 5 (Main Agency Record)',
2404 contact_first_name => 'Bob',
2405@@ -70,8 +84,9 @@
2406 );
2407
2408 our @program = (
2409- { agency_id => '1001',
2410- agency_name => 'Test Agency 1',
2411+ {
2412+ agency_id => '1001',
2413+ program_id => '1001',
2414 record_name => 'Test Program 1',
2415 contact_first_name => 'Joe',
2416 contact_last_name => 'Schmoe',
2417@@ -80,8 +95,9 @@
2418 last_updated => '2009-02-15',
2419 reminders_sent => '0',
2420 },
2421- { agency_id => '1002',
2422- agency_name => 'Test Agency 2',
2423+ {
2424+ agency_id => '1002',
2425+ program_id => '1002',
2426 record_name => 'Test Program 2',
2427 contact_first_name => 'Jane',
2428 contact_last_name => 'Lane',
2429@@ -90,8 +106,9 @@
2430 last_updated => '2008-12-02',
2431 reminders_sent => '0',
2432 },
2433- { agency_id => '1003',
2434- agency_name => 'Test Agency 3',
2435+ {
2436+ agency_id => '1003',
2437+ program_id => '1003',
2438 record_name => 'Test Program 3',
2439 contact_first_name => 'Jack',
2440 contact_last_name => 'Black',
2441@@ -100,8 +117,9 @@
2442 last_updated => '2008-12-02',
2443 reminders_sent => '2',
2444 },
2445- { agency_id => '1004',
2446- agency_name => 'Test Agency 4',
2447+ {
2448+ agency_id => '1004',
2449+ program_id => '1004',
2450 record_name => 'Test Program 4',
2451 contact_first_name => 'Sue',
2452 contact_last_name => 'Blue',
2453@@ -110,6 +128,17 @@
2454 last_updated => '2008-12-02',
2455 reminders_sent => '3',
2456 },
2457+ {
2458+ agency_id => '1004',
2459+ program_id => '1005',
2460+ record_name => 'Test Program 5',
2461+ contact_first_name => 'Lou',
2462+ contact_last_name => 'Blue',
2463+ contact_email => 'lou_blue@example.com',
2464+ email => 'lou_public@example.com',
2465+ last_updated => '2008-12-02',
2466+ reminders_sent => '2',
2467+ },
2468 );
2469
2470 our $notifications_href = {
2471@@ -119,7 +148,7 @@
2472 'contact_last_name' => 'Schmoe',
2473 'reminders_sent' => 0,
2474 'expired_record_list' =>
2475- [ 'Test Program 1', 'Test Agency 1 (Main Agency Record)' ],
2476+ [ 'Test Program 1', 'Test Agency 1 (Main Agency Record)' ],
2477 'agency_contact_email' => 'joe_agency@example.com',
2478 'contact_email' => $dev_user,
2479 'public_email' => 'joe_public@example.com',
2480@@ -132,7 +161,7 @@
2481 'contact_last_name' => 'Lane',
2482 'reminders_sent' => 1,
2483 'expired_record_list' =>
2484- [ 'Test Program 2', 'Test Agency 2 (Main Agency Record)' ],
2485+ [ 'Test Program 2', 'Test Agency 2 (Main Agency Record)' ],
2486 'agency_contact_email' => 'jane_agency@example.com',
2487 'contact_email' => $dev_user,
2488 'public_email' => 'jane_public@example.com',
2489@@ -145,7 +174,7 @@
2490 'contact_last_name' => 'Black',
2491 'reminders_sent' => 2,
2492 'expired_record_list' =>
2493- [ 'Test Program 3', 'Test Agency 3 (Main Agency Record)' ],
2494+ [ 'Test Program 3', 'Test Agency 3 (Main Agency Record)' ],
2495 'agency_contact_email' => 'jack_agency@example.com',
2496 'contact_email' => $dev_user,
2497 'public_email' => 'jack_public@example.com',
2498@@ -158,7 +187,7 @@
2499 'contact_last_name' => 'Blue',
2500 'reminders_sent' => 3,
2501 'expired_record_list' =>
2502- [ 'Test Program 4', 'Test Agency 4 (Main Agency Record)' ],
2503+ [ 'Test Program 4', 'Test Program 5', 'Test Agency 4 (Main Agency Record)' ],
2504 'agency_contact_email' => 'sue_agency@example.com',
2505 'contact_email' => $dev_user,
2506 'public_email' => 'sue_public@example.com',
2507@@ -180,5 +209,286 @@
2508 },
2509 };
2510
2511+our @category = (
2512+ {
2513+ category_id => '1001',
2514+ category_name => 'Cultural Heritage',
2515+ stems_name => 'cultur heritag',
2516+ },
2517+ {
2518+ category_id => '1002',
2519+ category_name => 'Music',
2520+ stems_name => 'music',
2521+ },
2522+ {
2523+ category_id => '1003',
2524+ category_name => 'Arts/Entertainment',
2525+ stems_name => 'entertain art',
2526+ },
2527+ {
2528+ category_id => '1004',
2529+ category_name => 'Theater/Performance Art',
2530+ stems_name => 'art perform theater',
2531+ },
2532+ {
2533+ category_id => '1005',
2534+ category_name => 'Museum',
2535+ stems_name => 'museum',
2536+ },
2537+ {
2538+ category_id => '1006',
2539+ category_name => 'Arts, Youth',
2540+ stems_name => 'youth art',
2541+ },
2542+ {
2543+ category_id => '1007',
2544+ category_name => 'Culture',
2545+ stems_name => 'cultur',
2546+ },
2547+ {
2548+ category_id => '1008',
2549+ category_name => 'Hotlines, City Services',
2550+ stems_name => 'servic hotlin citi',
2551+ },
2552+ {
2553+ category_id => '1009',
2554+ category_name => 'Waste Management',
2555+ stems_name => 'wast manag',
2556+ },
2557+ {
2558+ category_id => '1010',
2559+ category_name => 'Code Enforcement',
2560+ stems_name => 'enforc code',
2561+ },
2562+
2563+);
2564+our @program_category = (
2565+ {
2566+ category_id => '1001',
2567+ program_id => '1001',
2568+ },
2569+ {
2570+ category_id => '1002',
2571+ program_id => '1001',
2572+ },
2573+ {
2574+ category_id => '1003',
2575+ program_id => '1001',
2576+ },
2577+ {
2578+ category_id => '1004',
2579+ program_id => '1001',
2580+ },
2581+ {
2582+ category_id => '1005',
2583+ program_id => '1001',
2584+ },
2585+ {
2586+ category_id => '1001',
2587+ program_id => '1002',
2588+ },
2589+ {
2590+ category_id => '1002',
2591+ program_id => '1002',
2592+ },
2593+ {
2594+ category_id => '1003',
2595+ program_id => '1002',
2596+ },
2597+ {
2598+ category_id => '1004',
2599+ program_id => '1002',
2600+ },
2601+ {
2602+ category_id => '1001',
2603+ program_id => '1003',
2604+ },
2605+ {
2606+ category_id => '1002',
2607+ program_id => '1003',
2608+ },
2609+ {
2610+ category_id => '1003',
2611+ program_id => '1003',
2612+ },
2613+ {
2614+ category_id => '1001',
2615+ program_id => '1004',
2616+ },
2617+ {
2618+ category_id => '1002',
2619+ program_id => '1004',
2620+ },
2621+ {
2622+ category_id => '1001',
2623+ program_id => '1005',
2624+ },
2625+);
2626+
2627+our @alias = (
2628+ {
2629+ category_id => '1001',
2630+ alias_name => 'History',
2631+ stems_name => 'histori',
2632+ },
2633+ {
2634+ category_id => '1010',
2635+ alias_name => 'Code Violations',
2636+ stems_name => 'code violat',
2637+ },
2638+);
2639+
2640+our @synonym_stem = (
2641+ {
2642+ synonym_id => '1001',
2643+ word_stem => 'add',
2644+ submitted_word => 'ADD',
2645+ },
2646+ {
2647+ synonym_id => '1001',
2648+ word_stem => 'adhd',
2649+ submitted_word => 'ADHD',
2650+ },
2651+ {
2652+ synonym_id => '1001',
2653+ word_stem => 'hadd',
2654+ submitted_word => 'HADD',
2655+ },
2656+ {
2657+ synonym_id => '1002',
2658+ word_stem => 'alcohol',
2659+ submitted_word => 'alcohol',
2660+ },
2661+ {
2662+ synonym_id => '1002',
2663+ word_stem => 'drink',
2664+ submitted_word => 'drinking',
2665+ },
2666+ {
2667+ synonym_id => '1002',
2668+ word_stem => 'drunk',
2669+ submitted_word => 'drunk',
2670+ },
2671+ {
2672+ synonym_id => '1003',
2673+ word_stem => 'assist',
2674+ submitted_word => 'assist',
2675+ },
2676+ {
2677+ synonym_id => '1003',
2678+ word_stem => 'help',
2679+ submitted_word => 'help',
2680+ },
2681+ {
2682+ synonym_id => '1004',
2683+ word_stem => 'cessat',
2684+ submitted_word => 'cessation',
2685+ },
2686+ {
2687+ synonym_id => '1004',
2688+ word_stem => 'quit',
2689+ submitted_word => 'quit',
2690+ },
2691+ {
2692+ synonym_id => '1005',
2693+ word_stem => 'child',
2694+ submitted_word => 'child',
2695+ },
2696+ {
2697+ synonym_id => '1005',
2698+ word_stem => 'children',
2699+ submitted_word => 'children',
2700+ },
2701+ {
2702+ synonym_id => '1005',
2703+ word_stem => 'kid',
2704+ submitted_word => 'kid',
2705+ },
2706+ {
2707+ synonym_id => '1005',
2708+ word_stem => 'youth',
2709+ submitted_word => 'youth',
2710+ },
2711+ {
2712+ synonym_id => '1006',
2713+ word_stem => 'class',
2714+ submitted_word => 'class',
2715+ },
2716+ {
2717+ synonym_id => '1006',
2718+ word_stem => 'lesson',
2719+ submitted_word => 'lesson',
2720+ },
2721+ {
2722+ synonym_id => '1007',
2723+ word_stem => 'comput',
2724+ submitted_word => 'computer',
2725+ },
2726+ {
2727+ synonym_id => '1007',
2728+ word_stem => 'technologi',
2729+ submitted_word => 'technology',
2730+ },
2731+ {
2732+ synonym_id => '1007',
2733+ word_stem => 'internet',
2734+ submitted_word => 'internet',
2735+ },
2736+ {
2737+ synonym_id => '1008',
2738+ word_stem => 'counsel',
2739+ submitted_word => 'counseling',
2740+ },
2741+);
2742+
2743+our @heading = (
2744+ { heading_name => 'Arts/Culture/Entertainment', },
2745+ { heading_name => 'City/Town Services', },
2746+);
2747+
2748+our @heading_category = (
2749+ {
2750+ category_id => '1001',
2751+ heading_name => 'Arts/Culture/Entertainment',
2752+ },
2753+ {
2754+ category_id => '1002',
2755+ heading_name => 'Arts/Culture/Entertainment',
2756+ },
2757+ {
2758+ category_id => '1003',
2759+ heading_name => 'Arts/Culture/Entertainment',
2760+ },
2761+ {
2762+ category_id => '1004',
2763+ heading_name => 'Arts/Culture/Entertainment',
2764+ },
2765+ {
2766+ category_id => '1005',
2767+ heading_name => 'Arts/Culture/Entertainment',
2768+ },
2769+ {
2770+ category_id => '1006',
2771+ heading_name => 'Arts/Culture/Entertainment',
2772+ },
2773+ {
2774+ category_id => '1007',
2775+ heading_name => 'Arts/Culture/Entertainment',
2776+ },
2777+ {
2778+ category_id => '1008',
2779+ heading_name => 'City/Town Services',
2780+ },
2781+ {
2782+ category_id => '1009',
2783+ heading_name => 'City/Town Services',
2784+ },
2785+ {
2786+ category_id => '1010',
2787+ heading_name => 'City/Town Services',
2788+ },
2789+
2790+);
2791+
2792 # all good modules return true 1;
2793 1
2794
2795=== modified file 'lib-mvhub/t/lib/TestHelper.pm'
2796--- lib-mvhub/t/lib/TestHelper.pm 2011-03-31 16:31:33 +0000
2797+++ lib-mvhub/t/lib/TestHelper.pm 2011-09-11 23:59:29 +0000
2798@@ -20,6 +20,7 @@
2799 add_test_data
2800 create_sqlite_db
2801 create_temp_auth
2802+ get_admin_username_password();
2803 get_files
2804 get_files_from
2805 get_host_to_check
2806@@ -33,8 +34,6 @@
2807 submit_forms_ok
2808 );
2809
2810-our ($VERSION) = '$Revision: 1647 $' =~ /([.\d]+)/;
2811-
2812 sub add_test_data {
2813 my $dbh = shift or croak "missing parameter: \$dbh\n";
2814 my $sql = MVHub::Utils::DB::get_sql_insert_statement(
2815@@ -59,6 +58,57 @@
2816 );
2817 $dbh->do( $sql, undef, @bind_variables );
2818 }
2819+ $sql = MVHub::Utils::DB::get_sql_insert_statement(
2820+ 'CATEGORY_X_REQUIRED_FIELDS');
2821+ foreach my $test_data (@TestData::category) {
2822+ @bind_variables = (
2823+ $test_data->{'category_id'},
2824+ $test_data->{'category_name'},
2825+ $test_data->{'stems_name'}
2826+ );
2827+ $dbh->do( $sql, undef, @bind_variables );
2828+ }
2829+ $sql = MVHub::Utils::DB::get_sql_insert_statement(
2830+ 'PROGRAM_CATEGORY_X_REQUIRED_FIELDS');
2831+ foreach my $test_data (@TestData::program_category) {
2832+ @bind_variables
2833+ = ( $test_data->{'program_id'}, $test_data->{'category_id'} );
2834+ $dbh->do( $sql, undef, @bind_variables );
2835+ }
2836+ $sql = MVHub::Utils::DB::get_sql_insert_statement(
2837+ 'ALIAS_X_REQUIRED_FIELDS');
2838+ foreach my $test_data (@TestData::alias) {
2839+ @bind_variables = (
2840+ $test_data->{'category_id'},
2841+ $test_data->{'alias_name'},
2842+ $test_data->{'stems_name'}
2843+ );
2844+ $dbh->do( $sql, undef, @bind_variables );
2845+ }
2846+ $sql = MVHub::Utils::DB::get_sql_insert_statement(
2847+ 'SYNONYM_STEM_X_REQUIRED_FIELDS');
2848+ foreach my $test_data (@TestData::synonym_stem) {
2849+ @bind_variables = (
2850+ $test_data->{'synonym_id'},
2851+ $test_data->{'word_stem'},
2852+ $test_data->{'submitted_word'}
2853+ );
2854+ $dbh->do( $sql, undef, @bind_variables );
2855+ }
2856+ $sql = MVHub::Utils::DB::get_sql_insert_statement(
2857+ 'HEADING_X_REQUIRED_FIELDS');
2858+ foreach my $test_data (@TestData::heading) {
2859+ @bind_variables = ( $test_data->{'heading_name'} );
2860+ $dbh->do( $sql, undef, @bind_variables );
2861+ }
2862+ $sql = MVHub::Utils::DB::get_sql_insert_statement(
2863+ 'HEADING_CATEGORY_X_REQUIRED_FIELDS');
2864+ foreach my $test_data (@TestData::heading_category) {
2865+ @bind_variables
2866+ = ( $test_data->{'category_id'}, $test_data->{'heading_name'} );
2867+ $dbh->do( $sql, undef, @bind_variables );
2868+ }
2869+
2870 }
2871
2872 sub create_sqlite_db {
2873@@ -72,6 +122,7 @@
2874 my $sql = $sql_lib->retr($sql_label);
2875 $dbh->do($sql);
2876 }
2877+
2878 return 1;
2879 }
2880
2881@@ -85,6 +136,12 @@
2882 return $auth;
2883 }
2884
2885+sub get_admin_username_password {
2886+
2887+ return qw/ test test /;
2888+
2889+}
2890+
2891 # accept a list of wild card file patterns
2892 # return a list (array) of all files in project directory matching
2893 # examples:

Subscribers

People subscribed via source and target branches