<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://wiki.eprints.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Gobfrey</id>
	<title>EPrints Documentation - User contributions [en-gb]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.eprints.org/w/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Gobfrey"/>
	<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/Special:Contributions/Gobfrey"/>
	<updated>2026-04-11T12:08:47Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.31.8</generator>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6144</id>
		<title>Adding new views</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6144"/>
		<updated>2008-06-24T13:39:09Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* tags */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{development}}&lt;br /&gt;
&lt;br /&gt;
Browse views provide a way for visitors to your site to discover relevant content without a specific item in mind (for example, browsing all the content associated with a particular topic). Visitors arriving directly at the page for a specific item in the repository (for example, via a search engine) also use views you have defined to discover related content. &lt;br /&gt;
&lt;br /&gt;
There are two default views in EPrints - '''By Year''' and '''By Subject'''. This guide describes how to add additional views to your repository.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
===The basics===&lt;br /&gt;
&lt;br /&gt;
The views for your repository are defined in the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Open this file and find the browse_views configuration setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;-date;res=year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
The views are defined using a special (Perl) syntax: the view definition consists of a pair of curly braces (''note the comma after each closing brace'') enclosing a list of property/value pairs (note ''the comma'' after each line).&lt;br /&gt;
&lt;br /&gt;
The key part of the view definition is the '''fields''' property. This names the metadata field (or fields) that EPrints will use to construct the view. For example, for the '''By Year''' view, EPrints groups the records in the repository according to their '''date''' (note that the '''res=year''' suffix tells EPrints to only consider the year part), and constructs a Web page for each date listing the records. Similarly, the '''Browse by Subjscts''' view, groups the records according to the ''subjects'' they have been assigned to (a record may appear in more than one group!).&lt;br /&gt;
&lt;br /&gt;
Both the '''Browse by Year''' and '''Browse by Subject''' views are constructed using the values of a single field ('''date''' and '''subjects''' respectively).&lt;br /&gt;
&lt;br /&gt;
It is also possible to construct a view using the ''combined'' values of two or more fields (eg. group records by author '''and''' editor), or even using a sequence of two or more fields (eg. group records by journal title '''and then''' by volume number).&lt;br /&gt;
&lt;br /&gt;
===Worked example: browse by organisational structure===&lt;br /&gt;
&lt;br /&gt;
By default, EPrints has a ''divisions'' metadata field which allows authors to associate their deposits with the divisions (units, faculties, schools, departments, institutes, centres..) that were involved in producing their item (for example, the author's department, and the departments of any co-authors). This worked example allows visitors to browse the repository content by division.&lt;br /&gt;
&lt;br /&gt;
Open the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
                hideempty =&amp;gt; 1,&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
Save the file and generate the new view pages (this will also re-generate any existing views defined in the views configuration file):&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
Open the view page in a Web browser:&lt;br /&gt;
&lt;br /&gt;
 http://your.repository.url/view/&lt;br /&gt;
&lt;br /&gt;
The view page lists all the available views. You should see your new views on the list:&lt;br /&gt;
&lt;br /&gt;
[[Image:View_page2.png|frame|none|The view page lists available views]]&lt;br /&gt;
&lt;br /&gt;
'''Fixing the undefined phrase warning''' The new view may appear on the views page with an ''undefined phrase'' warning (you may also notice a similar warning message when running generate_views):&lt;br /&gt;
&lt;br /&gt;
 [&amp;quot;viewname_eprint_divisions&amp;quot; not defined]&lt;br /&gt;
&lt;br /&gt;
Each view you create needs to be assigned a ''human-readable'' name, which EPrints will use on the view Web pages.&lt;br /&gt;
&lt;br /&gt;
Edit the language-specific phrases file for view names:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/lang/en/phrases/views.xml&lt;br /&gt;
&lt;br /&gt;
Add an appropriate phrase which describes the new view, for example:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;epp:phrase id=&amp;quot;viewname_eprint_divisions&amp;quot;&amp;gt;Division&amp;lt;/epp:phrase&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Save the phrases file and regenerate the view pages:&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
===Example view definitions===&lt;br /&gt;
&lt;br /&gt;
====Browse by type====&lt;br /&gt;
&lt;br /&gt;
Every deposit in EPrints has a type (article, book, thesis...). To allow visitors to browse your repository content by type, add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id =&amp;gt; &amp;quot;types&amp;quot;,&lt;br /&gt;
        fields =&amp;gt; &amp;quot;type&amp;quot;,&lt;br /&gt;
        order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
====Browse by author====&lt;br /&gt;
&lt;br /&gt;
===Example views (combined fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by author and editor====&lt;br /&gt;
&lt;br /&gt;
===Example views (multiple fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by journal title, then by volume====&lt;br /&gt;
&lt;br /&gt;
This example lets visitors browse the journals items in your repository have been published in, and then volumes within each journal.&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id=&amp;gt;&amp;quot;journal_volume&amp;quot;,&lt;br /&gt;
        fields=&amp;gt;&amp;quot;publication,volume&amp;quot;,&lt;br /&gt;
        order=&amp;gt;&amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal_volume.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Linking in your view===&lt;br /&gt;
&lt;br /&gt;
You now need to add a link to your repository pages which takes visitors directly to your new view, or to the views page from where they can access all available views.&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_navbar.png]]&lt;br /&gt;
&lt;br /&gt;
====Generating CVs etc====&lt;br /&gt;
&lt;br /&gt;
===Linking items back to views===&lt;br /&gt;
===Views as collections===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===New options in EPrints 3.1===&lt;br /&gt;
&lt;br /&gt;
====subfield no longer supported====&lt;br /&gt;
&lt;br /&gt;
The subfield option is no longer supported in EPrints 3.1.&lt;br /&gt;
&lt;br /&gt;
====new_column_at====&lt;br /&gt;
&lt;br /&gt;
This is an array of integers representing the number of items in a view list before another column is added.  For example:&lt;br /&gt;
&lt;br /&gt;
 [ 10 ]&lt;br /&gt;
&lt;br /&gt;
This would have one column of values until there were 11, then there would be 2 columns.&lt;br /&gt;
&lt;br /&gt;
 [ 10, 10 ]&lt;br /&gt;
&lt;br /&gt;
This would have one column if there were ten or less values, two columns if there were between eleven and twenty (ten + ten) values, and three columns for all other cases.&lt;br /&gt;
&lt;br /&gt;
 [ 0, 0 ]&lt;br /&gt;
&lt;br /&gt;
This would always have three columns.&lt;br /&gt;
&lt;br /&gt;
Add one to the number of integers in the array and you get the maximum number of columns.  The value of each integer defines the point at which that column becomes full, and more values cause an 'overflow' into the next column.&lt;br /&gt;
&lt;br /&gt;
====variations====&lt;br /&gt;
&lt;br /&gt;
This controls the various ways in which a browse view can be subheaded.  It consists of a list of strings.  Each string is the name of a non-compound metadata field (or the keyword DEFAULT, for an unsubheaded list), optionally followed by a semi-colon and a comma separated list of options.  E.G:&lt;br /&gt;
&lt;br /&gt;
 variations =&amp;gt; [&lt;br /&gt;
  &amp;quot;creators_name;first_letter&amp;quot;,&lt;br /&gt;
  &amp;quot;type&amp;quot;,&lt;br /&gt;
  &amp;quot;DEFAULT&amp;quot;&lt;br /&gt;
 ],&lt;br /&gt;
&lt;br /&gt;
The following options are available:&lt;br /&gt;
&lt;br /&gt;
=====reverse=====&lt;br /&gt;
&lt;br /&gt;
Reverses the order in which the groupings are shown.  Default is the ordervalue for that field (usually alphanumeric).  Useful for dates as you may want the highest values first.&lt;br /&gt;
&lt;br /&gt;
=====filename=====&lt;br /&gt;
&lt;br /&gt;
Changes the filename of the view variation.  The default is the name of the metadata field used, so if two variations use the same metadata field with different options, this is needed.&lt;br /&gt;
&lt;br /&gt;
 filename=different_filename&lt;br /&gt;
&lt;br /&gt;
=====first_value=====&lt;br /&gt;
&lt;br /&gt;
If a field is multiple, only use the first value.  Otherwise each item will appear once for each value.&lt;br /&gt;
&lt;br /&gt;
=====first_initial=====&lt;br /&gt;
&lt;br /&gt;
If using a name, truncate the given name to the first initial.  This will make items like &amp;quot;Les Carr&amp;quot; and &amp;quot;Leslie Carr&amp;quot; appear together.  Note it will also make &amp;quot;John Smith&amp;quot; and &amp;quot;Jake Smith&amp;quot; appear together too, showing that you really never can win.&lt;br /&gt;
&lt;br /&gt;
=====first_letter=====&lt;br /&gt;
&lt;br /&gt;
The same as 'truncate=1'&lt;br /&gt;
&lt;br /&gt;
=====truncate=====&lt;br /&gt;
&lt;br /&gt;
Use the first X characters of a value to group by.  truncate=4 may be useful for dates as it will group by the first four digits (the year) only.&lt;br /&gt;
&lt;br /&gt;
 truncate=4&lt;br /&gt;
&lt;br /&gt;
=====tags=====&lt;br /&gt;
&lt;br /&gt;
Useful for fields like keywords where values may be separated by commas or semi-colons.  The value is split on these two characters ( , and ; ) and a heading is created for each.&lt;br /&gt;
&lt;br /&gt;
=====cloud=====&lt;br /&gt;
&lt;br /&gt;
Creates a tag cloud.  Sets jump to 'plain', cloudmax to 200, cloudmin to 80 and no_separator, then resizes the jump-to links according to frequency of use.&lt;br /&gt;
&lt;br /&gt;
=====cloudmax=====&lt;br /&gt;
&lt;br /&gt;
The % size of the largest tag in a tag cloud.&lt;br /&gt;
&lt;br /&gt;
=====cloudmin=====&lt;br /&gt;
&lt;br /&gt;
The % size of the smallest tag in a tag cloud.&lt;br /&gt;
&lt;br /&gt;
=====jump=====&lt;br /&gt;
&lt;br /&gt;
  jump=plain&lt;br /&gt;
&lt;br /&gt;
Turns of the 'jump to' text before the list of subheading navigation links.&lt;br /&gt;
&lt;br /&gt;
=====no_seperator (sic)=====&lt;br /&gt;
&lt;br /&gt;
Turns of the separator between each subheading navigation link (by default '|').&lt;br /&gt;
&lt;br /&gt;
=====string=====&lt;br /&gt;
&lt;br /&gt;
Uses values 'as is'.  No ordervalues, no phrases.&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6143</id>
		<title>Adding new views</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6143"/>
		<updated>2008-06-24T12:47:31Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* no_seperator */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{development}}&lt;br /&gt;
&lt;br /&gt;
Browse views provide a way for visitors to your site to discover relevant content without a specific item in mind (for example, browsing all the content associated with a particular topic). Visitors arriving directly at the page for a specific item in the repository (for example, via a search engine) also use views you have defined to discover related content. &lt;br /&gt;
&lt;br /&gt;
There are two default views in EPrints - '''By Year''' and '''By Subject'''. This guide describes how to add additional views to your repository.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
===The basics===&lt;br /&gt;
&lt;br /&gt;
The views for your repository are defined in the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Open this file and find the browse_views configuration setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;-date;res=year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
The views are defined using a special (Perl) syntax: the view definition consists of a pair of curly braces (''note the comma after each closing brace'') enclosing a list of property/value pairs (note ''the comma'' after each line).&lt;br /&gt;
&lt;br /&gt;
The key part of the view definition is the '''fields''' property. This names the metadata field (or fields) that EPrints will use to construct the view. For example, for the '''By Year''' view, EPrints groups the records in the repository according to their '''date''' (note that the '''res=year''' suffix tells EPrints to only consider the year part), and constructs a Web page for each date listing the records. Similarly, the '''Browse by Subjscts''' view, groups the records according to the ''subjects'' they have been assigned to (a record may appear in more than one group!).&lt;br /&gt;
&lt;br /&gt;
Both the '''Browse by Year''' and '''Browse by Subject''' views are constructed using the values of a single field ('''date''' and '''subjects''' respectively).&lt;br /&gt;
&lt;br /&gt;
It is also possible to construct a view using the ''combined'' values of two or more fields (eg. group records by author '''and''' editor), or even using a sequence of two or more fields (eg. group records by journal title '''and then''' by volume number).&lt;br /&gt;
&lt;br /&gt;
===Worked example: browse by organisational structure===&lt;br /&gt;
&lt;br /&gt;
By default, EPrints has a ''divisions'' metadata field which allows authors to associate their deposits with the divisions (units, faculties, schools, departments, institutes, centres..) that were involved in producing their item (for example, the author's department, and the departments of any co-authors). This worked example allows visitors to browse the repository content by division.&lt;br /&gt;
&lt;br /&gt;
Open the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
                hideempty =&amp;gt; 1,&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
Save the file and generate the new view pages (this will also re-generate any existing views defined in the views configuration file):&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
Open the view page in a Web browser:&lt;br /&gt;
&lt;br /&gt;
 http://your.repository.url/view/&lt;br /&gt;
&lt;br /&gt;
The view page lists all the available views. You should see your new views on the list:&lt;br /&gt;
&lt;br /&gt;
[[Image:View_page2.png|frame|none|The view page lists available views]]&lt;br /&gt;
&lt;br /&gt;
'''Fixing the undefined phrase warning''' The new view may appear on the views page with an ''undefined phrase'' warning (you may also notice a similar warning message when running generate_views):&lt;br /&gt;
&lt;br /&gt;
 [&amp;quot;viewname_eprint_divisions&amp;quot; not defined]&lt;br /&gt;
&lt;br /&gt;
Each view you create needs to be assigned a ''human-readable'' name, which EPrints will use on the view Web pages.&lt;br /&gt;
&lt;br /&gt;
Edit the language-specific phrases file for view names:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/lang/en/phrases/views.xml&lt;br /&gt;
&lt;br /&gt;
Add an appropriate phrase which describes the new view, for example:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;epp:phrase id=&amp;quot;viewname_eprint_divisions&amp;quot;&amp;gt;Division&amp;lt;/epp:phrase&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Save the phrases file and regenerate the view pages:&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
===Example view definitions===&lt;br /&gt;
&lt;br /&gt;
====Browse by type====&lt;br /&gt;
&lt;br /&gt;
Every deposit in EPrints has a type (article, book, thesis...). To allow visitors to browse your repository content by type, add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id =&amp;gt; &amp;quot;types&amp;quot;,&lt;br /&gt;
        fields =&amp;gt; &amp;quot;type&amp;quot;,&lt;br /&gt;
        order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
====Browse by author====&lt;br /&gt;
&lt;br /&gt;
===Example views (combined fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by author and editor====&lt;br /&gt;
&lt;br /&gt;
===Example views (multiple fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by journal title, then by volume====&lt;br /&gt;
&lt;br /&gt;
This example lets visitors browse the journals items in your repository have been published in, and then volumes within each journal.&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id=&amp;gt;&amp;quot;journal_volume&amp;quot;,&lt;br /&gt;
        fields=&amp;gt;&amp;quot;publication,volume&amp;quot;,&lt;br /&gt;
        order=&amp;gt;&amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal_volume.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Linking in your view===&lt;br /&gt;
&lt;br /&gt;
You now need to add a link to your repository pages which takes visitors directly to your new view, or to the views page from where they can access all available views.&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_navbar.png]]&lt;br /&gt;
&lt;br /&gt;
====Generating CVs etc====&lt;br /&gt;
&lt;br /&gt;
===Linking items back to views===&lt;br /&gt;
===Views as collections===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===New options in EPrints 3.1===&lt;br /&gt;
&lt;br /&gt;
====subfield no longer supported====&lt;br /&gt;
&lt;br /&gt;
The subfield option is no longer supported in EPrints 3.1.&lt;br /&gt;
&lt;br /&gt;
====new_column_at====&lt;br /&gt;
&lt;br /&gt;
This is an array of integers representing the number of items in a view list before another column is added.  For example:&lt;br /&gt;
&lt;br /&gt;
 [ 10 ]&lt;br /&gt;
&lt;br /&gt;
This would have one column of values until there were 11, then there would be 2 columns.&lt;br /&gt;
&lt;br /&gt;
 [ 10, 10 ]&lt;br /&gt;
&lt;br /&gt;
This would have one column if there were ten or less values, two columns if there were between eleven and twenty (ten + ten) values, and three columns for all other cases.&lt;br /&gt;
&lt;br /&gt;
 [ 0, 0 ]&lt;br /&gt;
&lt;br /&gt;
This would always have three columns.&lt;br /&gt;
&lt;br /&gt;
Add one to the number of integers in the array and you get the maximum number of columns.  The value of each integer defines the point at which that column becomes full, and more values cause an 'overflow' into the next column.&lt;br /&gt;
&lt;br /&gt;
====variations====&lt;br /&gt;
&lt;br /&gt;
This controls the various ways in which a browse view can be subheaded.  It consists of a list of strings.  Each string is the name of a non-compound metadata field (or the keyword DEFAULT, for an unsubheaded list), optionally followed by a semi-colon and a comma separated list of options.  E.G:&lt;br /&gt;
&lt;br /&gt;
 variations =&amp;gt; [&lt;br /&gt;
  &amp;quot;creators_name;first_letter&amp;quot;,&lt;br /&gt;
  &amp;quot;type&amp;quot;,&lt;br /&gt;
  &amp;quot;DEFAULT&amp;quot;&lt;br /&gt;
 ],&lt;br /&gt;
&lt;br /&gt;
The following options are available:&lt;br /&gt;
&lt;br /&gt;
=====reverse=====&lt;br /&gt;
&lt;br /&gt;
Reverses the order in which the groupings are shown.  Default is the ordervalue for that field (usually alphanumeric).  Useful for dates as you may want the highest values first.&lt;br /&gt;
&lt;br /&gt;
=====filename=====&lt;br /&gt;
&lt;br /&gt;
Changes the filename of the view variation.  The default is the name of the metadata field used, so if two variations use the same metadata field with different options, this is needed.&lt;br /&gt;
&lt;br /&gt;
 filename=different_filename&lt;br /&gt;
&lt;br /&gt;
=====first_value=====&lt;br /&gt;
&lt;br /&gt;
If a field is multiple, only use the first value.  Otherwise each item will appear once for each value.&lt;br /&gt;
&lt;br /&gt;
=====first_initial=====&lt;br /&gt;
&lt;br /&gt;
If using a name, truncate the given name to the first initial.  This will make items like &amp;quot;Les Carr&amp;quot; and &amp;quot;Leslie Carr&amp;quot; appear together.  Note it will also make &amp;quot;John Smith&amp;quot; and &amp;quot;Jake Smith&amp;quot; appear together too, showing that you really never can win.&lt;br /&gt;
&lt;br /&gt;
=====first_letter=====&lt;br /&gt;
&lt;br /&gt;
The same as 'truncate=1'&lt;br /&gt;
&lt;br /&gt;
=====truncate=====&lt;br /&gt;
&lt;br /&gt;
Use the first X characters of a value to group by.  truncate=4 may be useful for dates as it will group by the first four digits (the year) only.&lt;br /&gt;
&lt;br /&gt;
 truncate=4&lt;br /&gt;
&lt;br /&gt;
=====tags=====&lt;br /&gt;
&lt;br /&gt;
Useful for fields like keywords where values may be separated by commas or semi-colons.  The value is split on these two characters ( , and ; ) and treated as a multiple field.&lt;br /&gt;
&lt;br /&gt;
=====cloud=====&lt;br /&gt;
&lt;br /&gt;
Creates a tag cloud.  Sets jump to 'plain', cloudmax to 200, cloudmin to 80 and no_separator, then resizes the jump-to links according to frequency of use.&lt;br /&gt;
&lt;br /&gt;
=====cloudmax=====&lt;br /&gt;
&lt;br /&gt;
The % size of the largest tag in a tag cloud.&lt;br /&gt;
&lt;br /&gt;
=====cloudmin=====&lt;br /&gt;
&lt;br /&gt;
The % size of the smallest tag in a tag cloud.&lt;br /&gt;
&lt;br /&gt;
=====jump=====&lt;br /&gt;
&lt;br /&gt;
  jump=plain&lt;br /&gt;
&lt;br /&gt;
Turns of the 'jump to' text before the list of subheading navigation links.&lt;br /&gt;
&lt;br /&gt;
=====no_seperator (sic)=====&lt;br /&gt;
&lt;br /&gt;
Turns of the separator between each subheading navigation link (by default '|').&lt;br /&gt;
&lt;br /&gt;
=====string=====&lt;br /&gt;
&lt;br /&gt;
Uses values 'as is'.  No ordervalues, no phrases.&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6142</id>
		<title>Adding new views</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6142"/>
		<updated>2008-06-24T12:31:45Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* New options in EPrints 3.1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{development}}&lt;br /&gt;
