Added contact offers.
[readifood.git] / lib / report.php
1 <?php
2
3   if (isset($_POST['show_reports'])) {
4     header(sprintf("Location: http%s://%s/%s/from/%s/to/%s", ($_SERVER['HTTPS']) ? "s" : "", $_SERVER['HTTP_HOST'], $module, $_POST['from'], $_POST['to']));
5     exit;
6   }
7
8   function show_reports_form($from = null, $to = null) {
9     form("noprint standout");
10     echo "<p>Show reports covering the period from ";
11
12     /* Default to last month. */
13     list($y, $m, $d) = explode('-', date('Y-m-d', time()));
14     $latest = "$y-$m-$d";
15     $now = mktime(0, 0, 0, $m, $d, $y);
16     $first = mktime(0, 0, 0, $m, 1, $y);
17     $last = $first - 86400;
18     $date = date('Y-m-d', $last);
19     if (is_null($to)) $to = $date;
20     list($y, $m, $d) = explode('-', $date);
21     $first = mktime(0, 0, 0, $m, 1, $y);
22     if (is_null($from)) $from = date('Y-m-d', $first);
23     $date = $first;
24     for ($i = 0; $i < 2; $i++) {
25       $date -= 86400;
26       list ($y, $m, $d) = explode('-', date('Y-m-d', $date));
27       $date = mktime(0, 0, 0, $m, 1, $y);
28     }
29     $oldest = date('Y-m-d', $date);
30     $then = $date;
31
32     datepicker("from", $from, true, null, false, "to");
33     echo " to ";
34     datepicker("to", $to, true, "from", false);
35
36     submit("show_reports", "Show");
37     end_form();
38   }
39
40   function show_order_report($from, &$order_state_ids) {
41     echo "<h3>Orders by week</h3>\n";
42
43     /* Handle missing weeks at the start of the range. */
44     $dbh = Propel::getConnection();
45     $sth = $dbh->prepare("select yearweek(:from)");
46     $sth->execute(array(':from' => $from));
47     list($first_week) = $sth->fetch();
48     $year_offset = substr($first_week, 0, 4);
49     $week_offset = substr($first_week, 4, 2) - 1;
50
51     $q = new OrderStateQuery;
52     $q->filterById($order_state_ids);
53     $q->withColumn('yearweek(updated)', 'week');
54     $q->withColumn('count(*)', 'count');
55     $q->addGroupByColumn('week')->orderByUpdated();
56     $rows = $q->find();
57     $week = 1;
58     $last_week = 0;
59     $total = 0;
60     echo "<table class=\"report\">\n";
61     foreach ($rows as $row) {
62       /*
63         Convert week of year to date range.
64         Beware that week 201401 comes after 201352.
65       */
66       $yearweek = $row->getWeek();
67       $y = substr($yearweek, 0, 4);
68       $w = substr($yearweek, 4, 2);
69       $week = (($y - $year_offset) * 52) + ($w - $week_offset);
70       $total += $row->getCount();
71       /* Fill in missing weeks. XXX */
72       for ($missing_week = $last_week + 1; $missing_week < $week; $missing_week++) {
73         echo "<tr>\n";
74         printf("  <td align=\"right\">0</td><td>Week %d</td>\n", $missing_week);
75         echo "</tr>\n";
76       }
77       echo "<tr>\n";
78       printf("  <td align=\"right\">%d</td><td>Week %d</td>\n", $row->getCount(), $week);
79       echo "</tr>\n";
80       $last_week = $week;
81     }
82     echo "<tr>\n";
83     echo "  <td align=\"right\" class=\"strong\">$total</td><td class=\"strong\">Total</td>\n";
84     echo "</tr>\n";
85     echo "</table>\n";
86   }
87
88   function show_postcode_report(&$order_ids) {
89     echo "<h3>Orders by postcode</h3>\n";
90
91     /*
92       No regex replace support in MySQL so we'll have to retrieve all records
93       and group the postcodes ourselves.
94     */
95     $q = new OrderQuery;
96     $q->filterById($order_ids);
97     $q->join("Beneficiary");
98     /* No foreign key so we need to list the two tables. */
99     $q->join("Beneficiary.Address");
100     /* Not a FoodOrder column so we need to ask for it explicitly. */
101     $q->withColumn('upper(postcode)', 'postcode');
102     $rows = $q->find();
103
104     $total = 0;
105     $postcodes = array();
106     foreach ($rows as $row) {
107       $postcode = preg_replace('/\s*[0-9][A-Z]+$/', '', trim($row->getPostcode()));
108       if (! $postcode) $postcode = "Unknown";
109       $postcodes[$postcode]++;
110       $total++;
111     }
112     ksort($postcodes);
113
114     echo "<table class=\"report\">\n";
115     foreach ($postcodes as $postcode => $count) {
116       echo "<tr>\n";
117       printf("  <td align=\"right\">%d</td><td>%s</td>\n", $count, htmlspecialchars($postcode));
118       echo "</tr>\n";
119     }
120     echo "<tr>\n";
121     echo "  <td align=\"right\" class=\"strong\">$total</td><td class=\"strong\">Total</td>\n";
122     echo "</tr>\n";
123     echo "</table>\n";
124   }
125
126   function show_contents_report(&$order_ids, $parcel_size, $grand_total) {
127     global $parcel_sizes, $parcel_contents;
128
129     $total = 0;
130     for ($i = count($parcel_sizes); $i < count($parcel_contents); $i++) {
131       $q = new OrderQuery;
132       $q->filterById($order_ids);
133       $q->where(sprintf("parcel & %d", $parcel_size));
134       $q->where(sprintf("parcel & %d", (1 << $i)));
135       $contents = $q->find();
136       $total += count($contents);
137       echo "<tr class=\"small\">\n";
138       printf("  <td align=\"right\">%d</td><td>%s</td>\n", count($contents), htmlspecialchars($parcel_contents[$i]));
139       echo "</tr>\n";
140     }
141
142     /* No special contents. */
143     echo "<tr class=\"small\">\n";
144     printf("  <td align=\"right\">%d</td><td>%s no special contents</td>\n", $grand_total - $total, htmlspecialchars($parcel_sizes[$parcel_size >> 1]));
145     echo "</tr>\n";
146   }
147
148   function show_parcel_report(&$order_ids) {
149     global $parcel_sizes;
150     echo "<h3>Orders by parcel type</h3>\n";
151
152     $q = new OrderQuery;
153     $q->filterById($order_ids);
154     $q->withColumn(sprintf("parcel & %d", (1 << count($parcel_sizes)) - 1), 'size');
155     $q->withColumn('count(*)', 'count');
156     $q->addGroupByColumn('size')->addAscendingOrderByColumn('size');
157     $rows = $q->find();
158     $total = 0;
159     echo "<table class=\"report\">\n";
160     foreach ($rows as $row) {
161       echo "<tr>\n";
162       $requester = get_contact_by_id($row->getRequesterId());
163       printf("  <td align=\"right\">%d</td><td>%s</td>\n", $row->getCount(), htmlspecialchars($parcel_sizes[$row->getSize() >> 1]));
164       $total += $row->getCount();
165       echo "</tr>\n";
166       show_contents_report($order_ids, $row->getSize(), $row->getCount());
167     }
168     echo "<tr>\n";
169     echo "  <td align=\"right\" class=\"strong\">$total</td><td class=\"strong\">Total</td>\n";
170     echo "</tr>\n";
171     echo "</table>\n";
172   }
173
174   function show_beneficiary_report(&$order_ids) {
175     global $parcel_sizes;
176     echo "<h3>Unique beneficiaries</h3>\n";
177
178     $q = new OrderQuery;
179     $q->filterById($order_ids);
180     $q->withColumn(sprintf("parcel & %d", (1 << count($parcel_sizes)) - 1), 'size');
181     $q->withColumn('count(distinct beneficiary_id)', 'count');
182     $q->addGroupByColumn('size')->addAscendingOrderByColumn('size');
183     $rows = $q->find();
184     $total = 0;
185     echo "<table class=\"report\">\n";
186     foreach ($rows as $row) {
187       echo "<tr>\n";
188       /* This is only correct if we assume the sizes are 1 (single), 2 (couple) and 4 (family). */
189       $count = $row->getSize() * $row->getCount();
190       printf("  <td align=\"right\">%d</td><td>%s</td>\n", $row->getCount(), htmlspecialchars($parcel_sizes[$row->getSize() >> 1]));
191       if ($row->getSize() > 1) {
192         echo "<tr class=\"small\">\n";
193         printf("  <td align=\"right\">%d</td><td>individuals%s</td>\n", $count, $row->getSize() > 2 ? ' (estimated)' : '');
194         echo "</tr>\n";
195       }
196       $total += $count;
197       echo "</tr>\n";
198     }
199     echo "<tr>\n";
200     echo "  <td align=\"right\" class=\"strong\">$total</td><td class=\"strong\">Total individuals (estimated)</td>\n";
201     echo "</tr>\n";
202     echo "</table>\n";
203   }
204
205   function show_requester_report(&$order_ids) {
206     echo "<h3>Orders by referrer</h3>\n";
207
208     $q = new OrderQuery;
209     $q->filterById($order_ids);
210     $q->withColumn('count(*)', 'count');
211     $q->groupByRequesterId()->addDescendingOrderByColumn('count');
212     $rows = $q->find();
213     $total = 0;
214     echo "<table class=\"report\">\n";
215     /* XXX Join! */
216     foreach ($rows as $row) {
217       echo "<tr>\n";
218       $requester = get_contact_by_id($row->getRequesterId());
219       printf("  <td align=\"right\">%d</td><td>%s</td>\n", $row->getCount(), htmlspecialchars($requester->getDisplayname()));
220       $total += $row->getCount();
221       echo "</tr>\n";
222     }
223     echo "<tr>\n";
224     echo "  <td align=\"right\" class=\"strong\">$total</td><td class=\"strong\">Total</td>\n";
225     echo "</tr>\n";
226     echo "</table>\n";
227   }
228
229   function show_reports($from, $to) {
230     if (! check_dates('report', $from, $to)) return;
231
232     echo "<p>Showing reports for the period <strong>$from</strong> to <strong>$to</strong>.</p>\n";
233
234     /* Get orders. */
235     $order_ids = array();
236     $order_state_ids = array();
237     /* XXX: Order 51 changed to state delivered in May then updated in June. */
238     $dbh = Propel::getConnection();
239     $sth = $dbh->prepare("select * from OrderState o where updated=(select min(updated) from OrderState where order_id=o.order_id and state & " . $GLOBALS['STATE_DELIVERED'] . ") and updated between '$from' and '$to'");
240     $sth->execute();
241     $order_states = OrderStatePeer::populateObjects($sth);
242     $dups = array();
243     foreach ($order_states as $order_state) {
244       $order_id = $order_state->getOrderId();
245       $order_ids[] = $order_id;
246       if (! $dups[$order_id]) $order_state_ids[] = $order_state->getId();
247       $dups[$order_id] = true;
248     }
249     $q = new OrderQuery;
250     $q->filterById($order_ids);
251
252     if (! count($order_ids)) {
253       echo "<p>No results!</p>\n";
254       return;
255     }
256
257     show_order_report($from, $order_state_ids);
258     show_postcode_report($order_ids);
259     show_parcel_report($order_ids);
260     show_beneficiary_report($order_ids);
261     show_requester_report($order_ids);
262   }
263
264   if (count($parameters)) {
265     if ($parameters[0] == "from") {
266       $from = $parameters[1];
267       if ($parameters[2] == "to") $to = $parameters[3];
268       show_reports($from, $to);
269     }
270   }
271   show_reports_form($from, $to);
272 ?>