&lt;br /&gt;
Browse views provide a way for visitors to your site to discover relevant content without a specific item in mind (for example, browsing all the content associated with a particular topic). Visitors arriving directly at the page for a specific item in the repository (for example, via a search engine) also use views you have defined to discover related content. &lt;br /&gt;
&lt;br /&gt;
There are two default views in EPrints - '''By Year''' and '''By Subject'''. This guide describes how to add additional views to your repository.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
===The basics===&lt;br /&gt;
&lt;br /&gt;
The views for your repository are defined in the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Open this file and find the browse_views configuration setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;-date;res=year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
The views are defined using a special (Perl) syntax: the view definition consists of a pair of curly braces (''note the comma after each closing brace'') enclosing a list of property/value pairs (note ''the comma'' after each line).&lt;br /&gt;
&lt;br /&gt;
The key part of the view definition is the '''fields''' property. This names the metadata field (or fields) that EPrints will use to construct the view. For example, for the '''By Year''' view, EPrints groups the records in the repository according to their '''date''' (note that the '''res=year''' suffix tells EPrints to only consider the year part), and constructs a Web page for each date listing the records. Similarly, the '''Browse by Subjscts''' view, groups the records according to the ''subjects'' they have been assigned to (a record may appear in more than one group!).&lt;br /&gt;
&lt;br /&gt;
Both the '''Browse by Year''' and '''Browse by Subject''' views are constructed using the values of a single field ('''date''' and '''subjects''' respectively).&lt;br /&gt;
&lt;br /&gt;
It is also possible to construct a view using the ''combined'' values of two or more fields (eg. group records by author '''and''' editor), or even using a sequence of two or more fields (eg. group records by journal title '''and then''' by volume number).&lt;br /&gt;
&lt;br /&gt;
===Worked example: browse by organisational structure===&lt;br /&gt;
&lt;br /&gt;
By default, EPrints has a ''divisions'' metadata field which allows authors to associate their deposits with the divisions (units, faculties, schools, departments, institutes, centres..) that were involved in producing their item (for example, the author's department, and the departments of any co-authors). This worked example allows visitors to browse the repository content by division.&lt;br /&gt;
&lt;br /&gt;
Open the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
                hideempty =&amp;gt; 1,&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
Save the file and generate the new view pages (this will also re-generate any existing views defined in the views configuration file):&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
Open the view page in a Web browser:&lt;br /&gt;
&lt;br /&gt;
 http://your.repository.url/view/&lt;br /&gt;
&lt;br /&gt;
The view page lists all the available views. You should see your new views on the list:&lt;br /&gt;
&lt;br /&gt;
[[Image:View_page2.png|frame|none|The view page lists available views]]&lt;br /&gt;
&lt;br /&gt;
'''Fixing the undefined phrase warning''' The new view may appear on the views page with an ''undefined phrase'' warning (you may also notice a similar warning message when running generate_views):&lt;br /&gt;
&lt;br /&gt;
 [&amp;quot;viewname_eprint_divisions&amp;quot; not defined]&lt;br /&gt;
&lt;br /&gt;
Each view you create needs to be assigned a ''human-readable'' name, which EPrints will use on the view Web pages.&lt;br /&gt;
&lt;br /&gt;
Edit the language-specific phrases file for view names:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/lang/en/phrases/views.xml&lt;br /&gt;
&lt;br /&gt;
Add an appropriate phrase which describes the new view, for example:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;epp:phrase id=&amp;quot;viewname_eprint_divisions&amp;quot;&amp;gt;Division&amp;lt;/epp:phrase&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Save the phrases file and regenerate the view pages:&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
===Example view definitions===&lt;br /&gt;
&lt;br /&gt;
====Browse by type====&lt;br /&gt;
&lt;br /&gt;
Every deposit in EPrints has a type (article, book, thesis...). To allow visitors to browse your repository content by type, add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id =&amp;gt; &amp;quot;types&amp;quot;,&lt;br /&gt;
        fields =&amp;gt; &amp;quot;type&amp;quot;,&lt;br /&gt;
        order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
====Browse by author====&lt;br /&gt;
&lt;br /&gt;
===Example views (combined fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by author and editor====&lt;br /&gt;
&lt;br /&gt;
===Example views (multiple fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by journal title, then by volume====&lt;br /&gt;
&lt;br /&gt;
This example lets visitors browse the journals items in your repository have been published in, and then volumes within each journal.&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id=&amp;gt;&amp;quot;journal_volume&amp;quot;,&lt;br /&gt;
        fields=&amp;gt;&amp;quot;publication,volume&amp;quot;,&lt;br /&gt;
        order=&amp;gt;&amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal_volume.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Linking in your view===&lt;br /&gt;
&lt;br /&gt;
You now need to add a link to your repository pages which takes visitors directly to your new view, or to the views page from where they can access all available views.&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_navbar.png]]&lt;br /&gt;
&lt;br /&gt;
====Generating CVs etc====&lt;br /&gt;
&lt;br /&gt;
===Linking items back to views===&lt;br /&gt;
===Views as collections===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===New options in EPrints 3.1===&lt;br /&gt;
&lt;br /&gt;
====subfield no longer supported====&lt;br /&gt;
&lt;br /&gt;
The subfield option is no longer supported in EPrints 3.1.&lt;br /&gt;
&lt;br /&gt;
====new_column_at====&lt;br /&gt;
&lt;br /&gt;
This is an array of integers representing the number of items in a view list before another column is added.  For example:&lt;br /&gt;
&lt;br /&gt;
 [ 10 ]&lt;br /&gt;
&lt;br /&gt;
This would have one column of values until there were 11, then there would be 2 columns.&lt;br /&gt;
&lt;br /&gt;
 [ 10, 10 ]&lt;br /&gt;
&lt;br /&gt;
This would have one column if there were ten or less values, two columns if there were between eleven and twenty (ten + ten) values, and three columns for all other cases.&lt;br /&gt;
&lt;br /&gt;
 [ 0, 0 ]&lt;br /&gt;
&lt;br /&gt;
This would always have three columns.&lt;br /&gt;
&lt;br /&gt;
Add one to the number of integers in the array and you get the maximum number of columns.  The value of each integer defines the point at which that column becomes full, and more values cause an 'overflow' into the next column.&lt;br /&gt;
&lt;br /&gt;
====variations====&lt;br /&gt;
&lt;br /&gt;
This controls the various ways in which a browse view can be subheaded.  It consists of a list of strings.  Each string is the name of a non-compound metadata field (or the keyword DEFAULT, for an unsubheaded list), optionally followed by a semi-colon and a comma separated list of options.  E.G:&lt;br /&gt;
&lt;br /&gt;
 variations =&amp;gt; [&lt;br /&gt;
  &amp;quot;creators_name;first_letter&amp;quot;,&lt;br /&gt;
  &amp;quot;type&amp;quot;,&lt;br /&gt;
  &amp;quot;DEFAULT&amp;quot;&lt;br /&gt;
 ],&lt;br /&gt;
&lt;br /&gt;
The following options are available:&lt;br /&gt;
&lt;br /&gt;
=====reverse=====&lt;br /&gt;
&lt;br /&gt;
Reverses the order in which the groupings are shown.  Default is the ordervalue for that field (usually alphanumeric).  Useful for dates as you may want the highest values first.&lt;br /&gt;
&lt;br /&gt;
=====filename=====&lt;br /&gt;
&lt;br /&gt;
Changes the filename of the view variation.  The default is the name of the metadata field used, so if two variations use the same metadata field with different options, this is needed.&lt;br /&gt;
&lt;br /&gt;
 filename=different_filename&lt;br /&gt;
&lt;br /&gt;
=====first_value=====&lt;br /&gt;
&lt;br /&gt;
If a field is multiple, only use the first value.  Otherwise each item will appear once for each value.&lt;br /&gt;
&lt;br /&gt;
=====first_initial=====&lt;br /&gt;
&lt;br /&gt;
If using a name, truncate the given name to the first initial.  This will make items like &amp;quot;Les Carr&amp;quot; and &amp;quot;Leslie Carr&amp;quot; appear together.  Note it will also make &amp;quot;John Smith&amp;quot; and &amp;quot;Jake Smith&amp;quot; appear together too, showing that you really never can win.&lt;br /&gt;
&lt;br /&gt;
=====first_letter=====&lt;br /&gt;
&lt;br /&gt;
The same as 'truncate=1'&lt;br /&gt;
&lt;br /&gt;
=====truncate=====&lt;br /&gt;
&lt;br /&gt;
Use the first X characters of a value to group by.  truncate=4 may be useful for dates as it will group by the first four digits (the year) only.&lt;br /&gt;
&lt;br /&gt;
 truncate=4&lt;br /&gt;
&lt;br /&gt;
=====tags=====&lt;br /&gt;
&lt;br /&gt;
Useful for fields like keywords where values may be separated by commas or semi-colons.  The value is split on these two characters ( , and ; ) and treated as a multiple field.&lt;br /&gt;
&lt;br /&gt;
=====cloud=====&lt;br /&gt;
&lt;br /&gt;
Creates a tag cloud.  Sets jump to 'plain', cloudmax to 200, cloudmin to 80 and no_separator, then resizes the jump-to links according to frequency of use.&lt;br /&gt;
&lt;br /&gt;
=====cloudmax=====&lt;br /&gt;
&lt;br /&gt;
The % size of the largest tag in a tag cloud.&lt;br /&gt;
&lt;br /&gt;
=====cloudmin=====&lt;br /&gt;
&lt;br /&gt;
The % size of the smallest tag in a tag cloud.&lt;br /&gt;
&lt;br /&gt;
=====jump=====&lt;br /&gt;
&lt;br /&gt;
  jump=plain&lt;br /&gt;
&lt;br /&gt;
Turns of the 'jump to' text before the list of subheading navigation links.&lt;br /&gt;
&lt;br /&gt;
=====no_seperator=====&lt;br /&gt;
&lt;br /&gt;
Turns of the separator between each subheading navigation link (by default '|').&lt;br /&gt;
&lt;br /&gt;
=====string=====&lt;br /&gt;
&lt;br /&gt;
Uses values 'as is'.  No ordervalues, no phrases.&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6141</id>
		<title>Adding new views</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6141"/>
		<updated>2008-06-24T11:53:20Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* New options in EPrints 3.1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{development}}&lt;br /&gt;
&lt;br /&gt;
Browse views provide a way for visitors to your site to discover relevant content without a specific item in mind (for example, browsing all the content associated with a particular topic). Visitors arriving directly at the page for a specific item in the repository (for example, via a search engine) also use views you have defined to discover related content. &lt;br /&gt;
&lt;br /&gt;
There are two default views in EPrints - '''By Year''' and '''By Subject'''. This guide describes how to add additional views to your repository.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
===The basics===&lt;br /&gt;
&lt;br /&gt;
The views for your repository are defined in the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Open this file and find the browse_views configuration setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;-date;res=year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
The views are defined using a special (Perl) syntax: the view definition consists of a pair of curly braces (''note the comma after each closing brace'') enclosing a list of property/value pairs (note ''the comma'' after each line).&lt;br /&gt;
&lt;br /&gt;
The key part of the view definition is the '''fields''' property. This names the metadata field (or fields) that EPrints will use to construct the view. For example, for the '''By Year''' view, EPrints groups the records in the repository according to their '''date''' (note that the '''res=year''' suffix tells EPrints to only consider the year part), and constructs a Web page for each date listing the records. Similarly, the '''Browse by Subjscts''' view, groups the records according to the ''subjects'' they have been assigned to (a record may appear in more than one group!).&lt;br /&gt;
&lt;br /&gt;
Both the '''Browse by Year''' and '''Browse by Subject''' views are constructed using the values of a single field ('''date''' and '''subjects''' respectively).&lt;br /&gt;
&lt;br /&gt;
It is also possible to construct a view using the ''combined'' values of two or more fields (eg. group records by author '''and''' editor), or even using a sequence of two or more fields (eg. group records by journal title '''and then''' by volume number).&lt;br /&gt;
&lt;br /&gt;
===Worked example: browse by organisational structure===&lt;br /&gt;
&lt;br /&gt;
By default, EPrints has a ''divisions'' metadata field which allows authors to associate their deposits with the divisions (units, faculties, schools, departments, institutes, centres..) that were involved in producing their item (for example, the author's department, and the departments of any co-authors). This worked example allows visitors to browse the repository content by division.&lt;br /&gt;
&lt;br /&gt;
Open the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
                hideempty =&amp;gt; 1,&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
Save the file and generate the new view pages (this will also re-generate any existing views defined in the views configuration file):&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
Open the view page in a Web browser:&lt;br /&gt;
&lt;br /&gt;
 http://your.repository.url/view/&lt;br /&gt;
&lt;br /&gt;
The view page lists all the available views. You should see your new views on the list:&lt;br /&gt;
&lt;br /&gt;
[[Image:View_page2.png|frame|none|The view page lists available views]]&lt;br /&gt;
&lt;br /&gt;
'''Fixing the undefined phrase warning''' The new view may appear on the views page with an ''undefined phrase'' warning (you may also notice a similar warning message when running generate_views):&lt;br /&gt;
&lt;br /&gt;
 [&amp;quot;viewname_eprint_divisions&amp;quot; not defined]&lt;br /&gt;
&lt;br /&gt;
Each view you create needs to be assigned a ''human-readable'' name, which EPrints will use on the view Web pages.&lt;br /&gt;
&lt;br /&gt;
Edit the language-specific phrases file for view names:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/lang/en/phrases/views.xml&lt;br /&gt;
&lt;br /&gt;
Add an appropriate phrase which describes the new view, for example:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;epp:phrase id=&amp;quot;viewname_eprint_divisions&amp;quot;&amp;gt;Division&amp;lt;/epp:phrase&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Save the phrases file and regenerate the view pages:&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
===Example view definitions===&lt;br /&gt;
&lt;br /&gt;
====Browse by type====&lt;br /&gt;
&lt;br /&gt;
Every deposit in EPrints has a type (article, book, thesis...). To allow visitors to browse your repository content by type, add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id =&amp;gt; &amp;quot;types&amp;quot;,&lt;br /&gt;
        fields =&amp;gt; &amp;quot;type&amp;quot;,&lt;br /&gt;
        order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
====Browse by author====&lt;br /&gt;
&lt;br /&gt;
===Example views (combined fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by author and editor====&lt;br /&gt;
&lt;br /&gt;
===Example views (multiple fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by journal title, then by volume====&lt;br /&gt;
&lt;br /&gt;
This example lets visitors browse the journals items in your repository have been published in, and then volumes within each journal.&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id=&amp;gt;&amp;quot;journal_volume&amp;quot;,&lt;br /&gt;
        fields=&amp;gt;&amp;quot;publication,volume&amp;quot;,&lt;br /&gt;
        order=&amp;gt;&amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal_volume.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Linking in your view===&lt;br /&gt;
&lt;br /&gt;
You now need to add a link to your repository pages which takes visitors directly to your new view, or to the views page from where they can access all available views.&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_navbar.png]]&lt;br /&gt;
&lt;br /&gt;
====Generating CVs etc====&lt;br /&gt;
&lt;br /&gt;
===Linking items back to views===&lt;br /&gt;
===Views as collections===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===New options in EPrints 3.1===&lt;br /&gt;
&lt;br /&gt;
====subfield no longer supported====&lt;br /&gt;
&lt;br /&gt;
The subfield option is no longer supported in EPrints 3.1.&lt;br /&gt;
&lt;br /&gt;
====new_column_at====&lt;br /&gt;
&lt;br /&gt;
This is an array of integers representing the number of items in a view list before another column is added.  For example:&lt;br /&gt;
&lt;br /&gt;
 [ 10 ]&lt;br /&gt;
&lt;br /&gt;
This would have one column of values until there were 11, then there would be 2 columns.&lt;br /&gt;
&lt;br /&gt;
 [ 10, 10 ]&lt;br /&gt;
&lt;br /&gt;
This would have one column if there were ten or less values, two columns if there were between eleven and twenty (ten + ten) values, and three columns for all other cases.&lt;br /&gt;
&lt;br /&gt;
 [ 0, 0 ]&lt;br /&gt;
&lt;br /&gt;
This would always have three columns.&lt;br /&gt;
&lt;br /&gt;
Add one to the number of integers in the array and you get the maximum number of columns.  The value of each integer defines the point at which that column becomes full, and more values cause an 'overflow' into the next column.&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6140</id>
		<title>Adding new views</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Adding_new_views&amp;diff=6140"/>
		<updated>2008-06-24T11:45:06Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{development}}&lt;br /&gt;
&lt;br /&gt;
Browse views provide a way for visitors to your site to discover relevant content without a specific item in mind (for example, browsing all the content associated with a particular topic). Visitors arriving directly at the page for a specific item in the repository (for example, via a search engine) also use views you have defined to discover related content. &lt;br /&gt;
&lt;br /&gt;
There are two default views in EPrints - '''By Year''' and '''By Subject'''. This guide describes how to add additional views to your repository.&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
===The basics===&lt;br /&gt;
&lt;br /&gt;
The views for your repository are defined in the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Open this file and find the browse_views configuration setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;-date;res=year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
The views are defined using a special (Perl) syntax: the view definition consists of a pair of curly braces (''note the comma after each closing brace'') enclosing a list of property/value pairs (note ''the comma'' after each line).&lt;br /&gt;
&lt;br /&gt;
The key part of the view definition is the '''fields''' property. This names the metadata field (or fields) that EPrints will use to construct the view. For example, for the '''By Year''' view, EPrints groups the records in the repository according to their '''date''' (note that the '''res=year''' suffix tells EPrints to only consider the year part), and constructs a Web page for each date listing the records. Similarly, the '''Browse by Subjscts''' view, groups the records according to the ''subjects'' they have been assigned to (a record may appear in more than one group!).&lt;br /&gt;
&lt;br /&gt;
Both the '''Browse by Year''' and '''Browse by Subject''' views are constructed using the values of a single field ('''date''' and '''subjects''' respectively).&lt;br /&gt;
&lt;br /&gt;
It is also possible to construct a view using the ''combined'' values of two or more fields (eg. group records by author '''and''' editor), or even using a sequence of two or more fields (eg. group records by journal title '''and then''' by volume number).&lt;br /&gt;
&lt;br /&gt;
===Worked example: browse by organisational structure===&lt;br /&gt;
&lt;br /&gt;
By default, EPrints has a ''divisions'' metadata field which allows authors to associate their deposits with the divisions (units, faculties, schools, departments, institutes, centres..) that were involved in producing their item (for example, the author's department, and the departments of any co-authors). This worked example allows visitors to browse the repository content by division.&lt;br /&gt;
&lt;br /&gt;
Open the views configuration file:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/cfg.d/views.pl&lt;br /&gt;
&lt;br /&gt;
Add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 $c-&amp;gt;{browse_views} = [&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;year&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;subjects&amp;quot;,&lt;br /&gt;
                ...&lt;br /&gt;
        },&lt;br /&gt;
        {&lt;br /&gt;
                id =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                fields =&amp;gt; &amp;quot;divisions&amp;quot;,&lt;br /&gt;
                order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
                hideempty =&amp;gt; 1,&lt;br /&gt;
        },&lt;br /&gt;
 ];&lt;br /&gt;
&lt;br /&gt;
Save the file and generate the new view pages (this will also re-generate any existing views defined in the views configuration file):&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
Open the view page in a Web browser:&lt;br /&gt;
&lt;br /&gt;
 http://your.repository.url/view/&lt;br /&gt;
&lt;br /&gt;
The view page lists all the available views. You should see your new views on the list:&lt;br /&gt;
&lt;br /&gt;
[[Image:View_page2.png|frame|none|The view page lists available views]]&lt;br /&gt;
&lt;br /&gt;
'''Fixing the undefined phrase warning''' The new view may appear on the views page with an ''undefined phrase'' warning (you may also notice a similar warning message when running generate_views):&lt;br /&gt;
&lt;br /&gt;
 [&amp;quot;viewname_eprint_divisions&amp;quot; not defined]&lt;br /&gt;
&lt;br /&gt;
Each view you create needs to be assigned a ''human-readable'' name, which EPrints will use on the view Web pages.&lt;br /&gt;
&lt;br /&gt;
Edit the language-specific phrases file for view names:&lt;br /&gt;
&lt;br /&gt;
 /opt/eprints3/archives/ARCHIVEID/cfg/lang/en/phrases/views.xml&lt;br /&gt;
&lt;br /&gt;
Add an appropriate phrase which describes the new view, for example:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;epp:phrase id=&amp;quot;viewname_eprint_divisions&amp;quot;&amp;gt;Division&amp;lt;/epp:phrase&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Save the phrases file and regenerate the view pages:&lt;br /&gt;
&lt;br /&gt;
 bin/generate_views ARCHIVEID --verbose&lt;br /&gt;
&lt;br /&gt;
===Example view definitions===&lt;br /&gt;
&lt;br /&gt;
====Browse by type====&lt;br /&gt;
&lt;br /&gt;
Every deposit in EPrints has a type (article, book, thesis...). To allow visitors to browse your repository content by type, add the following definition to the browse_views setting:&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id =&amp;gt; &amp;quot;types&amp;quot;,&lt;br /&gt;
        fields =&amp;gt; &amp;quot;type&amp;quot;,&lt;br /&gt;
        order =&amp;gt; &amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
====Browse by author====&lt;br /&gt;
&lt;br /&gt;
===Example views (combined fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by author and editor====&lt;br /&gt;
&lt;br /&gt;
===Example views (multiple fields)===&lt;br /&gt;
&lt;br /&gt;
====Browse by journal title, then by volume====&lt;br /&gt;
&lt;br /&gt;
This example lets visitors browse the journals items in your repository have been published in, and then volumes within each journal.&lt;br /&gt;
&lt;br /&gt;
 {&lt;br /&gt;
        id=&amp;gt;&amp;quot;journal_volume&amp;quot;,&lt;br /&gt;
        fields=&amp;gt;&amp;quot;publication,volume&amp;quot;,&lt;br /&gt;
        order=&amp;gt;&amp;quot;-date/title&amp;quot;,&lt;br /&gt;
        hideempty =&amp;gt; 1,&lt;br /&gt;
 },&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal.png|border]]&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_journal_volume.png|border]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Linking in your view===&lt;br /&gt;
&lt;br /&gt;
You now need to add a link to your repository pages which takes visitors directly to your new view, or to the views page from where they can access all available views.&lt;br /&gt;
&lt;br /&gt;
[[Image:Browse_by_navbar.png]]&lt;br /&gt;
&lt;br /&gt;
====Generating CVs etc====&lt;br /&gt;
&lt;br /&gt;
===Linking items back to views===&lt;br /&gt;
===Views as collections===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===New options in EPrints 3.1===&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Migration&amp;diff=6131</id>
		<title>Migration</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Migration&amp;diff=6131"/>
		<updated>2008-06-13T18:28:27Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* export3data.pl */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page covers how to migrate from EPrints 2 to EPrints 3.&lt;br /&gt;
&lt;br /&gt;
== Migration Toolkit ==&lt;br /&gt;
&lt;br /&gt;
The migration toolkit, available from http://files.eprints.org/ does quite a bit of the heavy lifting. It is intended to help configure an EP3 archive to have the same files, eprint types etc. as an EPrint 2 repository and then copy the data over.&lt;br /&gt;
&lt;br /&gt;
Release 1.0-beta-1 should be a big improvement over 0.2 but it still doesn't do everything. &lt;br /&gt;
&lt;br /&gt;
=== Installation ===&lt;br /&gt;
&lt;br /&gt;
==== Backup ====&lt;br /&gt;
&lt;br /&gt;
First of all make sure your EPrints 2 repository is backed up, just in case things don't go to plan. You already back it up daily anyway, right...?&lt;br /&gt;
&lt;br /&gt;
==== Mtoolkit ====&lt;br /&gt;
&lt;br /&gt;
Un-tar the package on the same machine as your EPrints 2 repository.&lt;br /&gt;
&lt;br /&gt;
If your EPrints 2 was not installed in /opt/eprints2 then you'll need to modify the first line of the two .pl scripts in the toolkit.&lt;br /&gt;
&lt;br /&gt;
==== EPrints 3 ====&lt;br /&gt;
&lt;br /&gt;
Minimum version required: 3.0.2 (This version introduces some very small options and bugfixes aimed at migration).&lt;br /&gt;
&lt;br /&gt;
Also, get an EPrints 3 server set up. This can be either on the same machine (you'll need a separate instance of apache as ep2 and ep3 can't run under the same server at the same time, put it on port 8080 for now - see http://httpd.apache.org/docs/2.0/install.html for instructions - put it in another directory using the --PREFIX option!), or on a different machine. Get a repository created (probably with the same ID as your ep2 repo, although that's not essential). The database will need to be a different name or you'll get in an utter mess.&lt;br /&gt;
&lt;br /&gt;
=== mkconfig.pl ===&lt;br /&gt;
&lt;br /&gt;
This tool takes the id of an EPrints 2 repository and generates a number of EPrints 3 config. files. Copy these files into the cfg dir of your EPrints 3 repository. It also creates a file called migration_notes.txt with some helpful comments of anything it's messed with.&lt;br /&gt;
&lt;br /&gt;
Get your (empty) EP3 repository up and running using these configuration files. &lt;br /&gt;
&lt;br /&gt;
=== export3data.pl ===&lt;br /&gt;
&lt;br /&gt;
This script exports the data from your EPrints 2 repostory in a format which can be imported by EPrints 3.&lt;br /&gt;
&lt;br /&gt;
There have been some problems with exporting non Latin characters (e.g. letters with accents).  If you have any problems, these can probably be solved by editing the export3data script and adding the following line (put it just under the first line).&lt;br /&gt;
&lt;br /&gt;
  use encoding 'utf8';&lt;br /&gt;
&lt;br /&gt;
To export the data do the following:&lt;br /&gt;
&lt;br /&gt;
  export3data.pl ARCHIVEID eprints &amp;gt; eprints.xml&lt;br /&gt;
  export3data.pl ARCHIVEID users &amp;gt; users.xml&lt;br /&gt;
  export3data.pl ARCHIVEID subjects &amp;gt; subjects.xml&lt;br /&gt;
&lt;br /&gt;
eprints.xml references the full paths of the files in EPrints 2. If your EPrints 3 is on a different machine you'll need to either make sure they are the same on the new machine or do a big search-and-replace on eprints.xml!&lt;br /&gt;
&lt;br /&gt;
If the script has any problems, run with the 'skiplog' argument:&lt;br /&gt;
&lt;br /&gt;
  export3data.pl --skiplog errors.txt ARCHIVEID eprints &amp;gt; eprints.xml&lt;br /&gt;
&lt;br /&gt;
Any items with problems will be ignored, but the ids of them will be recorded in the 'errors.txt' file.  Export these by hand if they are important.&lt;br /&gt;
&lt;br /&gt;
=== Importing ===&lt;br /&gt;
&lt;br /&gt;
EPrints 3.0.2 no longer needs the hacks which were required for mtoolkit 0.2&lt;br /&gt;
&lt;br /&gt;
=== Empty out any test data ===&lt;br /&gt;
&lt;br /&gt;
To erase the current data in your EP3 repository use:&lt;br /&gt;
&lt;br /&gt;
 bin/epadmin erase_data ARCHIVEID&lt;br /&gt;
&lt;br /&gt;
=== Import the data ===&lt;br /&gt;
&lt;br /&gt;
To import the subjects and users do:&lt;br /&gt;
 /opt/eprints3/bin/import_subjects --verbose --force --xml ARCHIVEID subjects.xml&lt;br /&gt;
 /opt/eprints3/bin/import --verbose --migration ARCHIVEID user XML users.xml&lt;br /&gt;
If something goes wrong with subjects or users, use epadmin erase_data to empty the database and start again.&lt;br /&gt;
&lt;br /&gt;
To import the EPrints do:&lt;br /&gt;
 /opt/eprints3/bin/import --verbose --migration ARCHIVEID eprint XML eprints.xml&lt;br /&gt;
If something goes wrong with importing the eprints, use epadmin erase_eprints, to just erase the eprints data so you don't need to redo subjects and users.&lt;br /&gt;
&lt;br /&gt;
the --migration option tells the importer to:&lt;br /&gt;
* skip are-you-sure? messages.&lt;br /&gt;
* use the eprintid and userid from the XML rather than assigning them.&lt;br /&gt;
* use the &amp;quot;datestamp&amp;quot; from the XML rather than assign it.&lt;br /&gt;
* load files from the local file system (normally this would be a security hole)&lt;br /&gt;
&lt;br /&gt;
You may encounter some issues with badly formed XML. This is due to non correctly encoded data creeping into your database. It should all be utf-8 but earlier versions of EPrints didn't always check... If your EPrints 2 server is running perl 5.8 you can install the Perl module Encode which will clean up your data, but on our system our EPrints 2 was running on a machine with an older version of Perl and we didn't want to risk upgrading.&lt;br /&gt;
&lt;br /&gt;
== Finishing up after using mtoolkit ==&lt;br /&gt;
&lt;br /&gt;
You will probably still want to tweak some of the following things by hand, depending how much you customised EPrints 2:&lt;br /&gt;
&lt;br /&gt;
Some of these we can't easily add to the mtoolkit (those involving perl code). The XML files we could add in theory, but we've made a decision to release 1.0 with the current features, rather than delay it months but make it perfect.&lt;br /&gt;
&lt;br /&gt;
* the template&lt;br /&gt;
* the workflow (EPrints 3 offers some nice features, look at the lib/defaultcfg/workflows/ for an idea of what you can do)&lt;br /&gt;
* the static pages (.xpage)&lt;br /&gt;
* the citation files&lt;br /&gt;
* the /view/ browsing configuration&lt;br /&gt;
* the search configuration&lt;br /&gt;
* any custom render routines&lt;br /&gt;
* the render eprint method (eprint_render.pl)&lt;br /&gt;
* any custom document security options&lt;br /&gt;
* any custom validation options&lt;br /&gt;
* etc.&lt;br /&gt;
&lt;br /&gt;
Feel free to add tips on the wiki, linked from this section.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Known bugs in version 1.0 of toolkit / importing into EPrints 3.0.2 ==&lt;br /&gt;
&lt;br /&gt;
=== Documents with subdirectories fail to import ===&lt;br /&gt;
&lt;br /&gt;
FIX: do them by hand at the end.&lt;br /&gt;
&lt;br /&gt;
=== Warning messages about &amp;quot;hideemail&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
hideemail was introduced in a version of EPrints 2 (I forget which). Earlier repositories may not have this field. Some of the EPrints 3 default config files assume it exists (user_fields_default.pl and user_render.pl).&lt;br /&gt;
&lt;br /&gt;
FIX 1: Don't worry about it.&lt;br /&gt;
&lt;br /&gt;
FIX 2: Before importing users.xml, add the hideemail field back into user_fields.pl&lt;br /&gt;
          {&lt;br /&gt;
            'name' =&amp;gt; 'hideemail',&lt;br /&gt;
            'input_style' =&amp;gt; 'radio',&lt;br /&gt;
            'type' =&amp;gt; 'boolean',&lt;br /&gt;
          },&lt;br /&gt;
&lt;br /&gt;
=== Error missing field: X ===&lt;br /&gt;
&lt;br /&gt;
The default EPrints 3 config. may reference a field not imported. If so you can almost always just remove the offending section of configuration. Examples: searches, citations, views.&lt;br /&gt;
&lt;br /&gt;
=== Problems with bad characters in eprints.xml ===&lt;br /&gt;
&lt;br /&gt;
This is not tested, but I think this should clean it up...&lt;br /&gt;
 iconv -c eprints.xml --output=eprints_cleaned.xml -f utf-8 -t utf-8&lt;br /&gt;
&lt;br /&gt;
=== Warning about Pagerange ===&lt;br /&gt;
&lt;br /&gt;
 Argument &amp;quot;&amp;quot; isn't numeric in addition (+) at&lt;br /&gt;
  /opt/eprints3/perl_lib/EPrints/MetaField/Pagerange.pm line 182.&lt;br /&gt;
&lt;br /&gt;
This is a warning that is caused by having non-numeric data in the pagerange field. eg. &amp;quot;iii-xi&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
FIX: Don't worry about it.&lt;br /&gt;
&lt;br /&gt;
=== Can't import files which contain &amp;quot;/&amp;quot; ===&lt;br /&gt;
&lt;br /&gt;
eg if your document had index.html and images/dia.jpg&lt;br /&gt;
&lt;br /&gt;
FIX: Make a note of the offenders, and just add those documents by hand. &lt;br /&gt;
&lt;br /&gt;
FIX2: Bug chris to add this to fix this in the final release of 3.0.2 (it's not in beta-1)&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4361</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4361"/>
		<updated>2007-05-30T19:55:58Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.  For more detailed information, please see the [[IRStats Technical Documentation]].&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
A Visualisation takes a set of processed statistics and outputs them.  For example, Visualisation::Graph::Pie creates a pie chart.&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
The Database Interface object handles all queries to the database.  Most requests for statistics can be completed with a single call to the get_stats($params) method.&lt;br /&gt;
&lt;br /&gt;
=== Data Flow Diagram ===&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;br /&gt;
&lt;br /&gt;
== Required Data ==&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to run, it requires two things:&lt;br /&gt;
&lt;br /&gt;
* a database table containing all hits to the repository&lt;br /&gt;
* text files describing the contents of the repository&lt;br /&gt;
&lt;br /&gt;
=== The Hits Table ===&lt;br /&gt;
&lt;br /&gt;
Awaiting a redevelopment.&lt;br /&gt;
&lt;br /&gt;
=== The Text Files ===&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to build up a picture of a repository, a number of text files need to be created and stored in the cfg/ directory:&lt;br /&gt;
&lt;br /&gt;
* epstats_set_membership.txt&lt;br /&gt;
* epstats_set_member_codes.txt&lt;br /&gt;
* epstats_set_member_full_citations.txt&lt;br /&gt;
* epstats_set_member_short_citations.txt&lt;br /&gt;
* epstats_set_member_urls.txt&lt;br /&gt;
&lt;br /&gt;
==== Explanation by Example ====&lt;br /&gt;
&lt;br /&gt;
Imagine a very small repository.  Here are its contents:&lt;br /&gt;
&lt;br /&gt;
* eprints&lt;br /&gt;
** (1) The Smells of Cheese&lt;br /&gt;
** (2) The Tastes of Wines&lt;br /&gt;
** (3) The Sounds of Oboes&lt;br /&gt;
* Authors&lt;br /&gt;
** (1) John Smith&lt;br /&gt;
** (2) Harriet Jones&lt;br /&gt;
&lt;br /&gt;
If we then imagine that the following are also true:&lt;br /&gt;
&lt;br /&gt;
* John Smith is credited with being an author of eprints (1) and (2)&lt;br /&gt;
* Harriet Jones is credited with being an author of eprints (2) and (3)&lt;br /&gt;
* All three eprints are the output of a research group named &amp;quot;Senses&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===== Creating sets =====&lt;br /&gt;
&lt;br /&gt;
Sets are groups of eprints, and every eprint is a member of at least one set (the set containing only that eprint).  From the information above, we have three sets.  The eprint set, the author set and the research group set.  We need to add the following to epstats_set_membership.txt (the format is &amp;lt;id&amp;gt;&amp;lt;tab&amp;gt;&amp;lt;csv list of eprint ids&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 author_1        1,2&lt;br /&gt;
 author_2        2,3&lt;br /&gt;
 group_1         1,2,3&lt;br /&gt;
 eprint_1        1&lt;br /&gt;
 eprint_2        2&lt;br /&gt;
 eprint_3        3&lt;br /&gt;
&lt;br /&gt;
===== Giving Sets IDs =====&lt;br /&gt;
&lt;br /&gt;
So, we now have some sets, but we need to give them unique IDs so that we can retrieve stats for these sets.  To do this, we add the following to epstats_set_member_codes.txt:&lt;br /&gt;
&lt;br /&gt;
 author_1        js&lt;br /&gt;
 author_2        hj&lt;br /&gt;
 group_1         senses&lt;br /&gt;
 eprint_1        1&lt;br /&gt;
 eprint_2        2&lt;br /&gt;
 eprint_3        3&lt;br /&gt;
&lt;br /&gt;
IRStats now assigns the following unique IDs to each set: author_js, author_hj, group_senses, eprint_1, eprint_2, eprint_3.  Note that the IDs should probably be kept alphanumeric, and must be unique within a class of sets (but you can have author_hj, group_hj and eprint_hj).&lt;br /&gt;
&lt;br /&gt;
===== Citations =====&lt;br /&gt;
&lt;br /&gt;
IRStats uses two citations for each set member, one short and one long.  Which you use depends on how you would like your visualisation to look.  However, we do need to add these to the citations files:&lt;br /&gt;
&lt;br /&gt;
epstats_set_member_short_citations.txt&lt;br /&gt;
 author_1         Smith&lt;br /&gt;
&lt;br /&gt;
epstats_set_member_full_citations.txt&lt;br /&gt;
 author_1         Dr John Smith, PhD&lt;br /&gt;
&lt;br /&gt;
Note that the above examples are only for author_1.  It would be exactly the same for any set member.&lt;br /&gt;
&lt;br /&gt;
===== URLs =====&lt;br /&gt;
&lt;br /&gt;
Although URLs are not currently implemented, it is probably a good idea to include this information (in epstats_set_member_urls.txt) for future functionality.&lt;br /&gt;
&lt;br /&gt;
 author_1         http://homepage.john.smith.com/&lt;br /&gt;
&lt;br /&gt;
== Installing IRStats ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dependencies ===&lt;br /&gt;
&lt;br /&gt;
==== Logfile::EPrints ====&lt;br /&gt;
&lt;br /&gt;
The Logfile::Eprints modules are used to assist in filtering the raw access log.  They can be installed from CPAN.&lt;br /&gt;
&lt;br /&gt;
==== AWStats ====&lt;br /&gt;
&lt;br /&gt;
AWStats data is used to filter out webspiders and classify search engines.  The irstats.cfg must have an entry showing where the correct perl modules are.&lt;br /&gt;
&lt;br /&gt;
==== Geo::IP ====&lt;br /&gt;
&lt;br /&gt;
Geo::IP is used to fill in country and organisation information.  The country database is free, but if you want organisation information, you will have to purchase a subscription for their database.  The location of the database should also be inserted into irstats.cfg.&lt;br /&gt;
&lt;br /&gt;
Note: The pure perl version of Geo::IP does not support organisations.&lt;br /&gt;
&lt;br /&gt;
=== Installing ===&lt;br /&gt;
&lt;br /&gt;
=== Customising ===&lt;br /&gt;
&lt;br /&gt;
It will almost always be necessary to perform some customisation on IRStats because every repository is different.&lt;br /&gt;
&lt;br /&gt;
==== Updating the Table ====&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4356</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4356"/>
		<updated>2007-05-30T19:41:22Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the irstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in irstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cache ==&lt;br /&gt;
Contains cache files.  These should probably be deleted whenever the database is updated.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a IRStats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cfg ==&lt;br /&gt;
&lt;br /&gt;
Where the configuration file and the text files containing repository data are held.&lt;br /&gt;
&lt;br /&gt;
=== The Configuration File ===&lt;br /&gt;
&lt;br /&gt;
irstats.cfg contains a number of configuration strings.  Here are some of the more important ones, with the default in brackets:&lt;br /&gt;
&lt;br /&gt;
*configuration_path (/opt/irstats/cfg/) - The path of the configuration directory.&lt;br /&gt;
*view_path (/opt/irstats/perl_lib/IRStats/View/) - The directory containing the Views.&lt;br /&gt;
*cache_path (/opt/irstats/cache/) - The directory in which to store cache files.&lt;br /&gt;
*graph_path (/opt/irstats/img/graphs/) - The directory in which to store graph images.&lt;br /&gt;
*graph_relative_url_path (/img/graphs/) - The url of the directory in which the graph file is from the point of view of the web browser.&lt;br /&gt;
*update_lock_filename (/opt/irstats/bin/.lock) - The name of the file that is created to prevent the update process running twice concurrently&lt;br /&gt;
*The names of the files used to store set information&lt;br /&gt;
**set_member_full_citations_file (/opt/irstats/cfg/irstats_set_member_full_citations.txt)&lt;br /&gt;
**set_member_short_citations_file (/opt/irstats/cfg/irstats_set_member_short_citations.txt)&lt;br /&gt;
**set_membership_file (/opt/irstats/cfg/irstats_set_membership.txt)&lt;br /&gt;
**set_member_codes_file (/opt/irstats/cfg/irstats_set_member_codes.txt)&lt;br /&gt;
**set_member_urls_file (/opt/irstats/cfg/irstats_set_member_urls.txt)&lt;br /&gt;
*Referrer Scope Labels (note, if you change these, you should also change them in the database)&lt;br /&gt;
**referrer_scope_1 (Internal)&lt;br /&gt;
**referrer_scope_2 (ECS)&lt;br /&gt;
**referrer_scope_3 (Search)&lt;br /&gt;
**referrer_scope_4 (External)&lt;br /&gt;
**referrer_scope_no_referrer (None)&lt;br /&gt;
*awstats_search_engines (/usr/local/awstats/wwwroot/cgi-bin/lib/search_engines.pm) - The path to the awstats search engine module&lt;br /&gt;
*repeats_filter_file (/opt/irstats/bin/repeatscache) - The file to maintain state between updates&lt;br /&gt;
*repeats_filter_timeout (86400) - repeat timeout in seconds (the amount of time there needs to be between two hits for them both to be recorded, initially set to 60*60*24)&lt;br /&gt;
&lt;br /&gt;
*repository_url = http://eprints.ecs.soton.ac.uk - the path to the repository&lt;br /&gt;
&lt;br /&gt;
*database configuration&lt;br /&gt;
**database_driver (mysql)&lt;br /&gt;
**database_server (localhost)&lt;br /&gt;
**database_name&lt;br /&gt;
**database_user&lt;br /&gt;
**database_password&lt;br /&gt;
&lt;br /&gt;
*database_id_columns ([ requester_organisation, requester_host, referrer_scope, search_engine, search_terms, referring_entity_id ]) - The columns in the database that have a UID rather than data.  These need seperate tables in which to store the data.&lt;br /&gt;
&lt;br /&gt;
*Various table names and parts of names&lt;br /&gt;
**database_eprints_access_log_table (accesslog) ##Perhaps remove after update rewrite.&lt;br /&gt;
**database_main_stats_table (irstats_true_accesses_table)&lt;br /&gt;
**database_column_table_prefix (irstats_column_)&lt;br /&gt;
**database_set_table_prefix (irstats_set_)&lt;br /&gt;
**database_set_table_code_suffix (_code)&lt;br /&gt;
**database_set_table_citation_suffix (_citation)&lt;br /&gt;
&lt;br /&gt;
*id_parameters ([ start_date, end_date, eprints, view ]) - the parameters that are used to uniquely identify a view&lt;br /&gt;
*host_lookup_temp_dir (/opt/irstats/bin/convert_hosts_temp_files/) - The directory in which to store temp files for host lookups&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the irstats classes.&lt;br /&gt;
&lt;br /&gt;
= IRStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading IRStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
This object acts as an interface to the configuration file.&lt;br /&gt;
=== Configuration Contstants ===&lt;br /&gt;
*$configuration_file - The path to the configuration file.&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new - Parses the configuration file and returns a new object.&lt;br /&gt;
*get_value(config_id) - Returns a value.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  This is passed around the system.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(Configuration, [ CGI_object | params_hash ]) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(Configuration) - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  This can be used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_stats(params_object, query_params_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in query params hash.  The query params hash can contain the following key/value pairs&lt;br /&gt;
**columns =&amp;gt; column_name_array - Which columns are we interested in?&lt;br /&gt;
**order =&amp;gt; column_name - A hash containing a column name and directions (ASC or DESC)&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
**where =&amp;gt; where_hash_array - if additional logic needs to be applied, this array contains hashes containing a column name, an operator and a value.  These are ANDed together.&lt;br /&gt;
*check_tables() - If any IRStats tables are missing, this function will create them.&lt;br /&gt;
*insert_main_table_row(column_array) - inserts the values in the array into the main table (taking into account any tables that contain only IDs).&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.  This is the only point where sql is sent to the database.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
A date object was implemented because there were some specific things that needed to be done with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*difference(date_object) - returns the difference in days between itself and another date.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both IRStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.&lt;br /&gt;
===Functions===&lt;br /&gt;
new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
start_date_control() - returns the html for the three drop-boxes for selecting the year, month and day of the start date.&lt;br /&gt;
end_date_control() - return the html for the three drop-boxes for selecting the year, month and day of the end date.&lt;br /&gt;
eprint_control() - returns the html for the eprints text box.&lt;br /&gt;
drop_box(id, contents_array) - returns the html for a drop box containing what is in the array (each array element is a hash containing 'value' and 'display').&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.  It is intended that savvy users create their own views.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers IRStats.&lt;br /&gt;
&lt;br /&gt;
=== View::DownloadCountHTML ===&lt;br /&gt;
The DownloadCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing, making it ideal for a quick walkthrough:&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package IRStats::View::DownloadCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use IRStats::DatabaseInterface;&lt;br /&gt;
 use IRStats::Cache;&lt;br /&gt;
 use IRStats::Visualisation::HTML;&lt;br /&gt;
 use IRStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ IRStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We aren't actually interested in any columns, just in the count, but we put that in the columns array anyway.&lt;br /&gt;
We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {columns =&amp;gt; [ 'COUNT' ]};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = IRStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Almost every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = IRStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;irstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = IRStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.  This is to prevent sending huge tables to browsers which may not be able to handle it.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4355</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4355"/>
		<updated>2007-05-30T19:17:08Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the irstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in irstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cache ==&lt;br /&gt;
Contains cache files.  These should probably be deleted whenever the database is updated.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a IRStats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cfg ==&lt;br /&gt;
&lt;br /&gt;
Where the configuration file and the text files containing repository data are held.&lt;br /&gt;
&lt;br /&gt;
=== The Configuration File ===&lt;br /&gt;
&lt;br /&gt;
irstats.cfg contains a number of configuration strings.  Here are some of the more important ones, with the default in brackets:&lt;br /&gt;
&lt;br /&gt;
*configuration_path (/opt/irstats/cfg/) - The path of the configuration directory.&lt;br /&gt;
*view_path (/opt/irstats/perl_lib/IRStats/View/) - The directory containing the Views.&lt;br /&gt;
*cache_path (/opt/irstats/cache/) - The directory in which to store cache files.&lt;br /&gt;
*graph_path (/opt/irstats/img/graphs/) - The directory in which to store graph images.&lt;br /&gt;
*graph_relative_url_path (/img/graphs/) - The url of the directory in which the graph file is from the point of view of the web browser.&lt;br /&gt;
*update_lock_filename (/opt/irstats/bin/.lock) - The name of the file that is created to prevent the update process running twice concurrently&lt;br /&gt;
*The names of the files used to store set information&lt;br /&gt;
**set_member_full_citations_file (/opt/irstats/cfg/irstats_set_member_full_citations.txt)&lt;br /&gt;
**set_member_short_citations_file (/opt/irstats/cfg/irstats_set_member_short_citations.txt)&lt;br /&gt;
**set_membership_file (/opt/irstats/cfg/irstats_set_membership.txt)&lt;br /&gt;
**set_member_codes_file (/opt/irstats/cfg/irstats_set_member_codes.txt)&lt;br /&gt;
**set_member_urls_file (/opt/irstats/cfg/irstats_set_member_urls.txt)&lt;br /&gt;
*Referrer Scope Labels (note, if you change these, you should also change them in the database)&lt;br /&gt;
**referrer_scope_1 (Internal)&lt;br /&gt;
**referrer_scope_2 (ECS)&lt;br /&gt;
**referrer_scope_3 (Search)&lt;br /&gt;
**referrer_scope_4 (External)&lt;br /&gt;
**referrer_scope_no_referrer (None)&lt;br /&gt;
*awstats_search_engines (/usr/local/awstats/wwwroot/cgi-bin/lib/search_engines.pm) - The path to the awstats search engine module&lt;br /&gt;
*repeats_filter_file (/opt/irstats/bin/repeatscache) - The file to maintain state between updates&lt;br /&gt;
*repeats_filter_timeout (86400) - repeat timeout in seconds (the amount of time there needs to be between two hits for them both to be recorded, initially set to 60*60*24)&lt;br /&gt;
&lt;br /&gt;
*repository_url = http://eprints.ecs.soton.ac.uk - the path to the repository&lt;br /&gt;
&lt;br /&gt;
*database configuration&lt;br /&gt;
**database_driver (mysql)&lt;br /&gt;
**database_server (localhost)&lt;br /&gt;
**database_name&lt;br /&gt;
**database_user&lt;br /&gt;
**database_password&lt;br /&gt;
&lt;br /&gt;
*database_id_columns ([ requester_organisation, requester_host, referrer_scope, search_engine, search_terms, referring_entity_id ]) - The columns in the database that have a UID rather than data.  These need seperate tables in which to store the data.&lt;br /&gt;
&lt;br /&gt;
*Various table names and parts of names&lt;br /&gt;
**database_eprints_access_log_table (accesslog) ##Perhaps remove after update rewrite.&lt;br /&gt;
**database_main_stats_table (irstats_true_accesses_table)&lt;br /&gt;
**database_column_table_prefix (irstats_column_)&lt;br /&gt;
**database_set_table_prefix (irstats_set_)&lt;br /&gt;
**database_set_table_code_suffix (_code)&lt;br /&gt;
**database_set_table_citation_suffix (_citation)&lt;br /&gt;
&lt;br /&gt;
*id_parameters ([ start_date, end_date, eprints, view ]) - the parameters that are used to uniquely identify a view&lt;br /&gt;
*host_lookup_temp_dir (/opt/irstats/bin/convert_hosts_temp_files/) - The directory in which to store temp files for host lookups&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the irstats classes.&lt;br /&gt;
&lt;br /&gt;
= IRStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading IRStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Configuration ==&lt;br /&gt;
This object acts as an interface to the configuration file.&lt;br /&gt;
=== Configuration Contstants ===&lt;br /&gt;
*$configuration_file - The path to the configuration file.&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new - Parses the configuration file and returns a new object.&lt;br /&gt;
*get_value(config_id) - Returns a value.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  This is passed around the system.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(Configuration, [ CGI_object | params_hash ]) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(Configuration) - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  This can be used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_stats(params_object, query_params_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in query params hash.  The query params hash can contain the following key/value pairs&lt;br /&gt;
**columns =&amp;gt; column_name_array - Which columns are we interested in?&lt;br /&gt;
**order =&amp;gt; column_name - A hash containing a column name and directions (ASC or DESC)&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
**where =&amp;gt; where_hash_array - if additional logic needs to be applied, this array contains hashes containing a column name, an operator and a value.  These are ANDed together.&lt;br /&gt;
*check_tables() - If any IRStats tables are missing, this function will create them.&lt;br /&gt;
*insert_main_table_row(column_array) - inserts the values in the array into the main table (taking into account any tables that contain only IDs).&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.  This is the only point where sql is sent to the database.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
A date object was implemented because there were some specific things that needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*difference(date_object) - returns the difference in days between itself and another date.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both IRStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.  It is intended that savvy users create their own views.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers IRStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing, making it ideal for a quick walkthrough:&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package IRStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use IRStats::DatabaseInterface;&lt;br /&gt;
 use IRStats::Cache;&lt;br /&gt;
 use IRStats::Visualisation::HTML;&lt;br /&gt;
 use IRStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ IRStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = IRStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = IRStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;irstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = IRStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4354</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4354"/>
		<updated>2007-05-30T18:55:11Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the irstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in irstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cache ==&lt;br /&gt;
Contains cache files.  These should probably be deleted whenever the database is updated.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a IRStats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cfg ==&lt;br /&gt;
&lt;br /&gt;
Where the configuration file and the text files containing repository data are held.&lt;br /&gt;
&lt;br /&gt;
=== The Configuration File ===&lt;br /&gt;
&lt;br /&gt;
irstats.cfg contains a number of configuration strings.  Here are some of the more important ones, with the default in brackets:&lt;br /&gt;
&lt;br /&gt;
*configuration_path (/opt/irstats/cfg/) - The path of the configuration directory.&lt;br /&gt;
*view_path (/opt/irstats/perl_lib/IRStats/View/) - The directory containing the Views.&lt;br /&gt;
*cache_path (/opt/irstats/cache/) - The directory in which to store cache files.&lt;br /&gt;
*graph_path (/opt/irstats/img/graphs/) - The directory in which to store graph images.&lt;br /&gt;
*graph_relative_url_path (/img/graphs/) - The url of the directory in which the graph file is from the point of view of the web browser.&lt;br /&gt;
*update_lock_filename (/opt/irstats/bin/.lock) - The name of the file that is created to prevent the update process running twice concurrently&lt;br /&gt;
*The names of the files used to store set information&lt;br /&gt;
**set_member_full_citations_file (/opt/irstats/cfg/irstats_set_member_full_citations.txt)&lt;br /&gt;
**set_member_short_citations_file (/opt/irstats/cfg/irstats_set_member_short_citations.txt)&lt;br /&gt;
**set_membership_file (/opt/irstats/cfg/irstats_set_membership.txt)&lt;br /&gt;
**set_member_codes_file (/opt/irstats/cfg/irstats_set_member_codes.txt)&lt;br /&gt;
**set_member_urls_file (/opt/irstats/cfg/irstats_set_member_urls.txt)&lt;br /&gt;
*Referrer Scope Labels (note, if you change these, you should also change them in the database)&lt;br /&gt;
**referrer_scope_1 (Internal)&lt;br /&gt;
**referrer_scope_2 (ECS)&lt;br /&gt;
**referrer_scope_3 (Search)&lt;br /&gt;
**referrer_scope_4 (External)&lt;br /&gt;
**referrer_scope_no_referrer (None)&lt;br /&gt;
*awstats_search_engines (/usr/local/awstats/wwwroot/cgi-bin/lib/search_engines.pm) - The path to the awstats search engine module&lt;br /&gt;
*repeats_filter_file (/opt/irstats/bin/repeatscache) - The file to maintain state between updates&lt;br /&gt;
*repeats_filter_timeout (86400) - repeat timeout in seconds (the amount of time there needs to be between two hits for them both to be recorded, initially set to 60*60*24)&lt;br /&gt;
&lt;br /&gt;
*repository_url = http://eprints.ecs.soton.ac.uk - the path to the repository&lt;br /&gt;
&lt;br /&gt;
*database configuration&lt;br /&gt;
**database_driver (mysql)&lt;br /&gt;
**database_server (localhost)&lt;br /&gt;
**database_name&lt;br /&gt;
**database_user&lt;br /&gt;
**database_password&lt;br /&gt;
&lt;br /&gt;
*database_id_columns ([ requester_organisation, requester_host, referrer_scope, search_engine, search_terms, referring_entity_id ]) - The columns in the database that have a UID rather than data.  These need seperate tables in which to store the data.&lt;br /&gt;
&lt;br /&gt;
*Various table names and parts of names&lt;br /&gt;
**database_eprints_access_log_table (accesslog) ##Perhaps remove after update rewrite.&lt;br /&gt;
**database_main_stats_table (irstats_true_accesses_table)&lt;br /&gt;
**database_column_table_prefix (irstats_column_)&lt;br /&gt;
**database_set_table_prefix (irstats_set_)&lt;br /&gt;
**database_set_table_code_suffix (_code)&lt;br /&gt;
**database_set_table_citation_suffix (_citation)&lt;br /&gt;
&lt;br /&gt;
*id_parameters ([ start_date, end_date, eprints, view ]) - the parameters that are used to uniquely identify a view&lt;br /&gt;
*host_lookup_temp_dir (/opt/irstats/bin/convert_hosts_temp_files/) - The directory in which to store temp files for host lookups&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the irstats classes.&lt;br /&gt;
&lt;br /&gt;
= IRStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading IRStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  This is passed around the system.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, IRStats filters by inner joining the irstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both IRStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.  It is intended that savvy users create their own views.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers IRStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing, making it ideal for a quick walkthrough:&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package IRStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use IRStats::DatabaseInterface;&lt;br /&gt;
 use IRStats::Cache;&lt;br /&gt;
 use IRStats::Visualisation::HTML;&lt;br /&gt;
 use IRStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ IRStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = IRStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = IRStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;irstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = IRStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4353</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4353"/>
		<updated>2007-05-30T18:28:58Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Directory Structure */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the irstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in irstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cache ==&lt;br /&gt;
Contains cache files.  These should probably be deleted whenever the database is updated.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a IRStats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the irstats classes.&lt;br /&gt;
&lt;br /&gt;
= IRStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading IRStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, IRStats filters by inner joining the irstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both IRStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.  It is intended that savvy users create their own views.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers IRStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing, making it ideal for a quick walkthrough:&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package IRStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use IRStats::DatabaseInterface;&lt;br /&gt;
 use IRStats::Cache;&lt;br /&gt;
 use IRStats::Visualisation::HTML;&lt;br /&gt;
 use IRStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ IRStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = IRStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = IRStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;irstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = IRStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4352</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4352"/>
		<updated>2007-05-30T18:25:13Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the irstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in irstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a IRStats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/irstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the irstats classes.&lt;br /&gt;
&lt;br /&gt;
= IRStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading IRStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, IRStats filters by inner joining the irstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both IRStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.  It is intended that savvy users create their own views.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers IRStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing, making it ideal for a quick walkthrough:&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package IRStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use IRStats::DatabaseInterface;&lt;br /&gt;
 use IRStats::Cache;&lt;br /&gt;
 use IRStats::Visualisation::HTML;&lt;br /&gt;
 use IRStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ IRStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = IRStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = IRStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;irstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = IRStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4350</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4350"/>
		<updated>2007-05-30T18:22:52Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: IRS - EPStats Technical Documentation moved to IRStats Technical Documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.  It is intended that savvy users create their own views.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers EPStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing, making it ideal for a quick walkthrough:&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package EPStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use EPStats::DatabaseInterface;&lt;br /&gt;
 use EPStats::Cache;&lt;br /&gt;
 use EPStats::Visualisation::HTML;&lt;br /&gt;
 use EPStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ EPStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = EPStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = EPStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;epstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = EPStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRS_-_EPStats_Technical_Documentation&amp;diff=4351</id>
		<title>IRS - EPStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRS_-_EPStats_Technical_Documentation&amp;diff=4351"/>
		<updated>2007-05-30T18:22:52Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: IRS - EPStats Technical Documentation moved to IRStats Technical Documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#redirect [[IRStats Technical Documentation]]&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4349</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4349"/>
		<updated>2007-05-30T18:01:50Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Installing IRStats */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
A Visualisation takes a set of processed statistics and outputs them.  For example, Visualisation::Graph::Pie creates a pie chart.&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
The Database Interface object handles all queries to the database.  Most requests for statistics can be completed with a single call to the get_stats($params) method.&lt;br /&gt;
&lt;br /&gt;
=== Data Flow Diagram ===&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;br /&gt;
&lt;br /&gt;
== Required Data ==&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to run, it requires two things:&lt;br /&gt;
&lt;br /&gt;
* a database table containing all hits to the repository&lt;br /&gt;
* text files describing the contents of the repository&lt;br /&gt;
&lt;br /&gt;
=== The Hits Table ===&lt;br /&gt;
&lt;br /&gt;
Awaiting a redevelopment.&lt;br /&gt;
&lt;br /&gt;
=== The Text Files ===&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to build up a picture of a repository, a number of text files need to be created and stored in the cfg/ directory:&lt;br /&gt;
&lt;br /&gt;
* epstats_set_membership.txt&lt;br /&gt;
* epstats_set_member_codes.txt&lt;br /&gt;
* epstats_set_member_full_citations.txt&lt;br /&gt;
* epstats_set_member_short_citations.txt&lt;br /&gt;
* epstats_set_member_urls.txt&lt;br /&gt;
&lt;br /&gt;
==== Explanation by Example ====&lt;br /&gt;
&lt;br /&gt;
Imagine a very small repository.  Here are its contents:&lt;br /&gt;
&lt;br /&gt;
* eprints&lt;br /&gt;
** (1) The Smells of Cheese&lt;br /&gt;
** (2) The Tastes of Wines&lt;br /&gt;
** (3) The Sounds of Oboes&lt;br /&gt;
* Authors&lt;br /&gt;
** (1) John Smith&lt;br /&gt;
** (2) Harriet Jones&lt;br /&gt;
&lt;br /&gt;
If we then imagine that the following are also true:&lt;br /&gt;
&lt;br /&gt;
* John Smith is credited with being an author of eprints (1) and (2)&lt;br /&gt;
* Harriet Jones is credited with being an author of eprints (2) and (3)&lt;br /&gt;
* All three eprints are the output of a research group named &amp;quot;Senses&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===== Creating sets =====&lt;br /&gt;
&lt;br /&gt;
Sets are groups of eprints, and every eprint is a member of at least one set (the set containing only that eprint).  From the information above, we have three sets.  The eprint set, the author set and the research group set.  We need to add the following to epstats_set_membership.txt (the format is &amp;lt;id&amp;gt;&amp;lt;tab&amp;gt;&amp;lt;csv list of eprint ids&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 author_1        1,2&lt;br /&gt;
 author_2        2,3&lt;br /&gt;
 group_1         1,2,3&lt;br /&gt;
 eprint_1        1&lt;br /&gt;
 eprint_2        2&lt;br /&gt;
 eprint_3        3&lt;br /&gt;
&lt;br /&gt;
===== Giving Sets IDs =====&lt;br /&gt;
&lt;br /&gt;
So, we now have some sets, but we need to give them unique IDs so that we can retrieve stats for these sets.  To do this, we add the following to epstats_set_member_codes.txt:&lt;br /&gt;
&lt;br /&gt;
 author_1        js&lt;br /&gt;
 author_2        hj&lt;br /&gt;
 group_1         senses&lt;br /&gt;
 eprint_1        1&lt;br /&gt;
 eprint_2        2&lt;br /&gt;
 eprint_3        3&lt;br /&gt;
&lt;br /&gt;
IRStats now assigns the following unique IDs to each set: author_js, author_hj, group_senses, eprint_1, eprint_2, eprint_3.  Note that the IDs should probably be kept alphanumeric, and must be unique within a class of sets (but you can have author_hj, group_hj and eprint_hj).&lt;br /&gt;
&lt;br /&gt;
===== Citations =====&lt;br /&gt;
&lt;br /&gt;
IRStats uses two citations for each set member, one short and one long.  Which you use depends on how you would like your visualisation to look.  However, we do need to add these to the citations files:&lt;br /&gt;
&lt;br /&gt;
epstats_set_member_short_citations.txt&lt;br /&gt;
 author_1         Smith&lt;br /&gt;
&lt;br /&gt;
epstats_set_member_full_citations.txt&lt;br /&gt;
 author_1         Dr John Smith, PhD&lt;br /&gt;
&lt;br /&gt;
Note that the above examples are only for author_1.  It would be exactly the same for any set member.&lt;br /&gt;
&lt;br /&gt;
===== URLs =====&lt;br /&gt;
&lt;br /&gt;
Although URLs are not currently implemented, it is probably a good idea to include this information (in epstats_set_member_urls.txt) for future functionality.&lt;br /&gt;
&lt;br /&gt;
 author_1         http://homepage.john.smith.com/&lt;br /&gt;
&lt;br /&gt;
== Installing IRStats ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dependencies ===&lt;br /&gt;
&lt;br /&gt;
==== Logfile::EPrints ====&lt;br /&gt;
&lt;br /&gt;
The Logfile::Eprints modules are used to assist in filtering the raw access log.  They can be installed from CPAN.&lt;br /&gt;
&lt;br /&gt;
==== AWStats ====&lt;br /&gt;
&lt;br /&gt;
AWStats data is used to filter out webspiders and classify search engines.  The irstats.cfg must have an entry showing where the correct perl modules are.&lt;br /&gt;
&lt;br /&gt;
==== Geo::IP ====&lt;br /&gt;
&lt;br /&gt;
Geo::IP is used to fill in country and organisation information.  The country database is free, but if you want organisation information, you will have to purchase a subscription for their database.  The location of the database should also be inserted into irstats.cfg.&lt;br /&gt;
&lt;br /&gt;
Note: The pure perl version of Geo::IP does not support organisations.&lt;br /&gt;
&lt;br /&gt;
=== Installing ===&lt;br /&gt;
&lt;br /&gt;
=== Customising ===&lt;br /&gt;
&lt;br /&gt;
It will almost always be necessary to perform some customisation on IRStats because every repository is different.&lt;br /&gt;
&lt;br /&gt;
==== Updating the Table ====&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4348</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4348"/>
		<updated>2007-05-30T17:59:11Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Installing IRStats */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
A Visualisation takes a set of processed statistics and outputs them.  For example, Visualisation::Graph::Pie creates a pie chart.&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
The Database Interface object handles all queries to the database.  Most requests for statistics can be completed with a single call to the get_stats($params) method.&lt;br /&gt;
&lt;br /&gt;
=== Data Flow Diagram ===&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;br /&gt;
&lt;br /&gt;
== Required Data ==&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to run, it requires two things:&lt;br /&gt;
&lt;br /&gt;
* a database table containing all hits to the repository&lt;br /&gt;
* text files describing the contents of the repository&lt;br /&gt;
&lt;br /&gt;
=== The Hits Table ===&lt;br /&gt;
&lt;br /&gt;
Awaiting a redevelopment.&lt;br /&gt;
&lt;br /&gt;
=== The Text Files ===&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to build up a picture of a repository, a number of text files need to be created and stored in the cfg/ directory:&lt;br /&gt;
&lt;br /&gt;
* epstats_set_membership.txt&lt;br /&gt;
* epstats_set_member_codes.txt&lt;br /&gt;
* epstats_set_member_full_citations.txt&lt;br /&gt;
* epstats_set_member_short_citations.txt&lt;br /&gt;
* epstats_set_member_urls.txt&lt;br /&gt;
&lt;br /&gt;
==== Explanation by Example ====&lt;br /&gt;
&lt;br /&gt;
Imagine a very small repository.  Here are its contents:&lt;br /&gt;
&lt;br /&gt;
* eprints&lt;br /&gt;
** (1) The Smells of Cheese&lt;br /&gt;
** (2) The Tastes of Wines&lt;br /&gt;
** (3) The Sounds of Oboes&lt;br /&gt;
* Authors&lt;br /&gt;
** (1) John Smith&lt;br /&gt;
** (2) Harriet Jones&lt;br /&gt;
&lt;br /&gt;
If we then imagine that the following are also true:&lt;br /&gt;
&lt;br /&gt;
* John Smith is credited with being an author of eprints (1) and (2)&lt;br /&gt;
* Harriet Jones is credited with being an author of eprints (2) and (3)&lt;br /&gt;
* All three eprints are the output of a research group named &amp;quot;Senses&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===== Creating sets =====&lt;br /&gt;
&lt;br /&gt;
Sets are groups of eprints, and every eprint is a member of at least one set (the set containing only that eprint).  From the information above, we have three sets.  The eprint set, the author set and the research group set.  We need to add the following to epstats_set_membership.txt (the format is &amp;lt;id&amp;gt;&amp;lt;tab&amp;gt;&amp;lt;csv list of eprint ids&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 author_1        1,2&lt;br /&gt;
 author_2        2,3&lt;br /&gt;
 group_1         1,2,3&lt;br /&gt;
 eprint_1        1&lt;br /&gt;
 eprint_2        2&lt;br /&gt;
 eprint_3        3&lt;br /&gt;
&lt;br /&gt;
===== Giving Sets IDs =====&lt;br /&gt;
&lt;br /&gt;
So, we now have some sets, but we need to give them unique IDs so that we can retrieve stats for these sets.  To do this, we add the following to epstats_set_member_codes.txt:&lt;br /&gt;
&lt;br /&gt;
 author_1        js&lt;br /&gt;
 author_2        hj&lt;br /&gt;
 group_1         senses&lt;br /&gt;
 eprint_1        1&lt;br /&gt;
 eprint_2        2&lt;br /&gt;
 eprint_3        3&lt;br /&gt;
&lt;br /&gt;
IRStats now assigns the following unique IDs to each set: author_js, author_hj, group_senses, eprint_1, eprint_2, eprint_3.  Note that the IDs should probably be kept alphanumeric, and must be unique within a class of sets (but you can have author_hj, group_hj and eprint_hj).&lt;br /&gt;
&lt;br /&gt;
===== Citations =====&lt;br /&gt;
&lt;br /&gt;
IRStats uses two citations for each set member, one short and one long.  Which you use depends on how you would like your visualisation to look.  However, we do need to add these to the citations files:&lt;br /&gt;
&lt;br /&gt;
epstats_set_member_short_citations.txt&lt;br /&gt;
 author_1         Smith&lt;br /&gt;
&lt;br /&gt;
epstats_set_member_full_citations.txt&lt;br /&gt;
 author_1         Dr John Smith, PhD&lt;br /&gt;
&lt;br /&gt;
Note that the above examples are only for author_1.  It would be exactly the same for any set member.&lt;br /&gt;
&lt;br /&gt;
===== URLs =====&lt;br /&gt;
&lt;br /&gt;
Although URLs are not currently implemented, it is probably a good idea to include this information (in epstats_set_member_urls.txt) for future functionality.&lt;br /&gt;
&lt;br /&gt;
 author_1         http://homepage.john.smith.com/&lt;br /&gt;
&lt;br /&gt;
== Installing IRStats ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Dependencies ===&lt;br /&gt;
&lt;br /&gt;
==== Logfile::EPrints ====&lt;br /&gt;
&lt;br /&gt;
The Logfile::Eprints modules are used to assist in filtering the raw access log.  They can be installed from CPAN.&lt;br /&gt;
&lt;br /&gt;
==== AWStats ====&lt;br /&gt;
&lt;br /&gt;
AWStats data is used to filter out webspiders and classify search engines.  The irstats.cfg must have an entry showing where the correct perl modules are.&lt;br /&gt;
&lt;br /&gt;
==== Geo::IP ====&lt;br /&gt;
&lt;br /&gt;
Geo::IP is used to fill in country and organisation information.  The country database is free, but if you want organisation information, you will have to purchase a subscription for their database.  The location of the database should also be inserted into irstats.cfg.&lt;br /&gt;
&lt;br /&gt;
Note: The pure perl version of Geo::IP does not support organisations.&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4347</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4347"/>
		<updated>2007-05-30T15:13:35Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Explanation by Example */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
A Visualisation takes a set of processed statistics and outputs them.  For example, Visualisation::Graph::Pie creates a pie chart.&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
The Database Interface object handles all queries to the database.  Most requests for statistics can be completed with a single call to the get_stats($params) method.&lt;br /&gt;
&lt;br /&gt;
=== Data Flow Diagram ===&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;br /&gt;
&lt;br /&gt;
== Required Data ==&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to run, it requires two things:&lt;br /&gt;
&lt;br /&gt;
* a database table containing all hits to the repository&lt;br /&gt;
* text files describing the contents of the repository&lt;br /&gt;
&lt;br /&gt;
=== The Hits Table ===&lt;br /&gt;
&lt;br /&gt;
Awaiting a redevelopment.&lt;br /&gt;
&lt;br /&gt;
=== The Text Files ===&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to build up a picture of a repository, a number of text files need to be created and stored in the cfg/ directory:&lt;br /&gt;
&lt;br /&gt;
* epstats_set_membership.txt&lt;br /&gt;
* epstats_set_member_codes.txt&lt;br /&gt;
* epstats_set_member_full_citations.txt&lt;br /&gt;
* epstats_set_member_short_citations.txt&lt;br /&gt;
* epstats_set_member_urls.txt&lt;br /&gt;
&lt;br /&gt;
==== Explanation by Example ====&lt;br /&gt;
&lt;br /&gt;
Imagine a very small repository.  Here are its contents:&lt;br /&gt;
&lt;br /&gt;
* eprints&lt;br /&gt;
** (1) The Smells of Cheese&lt;br /&gt;
** (2) The Tastes of Wines&lt;br /&gt;
** (3) The Sounds of Oboes&lt;br /&gt;
* Authors&lt;br /&gt;
** (1) John Smith&lt;br /&gt;
** (2) Harriet Jones&lt;br /&gt;
&lt;br /&gt;
If we then imagine that the following are also true:&lt;br /&gt;
&lt;br /&gt;
* John Smith is credited with being an author of eprints (1) and (2)&lt;br /&gt;
* Harriet Jones is credited with being an author of eprints (2) and (3)&lt;br /&gt;
* All three eprints are the output of a research group named &amp;quot;Senses&amp;quot;&lt;br /&gt;
&lt;br /&gt;
===== Creating sets =====&lt;br /&gt;
&lt;br /&gt;
Sets are groups of eprints, and every eprint is a member of at least one set (the set containing only that eprint).  From the information above, we have three sets.  The eprint set, the author set and the research group set.  We need to add the following to epstats_set_membership.txt (the format is &amp;lt;id&amp;gt;&amp;lt;tab&amp;gt;&amp;lt;csv list of eprint ids&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 author_1        1,2&lt;br /&gt;
 author_2        2,3&lt;br /&gt;
 group_1         1,2,3&lt;br /&gt;
 eprint_1        1&lt;br /&gt;
 eprint_2        2&lt;br /&gt;
 eprint_3        3&lt;br /&gt;
&lt;br /&gt;
===== Giving Sets IDs =====&lt;br /&gt;
&lt;br /&gt;
So, we now have some sets, but we need to give them unique IDs so that we can retrieve stats for these sets.  To do this, we add the following to epstats_set_member_codes.txt:&lt;br /&gt;
&lt;br /&gt;
 author_1        js&lt;br /&gt;
 author_2        hj&lt;br /&gt;
 group_1         senses&lt;br /&gt;
 eprint_1        1&lt;br /&gt;
 eprint_2        2&lt;br /&gt;
 eprint_3        3&lt;br /&gt;
&lt;br /&gt;
IRStats now assigns the following unique IDs to each set: author_js, author_hj, group_senses, eprint_1, eprint_2, eprint_3.  Note that the IDs should probably be kept alphanumeric, and must be unique within a class of sets (but you can have author_hj, group_hj and eprint_hj).&lt;br /&gt;
&lt;br /&gt;
===== Citations =====&lt;br /&gt;
&lt;br /&gt;
IRStats uses two citations for each set member, one short and one long.  Which you use depends on how you would like your visualisation to look.  However, we do need to add these to the citations files:&lt;br /&gt;
&lt;br /&gt;
epstats_set_member_short_citations.txt&lt;br /&gt;
 author_1         Smith&lt;br /&gt;
&lt;br /&gt;
epstats_set_member_full_citations.txt&lt;br /&gt;
 author_1         Dr John Smith, PhD&lt;br /&gt;
&lt;br /&gt;
Note that the above examples are only for author_1.  It would be exactly the same for any set member.&lt;br /&gt;
&lt;br /&gt;
===== URLs =====&lt;br /&gt;
&lt;br /&gt;
Although URLs are not currently implemented, it is probably a good idea to include this information (in epstats_set_member_urls.txt) for future functionality.&lt;br /&gt;
&lt;br /&gt;
 author_1         http://homepage.john.smith.com/&lt;br /&gt;
&lt;br /&gt;
== Installing IRStats ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4346</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4346"/>
		<updated>2007-05-30T13:19:21Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* The Text Files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
A Visualisation takes a set of processed statistics and outputs them.  For example, Visualisation::Graph::Pie creates a pie chart.&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
The Database Interface object handles all queries to the database.  Most requests for statistics can be completed with a single call to the get_stats($params) method.&lt;br /&gt;
&lt;br /&gt;
=== Data Flow Diagram ===&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;br /&gt;
&lt;br /&gt;
== Required Data ==&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to run, it requires two things:&lt;br /&gt;
&lt;br /&gt;
* a database table containing all hits to the repository&lt;br /&gt;
* text files describing the contents of the repository&lt;br /&gt;
&lt;br /&gt;
=== The Hits Table ===&lt;br /&gt;
&lt;br /&gt;
Awaiting a redevelopment.&lt;br /&gt;
&lt;br /&gt;
=== The Text Files ===&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to build up a picture of a repository, a number of text files need to be created and stored in the cfg/ directory:&lt;br /&gt;
&lt;br /&gt;
* epstats_set_membership.txt&lt;br /&gt;
* epstats_set_member_codes.txt&lt;br /&gt;
* epstats_set_member_full_citations.txt&lt;br /&gt;
* epstats_set_member_short_citations.txt&lt;br /&gt;
* epstats_set_member_urls.txt&lt;br /&gt;
&lt;br /&gt;
==== Explanation by Example ====&lt;br /&gt;
&lt;br /&gt;
Imagine a very small repository.  Here are its contents:&lt;br /&gt;
&lt;br /&gt;
* eprints&lt;br /&gt;
** (1) The Smells of Cheese&lt;br /&gt;
** (2) The Tastes of Wines&lt;br /&gt;
** (3) The Sounds of Oboes&lt;br /&gt;
* Authors&lt;br /&gt;
** (1) John Smith&lt;br /&gt;
** (2) Harriet Jones&lt;br /&gt;
&lt;br /&gt;
== Installing IRStats ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4345</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4345"/>
		<updated>2007-05-30T11:49:39Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* The Hits Table */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
A Visualisation takes a set of processed statistics and outputs them.  For example, Visualisation::Graph::Pie creates a pie chart.&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
The Database Interface object handles all queries to the database.  Most requests for statistics can be completed with a single call to the get_stats($params) method.&lt;br /&gt;
&lt;br /&gt;
=== Data Flow Diagram ===&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;br /&gt;
&lt;br /&gt;
== Required Data ==&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to run, it requires two things:&lt;br /&gt;
&lt;br /&gt;
* a database table containing all hits to the repository&lt;br /&gt;
* text files describing the contents of the repository&lt;br /&gt;
&lt;br /&gt;
=== The Hits Table ===&lt;br /&gt;
&lt;br /&gt;
Awaiting a redevelopment.&lt;br /&gt;
&lt;br /&gt;
=== The Text Files ===&lt;br /&gt;
&lt;br /&gt;
== Installing IRStats ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4344</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4344"/>
		<updated>2007-05-30T10:34:29Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Required Data */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
A Visualisation takes a set of processed statistics and outputs them.  For example, Visualisation::Graph::Pie creates a pie chart.&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
The Database Interface object handles all queries to the database.  Most requests for statistics can be completed with a single call to the get_stats($params) method.&lt;br /&gt;
&lt;br /&gt;
=== Data Flow Diagram ===&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;br /&gt;
&lt;br /&gt;
== Required Data ==&lt;br /&gt;
&lt;br /&gt;
In order for IRStats to run, it requires two things:&lt;br /&gt;
&lt;br /&gt;
* a database table containing all hits to the repository&lt;br /&gt;
* text files describing the contents of the repository&lt;br /&gt;
&lt;br /&gt;
=== The Hits Table ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== The Text Files ===&lt;br /&gt;
&lt;br /&gt;
== Installing IRStats ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4343</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4343"/>
		<updated>2007-05-30T10:25:57Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
A Visualisation takes a set of processed statistics and outputs them.  For example, Visualisation::Graph::Pie creates a pie chart.&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
The Database Interface object handles all queries to the database.  Most requests for statistics can be completed with a single call to the get_stats($params) method.&lt;br /&gt;
&lt;br /&gt;
=== Data Flow Diagram ===&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;br /&gt;
&lt;br /&gt;
== Required Data ==&lt;br /&gt;
&lt;br /&gt;
== Installing IRStats ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4342</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4342"/>
		<updated>2007-05-30T10:20:18Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
A Visualisation takes a set of processed statistics and outputs them.  For example, Visualisation::Graph::Pie creates a pie chart.&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
The Database Interface object handles all queries to the database.  Most requests for statistics can be completed with a single call to the get_stats($params) method.&lt;br /&gt;
&lt;br /&gt;
=== Data Flow Diagram ===&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4341</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4341"/>
		<updated>2007-05-30T10:14:54Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.  They have been designed to be user configurable, though some knowledge of perl is probably required.  When a query is made to IRStats, a View is created.  It generates some parameters for the DatabaseInterface object, which queries the database and passes back the results of the query.  The View then iterates over the database rows and processes the stats in any way programmatically possible.  These processed results are then passed to a Visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4340</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4340"/>
		<updated>2007-05-30T10:10:37Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Set ====&lt;br /&gt;
&lt;br /&gt;
As well as defining a daterange, we also have to inform IRStats of which publications we are interested in.  Any publication not in the set will be ignored.  A set of eprints can either be a single eprint or any set of eprints the system administrator wishes to define in the config files.&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
The final parameter tells IRStats how we want to process and display the statistics.  This is done by selecting a View.&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
Views are perl modules which plug in to IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4339</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4339"/>
		<updated>2007-05-30T10:04:42Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;IRStats is a flexible statistics package which allows easy processing of accesses to fulltext and abstract pages of eprints.&lt;br /&gt;
&lt;br /&gt;
== Technical Overview ==&lt;br /&gt;
&lt;br /&gt;
The following is a quick tour of IRStats.&lt;br /&gt;
&lt;br /&gt;
=== Parameters ===&lt;br /&gt;
&lt;br /&gt;
IRStats output depends on four parameters, which need to be passed as cgi parameters if called through a web browser, or in a hash if called through the Perl API.  These are:&lt;br /&gt;
&lt;br /&gt;
==== Start Date and End Date ====&lt;br /&gt;
&lt;br /&gt;
Date parameters are implemented as separate day, month and year parameters, so these two parameters are actually six (start_day, start_month, start_year, end_day, end_month, end_year).  Any statistics outside this daterange are ignored.&lt;br /&gt;
&lt;br /&gt;
==== An Eprint Sets ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== View ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
=== Visualisations ===&lt;br /&gt;
&lt;br /&gt;
=== The Database Interface ===&lt;br /&gt;
&lt;br /&gt;
[[Image:irstats_overview.png]]&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4338</id>
		<title>IRStats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats&amp;diff=4338"/>
		<updated>2007-05-30T09:46:34Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Image:irstats_overview.png]]&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=File:Irstats_overview.png&amp;diff=4337</id>
		<title>File:Irstats overview.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=File:Irstats_overview.png&amp;diff=4337"/>
		<updated>2007-05-30T09:46:00Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: A system overview of IRStats, showing how data flows through the system.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A system overview of IRStats, showing how data flows through the system.&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4297</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4297"/>
		<updated>2007-05-21T16:04:52Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: Epstats moved to IRS - EPStats Technical Documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.  It is intended that savvy users create their own views.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers EPStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing, making it ideal for a quick walkthrough:&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package EPStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use EPStats::DatabaseInterface;&lt;br /&gt;
 use EPStats::Cache;&lt;br /&gt;
 use EPStats::Visualisation::HTML;&lt;br /&gt;
 use EPStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ EPStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = EPStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = EPStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;epstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = EPStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=Epstats&amp;diff=4298</id>
		<title>Epstats</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=Epstats&amp;diff=4298"/>
		<updated>2007-05-21T16:04:52Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: Epstats moved to IRS - EPStats Technical Documentation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#redirect [[IRS - EPStats Technical Documentation]]&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4212</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4212"/>
		<updated>2007-03-29T21:49:37Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* View */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.  It is intended that savvy users create their own views.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers EPStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing, making it ideal for a quick walkthrough:&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package EPStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use EPStats::DatabaseInterface;&lt;br /&gt;
 use EPStats::Cache;&lt;br /&gt;
 use EPStats::Visualisation::HTML;&lt;br /&gt;
 use EPStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ EPStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = EPStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = EPStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;epstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = EPStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4211</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4211"/>
		<updated>2007-03-29T21:48:39Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* View::FullTextCountHTML */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers EPStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing, making it ideal for a quick walkthrough:&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package EPStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use EPStats::DatabaseInterface;&lt;br /&gt;
 use EPStats::Cache;&lt;br /&gt;
 use EPStats::Visualisation::HTML;&lt;br /&gt;
 use EPStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ EPStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = EPStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = EPStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;epstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = EPStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4210</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4210"/>
		<updated>2007-03-29T21:46:16Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* View::FullTextCountHTML */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers EPStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing.&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package EPStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use EPStats::DatabaseInterface;&lt;br /&gt;
 use EPStats::Cache;&lt;br /&gt;
 use EPStats::Visualisation::HTML;&lt;br /&gt;
 use EPStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ EPStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = EPStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = EPStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;epstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
=== Using Periods ===&lt;br /&gt;
&lt;br /&gt;
If we wanted to break our daterange into periods, we'd need to do something like this:&lt;br /&gt;
&lt;br /&gt;
 my $periods = EPStats::Periods-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;{'start_date'},$self-&amp;gt;{'params'}-&amp;gt;{'end_date'});&lt;br /&gt;
 foreach my $period ( @{$periods-&amp;gt;calandar_months()} )&lt;br /&gt;
 {&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;mask($period);&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
    );&lt;br /&gt;
    $self-&amp;gt;{'params'}-&amp;gt;unmask();&lt;br /&gt;
    #process and put into variables&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4209</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4209"/>
		<updated>2007-03-29T21:40:47Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* View::FullTextCountHTML */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers EPStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing.&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package EPStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use EPStats::DatabaseInterface;&lt;br /&gt;
 use EPStats::Cache;&lt;br /&gt;
 use EPStats::Visualisation::HTML;&lt;br /&gt;
 use EPStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ EPStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = EPStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = EPStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;epstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4208</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4208"/>
		<updated>2007-03-29T21:40:19Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* View::FullTextCountHTML */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers EPStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing.&lt;br /&gt;
&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package EPStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use EPStats::DatabaseInterface;&lt;br /&gt;
 use EPStats::Cache;&lt;br /&gt;
 use EPStats::Visualisation::HTML;&lt;br /&gt;
 use EPStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ EPStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = EPStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = EPStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;epstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4207</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4207"/>
		<updated>2007-03-29T21:40:01Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* View */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers EPStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing.&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package EPStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use EPStats::DatabaseInterface;&lt;br /&gt;
 use EPStats::Cache;&lt;br /&gt;
 use EPStats::Visualisation::HTML;&lt;br /&gt;
 use EPStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ EPStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = EPStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
    my $cache = EPStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
Next, we have to retreive from the database:&lt;br /&gt;
&lt;br /&gt;
    my $query = $self-&amp;gt;{'database'}-&amp;gt;get_stats(&lt;br /&gt;
            $self-&amp;gt;{'params'},&lt;br /&gt;
            $self-&amp;gt;{'sql_columns'},&lt;br /&gt;
            $self-&amp;gt;{'sql_params'}&lt;br /&gt;
            );&lt;br /&gt;
Now we process them.  In this case, we don't even need a loop as we know there's only going to be one row.  We'll stick the result straight into some html, and save it.  Don't forget that if there isn't any data, you still have to output something.&lt;br /&gt;
    my @row = $query-&amp;gt;fetchrow_array();&lt;br /&gt;
    my $html = '&amp;lt;span class=&amp;quot;epstats_view_fulltextcounthtml&amp;quot;&amp;gt;' . ($row[1] ? $row[1] : '0') . &amp;quot;&amp;lt;/span&amp;gt;&amp;quot;;&lt;br /&gt;
A little housekeeping:&lt;br /&gt;
    $query-&amp;gt;finish();&lt;br /&gt;
Pop the data into the visualisation:&lt;br /&gt;
    $self-&amp;gt;{'visualisation'}-&amp;gt;set('html',$html);&lt;br /&gt;
Finally, we should write to the cache so we don't have to query the database next time.&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
And that's a really simple view.&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4206</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4206"/>
		<updated>2007-03-29T21:34:17Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* View */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
A view processes the stats data filtered by the parameters and creates a visualisation.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All views inherit:&lt;br /&gt;
*new(params_obj, database_interface_object) - returns the object.&lt;br /&gt;
*render - calls populate, then returns whatever the visualisation renders&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*new - passes arguments to superclass, then calls 'initialise'.&lt;br /&gt;
*initialise - the Configuration Constants are set here.&lt;br /&gt;
*populate - The engine that powers EPStats.&lt;br /&gt;
&lt;br /&gt;
=== View::FullTextCountHTML ===&lt;br /&gt;
The FullTextCountHTML is an extremely simple view.  It retrieves one row from the database and does no processing.&lt;br /&gt;
&lt;br /&gt;
==== Housekeeping ====&lt;br /&gt;
At the top of the file, we need:&lt;br /&gt;
 package EPStats::View::FullTextCountHTML;&lt;br /&gt;
 use strict;&lt;br /&gt;
 use warnings;&lt;br /&gt;
Now, which modules will we use.  I've included perchardir, the graph making package, even though we're not using it.&lt;br /&gt;
 use EPStats::DatabaseInterface;&lt;br /&gt;
 use EPStats::Cache;&lt;br /&gt;
 use EPStats::Visualisation::HTML;&lt;br /&gt;
 use EPStats::View;&lt;br /&gt;
 use perlchartdir;&lt;br /&gt;
And link to superclass. &lt;br /&gt;
 our @ISA = qw/ EPStats::View /;&lt;br /&gt;
&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
We are interested in retreiving the fulltxt column, and a count as we will be aggregating.  The sql_params are set, so that we can filter on fulltext downloads, and we need to group as we are counting.  We also create our visualisation here.&lt;br /&gt;
 sub initialise&lt;br /&gt;
 {&lt;br /&gt;
        my ($self) = @_;&lt;br /&gt;
        $self-&amp;gt;{'sql_columns'} = [ 'fulltxt', 'COUNT(fulltxt)' ];&lt;br /&gt;
        $self-&amp;gt;{'sql_params'} = {where =&amp;gt; &amp;quot;fulltxt = 'F'&amp;quot;, group_by =&amp;gt; 'fulltxt'};&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = EPStats::Visualisation::HTML-&amp;gt;new();&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== new ====&lt;br /&gt;
The new function shouldn't ever need to be any different from this:&lt;br /&gt;
 sub new&lt;br /&gt;
 {&lt;br /&gt;
        my( $class, $params, $database ) = @_;&lt;br /&gt;
        my $self = $class-&amp;gt;SUPER::new($params, $database);;&lt;br /&gt;
        $self-&amp;gt;initialise();&lt;br /&gt;
        return $self;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
==== populate ====&lt;br /&gt;
Every populate function should start by checking the cache, and finish by writing to the cache.&lt;br /&gt;
&lt;br /&gt;
 sub populate&lt;br /&gt;
 {&lt;br /&gt;
    my ($self) = @_;&lt;br /&gt;
&lt;br /&gt;
    my $cache = EPStats::Cache-&amp;gt;new($self-&amp;gt;{'params'}-&amp;gt;get('id'));&lt;br /&gt;
    if ($cache-&amp;gt;exists)&lt;br /&gt;
    {&lt;br /&gt;
        $self-&amp;gt;{'visualisation'} = $cache-&amp;gt;read();&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
 .&lt;br /&gt;
 .&lt;br /&gt;
 .&lt;br /&gt;
    $cache-&amp;gt;write($self-&amp;gt;{'visualisation'});&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4205</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4205"/>
		<updated>2007-03-29T21:12:13Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Visualisation::Graph */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.  The Graph object initialised the colours that the graph may be using.&lt;br /&gt;
&lt;br /&gt;
Every graph must be created with at least the filename:&lt;br /&gt;
*new({filename =&amp;gt; string}) - the filename comes from the ID of the param object.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
=== Sub Classes ===&lt;br /&gt;
Note that in the Visualisation/Graph/ directory, there is 'GraphLegend.pm'.  This is used to create the html for the graph legends.&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Bar.pm====&lt;br /&gt;
A Bar Graph.  It can have one or more bars in each division of the x axis.&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each set of bars&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Line.pm====&lt;br /&gt;
A Line Graph.  There can be many lines on it&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('x_title',string) - The title of the x axis.&lt;br /&gt;
*set('y_title',string) - The title of the y axis.&lt;br /&gt;
*set('x_labels',array_ref) - an array containing the labels for the x axis&lt;br /&gt;
*set('data_series, array_ref) - an array of arrayrefs, referencing data for each line&lt;br /&gt;
&lt;br /&gt;
====Visualisation::Graph::Pie.pm====&lt;br /&gt;
A Pie Graph&lt;br /&gt;
&lt;br /&gt;
To implement:&lt;br /&gt;
*set('title',string) - The title that will be in the graph image.&lt;br /&gt;
*set('data_series, array_ref) - an array of hashrefs, {data =&amp;gt; int, citation =&amp;gt; string}, one for each slice&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4204</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4204"/>
		<updated>2007-03-29T20:45:23Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Configuration Constants */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
*$graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
*$url_relative - this will have the filename added to the end and put in the img html tag.&lt;br /&gt;
&lt;br /&gt;
To Populate&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4203</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4203"/>
		<updated>2007-03-29T20:44:53Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Visualisation::Graph */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;br /&gt;
The graph objects all use Chart Director to generate graphs.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
These are set in the 'new' function.&lt;br /&gt;
    my $graph_dir - the path to the directory where the image file will be saved.&lt;br /&gt;
    my $url_relative - this will have the filename added to the end and put in the img html tag.&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4202</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4202"/>
		<updated>2007-03-29T20:34:55Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* isualisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== Visualisation ==&lt;br /&gt;
Currently Visualisations are Graph, Table or HTML.  These are what the user will look at in the broswer or download (in the case of CSV).&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
All visualisations inherit:&lt;br /&gt;
*new(data_hash) - a hash can optionally be passed containing the values that would otherwise be set using the 'set' function.&lt;br /&gt;
*set(param_name, value) - sets something to something - see subclasses&lt;br /&gt;
&lt;br /&gt;
All visualisations must implement:&lt;br /&gt;
*render() - returns what will be passed to the script.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Visualisation::HTML ==&lt;br /&gt;
The simplest visualisation.  Just a chunk of html.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('html', html_string) - takes the html as a string.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Table ==&lt;br /&gt;
The Visualisation::Table currently just passes the buck to its superclass.&lt;br /&gt;
&lt;br /&gt;
There are currently three table Visualisations:&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::CSV ===&lt;br /&gt;
Returns a CSV table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('headings', headings_arrayref) - pass an array containing headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML ===&lt;br /&gt;
A basic HTML table.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
And then optionally&lt;br /&gt;
*set('totals', totals_arrayref) - an array of totals to put at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
=== Visualisation::Table::HTML_Columned ===&lt;br /&gt;
An HTML table that is rendered in several columns.&lt;br /&gt;
==== Configuration Constants ====&lt;br /&gt;
$default_number_of_rows - an int representing the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
==== Overridden Functions ====&lt;br /&gt;
*new(data_hash, number_of_rows) - Both data_hash and number_of_rows are optional.  Both can be set with 'set'.&lt;br /&gt;
&lt;br /&gt;
To Populate:&lt;br /&gt;
*set('columns', headings_arrayref) - pass an array containing column headings.&lt;br /&gt;
*set('rows', rows_arrayref) - an array of arrayrefs, each referencing a row of data.&lt;br /&gt;
*set('number_of_rows', int) - set the maximum number of rows the table should have.&lt;br /&gt;
&lt;br /&gt;
== Visualisation::Graph ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4201</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4201"/>
		<updated>2007-03-29T20:10:14Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* UserInterface::Controls */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daughter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== isualisation ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4200</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4200"/>
		<updated>2007-03-29T20:10:03Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* UserInterface::Controls */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
This is used to generate the drop boxes in the stats cgi script.  If I had more time I'd document it fully, but my daugter's going to be born in less than 12 hours.&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== isualisation ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4199</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4199"/>
		<updated>2007-03-29T20:08:57Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Periods */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
The Periods object is used when you want to break a daterange down into sub-ranges.  Used with the params-&amp;gt;mask() function, stats can be retrieved for periods inside a date range.&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(start_date_obj, end_date_obj) - doesn't do anything, just returns the object.&lt;br /&gt;
&lt;br /&gt;
The following functions all return an array of hashes.  Each hash has the keys 'start_date' and 'end_date', and the values are both EPStats::Date objects.&lt;br /&gt;
&lt;br /&gt;
*calandar_months - Returns full months (each element starts on the 1st, and ends on the last day).&lt;br /&gt;
*months - Returns month periods (if the start_date is the 15th, then each period starts on the 15th and ends on the 14th of the next month - except the last period, which only has about a 1/30 chance of doing so).&lt;br /&gt;
*weeks - returns 7-day periods (except the last, which has a 1/7 chance of being 7 days long).&lt;br /&gt;
*days - returns single days (for each period, the start_date and end_date are the same).&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== isualisation ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4198</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4198"/>
		<updated>2007-03-29T20:00:41Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
*exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
*write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
*read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== isualisation ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4197</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4197"/>
		<updated>2007-03-29T20:00:26Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Cache */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
The interface to the cache.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cache_directory - a string containing a path to the directory in which the cache files are located.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
new(id) - takes the ID of the params object we're using at the moment.&lt;br /&gt;
exists() - returns true if there's a cached file, false if there isn't one.&lt;br /&gt;
write(visualisation_object) - writes the data to the cache file.&lt;br /&gt;
read() - returns the data from the cache.&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== isualisation ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4196</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4196"/>
		<updated>2007-03-29T19:55:12Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Date */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Cache ==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Periods ==&lt;br /&gt;
&lt;br /&gt;
== UserInterface::Controls ==&lt;br /&gt;
&lt;br /&gt;
== Page (depricated) ==&lt;br /&gt;
Harkens back to the day when a page object contained views.&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
&lt;br /&gt;
== isualisation ==&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4195</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4195"/>
		<updated>2007-03-29T19:52:33Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Date */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
*less_than(date_object) - compares itself to another date object.  Returns 1 if it's less than it, otherwise returns 0.&lt;br /&gt;
*greater_than(date_object) - compares itself to another date object.  Returns 1 if it's greater than it, otherwise returns 0.&lt;br /&gt;
*month_name() - returns the three letter string of the month.&lt;br /&gt;
*render(format_string) - returns a date string.  Format can be:&lt;br /&gt;
**'short' - Calls render_abbreviated - returns a date like this: 05-Jul-77&lt;br /&gt;
**'long' - Calls render_full (not implemented).&lt;br /&gt;
**'numerical' (default) - Calls render_numerical - returns a date like this: 19770705&lt;br /&gt;
*clone - returns an new, identical date object.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Cache&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Periods&lt;br /&gt;
&lt;br /&gt;
UserInterface::Controls&lt;br /&gt;
&lt;br /&gt;
Page (depricated)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
View&lt;br /&gt;
View.pm&lt;br /&gt;
Visualisation&lt;br /&gt;
Visualisation.pm&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4194</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4194"/>
		<updated>2007-03-29T19:45:00Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* Date */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
I implemented a date object because there were some specific things I needed to do with dates.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
*new(date_hash) - Creates a new date object when passed a hash with the keys 'day', 'month' and 'year'.&lt;br /&gt;
*validate() - If the date is not valid, it will be modified to a sensible value.  E.G. if it's Feb 30th, it will be modified to Feb 29th or 28th, dependant on if it's a leap year.&lt;br /&gt;
*set(part_name, int) - Sets part of the date ('year','month' or 'day') to a specific value.&lt;br /&gt;
*decrement(period) - increments the date by a period ('day', 'week', 'month', 'quarter', 'year').  Calls the mod_date function, which does the muscle work.&lt;br /&gt;
*increment(period) - decrements by calling mod_date.&lt;br /&gt;
*part(part_name, style - Returns the day, month or year.  For month, if style=='text', returns a three letter string, otherwise returns an integer.  For year, if style=='short', returns the last two digits, otherwise returns all four.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Cache&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Periods&lt;br /&gt;
&lt;br /&gt;
UserInterface::Controls&lt;br /&gt;
&lt;br /&gt;
Page (depricated)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
View&lt;br /&gt;
View.pm&lt;br /&gt;
Visualisation&lt;br /&gt;
Visualisation.pm&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4193</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4193"/>
		<updated>2007-03-29T19:30:26Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* DatabaseInterface */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;IMPORTANT&amp;lt;/strong&amp;gt; - the mysql generated has been developed on a machine running mysql 5.  Installing on the EPrints server has broken this (as it's running mysql 4).  I placed a quick and dirty hack into the do_sql function, and modified the create_top_table function.  I have no idea if this works well.  &amp;lt;strong&amp;gt;IT NEEDS TO BE CHECKED&amp;lt;/strong&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
&lt;br /&gt;
Cache&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Periods&lt;br /&gt;
&lt;br /&gt;
UserInterface::Controls&lt;br /&gt;
&lt;br /&gt;
Page (depricated)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
View&lt;br /&gt;
View.pm&lt;br /&gt;
Visualisation&lt;br /&gt;
Visualisation.pm&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
	<entry>
		<id>https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4192</id>
		<title>IRStats Technical Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.eprints.org/w/index.php?title=IRStats_Technical_Documentation&amp;diff=4192"/>
		<updated>2007-03-29T19:27:48Z</updated>

		<summary type="html">&lt;p&gt;Gobfrey: /* EPStats Classes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This document is intended as guidance to the last stage of development of EPstats.&lt;br /&gt;
&lt;br /&gt;
= Directory Structure =&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats ==&lt;br /&gt;
Contains data files for GeoIP.  If I had had root access, I would have put them in the correct place.  They are linked to from the correct place.  These need regular updating, something which hasn't been implemented.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/bin ==&lt;br /&gt;
Contains the scripts needed to update the table.&lt;br /&gt;
&lt;br /&gt;
*daily_update.sh - Runs all the scripts in the right order.&lt;br /&gt;
*extract_metadata_from_archive.pl - Extracts eprint, author and group metadata from the repository by iterating over every eprint.&lt;br /&gt;
*update_table.pl - Filters and processes new entries in the accesslog to update the epstats_true_acesses_table.  Uses 'SearchParser.pm' and 'repeatscache'.&lt;br /&gt;
* convert_ip_to_host.pl - Attempts to convert ip addresses of the new entries in epstats_true_acesses_table to hostnames.  Uses 'host_updated' to keep track of where it got to last time.&lt;br /&gt;
&lt;br /&gt;
Note that most of these scripts probably need to be tidied up.  They were written in a hurry and were never polished.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cache ==&lt;br /&gt;
Contains cache files.  Feel free to delete these whenever you like.  &lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/cgi ==&lt;br /&gt;
&lt;br /&gt;
Contains two scripts, 'get_view' and 'stats'.&lt;br /&gt;
&lt;br /&gt;
*get_view returns the output of a EPstats::View (see below), which is currently a chunk of html or csv, but could be almost anything.&lt;br /&gt;
*stats is a handy cgi form that passes arguements to get_view&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/img ==&lt;br /&gt;
&lt;br /&gt;
Conceptually, where any images would be kept (e.g. national flags).  At the moment, only the img/graphs directory is used.  This is where generated graphs are stored.&lt;br /&gt;
&lt;br /&gt;
== /opt/epstats/perl_lib ==&lt;br /&gt;
&lt;br /&gt;
Contains all the epstats classes.&lt;br /&gt;
&lt;br /&gt;
= EPStats Classes =&lt;br /&gt;
&lt;br /&gt;
Note that the leading EPStats:: has been left out for brevity.&lt;br /&gt;
&lt;br /&gt;
== Params ==&lt;br /&gt;
This object holds the parameters that are used to generate the statistics.  The most imortant of these are a date range and an eprint set.&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
*$cgi_script - the name of the cgi script (currently unused)&lt;br /&gt;
*$id_params - When generating an ID, which parameters are important.&lt;br /&gt;
*$defaults - Any default parameters you wish to set.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new(CGI_object) - returns new object&lt;br /&gt;
*mask(params_hash) - used when you want to temporarily overwrite parameter(s).  Overwrites values with contents of params_hash.  Overwritten values get pushed onto a stack.&lt;br /&gt;
*unmask - Sets parameters back to how they were before the last mask.&lt;br /&gt;
*generate_cgi - returns a string containing the name of the cgi script, and all parameters, to enable the creation of links. (currently unused)&lt;br /&gt;
*get(param_name) - returns the value of a single parameter.&lt;br /&gt;
*create_id - Uses MD5 to create a unique ID from the id_params (see Constants above).  This is called whenever get('id') is called.&lt;br /&gt;
&lt;br /&gt;
== DatabaseInterface ==&lt;br /&gt;
This object does what it says on the tin.  Any access to the database is done though it.&lt;br /&gt;
&lt;br /&gt;
=== Configuration Constants ===&lt;br /&gt;
Constants are contained in the new function.&lt;br /&gt;
&lt;br /&gt;
*DBI Configuration Constants - $driver, $server, $database, $user, $password are all used to create the connection to the database.&lt;br /&gt;
*source_table - The table in which the stats are stored.&lt;br /&gt;
&lt;br /&gt;
=== Functions ===&lt;br /&gt;
*new() - returns object.&lt;br /&gt;
*retreive_set_names() - returns a list of eprint sets.  Currently 'group' and 'author' are implemented.  This is used to verify cgi input.&lt;br /&gt;
*get_membership(eprint_id, set_name) - For a given eprint ID, which of a named set does it belong to.  For example, we can find out which authors eprint 12614 has by get_membership(12614, 'author').&lt;br /&gt;
*get_citation(id, set, length) - returns a citation.  Every set member (eprint, author, group) has two citations.  short and full.  We only return a short citation if length == 'short'.  So, to get the short citation of a group 3: get_citation(3,'group','short').&lt;br /&gt;
*get_code($id,$set) - UNWRITTEN - Set member have codes.  This how they are identified by the user.  For example author_lac is the member of the author set whose code is lac.  To get the code for group 3: get_code(3,'group').&lt;br /&gt;
&lt;br /&gt;
When retreiving statistics, EPStats filters by inner joining the epstats_true_accesses_table to other tables contining eprint IDs.  Sometimes it has to create these tables.&lt;br /&gt;
&lt;br /&gt;
*create_top_table(param_object) - This creates a table containing the eprint IDs of the top X by fulltext download between two dates.&lt;br /&gt;
*create_list_table(table_name, eprint_ids) - Takes two strings, one the name of the table, the other a space seperated list of eprint IDs.  Creates a temporary table.&lt;br /&gt;
&lt;br /&gt;
The following are the only two functions that actually make calls to the database. &lt;br /&gt;
&lt;br /&gt;
*do_sql(sql_query_string) - takes a string and performs a query, returning the dbi object containing the results.&lt;br /&gt;
*insert_values(table_name, values) - inserts a row of data into a table.&lt;br /&gt;
&lt;br /&gt;
And finally, the meat and potatoes.  The functions that return the statistics we're interested in.&lt;br /&gt;
&lt;br /&gt;
*get_stats(params_object, column_name_list, options_hash) - returns a dbi object containing the stats we are interested.  i.e. the params_object's date range and eprints sets, and only the columns in column_list.  The options hash can contain the following key/value pairs&lt;br /&gt;
**order =&amp;gt; column_name - the column on which to order it.  append with '-' or ' DESC' to order it descending.&lt;br /&gt;
**limit =&amp;gt; int - How many results to return&lt;br /&gt;
**group_by =&amp;gt; column_name - if we need to group by a column.&lt;br /&gt;
get_stats works by examining the 'eprints' parameter and calling one of the following functions:&lt;br /&gt;
**get_list_stats&lt;br /&gt;
**get_top_stats&lt;br /&gt;
**get_set_stats&lt;br /&gt;
**get_all_stats&lt;br /&gt;
These functions generate slightly different mysql queries, and pass them to the do_sql function.&lt;br /&gt;
&lt;br /&gt;
== Date ==&lt;br /&gt;
&lt;br /&gt;
Cache&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Periods&lt;br /&gt;
&lt;br /&gt;
UserInterface::Controls&lt;br /&gt;
&lt;br /&gt;
Page (depricated)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
View&lt;br /&gt;
View.pm&lt;br /&gt;
Visualisation&lt;br /&gt;
Visualisation.pm&lt;/div&gt;</summary>
		<author><name>Gobfrey</name></author>
		
	</entry>
</feed>